ixg_platform/templates/ops/batch_detail.html
ixgadmin db05d6052a 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.
2026-09-17 13:57:53 +00:00

110 lines
4.6 KiB
HTML

{% extends 'ops/base_ops.html' %}
{% block title %}{{ batch.batch_reference }} - IXG Operations{% endblock %}
{% block content %}
<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>
</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>
</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>
</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>
{% if qc_results %}
<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>{{ r.ffa_percentage }}</td>
<td>{{ r.moisture_percentage }}</td>
<td>{{ r.peroxide_value }}</td>
<td>{{ r.tested_at|date:'d/m/Y' }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% 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>
{% if shipments %}
<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>{{ 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>
</tr>
{% endfor %}
</tbody>
</table>
{% 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>
{% if capa_records %}
<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>{{ 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 %}
</div>
</div>
<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">
{% 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>
{% endfor %}
</ul>
{% else %}<p class="text-muted mb-0">No documents</p>{% endif %}
</div>
</div>
</div>
{% endblock %}