Quote the ops templates' HTML attributes so the classes apply
An unquoted attribute value ends at the first whitespace, so ``class=badge bg-danger`` is read by every browser as ``class="badge"`` plus two meaningless attributes. Bootstrap was loading perfectly well; the markup simply never asked for the styles. Across the ops portal that meant buttons rendered as plain links, status badges were invisible (white text on a white background: a failed result showed as nothing at all), the active sidebar item never highlighted, and the flex utility classes did nothing. The buyer portal templates quote their attributes, which is why only the ops portal looked unstyled. 346 attributes across 20 templates, rewritten mechanically with a conservative transform that handles the three shapes in use — a Django tag as the value, a multi-token class list, and a trailing simple conditional (the active-nav highlight) — and leaves anything it does not recognise exactly as it was.
This commit is contained in:
parent
7d7248a23b
commit
db05d6052a
20 changed files with 516 additions and 516 deletions
|
|
@ -1,11 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<meta name=viewport content=width=device-width, initial-scale=1>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}IXG Operations{% endblock %}</title>
|
||||
<link href=https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css rel=stylesheet>
|
||||
<link rel=stylesheet href=https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
<style>
|
||||
body { font-size: .875rem; background: #f5f6fa; }
|
||||
.sidebar { position: fixed; top: 0; bottom: 0; left: 0; z-index: 100; padding: 48px 0 0; width: 240px; background: #1e2a3a; }
|
||||
|
|
@ -32,38 +32,38 @@ body { font-size: .875rem; background: #f5f6fa; }
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<div class=sidebar>
|
||||
<div class=brand>IXG Platform</div>
|
||||
<ul class=nav flex-column>
|
||||
<li class=nav-item><a class=nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %} href={% url 'ops:dashboard' %}><i class=bi bi-speedometer2></i> Dashboard</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'intake' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:intake_list' %}><i class=bi bi-basket></i> Harvest Intakes</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'batch' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:batch_list' %}><i class=bi bi-box-seam></i> Batches</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'qc' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:qc_list' %}><i class=bi bi-clipboard-check></i> QC Results</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'verify' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:qc_verify_list' %}><i class=bi bi-person-check></i> QC Approvals</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'coa' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:coa_review_list' %}><i class=bi bi-file-earmark-medical></i> CoA Reviews</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'eudr' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:eudr_list' %}><i class=bi bi-shield-check></i> EUDR Compliance</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'shipment' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:shipment_list' %}><i class=bi bi-truck></i> Shipments</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'capa' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:capa_list' %}><i class=bi bi-exclamation-triangle></i> CAPA Records</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'document' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:document_list' %}><i class=bi bi-file-earmark-text></i> Documents</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'esg' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:esg_list' %}><i class=bi bi-tree></i> ESG Reports</a></li>
|
||||
<li class=nav-item><a class=nav-link {% if 'guide' in request.resolver_match.url_name %}active{% endif %} href={% url 'ops:guide_index' %}><i class=bi bi-book></i> Help</a></li>
|
||||
<div class="sidebar">
|
||||
<div class="brand">IXG Platform</div>
|
||||
<ul class="nav flex-column">
|
||||
<li class="nav-item"><a class="nav-link {% if request.resolver_match.url_name == 'dashboard' %}active{% endif %}" href="{% url 'ops:dashboard' %}"><i class="bi bi-speedometer2"></i> Dashboard</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'intake' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:intake_list' %}"><i class="bi bi-basket"></i> Harvest Intakes</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'batch' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:batch_list' %}"><i class="bi bi-box-seam"></i> Batches</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'qc' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:qc_list' %}"><i class="bi bi-clipboard-check"></i> QC Results</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'verify' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:qc_verify_list' %}"><i class="bi bi-person-check"></i> QC Approvals</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'coa' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:coa_review_list' %}"><i class="bi bi-file-earmark-medical"></i> CoA Reviews</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'eudr' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:eudr_list' %}"><i class="bi bi-shield-check"></i> EUDR Compliance</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'shipment' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:shipment_list' %}"><i class="bi bi-truck"></i> Shipments</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'capa' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:capa_list' %}"><i class="bi bi-exclamation-triangle"></i> CAPA Records</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'document' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:document_list' %}"><i class="bi bi-file-earmark-text"></i> Documents</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'esg' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:esg_list' %}"><i class="bi bi-tree"></i> ESG Reports</a></li>
|
||||
<li class="nav-item"><a class="nav-link {% if 'guide' in request.resolver_match.url_name %}active{% endif %}" href="{% url 'ops:guide_index' %}"><i class="bi bi-book"></i> Help</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class=main>
|
||||
<div class=topbar>
|
||||
<div class="main">
|
||||
<div class="topbar">
|
||||
<span></span>
|
||||
<div class=d-flex align-items-center gap-3>
|
||||
<a href=/admin/ class=text-decoration-none text-secondary><i class=bi bi-shield-lock></i> Admin</a>
|
||||
<span class=text-secondary>{{ request.user.full_name|default:request.user.email }}</span>
|
||||
<a href={% url 'oidc_logout' %} class=btn btn-sm btn-outline-secondary>Logout</a>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<a href="/admin/" class="text-decoration-none text-secondary"><i class="bi bi-shield-lock"></i> Admin</a>
|
||||
<span class="text-secondary">{{ request.user.full_name|default:request.user.email }}</span>
|
||||
<a href="{% url 'oidc_logout' %}" class="btn btn-sm btn-outline-secondary">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class=content>
|
||||
<div class="content">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src=https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -2,44 +2,44 @@
|
|||
{% block title %}{{ batch.batch_reference }} - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Batch: {{ batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:batch_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:batch_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
<div class=row g-3 mb-4>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<tr><td class=text-secondary>Status</td><td><span class=badge bg-secondary badge-status>{{ batch.get_status_display }}</span></td></tr>
|
||||
<tr><td class=text-secondary>Grade</td><td>{{ batch.grade|default:'Not graded' }}</td></tr>
|
||||
<tr><td class=text-secondary>Weight</td><td>{{ batch.weight_kg|default:'-' }} kg</td></tr>
|
||||
<tr><td class=text-secondary>Origin Facility</td><td>{{ batch.origin_facility }}</td></tr>
|
||||
<tr><td class=text-secondary>Harvest Date</td><td>{{ batch.harvest_date|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Created By</td><td>{% if batch.created_by %}{{ batch.created_by.full_name|default:batch.created_by.email }}{% else %}-{% endif %}</td></tr>
|
||||
<tr><td class=text-secondary>Created At</td><td>{{ batch.created_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr><td class="text-secondary">Status</td><td><span class="badge bg-secondary badge-status">{{ batch.get_status_display }}</span></td></tr>
|
||||
<tr><td class="text-secondary">Grade</td><td>{{ batch.grade|default:'Not graded' }}</td></tr>
|
||||
<tr><td class="text-secondary">Weight</td><td>{{ batch.weight_kg|default:'-' }} kg</td></tr>
|
||||
<tr><td class="text-secondary">Origin Facility</td><td>{{ batch.origin_facility }}</td></tr>
|
||||
<tr><td class="text-secondary">Harvest Date</td><td>{{ batch.harvest_date|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Created By</td><td>{% if batch.created_by %}{{ batch.created_by.full_name|default:batch.created_by.email }}{% else %}-{% endif %}</td></tr>
|
||||
<tr><td class="text-secondary">Created At</td><td>{{ batch.created_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Notes</h6>
|
||||
<p class=mb-0>{{ batch.notes|linebreaks|default:'<span class=text-muted>No notes</span>' }}</p>
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Notes</h6>
|
||||
<p class="mb-0">{{ batch.notes|linebreaks|default:'<span class=text-muted>No notes</span>' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=row g-3 mb-4>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>QC Results ({{ qc_results|length }})</h6>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">QC Results ({{ qc_results|length }})</h6>
|
||||
{% if qc_results %}
|
||||
<table class=table table-sm mb-0>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Result</th><th>FFA%</th><th>Moisture%</th><th>Peroxide</th><th>Date</th></tr></thead>
|
||||
<tbody>
|
||||
{% for r in qc_results %}
|
||||
<tr>
|
||||
<td>{% if r.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td>
|
||||
<td>{% if r.overall_pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td>
|
||||
<td>{{ r.ffa_percentage }}</td>
|
||||
<td>{{ r.moisture_percentage }}</td>
|
||||
<td>{{ r.peroxide_value }}</td>
|
||||
|
|
@ -48,19 +48,19 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}<p class=text-muted mb-0>No QC results</p>{% endif %}
|
||||
{% else %}<p class="text-muted mb-0">No QC results</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Shipments ({{ shipments|length }})</h6>
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Shipments ({{ shipments|length }})</h6>
|
||||
{% if shipments %}
|
||||
<table class=table table-sm mb-0>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Status</th><th>Vessel</th><th>ETD</th><th>ETA</th></tr></thead>
|
||||
<tbody>
|
||||
{% for s in shipments %}
|
||||
<tr>
|
||||
<td><span class=badge bg-secondary badge-status>{{ s.get_status_display }}</span></td>
|
||||
<td><span class="badge bg-secondary badge-status">{{ s.get_status_display }}</span></td>
|
||||
<td>{{ s.vessel_name|default:'-' }}</td>
|
||||
<td>{{ s.etd_nigeria|date:'d/m/Y'|default:'-' }}</td>
|
||||
<td>{{ s.eta_uk|date:'d/m/Y'|default:'-' }}</td>
|
||||
|
|
@ -68,41 +68,41 @@
|
|||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}<p class=text-muted mb-0>No shipments</p>{% endif %}
|
||||
{% else %}<p class="text-muted mb-0">No shipments</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>CAPA Records ({{ capa_records|length }})</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">CAPA Records ({{ capa_records|length }})</h6>
|
||||
{% if capa_records %}
|
||||
<table class=table table-sm mb-0>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Issue</th><th>Status</th><th>Due Date</th></tr></thead>
|
||||
<tbody>
|
||||
{% for c in capa_records %}
|
||||
<tr>
|
||||
<td>{{ c.issue_description|truncatewords:10 }}</td>
|
||||
<td><span class=badge bg-warning text-dark badge-status>{{ c.get_status_display }}</span></td>
|
||||
<td><span class="badge bg-warning text-dark badge-status">{{ c.get_status_display }}</span></td>
|
||||
<td>{{ c.due_date|date:'d/m/Y'|default:'-' }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}<p class=text-muted mb-0>No CAPA records</p>{% endif %}
|
||||
{% else %}<p class="text-muted mb-0">No CAPA records</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Documents ({{ documents|length }})</h6>
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Documents ({{ documents|length }})</h6>
|
||||
{% if documents %}
|
||||
<ul class=list-unstyled mb-0>
|
||||
<ul class="list-unstyled mb-0">
|
||||
{% for d in documents %}
|
||||
<li class=border-bottom pb-1 mb-1><a href="{% url 'ops:document_download' d.id %}" target=_blank class=text-decoration-none><i class=bi bi-file-earmark></i> {{ d.file_name }}</a> <span class=badge bg-light text-dark>{{ d.get_doc_type_display }}</span></li>
|
||||
<li class="border-bottom pb-1 mb-1"><a href="{% url 'ops:document_download' d.id %}" target="_blank" class="text-decoration-none"><i class="bi bi-file-earmark"></i> {{ d.file_name }}</a> <span class="badge bg-light text-dark">{{ d.get_doc_type_display }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}<p class=text-muted mb-0>No documents</p>{% endif %}
|
||||
{% else %}<p class="text-muted mb-0">No documents</p>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,41 +2,41 @@
|
|||
{% block title %}Batches - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Processing Batches</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</div>
|
||||
|
||||
<div class=filter-bar>
|
||||
<form method=get class=row g-2 align-items-end>
|
||||
<div class=col-auto>
|
||||
<input type=text name=q class=form-control form-control-sm placeholder=Search reference... value={{ current_q }}>
|
||||
<div class="filter-bar">
|
||||
<form method="get" class="row g-2 align-items-end">
|
||||
<div class="col-auto">
|
||||
<input type="text" name="q" class="form-control form-control-sm" placeholder="Search reference..." value="{{ current_q }}">
|
||||
</div>
|
||||
<div class=col-auto>
|
||||
<select name=status class=form-select form-select-sm>
|
||||
<option value=>All Statuses</option>
|
||||
<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>
|
||||
<option value="{{ val }}" {% if current_status == val %}selected{% endif %}>{{ label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class=col-auto>
|
||||
<select name=grade class=form-select form-select-sm>
|
||||
<option value=>All Grades</option>
|
||||
<div class="col-auto">
|
||||
<select name="grade" class="form-select form-select-sm">
|
||||
<option value="">All Grades</option>
|
||||
{% for val, label in grade_choices %}
|
||||
<option value={{ val }} {% if current_grade == val %}selected{% endif %}>{{ label }}</option>
|
||||
<option value="{{ val }}" {% if current_grade == 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:batch_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:batch_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Reference</th>
|
||||
|
|
@ -54,29 +54,29 @@
|
|||
<td><strong>{{ b.batch_reference }}</strong></td>
|
||||
<td>{{ b.grade|default:'-' }}</td>
|
||||
<td>{{ b.weight_kg|default:'-' }}</td>
|
||||
<td><span class=badge bg-secondary badge-status>{{ b.get_status_display }}</span></td>
|
||||
<td><span class="badge bg-secondary badge-status">{{ b.get_status_display }}</span></td>
|
||||
<td>{{ b.harvest_date|date:'d/m/Y'|default:'-' }}</td>
|
||||
<td>{{ b.created_at|date:'d/m/Y' }}</td>
|
||||
<td><a href={% url 'ops:batch_detail' b.pk %} class=btn btn-sm btn-outline-primary>View</a></td>
|
||||
<td><a href="{% url 'ops:batch_detail' b.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 batches found</td></tr>
|
||||
<tr><td colspan="7" class="text-muted text-center py-3">No batches found</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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -2,45 +2,45 @@
|
|||
{% block title %}CAPA Detail - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>CAPA Record: {{ record.batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:capa_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:capa_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-8>
|
||||
<div class=stat-card mb-3>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8">
|
||||
<div class="stat-card mb-3">
|
||||
<h6>Issue Description</h6>
|
||||
<p>{{ record.issue_description|linebreaks }}</p>
|
||||
</div>
|
||||
{% if record.root_cause %}
|
||||
<div class=stat-card mb-3>
|
||||
<div class="stat-card mb-3">
|
||||
<h6>Root Cause</h6>
|
||||
<p>{{ record.root_cause|linebreaks }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if record.corrective_action %}
|
||||
<div class=stat-card mb-3>
|
||||
<div class="stat-card mb-3">
|
||||
<h6>Corrective Action</h6>
|
||||
<p>{{ record.corrective_action|linebreaks }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if record.preventive_action %}
|
||||
<div class=stat-card>
|
||||
<div class="stat-card">
|
||||
<h6>Preventive Action</h6>
|
||||
<p>{{ record.preventive_action|linebreaks }}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class=col-md-4>
|
||||
<div class=stat-card>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<tr><td class=text-secondary>Batch</td><td><a href={% url 'ops:batch_detail' record.batch.pk %}>{{ record.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class=text-secondary>Status</td><td><span class=badge {% if record.status == open %}bg-danger{% elif record.status == in_progress %}bg-warning text-dark{% else %}bg-success{% endif %} badge-status>{{ record.get_status_display }}</span></td></tr>
|
||||
<tr><td class=text-secondary>Assigned To</td><td>{{ record.assigned_to|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Due Date</td><td>{{ record.due_date|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Created</td><td>{{ record.created_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
{% if record.closed_at %}<tr><td class=text-secondary>Closed</td><td>{{ record.closed_at|date:'d/m/Y H:i' }}</td></tr>{% endif %}
|
||||
<div class="col-md-4">
|
||||
<div class="stat-card">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr><td class="text-secondary">Batch</td><td><a href="{% url 'ops:batch_detail' record.batch.pk %}">{{ record.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class="text-secondary">Status</td><td><span class="badge" {% if record.status == open %}bg-danger{% elif record.status == in_progress %}bg-warning text-dark{% else %}bg-success{% endif %} badge-status>{{ record.get_status_display }}</span></td></tr>
|
||||
<tr><td class="text-secondary">Assigned To</td><td>{{ record.assigned_to|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Due Date</td><td>{{ record.due_date|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Created</td><td>{{ record.created_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
{% if record.closed_at %}<tr><td class="text-secondary">Closed</td><td>{{ record.closed_at|date:'d/m/Y H:i' }}</td></tr>{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,30 +2,30 @@
|
|||
{% block title %}CAPA Records - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>CAPA Records</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</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>
|
||||
<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>
|
||||
<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:capa_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:capa_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>Batch</th><th>Issue</th><th>Status</th><th>Assigned To</th><th>Due Date</th><th>Created</th><th></th></tr>
|
||||
</thead>
|
||||
|
|
@ -34,24 +34,24 @@
|
|||
<tr>
|
||||
<td><strong>{{ r.batch.batch_reference }}</strong></td>
|
||||
<td>{{ r.issue_description|truncatewords:15 }}</td>
|
||||
<td><span class=badge {% if r.status == open %}bg-danger{% elif r.status == in_progress %}bg-warning text-dark{% else %}bg-success{% endif %} badge-status>{{ r.get_status_display }}</span></td>
|
||||
<td><span class="badge" {% if r.status == open %}bg-danger{% elif r.status == in_progress %}bg-warning text-dark{% else %}bg-success{% endif %} badge-status>{{ r.get_status_display }}</span></td>
|
||||
<td>{{ r.assigned_to|default:'-' }}</td>
|
||||
<td>{{ r.due_date|date:'d/m/Y'|default:'-' }}</td>
|
||||
<td>{{ r.created_at|date:'d/m/Y' }}</td>
|
||||
<td><a href={% url 'ops:capa_detail' r.pk %} class=btn btn-sm btn-outline-primary>View</a></td>
|
||||
<td><a href="{% url 'ops:capa_detail' r.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 CAPA records</td></tr>
|
||||
<tr><td colspan="7" class="text-muted text-center py-3">No CAPA records</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,58 +2,58 @@
|
|||
{% block title %}CoA Review - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>CoA Review: {{ coa.batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:coa_review_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:coa_review_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
{% for m in messages %}<div class="alert alert-{{ m.tags }} py-2">{{ m }}</div>{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-7>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Certificate Details</h6>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<tr><td class=text-secondary>Batch</td><td><a href={% url 'ops:batch_detail' coa.batch.pk %}>{{ coa.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class=text-secondary>Status</td><td><span class=badge {% if coa.status == 'published' %}bg-success{% elif coa.status == 'superseded' %}bg-secondary{% else %}bg-warning text-dark{% endif %}>{{ coa.get_status_display }}</span></td></tr>
|
||||
<tr><td class=text-secondary>Version</td><td>v{{ coa.version }}</td></tr>
|
||||
<tr><td class=text-secondary>Specification</td><td>{{ coa.spec_version|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Issuer</td><td>{{ coa.issuer|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Issue Date</td><td>{{ coa.issue_date|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Quality Approved By</td><td>{% if coa.quality_approved_by %}{{ coa.quality_approved_by.full_name|default:coa.quality_approved_by.email }}{% else %}-{% endif %}</td></tr>
|
||||
<tr><td class=text-secondary>Quality Approved At</td><td>{{ coa.quality_approved_at|date:'d/m/Y H:i'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Released At</td><td>{{ coa.released_at|date:'d/m/Y H:i'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Published At</td><td>{{ coa.published_at|date:'d/m/Y H:i'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>SHA-256</td><td><code>{{ coa.sha256_hash|default:'-' }}</code></td></tr>
|
||||
<tr><td class=text-secondary>Summary PDF</td><td><a href="{% url 'ops:coa_download' coa.id %}" target=_blank class=text-decoration-none><i class=bi bi-file-earmark-pdf></i> {{ coa.file_name }}</a></td></tr>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-7">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Certificate Details</h6>
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr><td class="text-secondary">Batch</td><td><a href="{% url 'ops:batch_detail' coa.batch.pk %}">{{ coa.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class="text-secondary">Status</td><td><span class="badge" {% if coa.status == 'published' %}bg-success{% elif coa.status == 'superseded' %}bg-secondary{% else %}bg-warning text-dark{% endif %}>{{ coa.get_status_display }}</span></td></tr>
|
||||
<tr><td class="text-secondary">Version</td><td>v{{ coa.version }}</td></tr>
|
||||
<tr><td class="text-secondary">Specification</td><td>{{ coa.spec_version|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Issuer</td><td>{{ coa.issuer|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Issue Date</td><td>{{ coa.issue_date|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Quality Approved By</td><td>{% if coa.quality_approved_by %}{{ coa.quality_approved_by.full_name|default:coa.quality_approved_by.email }}{% else %}-{% endif %}</td></tr>
|
||||
<tr><td class="text-secondary">Quality Approved At</td><td>{{ coa.quality_approved_at|date:'d/m/Y H:i'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Released At</td><td>{{ coa.released_at|date:'d/m/Y H:i'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Published At</td><td>{{ coa.published_at|date:'d/m/Y H:i'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">SHA-256</td><td><code>{{ coa.sha256_hash|default:'-' }}</code></td></tr>
|
||||
<tr><td class="text-secondary">Summary PDF</td><td><a href="{% url 'ops:coa_download' coa.id %}" target="_blank" class="text-decoration-none"><i class="bi bi-file-earmark-pdf"></i> {{ coa.file_name }}</a></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-5>
|
||||
<div class=stat-card mb-3>
|
||||
<h6 class=mb-3>Source Documents</h6>
|
||||
<p class=small text-muted>Independent laboratory report (authoritative):</p>
|
||||
<div class="col-md-5">
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class="mb-3">Source Documents</h6>
|
||||
<p class="small text-muted">Independent laboratory report (authoritative):</p>
|
||||
{% if coa.source_document %}
|
||||
<a href="{% url 'ops:document_download' coa.source_document.id %}" target=_blank class=text-decoration-none><i class=bi bi-file-earmark-pdf></i> {{ coa.source_document.file_name }}</a>
|
||||
<a href="{% url 'ops:document_download' coa.source_document.id %}" target="_blank" class="text-decoration-none"><i class="bi bi-file-earmark-pdf"></i> {{ coa.source_document.file_name }}</a>
|
||||
{% else %}
|
||||
<p class=text-muted mb-0>No lab report linked. <a href={% url 'ops:document_list' %}>Upload</a> the original laboratory PDF for the batch.</p>
|
||||
<p class="text-muted mb-0">No lab report linked. <a href="{% url 'ops:document_list' %}">Upload</a> the original laboratory PDF for the batch.</p>
|
||||
{% endif %}
|
||||
{% if lab_documents %}
|
||||
<hr>
|
||||
<p class=small text-muted mb-1>All lab documents for batch:</p>
|
||||
{% for d in lab_documents %}<div><a href="{% url 'ops:document_download' d.id %}" target=_blank class=text-decoration-none><i class=bi bi-file-earmark-pdf></i> {{ d.file_name }}</a></div>{% endfor %}
|
||||
<p class="small text-muted mb-1">All lab documents for batch:</p>
|
||||
{% for d in lab_documents %}<div><a href="{% url 'ops:document_download' d.id %}" target="_blank" class="text-decoration-none"><i class="bi bi-file-earmark-pdf"></i> {{ d.file_name }}</a></div>{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Commercial Release</h6>
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Commercial Release</h6>
|
||||
{% if coa.status == 'pending_approval' %}
|
||||
<form method=post>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name=action value=approve>
|
||||
<p class=text-muted>Publishing exposes the summary PDF to authorised buyers and triggers buyer notification.</p>
|
||||
<button type=submit class="btn btn-success w-100"><i class=bi bi-send></i> Approve & Publish</button>
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<p class="text-muted">Publishing exposes the summary PDF to authorised buyers and triggers buyer notification.</p>
|
||||
<button type="submit" class="btn btn-success w-100"><i class="bi bi-send"></i> Approve & Publish</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p class=text-muted mb-0">Commercial release approved. Buyer document and notification created.</p>
|
||||
|
|
|
|||
|
|
@ -2,30 +2,30 @@
|
|||
{% block title %}CoA Reviews - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Certificate of Analysis Reviews</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</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=>Pending Approval</option>
|
||||
<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="">Pending Approval</option>
|
||||
{% for val, label in status_choices %}
|
||||
<option value={{ val }} {% if current_status == val %}selected{% endif %}>{{ label }}</option>
|
||||
<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:coa_review_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:coa_review_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>Batch</th><th>Version</th><th>Status</th><th>Spec</th><th>Quality Approved</th><th>Issued</th><th></th></tr>
|
||||
</thead>
|
||||
|
|
@ -34,24 +34,24 @@
|
|||
<tr>
|
||||
<td><strong>{{ c.batch.batch_reference }}</strong></td>
|
||||
<td>v{{ c.version }}</td>
|
||||
<td><span class=badge {% if c.status == 'published' %}bg-success{% elif c.status == 'superseded' %}bg-secondary{% else %}bg-warning text-dark{% endif %}>{{ c.get_status_display }}</span></td>
|
||||
<td><span class="badge" {% if c.status == 'published' %}bg-success{% elif c.status == 'superseded' %}bg-secondary{% else %}bg-warning text-dark{% endif %}>{{ c.get_status_display }}</span></td>
|
||||
<td>{{ c.spec_version|default:'-' }}</td>
|
||||
<td>{{ c.quality_approved_by.full_name|default:'-' }}</td>
|
||||
<td>{{ c.issue_date|date:'d/m/Y'|default:'-' }}</td>
|
||||
<td><a href={% url 'ops:coa_review_detail' c.pk %} class="btn btn-sm {% if c.status == 'pending_approval' %}btn-primary{% else %}btn-outline-secondary{% endif %}">Review</a></td>
|
||||
<td><a href="{% url 'ops:coa_review_detail' c.pk %}" class="btn btn-sm {% if c.status == 'pending_approval' %}btn-primary{% else %}btn-outline-secondary{% endif %}">Review</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=7 class=text-muted text-center py-3>No CoA records</td></tr>
|
||||
<tr><td colspan="7" class="text-muted text-center py-3">No CoA records</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,69 +2,69 @@
|
|||
{% block title %}Dashboard - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Operations Dashboard</h2>
|
||||
<span class=text-secondary>Total Batches: <strong>{{ total_batches }}</strong></span>
|
||||
<span class="text-secondary">Total Batches: <strong>{{ total_batches }}</strong></span>
|
||||
</div>
|
||||
|
||||
<div class=row g-3 mb-4>
|
||||
<div class=col-md-3>
|
||||
<div class=stat-card>
|
||||
<div class=stat-value text-success>{{ qc_summary.passed }}</div>
|
||||
<div class=stat-label>QC Passed</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value text-success">{{ qc_summary.passed }}</div>
|
||||
<div class="stat-label">QC Passed</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-3>
|
||||
<div class=stat-card>
|
||||
<div class=stat-value text-danger>{{ qc_summary.failed }}</div>
|
||||
<div class=stat-label>QC Failed</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value text-danger">{{ qc_summary.failed }}</div>
|
||||
<div class="stat-label">QC Failed</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-3>
|
||||
<div class=stat-card>
|
||||
<div class=stat-value text-primary>{{ active_shipments }}</div>
|
||||
<div class=stat-label>Active Shipments</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value text-primary">{{ active_shipments }}</div>
|
||||
<div class="stat-label">Active Shipments</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-3>
|
||||
<div class=stat-card>
|
||||
<div class=stat-value text-warning>{{ open_capas }}</div>
|
||||
<div class=stat-label>Open CAPAs</div>
|
||||
<div class="col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-value text-warning">{{ open_capas }}</div>
|
||||
<div class="stat-label">Open CAPAs</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=row g-3 mb-4>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Batches by Status</h6>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Batches by Status</h6>
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
{% for label, count in batches_by_status.items %}
|
||||
<tr>
|
||||
<td><span class=badge bg-secondary badge-status>{{ label }}</span></td>
|
||||
<td class=text-end><strong>{{ count }}</strong></td>
|
||||
<td><span class="badge bg-secondary badge-status">{{ label }}</span></td>
|
||||
<td class="text-end"><strong>{{ count }}</strong></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td class=text-muted>No batches yet</td></tr>
|
||||
<tr><td class="text-muted">No batches yet</td></tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Recent Batches</h6>
|
||||
<table class=table table-sm mb-0>
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Recent Batches</h6>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Reference</th><th>Status</th><th>Grade</th><th>Created</th></tr></thead>
|
||||
<tbody>
|
||||
{% for b in recent_batches %}
|
||||
<tr>
|
||||
<td><a href={% url 'ops:batch_detail' b.pk %}>{{ b.batch_reference }}</a></td>
|
||||
<td><span class=badge bg-secondary badge-status>{{ b.get_status_display }}</span></td>
|
||||
<td><a href="{% url 'ops:batch_detail' b.pk %}">{{ b.batch_reference }}</a></td>
|
||||
<td><span class="badge bg-secondary badge-status">{{ b.get_status_display }}</span></td>
|
||||
<td>{{ b.grade|default:'-' }}</td>
|
||||
<td>{{ b.created_at|date:'d/m/Y' }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=4 class=text-muted>No batches yet</td></tr>
|
||||
<tr><td colspan="4" class="text-muted">No batches yet</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
@ -72,37 +72,37 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Recent QC Results</h6>
|
||||
<table class=table table-sm mb-0>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Recent QC Results</h6>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Batch</th><th>Result</th><th>FFA%</th><th>Date</th></tr></thead>
|
||||
<tbody>
|
||||
{% for r in recent_qc %}
|
||||
<tr>
|
||||
<td><a href={% url 'ops:qc_detail' r.pk %}>{{ r.batch.batch_reference }}</a></td>
|
||||
<td>{% if r.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td>
|
||||
<td><a href="{% url 'ops:qc_detail' r.pk %}">{{ r.batch.batch_reference }}</a></td>
|
||||
<td>{% if r.overall_pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td>
|
||||
<td>{{ r.ffa_percentage }}</td>
|
||||
<td>{{ r.tested_at|date:'d/m/Y' }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=4 class=text-muted>No QC results yet</td></tr>
|
||||
<tr><td colspan="4" class="text-muted">No QC results yet</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Unread Notifications</h6>
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Unread Notifications</h6>
|
||||
{% for n in recent_notifications %}
|
||||
<div class=border-bottom pb-2 mb-2>
|
||||
<div class="border-bottom pb-2 mb-2">
|
||||
<strong>{{ n.title }}</strong>
|
||||
<p class=mb-0 text-muted small>{{ n.message|truncatewords:20 }}</p>
|
||||
<p class="mb-0 text-muted small">{{ n.message|truncatewords:20 }}</p>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class=text-muted mb-0>No unread notifications</p>
|
||||
<p class="text-muted mb-0">No unread notifications</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,55 +2,55 @@
|
|||
{% block title %}Documents - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Documents</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</div>
|
||||
|
||||
<div class=filter-bar>
|
||||
<form method=get class=row g-2 align-items-end>
|
||||
<div class=col-auto>
|
||||
<select name=doc_type class=form-select form-select-sm>
|
||||
<option value=>All Types</option>
|
||||
<div class="filter-bar">
|
||||
<form method="get" class="row g-2 align-items-end">
|
||||
<div class="col-auto">
|
||||
<select name="doc_type" class="form-select form-select-sm">
|
||||
<option value="">All Types</option>
|
||||
{% for val, label in doc_type_choices %}
|
||||
<option value={{ val }} {% if current_doc_type == val %}selected{% endif %}>{{ label }}</option>
|
||||
<option value="{{ val }}" {% if current_doc_type == 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:document_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:document_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>File Name</th><th>Type</th><th>Batch</th><th>Visibility</th><th>Uploaded By</th><th>Uploaded</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for d in documents %}
|
||||
<tr>
|
||||
<td><a href="{% url 'ops:document_download' d.id %}" target=_blank class=text-decoration-none><i class=bi bi-file-earmark></i> {{ d.file_name }}</a></td>
|
||||
<td><span class=badge bg-light text-dark>{{ d.get_doc_type_display }}</span></td>
|
||||
<td><a href="{% url 'ops:document_download' d.id %}" target="_blank" class="text-decoration-none"><i class="bi bi-file-earmark"></i> {{ d.file_name }}</a></td>
|
||||
<td><span class="badge bg-light text-dark">{{ d.get_doc_type_display }}</span></td>
|
||||
<td>{{ d.batch.batch_reference|default:'-' }}</td>
|
||||
<td><span class=badge bg-info text-dark>{{ d.get_visibility_display }}</span></td>
|
||||
<td><span class="badge bg-info text-dark">{{ d.get_visibility_display }}</span></td>
|
||||
<td>{% if d.uploaded_by %}{{ d.uploaded_by.full_name|default:d.uploaded_by.email }}{% else %}-{% endif %}</td>
|
||||
<td>{{ d.uploaded_at|date:'d/m/Y' }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=6 class=text-muted text-center py-3>No documents</td></tr>
|
||||
<tr><td colspan="6" class="text-muted text-center py-3">No documents</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
{% block title %}ESG Reports - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>ESG Field Reports</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</div>
|
||||
|
||||
<div class=stat-card p-0>
|
||||
<table class=table table-hover mb-0>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>Batch</th><th>Women Employed</th><th>Total Workers</th><th>Fair Wage</th><th>Safety Equipment</th><th>Report Date</th><th>Reported By</th></tr>
|
||||
</thead>
|
||||
|
|
@ -18,23 +18,23 @@
|
|||
<td><strong>{{ r.batch.batch_reference }}</strong></td>
|
||||
<td>{{ r.women_employed }}</td>
|
||||
<td>{{ r.total_workers }}</td>
|
||||
<td>{% if r.fair_wage_paid %}<span class=badge bg-success>Yes</span>{% else %}<span class=badge bg-danger>No</span>{% endif %}</td>
|
||||
<td>{% if r.safety_equipment_used %}<span class=badge bg-success>Yes</span>{% else %}<span class=badge bg-danger>No</span>{% endif %}</td>
|
||||
<td>{% if r.fair_wage_paid %}<span class="badge bg-success">Yes</span>{% else %}<span class="badge bg-danger">No</span>{% endif %}</td>
|
||||
<td>{% if r.safety_equipment_used %}<span class="badge bg-success">Yes</span>{% else %}<span class="badge bg-danger">No</span>{% endif %}</td>
|
||||
<td>{{ r.report_date|date:'d/m/Y'|default:'-' }}</td>
|
||||
<td>{{ r.reported_by|default:'-' }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=7 class=text-muted text-center py-3>No ESG reports</td></tr>
|
||||
<tr><td colspan="7" class="text-muted text-center py-3">No ESG reports</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,41 +2,41 @@
|
|||
{% block title %}EUDR Case - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>EUDR Case: {{ case.batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:eudr_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:eudr_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
{% for m in messages %}<div class="alert alert-{{ m.tags }} py-2">{{ m }}</div>{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-8>
|
||||
<div class=stat-card mb-3>
|
||||
<h6 class=mb-3>Completeness Check</h6>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-8">
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class="mb-3">Completeness Check</h6>
|
||||
{% if case.completeness_results %}
|
||||
<table class=table table-sm mb-0>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Check</th><th>Status</th><th>Detail</th></tr></thead>
|
||||
<tbody>
|
||||
{% for key, item in case.completeness_results.items %}
|
||||
<tr>
|
||||
<td>{{ key }}</td>
|
||||
<td>{% if item.pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td>
|
||||
<td>{% if item.pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td>
|
||||
<td>{{ item.detail }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class=text-muted mb-0>Not yet run.</p>
|
||||
<p class="text-muted mb-0">Not yet run.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class=stat-card mb-3>
|
||||
<h6 class=mb-3>Intake Lineage</h6>
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class="mb-3">Intake Lineage</h6>
|
||||
{% if lineage %}
|
||||
<table class=table table-sm mb-0>
|
||||
<table class="table table-sm mb-0">
|
||||
<thead><tr><th>Intake</th><th>Date</th><th>Weight (kg)</th><th>Zone</th><th>Zone Status</th><th>Collector</th></tr></thead>
|
||||
<tbody>
|
||||
{% for link in lineage %}
|
||||
|
|
@ -45,94 +45,94 @@
|
|||
<td>{{ link.harvest_intake.intake_date|date:'d/m/Y'|default:'-' }}</td>
|
||||
<td>{% if link.weight_allocated_kg %}{{ link.weight_allocated_kg }}{% elif link.harvest_intake %}{{ link.harvest_intake.weight_kg }}{% else %}-{% endif %}</td>
|
||||
<td>{{ link.harvest_intake.origin_zone.zone_code|default:'-' }}</td>
|
||||
<td>{% if link.harvest_intake.origin_zone %}<span class=badge {% if link.harvest_intake.origin_zone.verification_status == 'verified' %}bg-success{% elif link.harvest_intake.origin_zone.verification_status == 'rejected' %}bg-danger{% else %}bg-warning text-dark{% endif %}>{{ link.harvest_intake.origin_zone.get_verification_status_display }}</span>{% else %}-{% endif %}</td>
|
||||
<td>{% if link.harvest_intake.origin_zone %}<span class="badge" {% if link.harvest_intake.origin_zone.verification_status == 'verified' %}bg-success{% elif link.harvest_intake.origin_zone.verification_status == 'rejected' %}bg-danger{% else %}bg-warning text-dark{% endif %}>{{ link.harvest_intake.origin_zone.get_verification_status_display }}</span>{% else %}-{% endif %}</td>
|
||||
<td>{{ link.harvest_intake.collector_group.name|default:'-' }}</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=6 class=text-muted>No intakes linked. Record intake lineage via API/admin.</td></tr>
|
||||
<tr><td colspan="6" class="text-muted">No intakes linked. Record intake lineage via API/admin.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class=text-muted mb-0>No intake lineage recorded for this batch.</p>
|
||||
<p class="text-muted mb-0">No intake lineage recorded for this batch.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class=stat-card mb-3>
|
||||
<h6 class=mb-3>Risk Assessments</h6>
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class="mb-3">Risk Assessments</h6>
|
||||
{% for r in risk_assessments %}
|
||||
<div class=border-bottom pb-2 mb-2>
|
||||
<span class=badge {% if r.risk_level == 'high' %}bg-danger{% elif r.risk_level == 'low' %}bg-success{% else %}bg-info text-dark{% endif %}>{{ r.get_risk_level_display }}</span>
|
||||
<span class=text-muted small>by {{ r.assessed_by.full_name|default:'-' }} · {{ r.assessed_at|date:'d/m/Y H:i' }}</span>
|
||||
<div class=small mt-1>{{ r.basis }}</div>
|
||||
<div class="border-bottom pb-2 mb-2">
|
||||
<span class="badge" {% if r.risk_level == 'high' %}bg-danger{% elif r.risk_level == 'low' %}bg-success{% else %}bg-info text-dark{% endif %}>{{ r.get_risk_level_display }}</span>
|
||||
<span class="text-muted small">by {{ r.assessed_by.full_name|default:'-' }} · {{ r.assessed_at|date:'d/m/Y H:i' }}</span>
|
||||
<div class="small mt-1">{{ r.basis }}</div>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class=text-muted mb-0>No risk assessment recorded.</p>
|
||||
<p class="text-muted mb-0">No risk assessment recorded.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class=stat-card mb-3>
|
||||
<h6 class=mb-3>Mitigation Actions</h6>
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class="mb-3">Mitigation Actions</h6>
|
||||
{% for m in mitigation_actions %}
|
||||
<div class=border-bottom pb-2 mb-2>
|
||||
<span class=badge {% if m.status == 'verified' %}bg-success{% elif m.status == 'completed' %}bg-primary{% else %}bg-warning text-dark{% endif %}>{{ m.get_status_display }}</span>
|
||||
<span class=small>{{ m.action }} <span class=text-muted>(owner: {{ m.owner|default:'-' }}{% if m.due_date %}, due {{ m.due_date|date:'d/m/Y' }}{% endif %})</span></span>
|
||||
<div class="border-bottom pb-2 mb-2">
|
||||
<span class="badge" {% if m.status == 'verified' %}bg-success{% elif m.status == 'completed' %}bg-primary{% else %}bg-warning text-dark{% endif %}>{{ m.get_status_display }}</span>
|
||||
<span class="small">{{ m.action }} <span class="text-muted">(owner: {{ m.owner|default:'-' }}{% if m.due_date %}, due {{ m.due_date|date:'d/m/Y' }}{% endif %})</span></span>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class=text-muted mb-0>No mitigation actions.</p>
|
||||
<p class="text-muted mb-0">No mitigation actions.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>External Submission (DDS) References</h6>
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">External Submission (DDS) References</h6>
|
||||
{% for s in statement_references %}
|
||||
<div class=border-bottom pb-2 mb-2>
|
||||
<span class=badge bg-secondary>{{ s.reconciliation_status }}</span>
|
||||
<span class=small>Ref: <code>{{ s.dds_reference }}</code> · {{ s.submission_date|date:'d/m/Y'|default:'-' }} · by {{ s.submitted_by.full_name|default:'-' }}</span>
|
||||
<div class="border-bottom pb-2 mb-2">
|
||||
<span class="badge bg-secondary">{{ s.reconciliation_status }}</span>
|
||||
<span class="small">Ref: <code>{{ s.dds_reference }}</code> · {{ s.submission_date|date:'d/m/Y'|default:'-' }} · by {{ s.submitted_by.full_name|default:'-' }}</span>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p class=text-muted mb-0>No external submission recorded. Submissions are manual.</p>
|
||||
<p class="text-muted mb-0">No external submission recorded. Submissions are manual.</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=col-md-4>
|
||||
<div class=stat-card mb-3>
|
||||
<h6 class=mb-3>Case Controls</h6>
|
||||
<form method=post class=mb-2>
|
||||
<div class="col-md-4">
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class="mb-3">Case Controls</h6>
|
||||
<form method="post" class="mb-2">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name=action value=check>
|
||||
<button type=submit class="btn btn-outline-primary w-100"><i class=bi bi-check2-square></i> Run Completeness Check</button>
|
||||
<input type="hidden" name="action" value="check">
|
||||
<button type="submit" class="btn btn-outline-primary w-100"><i class="bi bi-check2-square"></i> Run Completeness Check</button>
|
||||
</form>
|
||||
<form method=post class=mb-2>
|
||||
<form method="post" class="mb-2">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name=action value=assess_risk>
|
||||
<button type=submit class="btn btn-outline-warning w-100"><i class=bi bi-activity></i> Run Risk Assessment</button>
|
||||
<input type="hidden" name="action" value="assess_risk">
|
||||
<button type="submit" class="btn btn-outline-warning w-100"><i class="bi bi-activity"></i> Run Risk Assessment</button>
|
||||
</form>
|
||||
<form method=post class=mb-2>
|
||||
<form method="post" class="mb-2">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name=action value=approve>
|
||||
<button type=submit class="btn btn-success w-100"><i class=bi bi-check-circle></i> Approve Case</button>
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<button type="submit" class="btn btn-success w-100"><i class="bi bi-check-circle"></i> Approve Case</button>
|
||||
</form>
|
||||
<form method=post>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name=action value=reject>
|
||||
<label class=small>Reject reason</label>
|
||||
<input type=text name=reason class="form-control form-control-sm mb-2" required>
|
||||
<button type=submit class="btn btn-outline-danger w-100"><i class=bi bi-x-circle></i> Reject Case</button>
|
||||
<input type="hidden" name="action" value="reject">
|
||||
<label class="small">Reject reason</label>
|
||||
<input type="text" name="reason" class="form-control form-control-sm mb-2" required>
|
||||
<button type="submit" class="btn btn-outline-danger w-100"><i class="bi bi-x-circle"></i> Reject Case</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Record External Submission</h6>
|
||||
<form method=post>
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Record External Submission</h6>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<input type=hidden name=action value=record_submission>
|
||||
<label class=small>DDS reference</label>
|
||||
<input type=text name=dds_reference class="form-control form-control-sm mb-2" placeholder="e.g. DD-2026-00001" required>
|
||||
<label class=small>Submission date</label>
|
||||
<input type=date name=submission_date class="form-control form-control-sm mb-2">
|
||||
<button type=submit class="btn btn-primary w-100"><i class=bi bi-send></i> Record Submission</button>
|
||||
<input type="hidden" name="action" value="record_submission">
|
||||
<label class="small">DDS reference</label>
|
||||
<input type="text" name="dds_reference" class="form-control form-control-sm mb-2" placeholder="e.g. DD-2026-00001" required>
|
||||
<label class="small">Submission date</label>
|
||||
<input type="date" name="submission_date" class="form-control form-control-sm mb-2">
|
||||
<button type="submit" class="btn btn-primary w-100"><i class="bi bi-send"></i> Record Submission</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,35 +2,35 @@
|
|||
{% block title %}EUDR Compliance - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>EUDR Due Diligence Cases</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</div>
|
||||
|
||||
<div class=alert alert-light border py-2 small>
|
||||
<i class=bi bi-info-circle></i> Shea butter is not listed in EUDR Annex I; this tracker provides due-diligence
|
||||
<div class="alert alert-light border py-2 small">
|
||||
<i class="bi bi-info-circle"></i> Shea butter is not listed in EUDR Annex I; this tracker provides due-diligence
|
||||
evidence and buyer assurance, not an EU-mandated filing.
|
||||
</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>
|
||||
<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>
|
||||
<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:eudr_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:eudr_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>Batch</th><th>Status</th><th>Risk Level</th><th>Approved</th><th>Updated</th><th></th></tr>
|
||||
</thead>
|
||||
|
|
@ -38,29 +38,29 @@
|
|||
{% for c in cases %}
|
||||
<tr>
|
||||
<td><strong>{{ c.batch.batch_reference }}</strong></td>
|
||||
<td><span class=badge
|
||||
<td><span class="badge"
|
||||
{% if c.status == 'approved' %}bg-success
|
||||
{% elif c.status == 'rejected' %}bg-danger
|
||||
{% elif c.status == 'complete_for_review' %}bg-primary
|
||||
{% elif c.status == 'mitigation_required' or c.status == 'risk_identified' %}bg-warning text-dark
|
||||
{% else %}bg-secondary{% endif %}>{{ c.get_status_display }}</span></td>
|
||||
<td><span class=badge {% if c.risk_level == 'high' %}bg-danger{% elif c.risk_level == 'low' %}bg-success{% else %}bg-info text-dark{% endif %}>{{ c.get_risk_level_display }}</span></td>
|
||||
<td><span class="badge" {% if c.risk_level == 'high' %}bg-danger{% elif c.risk_level == 'low' %}bg-success{% else %}bg-info text-dark{% endif %}>{{ c.get_risk_level_display }}</span></td>
|
||||
<td>{{ c.approved_at|date:'d/m/Y H:i'|default:'-' }}</td>
|
||||
<td>{{ c.updated_at|date:'d/m/Y' }}</td>
|
||||
<td><a href={% url 'ops:eudr_detail' c.pk %} class=btn btn-sm btn-primary>Review</a></td>
|
||||
<td><a href="{% url 'ops:eudr_detail' c.pk %}" class="btn btn-sm btn-primary">Review</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=6 class=text-muted text-center py-3>No due diligence cases</td></tr>
|
||||
<tr><td colspan="6" class="text-muted text-center py-3">No due diligence cases</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@
|
|||
{% block title %}{{ page_title }} - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>{{ page_title }}</h2>
|
||||
<a href={% url 'ops:intake_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:intake_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-7>
|
||||
<div class=stat-card>
|
||||
<form method=post novalidate id=intake-form data-group-zones="{{ group_zones }}">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-7">
|
||||
<div class="stat-card">
|
||||
<form method="post novalidate" id="intake-form" data-group-zones="{{ group_zones }}">
|
||||
{% csrf_token %}
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
|
|
@ -19,29 +19,29 @@
|
|||
{% endif %}
|
||||
|
||||
{% for field in form %}
|
||||
<div class=mb-3>
|
||||
<label class=form-label for="{{ field.id_for_label }}">
|
||||
{{ field.label }}{% if field.field.required %} <span class=text-danger>*</span>{% endif %}
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="{{ field.id_for_label }}">
|
||||
{{ field.label }}{% if field.field.required %} <span class="text-danger">*</span>{% endif %}
|
||||
</label>
|
||||
{{ field }}
|
||||
{% if field.help_text %}<div class=form-text>{{ field.help_text }}</div>{% endif %}
|
||||
{% if field.help_text %}<div class="form-text">{{ field.help_text }}</div>{% endif %}
|
||||
{% for e in field.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<button type=submit class="btn btn-primary">{{ submit_label }}</button>
|
||||
<a href={% url 'ops:intake_list' %} class="btn btn-outline-secondary">Cancel</a>
|
||||
<button type="submit" class="btn btn-primary">{{ submit_label }}</button>
|
||||
<a href="{% url 'ops:intake_list' %}" class="btn btn-outline-secondary">Cancel</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=col-md-5>
|
||||
<div class="col-md-5">
|
||||
<div class="stat-card mb-3">
|
||||
<h6 class=mb-3><i class=bi bi-info-circle></i> Recording an intake</h6>
|
||||
<h6 class="mb-3"><i class="bi bi-info-circle"></i> Recording an intake</h6>
|
||||
<p class="small mb-2">
|
||||
An intake is the moment a collector group hands over shea at the processing
|
||||
site. It is the record a finished batch is traced back to, so the fields
|
||||
marked <span class=text-danger>*</span> are required: they are the ones the
|
||||
marked <span class="text-danger">*</span> are required: they are the ones the
|
||||
platform's due-diligence checks read.
|
||||
</p>
|
||||
<p class="small mb-0">
|
||||
|
|
@ -51,8 +51,8 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3><i class=bi bi-shield-check></i> What these records feed</h6>
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3"><i class="bi bi-shield-check"></i> What these records feed</h6>
|
||||
<ul class="small mb-0 ps-3">
|
||||
<li><strong>Lineage</strong> — a batch needs at least one intake linked to it</li>
|
||||
<li><strong>Intake data</strong> — a linked intake must have both a date and a weight</li>
|
||||
|
|
|
|||
|
|
@ -6,36 +6,36 @@
|
|||
{% for m in messages %}<div class="alert alert-{{ m.tags }} py-2">{{ m }}</div>{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Harvest Intakes</h2>
|
||||
<div>
|
||||
<span class="text-secondary me-2">{{ page_obj.paginator.count }} total</span>
|
||||
<a href={% url 'ops:intake_create' %} class=btn btn-sm btn-primary><i class=bi bi-plus-lg></i> New intake</a>
|
||||
<a href="{% url 'ops:intake_create' %}" class="btn btn-sm btn-primary"><i class="bi bi-plus-lg"></i> New intake</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=filter-bar>
|
||||
<form method=get class=row g-2 align-items-end>
|
||||
<div class=col-auto>
|
||||
<input type=text name=q class=form-control form-control-sm placeholder=Search reference... value={{ current_q }}>
|
||||
<div class="filter-bar">
|
||||
<form method="get" class="row g-2 align-items-end">
|
||||
<div class="col-auto">
|
||||
<input type="text" name="q" class="form-control form-control-sm" placeholder="Search reference..." value="{{ current_q }}">
|
||||
</div>
|
||||
<div class=col-auto>
|
||||
<select name=status class=form-select form-select-sm>
|
||||
<option value=>All Statuses</option>
|
||||
<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>
|
||||
<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:intake_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:intake_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Reference</th>
|
||||
|
|
@ -64,27 +64,27 @@
|
|||
{% endif %}
|
||||
{% else %}-{% endif %}
|
||||
</td>
|
||||
<td><span class=badge bg-secondary badge-status>{{ i.get_status_display }}</span></td>
|
||||
<td><a href={% url 'ops:intake_update' i.pk %} class=btn btn-sm btn-outline-primary>Correct</a></td>
|
||||
<td><span class="badge bg-secondary badge-status">{{ i.get_status_display }}</span></td>
|
||||
<td><a href="{% url 'ops:intake_update' i.pk %}" class="btn btn-sm btn-outline-primary">Correct</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=8 class=text-muted text-center py-3>No intakes recorded yet</td></tr>
|
||||
<tr><td colspan="8" class="text-muted text-center py-3">No intakes recorded yet</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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -2,24 +2,24 @@
|
|||
{% block title %}QC Detail - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>QC Result: {{ result.batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:qc_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:qc_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<tr><td class=text-secondary>Batch</td><td><a href={% url 'ops:batch_detail' result.batch.pk %}>{{ result.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class=text-secondary>Overall Result</td><td>{% if result.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td></tr>
|
||||
<tr><td class=text-secondary>FFA %</td><td>{{ result.ffa_percentage }}</td></tr>
|
||||
<tr><td class=text-secondary>Moisture %</td><td>{{ result.moisture_percentage }}</td></tr>
|
||||
<tr><td class=text-secondary>Peroxide Value</td><td>{{ result.peroxide_value }}</td></tr>
|
||||
<tr><td class=text-secondary>Visual Grade</td><td>{{ result.visual_grade }}</td></tr>
|
||||
<tr><td class=text-secondary>Tested By</td><td>{{ result.tested_by|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Tested At</td><td>{{ result.tested_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
<tr><td class=text-secondary>Lab Reference</td><td>{{ result.lab_reference|default:'-' }}</td></tr>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr><td class="text-secondary">Batch</td><td><a href="{% url 'ops:batch_detail' result.batch.pk %}">{{ result.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class="text-secondary">Overall Result</td><td>{% if result.overall_pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td></tr>
|
||||
<tr><td class="text-secondary">FFA %</td><td>{{ result.ffa_percentage }}</td></tr>
|
||||
<tr><td class="text-secondary">Moisture %</td><td>{{ result.moisture_percentage }}</td></tr>
|
||||
<tr><td class="text-secondary">Peroxide Value</td><td>{{ result.peroxide_value }}</td></tr>
|
||||
<tr><td class="text-secondary">Visual Grade</td><td>{{ result.visual_grade }}</td></tr>
|
||||
<tr><td class="text-secondary">Tested By</td><td>{{ result.tested_by|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Tested At</td><td>{{ result.tested_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
<tr><td class="text-secondary">Lab Reference</td><td>{{ result.lab_reference|default:'-' }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,29 +2,29 @@
|
|||
{% block title %}QC Results - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>QC Results</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</div>
|
||||
|
||||
<div class=filter-bar>
|
||||
<form method=get class=row g-2 align-items-end>
|
||||
<div class=col-auto>
|
||||
<select name=passed class=form-select form-select-sm>
|
||||
<option value=>All Results</option>
|
||||
<option value=1 {% if current_passed == '1' %}selected{% endif %}>Passed</option>
|
||||
<option value=0 {% if current_passed == '0' %}selected{% endif %}>Failed</option>
|
||||
<div class="filter-bar">
|
||||
<form method="get" class="row g-2 align-items-end">
|
||||
<div class="col-auto">
|
||||
<select name="passed" class="form-select form-select-sm">
|
||||
<option value="">All Results</option>
|
||||
<option value="1" {% if current_passed == '1' %}selected{% endif %}>Passed</option>
|
||||
<option value="0" {% if current_passed == '0' %}selected{% endif %}>Failed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class=col-auto>
|
||||
<button type=submit class=btn btn-sm btn-primary>Filter</button>
|
||||
<a href={% url 'ops:qc_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:qc_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>Batch</th><th>Result</th><th>FFA%</th><th>Moisture%</th><th>Peroxide</th><th>Visual</th><th>Tested</th><th></th></tr>
|
||||
</thead>
|
||||
|
|
@ -32,26 +32,26 @@
|
|||
{% for r in results %}
|
||||
<tr>
|
||||
<td><strong>{{ r.batch.batch_reference }}</strong></td>
|
||||
<td>{% if r.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td>
|
||||
<td>{% if r.overall_pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td>
|
||||
<td>{{ r.ffa_percentage }}</td>
|
||||
<td>{{ r.moisture_percentage }}</td>
|
||||
<td>{{ r.peroxide_value }}</td>
|
||||
<td>{{ r.visual_grade }}</td>
|
||||
<td>{{ r.tested_at|date:'d/m/Y' }}</td>
|
||||
<td><a href={% url 'ops:qc_detail' r.pk %} class=btn btn-sm btn-outline-primary>View</a></td>
|
||||
<td><a href="{% url 'ops:qc_detail' r.pk %}" class="btn btn-sm btn-outline-primary">View</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=8 class=text-muted text-center py-3>No QC results</td></tr>
|
||||
<tr><td colspan="8" class="text-muted text-center py-3">No QC results</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,48 +2,48 @@
|
|||
{% block title %}QC Verification - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>QC Verification: {{ result.batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:qc_verify_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:qc_verify_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
{% if messages %}
|
||||
{% for m in messages %}<div class="alert alert-{{ m.tags }} py-2">{{ m }}</div>{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-7>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Structured QC Result</h6>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<tr><td class=text-secondary>Batch</td><td><a href={% url 'ops:batch_detail' result.batch.pk %}>{{ result.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class=text-secondary>Overall Result</td><td>{% if result.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td></tr>
|
||||
<tr><td class=text-secondary>Specification</td><td>{{ result.spec_version|default:'legacy limits' }}</td></tr>
|
||||
<tr><td class=text-secondary>FFA %</td><td>{{ result.ffa_percentage }} <span class=text-muted>({{ result.method_ffa|default:'no method' }})</span></td></tr>
|
||||
<tr><td class=text-secondary>Moisture %</td><td>{{ result.moisture_percentage }} <span class=text-muted>({{ result.method_moisture|default:'no method' }})</span></td></tr>
|
||||
<tr><td class=text-secondary>Peroxide Value</td><td>{{ result.peroxide_value }} <span class=text-muted>({{ result.method_peroxide|default:'no method' }})</span></td></tr>
|
||||
<tr><td class=text-secondary>Visual Grade</td><td>{{ result.visual_grade }}</td></tr>
|
||||
<tr><td class=text-secondary>Result Status</td><td>{{ result.get_result_status_display }}</td></tr>
|
||||
<tr><td class=text-secondary>Lab Reference</td><td>{{ result.lab_reference|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Tested By</td><td>{{ result.tested_by|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Entered By</td><td>{% if result.entered_by %}{{ result.entered_by.full_name|default:result.entered_by.email }}{% else %}-{% endif %}</td></tr>
|
||||
<tr><td class=text-secondary>Tested At</td><td>{{ result.tested_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-7">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Structured QC Result</h6>
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr><td class="text-secondary">Batch</td><td><a href="{% url 'ops:batch_detail' result.batch.pk %}">{{ result.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class="text-secondary">Overall Result</td><td>{% if result.overall_pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td></tr>
|
||||
<tr><td class="text-secondary">Specification</td><td>{{ result.spec_version|default:'legacy limits' }}</td></tr>
|
||||
<tr><td class="text-secondary">FFA %</td><td>{{ result.ffa_percentage }} <span class="text-muted">({{ result.method_ffa|default:'no method' }})</span></td></tr>
|
||||
<tr><td class="text-secondary">Moisture %</td><td>{{ result.moisture_percentage }} <span class="text-muted">({{ result.method_moisture|default:'no method' }})</span></td></tr>
|
||||
<tr><td class="text-secondary">Peroxide Value</td><td>{{ result.peroxide_value }} <span class="text-muted">({{ result.method_peroxide|default:'no method' }})</span></td></tr>
|
||||
<tr><td class="text-secondary">Visual Grade</td><td>{{ result.visual_grade }}</td></tr>
|
||||
<tr><td class="text-secondary">Result Status</td><td>{{ result.get_result_status_display }}</td></tr>
|
||||
<tr><td class="text-secondary">Lab Reference</td><td>{{ result.lab_reference|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Tested By</td><td>{{ result.tested_by|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Entered By</td><td>{% if result.entered_by %}{{ result.entered_by.full_name|default:result.entered_by.email }}{% else %}-{% endif %}</td></tr>
|
||||
<tr><td class="text-secondary">Tested At</td><td>{{ result.tested_at|date:'d/m/Y H:i' }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class=col-md-5>
|
||||
<div class=stat-card>
|
||||
<h6 class=mb-3>Approval Decision</h6>
|
||||
<div class="col-md-5">
|
||||
<div class="stat-card">
|
||||
<h6 class="mb-3">Approval Decision</h6>
|
||||
{% if result.verification_status == 'draft' %}
|
||||
<form method=post>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<p class=text-muted>This action is recorded as the quality approval. The submitter cannot approve their own result.</p>
|
||||
<button type=submit name=approved value=1 class="btn btn-success w-100 mb-2"><i class=bi bi-check-circle></i> Approve & Generate CoA</button>
|
||||
<button type=submit name=approved value=0 class="btn btn-outline-danger w-100"><i class=bi bi-x-circle></i> Reject</button>
|
||||
<p class="text-muted">This action is recorded as the quality approval. The submitter cannot approve their own result.</p>
|
||||
<button type="submit" name="approved" value="1" class="btn btn-success w-100 mb-2"><i class="bi bi-check-circle"></i> Approve & Generate CoA</button>
|
||||
<button type="submit" name="approved" value="0" class="btn btn-outline-danger w-100"><i class="bi bi-x-circle"></i> Reject</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p><span class=badge {% if result.verification_status == 'approved' %}bg-success{% else %}bg-danger{% endif %}>{{ result.get_verification_status_display }}</span></p>
|
||||
{% if result.verified_by %}<p class=text-muted mb-0>Verified by {{ result.verified_by.full_name|default:result.verified_by.email }} on {{ result.approved_at|date:'d/m/Y H:i' }}</p>{% endif %}
|
||||
<p><span class="badge" {% if result.verification_status == 'approved' %}bg-success{% else %}bg-danger{% endif %}>{{ result.get_verification_status_display }}</span></p>
|
||||
{% if result.verified_by %}<p class="text-muted mb-0">Verified by {{ result.verified_by.full_name|default:result.verified_by.email }} on {{ result.approved_at|date:'d/m/Y H:i' }}</p>{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,29 +2,29 @@
|
|||
{% block title %}QC Approvals - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>QC Approvals (four-eyes verification)</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} pending</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} pending</span>
|
||||
</div>
|
||||
|
||||
<div class=filter-bar>
|
||||
<form method=get class=row g-2 align-items-end>
|
||||
<div class=col-auto>
|
||||
<select name=passed class=form-select form-select-sm>
|
||||
<option value=>All Results</option>
|
||||
<option value=1 {% if current_passed == '1' %}selected{% endif %}>Passed</option>
|
||||
<option value=0 {% if current_passed == '0' %}selected{% endif %}>Failed</option>
|
||||
<div class="filter-bar">
|
||||
<form method="get" class="row g-2 align-items-end">
|
||||
<div class="col-auto">
|
||||
<select name="passed" class="form-select form-select-sm">
|
||||
<option value="">All Results</option>
|
||||
<option value="1" {% if current_passed == '1' %}selected{% endif %}>Passed</option>
|
||||
<option value="0" {% if current_passed == '0' %}selected{% endif %}>Failed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class=col-auto>
|
||||
<button type=submit class=btn btn-sm btn-primary>Filter</button>
|
||||
<a href={% url 'ops:qc_verify_list' %} class=btn btn-sm btn-outline-secondary>Clear</a>
|
||||
<div class="col-auto">
|
||||
<button type="submit" class="btn btn-sm btn-primary">Filter</button>
|
||||
<a href="{% url 'ops:qc_verify_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>
|
||||
<div class="stat-card p-0">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr><th>Batch</th><th>Result</th><th>FFA%</th><th>Moisture%</th><th>Peroxide</th><th>Visual</th><th>Entered By</th><th>Spec</th><th></th></tr>
|
||||
</thead>
|
||||
|
|
@ -32,27 +32,27 @@
|
|||
{% for r in results %}
|
||||
<tr>
|
||||
<td><strong>{{ r.batch.batch_reference }}</strong></td>
|
||||
<td>{% if r.overall_pass %}<span class=badge bg-success>Pass</span>{% else %}<span class=badge bg-danger>Fail</span>{% endif %}</td>
|
||||
<td>{% if r.overall_pass %}<span class="badge bg-success">Pass</span>{% else %}<span class="badge bg-danger">Fail</span>{% endif %}</td>
|
||||
<td>{{ r.ffa_percentage }}</td>
|
||||
<td>{{ r.moisture_percentage }}</td>
|
||||
<td>{{ r.peroxide_value }}</td>
|
||||
<td>{{ r.visual_grade }}</td>
|
||||
<td>{% if r.entered_by %}{{ r.entered_by.full_name|default:r.entered_by.email }}{% else %}-{% endif %}</td>
|
||||
<td>{{ r.spec_version|default:'-' }}</td>
|
||||
<td><a href={% url 'ops:qc_verify_detail' r.pk %} class=btn btn-sm btn-primary>Verify</a></td>
|
||||
<td><a href="{% url 'ops:qc_verify_detail' r.pk %}" class="btn btn-sm btn-primary">Verify</a></td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td colspan=9 class=text-muted text-center py-3>No QC results awaiting verification</td></tr>
|
||||
<tr><td colspan="9" class="text-muted text-center py-3">No QC results awaiting verification</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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,25 +2,25 @@
|
|||
{% block title %}Shipment Detail - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Shipment: {{ shipment.batch.batch_reference }}</h2>
|
||||
<a href={% url 'ops:shipment_list' %} class=btn btn-sm btn-outline-secondary><i class=bi bi-arrow-left></i> Back</a>
|
||||
<a href="{% url 'ops:shipment_list' %}" class="btn btn-sm btn-outline-secondary"><i class="bi bi-arrow-left"></i> Back</a>
|
||||
</div>
|
||||
|
||||
<div class=row g-3>
|
||||
<div class=col-md-6>
|
||||
<div class=stat-card>
|
||||
<table class=table table-sm table-borderless mb-0>
|
||||
<tr><td class=text-secondary>Batch</td><td><a href={% url 'ops:batch_detail' shipment.batch.pk %}>{{ shipment.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class=text-secondary>Status</td><td><span class=badge bg-secondary badge-status>{{ shipment.get_status_display }}</span></td></tr>
|
||||
<tr><td class=text-secondary>Origin Port</td><td>{{ shipment.origin_port }}</td></tr>
|
||||
<tr><td class=text-secondary>Destination Port</td><td>{{ shipment.destination_port }}</td></tr>
|
||||
<tr><td class=text-secondary>Vessel Name</td><td>{{ shipment.vessel_name|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Container Ref</td><td>{{ shipment.container_ref|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>ETD Nigeria</td><td>{{ shipment.etd_nigeria|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>ETA UK</td><td>{{ shipment.eta_uk|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Bill of Lading</td><td>{{ shipment.bl_number|default:'-' }}</td></tr>
|
||||
<tr><td class=text-secondary>Freight Forwarder</td><td>{{ shipment.freight_forwarder|default:'-' }}</td></tr>
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="stat-card">
|
||||
<table class="table table-sm table-borderless mb-0">
|
||||
<tr><td class="text-secondary">Batch</td><td><a href="{% url 'ops:batch_detail' shipment.batch.pk %}">{{ shipment.batch.batch_reference }}</a></td></tr>
|
||||
<tr><td class="text-secondary">Status</td><td><span class="badge bg-secondary badge-status">{{ shipment.get_status_display }}</span></td></tr>
|
||||
<tr><td class="text-secondary">Origin Port</td><td>{{ shipment.origin_port }}</td></tr>
|
||||
<tr><td class="text-secondary">Destination Port</td><td>{{ shipment.destination_port }}</td></tr>
|
||||
<tr><td class="text-secondary">Vessel Name</td><td>{{ shipment.vessel_name|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Container Ref</td><td>{{ shipment.container_ref|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">ETD Nigeria</td><td>{{ shipment.etd_nigeria|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">ETA UK</td><td>{{ shipment.eta_uk|date:'d/m/Y'|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Bill of Lading</td><td>{{ shipment.bl_number|default:'-' }}</td></tr>
|
||||
<tr><td class="text-secondary">Freight Forwarder</td><td>{{ shipment.freight_forwarder|default:'-' }}</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2,30 +2,30 @@
|
|||
{% block title %}Shipments - IXG Operations{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<div class=page-header d-flex justify-content-between align-items-center>
|
||||
<div class="page-header d-flex justify-content-between align-items-center">
|
||||
<h2>Shipments</h2>
|
||||
<span class=text-secondary>{{ page_obj.paginator.count }} total</span>
|
||||
<span class="text-secondary">{{ page_obj.paginator.count }} total</span>
|
||||
</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>
|
||||
<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>
|
||||
<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 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>
|
||||
<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>
|
||||
|
|
@ -33,25 +33,25 @@
|
|||
{% 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><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>
|
||||
<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>
|
||||
<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 %}
|
||||
<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 %}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue