06/12 Add features: 1. Vendor/Contractor assignment 2. Period-over-period comparison 3. Trend Alerts (cron)

This commit is contained in:
2026-06-12 16:27:09 -04:00
parent cf22fe87fe
commit 0fe5954794
12 changed files with 447 additions and 2 deletions
+37
View File
@@ -76,6 +76,20 @@
<dt class="col-sm-3">Assigned To</dt>
<dd class="col-sm-9">{{ issue.assigned_user.display_name if issue.assigned_user else '— Unassigned —' }}</dd>
{% if issue.vendor_name %}
<dt class="col-sm-3">Contractor</dt>
<dd class="col-sm-9">
<i class="bi bi-person-gear text-secondary me-1"></i>
<strong>{{ issue.vendor_name }}</strong>
{% if issue.vendor_contact %}
<span class="text-muted ms-2">{{ issue.vendor_contact }}</span>
{% endif %}
{% if issue.vendor_notes %}
<div class="text-muted small mt-1" style="white-space:pre-wrap;">{{ issue.vendor_notes }}</div>
{% endif %}
</dd>
{% endif %}
{% if issue.resolved_at %}
<dt class="col-sm-3">Resolved</dt>
<dd class="col-sm-9">{{ issue.resolved_at.strftime('%Y-%m-%d %H:%M') }}</dd>
@@ -350,6 +364,29 @@
</div>
{% endif %}
</div>
{% if current_user.role in ['admin','director','project_manager'] %}
<hr class="my-3">
<p class="fw-semibold small mb-2">
<i class="bi bi-person-gear me-1 text-secondary"></i>External Contractor
</p>
<div class="mb-2">
{{ form.vendor_name.label(class="form-label small fw-semibold mb-1") }}
{{ form.vendor_name(class="form-control form-control-sm",
placeholder="Contractor or vendor name",
value=issue.vendor_name or '') }}
</div>
<div class="mb-2">
{{ form.vendor_contact.label(class="form-label small fw-semibold mb-1") }}
{{ form.vendor_contact(class="form-control form-control-sm",
placeholder="Phone or email",
value=issue.vendor_contact or '') }}
</div>
<div class="mb-3">
{{ form.vendor_notes.label(class="form-label small fw-semibold mb-1") }}
{{ form.vendor_notes(class="form-control form-control-sm", rows=2,
placeholder="Notes about what the contractor is handling…") }}
</div>
{% endif %}
<button type="submit" class="btn btn-primary w-100">Save Update</button>
</form>
{% if issue.status in ['in_progress', 'resolved'] and current_user.role not in ['customer'] %}
+62
View File
@@ -117,6 +117,68 @@
</div>
</div>
{# ── Facility period-over-period comparison table ── #}
{% if facility_scores %}
<div class="card shadow-sm mb-4">
<div class="card-header bg-light d-flex align-items-center gap-2">
<h6 class="mb-0"><i class="bi bi-building"></i> Facility Score Comparison</h6>
<span class="text-muted small">vs. prior equal-length period</span>
</div>
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle">
<thead class="table-light">
<tr>
<th>Facility</th>
<th class="text-end">Current Period</th>
<th class="text-end">Prior Period</th>
<th class="text-end">Change</th>
<th class="text-end">Inspections</th>
</tr>
</thead>
<tbody>
{% for row in facility_scores %}
<tr>
<td class="fw-semibold">{{ row.name }}</td>
<td class="text-end">
<span class="badge bg-{{ 'success' if row.avg_score >= 90 else 'warning text-dark' if row.avg_score >= 70 else 'danger' }}">
{{ '%.1f'|format(row.avg_score|float) }}%
</span>
</td>
<td class="text-end text-muted">
{% if row.prior_avg is not none %}
{{ '%.1f'|format(row.prior_avg|float) }}%
{% else %}
<span class="text-muted"></span>
{% endif %}
</td>
<td class="text-end">
{% if row.delta is not none %}
{% if row.delta > 0 %}
<span class="badge bg-success-subtle text-success border border-success fw-semibold">
<i class="bi bi-arrow-up-short"></i>+{{ '%.1f'|format(row.delta|float) }}
</span>
{% elif row.delta < 0 %}
<span class="badge bg-danger-subtle text-danger border border-danger fw-semibold">
<i class="bi bi-arrow-down-short"></i>{{ '%.1f'|format(row.delta|float) }}
</span>
{% else %}
<span class="badge bg-secondary-subtle text-secondary border fw-semibold">
<i class="bi bi-dash"></i> 0.0
</span>
{% endif %}
{% else %}
<span class="text-muted small">No prior data</span>
{% endif %}
</td>
<td class="text-end text-muted small">{{ row.count }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{# ── Top inspectors table ── #}
{% if top_inspectors %}
<div class="row mb-4">