154 lines
5.9 KiB
HTML
154 lines
5.9 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}New Inspection{% endblock %}
|
|
{% block content %}
|
|
<div class="row justify-content-center">
|
|
<div class="col-lg-6">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-primary text-white">
|
|
<h5 class="mb-0"><i class="bi bi-play-circle"></i> New Inspection</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="post">
|
|
{{ form.hidden_tag() }}
|
|
|
|
<div class="mb-3">
|
|
{{ form.template_id.label(class="form-label fw-semibold") }}
|
|
{{ form.template_id(class="form-select" + (" is-invalid" if form.template_id.errors else "")) }}
|
|
{% for e in form.template_id.errors %}<div class="invalid-feedback">{{ e }}</div>{% endfor %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
{{ form.project_id.label(class="form-label fw-semibold") }}
|
|
{{ form.project_id(class="form-select" + (" is-invalid" if form.project_id.errors else ""), id="projectSelect") }}
|
|
{% for e in form.project_id.errors %}<div class="invalid-feedback">{{ e }}</div>{% endfor %}
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
{{ form.facility_id.label(class="form-label fw-semibold") }}
|
|
{{ form.facility_id(class="form-select" + (" is-invalid" if form.facility_id.errors else ""), id="facilitySelect") }}
|
|
{% for e in form.facility_id.errors %}<div class="invalid-feedback">{{ e }}</div>{% endfor %}
|
|
<div id="facilitySpinner" class="form-text text-muted d-none">
|
|
<span class="spinner-border spinner-border-sm" role="status"></span> Loading facilities…
|
|
</div>
|
|
<div id="facilityEmpty" class="form-text text-warning d-none">
|
|
No active facilities found for this contract.
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3" id="areaGroup" style="display:none;">
|
|
{{ form.area_id.label(class="form-label fw-semibold") }}
|
|
{{ form.area_id(class="form-select", id="areaSelect") }}
|
|
</div>
|
|
|
|
<div class="d-grid">
|
|
<button type="submit" class="btn btn-primary btn-lg">
|
|
<i class="bi bi-play-fill"></i> Begin Inspection
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
const projectSel = document.getElementById('projectSelect');
|
|
const facilitySel = document.getElementById('facilitySelect');
|
|
const spinner = document.getElementById('facilitySpinner');
|
|
const emptyMsg = document.getElementById('facilityEmpty');
|
|
const areaGroup = document.getElementById('areaGroup');
|
|
const areaSel = document.getElementById('areaSelect');
|
|
|
|
const FACILITIES_URL = `{{ url_for('inspections.facilities_for_project', project_id=0) }}`.replace('/0', '/');
|
|
const AREAS_URL = `{{ url_for('inspections.areas_for_facility', facility_id=0) }}`.replace('/0', '/');
|
|
|
|
function loadAreas(facilityId, selectedAreaId) {
|
|
if (!facilityId) {
|
|
areaGroup.style.display = 'none';
|
|
areaSel.innerHTML = '<option value="0">— No specific area —</option>';
|
|
return;
|
|
}
|
|
fetch(AREAS_URL + facilityId)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.length === 0) {
|
|
areaGroup.style.display = 'none';
|
|
areaSel.innerHTML = '<option value="0">— No specific area —</option>';
|
|
} else {
|
|
areaSel.innerHTML = '<option value="0">— No specific area —</option>';
|
|
data.forEach(a => {
|
|
const opt = document.createElement('option');
|
|
opt.value = a.id;
|
|
opt.textContent = a.name;
|
|
if (selectedAreaId && a.id === selectedAreaId) opt.selected = true;
|
|
areaSel.appendChild(opt);
|
|
});
|
|
areaGroup.style.display = '';
|
|
}
|
|
})
|
|
.catch(() => {
|
|
areaGroup.style.display = 'none';
|
|
});
|
|
}
|
|
|
|
function loadFacilities(projectId, selectedFacilityId, selectedAreaId) {
|
|
if (!projectId) return;
|
|
spinner.classList.remove('d-none');
|
|
emptyMsg.classList.add('d-none');
|
|
facilitySel.disabled = true;
|
|
areaGroup.style.display = 'none';
|
|
|
|
fetch(FACILITIES_URL + projectId)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
facilitySel.innerHTML = '';
|
|
if (data.length === 0) {
|
|
emptyMsg.classList.remove('d-none');
|
|
facilitySel.innerHTML = '<option value="">— no facilities —</option>';
|
|
} else {
|
|
data.forEach(f => {
|
|
const opt = document.createElement('option');
|
|
opt.value = f.id;
|
|
opt.textContent = f.name;
|
|
if (selectedFacilityId && f.id === selectedFacilityId) opt.selected = true;
|
|
facilitySel.appendChild(opt);
|
|
});
|
|
loadAreas(facilitySel.value, selectedAreaId);
|
|
}
|
|
})
|
|
.catch(() => {
|
|
facilitySel.innerHTML = '<option value="">— error loading facilities —</option>';
|
|
})
|
|
.finally(() => {
|
|
spinner.classList.add('d-none');
|
|
facilitySel.disabled = false;
|
|
});
|
|
}
|
|
|
|
projectSel.addEventListener('change', function () {
|
|
loadFacilities(this.value, null, null);
|
|
});
|
|
|
|
facilitySel.addEventListener('change', function () {
|
|
loadAreas(this.value, null);
|
|
});
|
|
|
|
// On page load: if project already selected (e.g. validation error or reinspect),
|
|
// reload facility list preserving the currently selected facility and area values.
|
|
const initProject = projectSel.value;
|
|
const initFacility = facilitySel.value ? parseInt(facilitySel.value, 10) : null;
|
|
const initArea = areaSel.value ? parseInt(areaSel.value, 10) : null;
|
|
if (initProject) {
|
|
const hasRealOptions = Array.from(facilitySel.options).some(o => parseInt(o.value, 10) > 0);
|
|
if (!hasRealOptions) {
|
|
loadFacilities(initProject, initFacility, initArea);
|
|
} else {
|
|
// Facilities already rendered server-side — just load areas for the selected facility
|
|
if (initFacility) loadAreas(initFacility, initArea);
|
|
}
|
|
}
|
|
})();
|
|
</script>
|
|
{% endblock %}
|