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.
111 lines
3.7 KiB
HTML
111 lines
3.7 KiB
HTML
{% extends 'ops/base_ops.html' %}
|
|
{% block title %}Dashboard - IXG Operations{% endblock %}
|
|
{% block content %}
|
|
|
|
<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>
|
|
</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>
|
|
</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>
|
|
</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">
|
|
{% 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>
|
|
</tr>
|
|
{% empty %}
|
|
<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">
|
|
<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>{{ 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>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</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">
|
|
<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>{{ 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>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<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">
|
|
<strong>{{ n.title }}</strong>
|
|
<p class="mb-0 text-muted small">{{ n.message|truncatewords:20 }}</p>
|
|
</div>
|
|
{% empty %}
|
|
<p class="text-muted mb-0">No unread notifications</p>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock %}
|