Jul 8 - Update scheduled inspection
This commit is contained in:
@@ -11,6 +11,53 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# ── Scheduled inspections: upcoming / overdue (phase36) ─────────────────── #}
|
||||
{% if current_user.role != 'customer' and (sched_upcoming or sched_overdue_count) %}
|
||||
<div class="card shadow-sm mb-4 border-0" style="border-left:4px solid #6366f1 !important;">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<span class="fw-bold"><i class="bi bi-calendar-check text-primary"></i> Scheduled Inspections</span>
|
||||
<a href="{{ url_for('scheduled_inspections.index') }}" class="btn btn-sm btn-outline-primary">View all</a>
|
||||
</div>
|
||||
{% if sched_overdue_count %}
|
||||
<div class="alert alert-danger py-2 mb-2">
|
||||
<i class="bi bi-alarm-fill"></i>
|
||||
<strong>{{ sched_overdue_count }}</strong> scheduled inspection{{ 's' if sched_overdue_count != 1 }}
|
||||
{{ 'are' if sched_overdue_count != 1 else 'is' }} <strong>overdue</strong>.
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if sched_upcoming %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-hover mb-0 align-middle">
|
||||
<thead class="table-light">
|
||||
<tr><th>Facility</th><th>Template</th><th>Inspector</th><th>Due</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for s in sched_upcoming %}
|
||||
<tr>
|
||||
<td>{{ s.facility.name if s.facility else '—' }}</td>
|
||||
<td class="small">{{ s.template.name if s.template else '—' }}</td>
|
||||
<td class="small">{{ s.inspector.display_name if s.inspector else '—' }}</td>
|
||||
<td class="small">{{ s.next_due_date.strftime('%b %d') }}</td>
|
||||
<td class="text-end">
|
||||
{% if current_user.role in ['admin','director','project_manager']
|
||||
or (current_user.role == 'inspector' and s.inspector_id == current_user.id) %}
|
||||
<a href="{{ url_for('scheduled_inspections.start', schedule_id=s.id) }}"
|
||||
class="btn btn-sm btn-success py-0"><i class="bi bi-play-fill"></i> Start</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted small mb-0">No inspections due in the next 7 days.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# ── Inspections section ─────────────────────────────────────────────────── #}
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<i class="bi bi-clipboard-data-fill text-primary"></i>
|
||||
|
||||
@@ -4,9 +4,14 @@
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h2><i class="bi bi-clipboard-data"></i> Inspections</h2>
|
||||
{% if current_user.role != 'customer' %}
|
||||
<a href="{{ url_for('inspections.start') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> New Inspection
|
||||
</a>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ url_for('scheduled_inspections.index') }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-calendar-check"></i> Scheduled
|
||||
</a>
|
||||
<a href="{{ url_for('inspections.start') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> New Inspection
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-7">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light"><h5 class="mb-0">{{ title }}</h5></div>
|
||||
<div class="card-body">
|
||||
<form method="POST" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.facility_id.label(class="form-label fw-semibold") }}
|
||||
{{ form.facility_id(class="form-select") }}
|
||||
{% for e in form.facility_id.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.template_id.label(class="form-label fw-semibold") }}
|
||||
{{ form.template_id(class="form-select") }}
|
||||
{% for e in form.template_id.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.inspector_id.label(class="form-label fw-semibold") }}
|
||||
{{ form.inspector_id(class="form-select") }}
|
||||
{% for e in form.inspector_id.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.frequency.label(class="form-label fw-semibold") }}
|
||||
{{ form.frequency(class="form-select") }}
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.next_due_date.label(class="form-label fw-semibold") }}
|
||||
{{ form.next_due_date(class="form-control", type="date") }}
|
||||
{% for e in form.next_due_date.errors %}<div class="text-danger small">{{ e }}</div>{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.notes.label(class="form-label fw-semibold") }}
|
||||
{{ form.notes(class="form-control", rows=2, placeholder="Optional instructions for the inspector…") }}
|
||||
</div>
|
||||
|
||||
<div class="form-check mb-3">
|
||||
{{ form.active(class="form-check-input") }}
|
||||
{{ form.active.label(class="form-check-label") }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<a href="{{ url_for('scheduled_inspections.index') }}" class="btn btn-outline-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-muted small mt-2">
|
||||
Recurring schedules automatically roll their due date forward each time the
|
||||
inspection is completed. The assigned inspector is reminded the day before
|
||||
and on the due date; managers are alerted if it becomes overdue.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,96 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Scheduled Inspections{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4 align-items-center">
|
||||
<div class="col">
|
||||
<h2><i class="bi bi-calendar-check"></i> Scheduled Inspections</h2>
|
||||
<p class="text-muted mb-0">Planned and recurring inspection assignments.</p>
|
||||
</div>
|
||||
<div class="col-auto d-flex gap-2">
|
||||
<a href="{{ url_for('inspections.index') }}" class="btn btn-outline-secondary">
|
||||
<i class="bi bi-arrow-left"></i> Inspections
|
||||
</a>
|
||||
{% if current_user.role in ['admin','director','project_manager'] %}
|
||||
<a href="{{ url_for('scheduled_inspections.create') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> New Schedule
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body p-0">
|
||||
{% if schedules %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0 align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Facility</th>
|
||||
<th>Template</th>
|
||||
<th>Inspector</th>
|
||||
<th>Frequency</th>
|
||||
<th>Next Due</th>
|
||||
<th>Status</th>
|
||||
<th class="text-end"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for s in schedules %}
|
||||
{% set overdue = s.active and s.next_due_date < today %}
|
||||
{% set due_soon = s.active and not overdue and (s.next_due_date - today).days <= 7 %}
|
||||
<tr class="{{ 'table-danger' if overdue else 'table-warning' if due_soon else '' }}">
|
||||
<td><strong>{{ s.facility.name if s.facility else '—' }}</strong></td>
|
||||
<td>{{ s.template.name if s.template else '—' }}</td>
|
||||
<td>{{ s.inspector.display_name if s.inspector else '— Unassigned —' }}</td>
|
||||
<td><span class="badge bg-secondary">{{ s.frequency_label }}</span></td>
|
||||
<td>
|
||||
{{ s.next_due_date.strftime('%b %d, %Y') }}
|
||||
{% if overdue %}
|
||||
<span class="badge bg-danger ms-1"><i class="bi bi-alarm"></i> Overdue</span>
|
||||
{% elif due_soon %}
|
||||
<span class="badge bg-warning text-dark ms-1">Due soon</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if s.active %}
|
||||
<span class="badge bg-success">Active</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end text-nowrap">
|
||||
{% if s.active and (current_user.role in ['admin','director','project_manager']
|
||||
or (current_user.role == 'inspector' and s.inspector_id == current_user.id)) %}
|
||||
<a href="{{ url_for('scheduled_inspections.start', schedule_id=s.id) }}"
|
||||
class="btn btn-sm btn-success" title="Start this inspection">
|
||||
<i class="bi bi-play-fill"></i> Start
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if current_user.role in ['admin','director','project_manager'] %}
|
||||
<a href="{{ url_for('scheduled_inspections.edit', schedule_id=s.id) }}"
|
||||
class="btn btn-sm btn-outline-primary"><i class="bi bi-pencil"></i></a>
|
||||
<form method="POST" class="d-inline"
|
||||
action="{{ url_for('scheduled_inspections.delete', schedule_id=s.id) }}"
|
||||
onsubmit="return confirm('Delete this scheduled inspection?');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="p-4 text-muted text-center">
|
||||
No scheduled inspections yet.
|
||||
{% if current_user.role in ['admin','director','project_manager'] %}
|
||||
<a href="{{ url_for('scheduled_inspections.create') }}">Create one</a>.
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user