05/11 Update: add contract selection when start an inspection
This commit is contained in:
@@ -10,6 +10,7 @@ from app import db
|
||||
from app.models.inspection import (Inspection, InspectionTemplate,
|
||||
ChecklistItem, InspectionResult)
|
||||
from app.models.facility import Facility, Area
|
||||
from app.models.project import Project
|
||||
from app.models.issue import Issue
|
||||
from app.models.user import User
|
||||
from app.utils.forms import StartInspectionForm, IssueForm
|
||||
@@ -229,18 +230,39 @@ def index():
|
||||
def start():
|
||||
form = StartInspectionForm()
|
||||
|
||||
templates = InspectionTemplate.query.order_by(InspectionTemplate.name).all()
|
||||
facilities = Facility.query.filter_by(active=True).order_by(Facility.name).all()
|
||||
templates = InspectionTemplate.query.order_by(InspectionTemplate.name).all()
|
||||
projects = Project.query.filter_by(active=True).order_by(Project.name).all()
|
||||
|
||||
form.template_id.choices = [(t.id, t.name) for t in templates]
|
||||
form.facility_id.choices = [(f.id, f.name) for f in facilities]
|
||||
form.project_id.choices = [(p.id, p.name) for p in projects]
|
||||
|
||||
# Seed facility choices: use submitted project_id, session value, or first project
|
||||
from flask import session as _session
|
||||
if form.is_submitted():
|
||||
selected_project_id = form.project_id.data
|
||||
elif _session.get('reinspect_facility_id'):
|
||||
# Derive project from the reinspect facility
|
||||
_rf = db.session.get(Facility, _session['reinspect_facility_id'])
|
||||
selected_project_id = _rf.project_id if _rf and _rf.project_id else (projects[0].id if projects else None)
|
||||
else:
|
||||
selected_project_id = projects[0].id if projects else None
|
||||
|
||||
if selected_project_id:
|
||||
facilities = Facility.query.filter_by(active=True, project_id=selected_project_id).order_by(Facility.name).all()
|
||||
else:
|
||||
facilities = []
|
||||
|
||||
form.facility_id.choices = [(f.id, f.name) for f in facilities]
|
||||
if not form.facility_id.choices:
|
||||
form.facility_id.choices = [('', '— no facilities —')]
|
||||
|
||||
if not form.is_submitted():
|
||||
if _session.get('reinspect_template_id'):
|
||||
form.template_id.data = _session['reinspect_template_id']
|
||||
if _session.get('reinspect_facility_id'):
|
||||
form.facility_id.data = _session['reinspect_facility_id']
|
||||
if selected_project_id:
|
||||
form.project_id.data = selected_project_id
|
||||
|
||||
if form.validate_on_submit():
|
||||
template = db.session.get(InspectionTemplate, form.template_id.data)
|
||||
@@ -272,7 +294,7 @@ def start():
|
||||
flash('Inspection started. Fill in the form below and submit when complete.', 'info')
|
||||
return redirect(url_for('inspections.execute', inspection_id=inspection.id))
|
||||
|
||||
return render_template('inspections/start.html', form=form, facilities=facilities)
|
||||
return render_template('inspections/start.html', form=form, projects=projects)
|
||||
|
||||
|
||||
# ── AJAX: areas for a given facility ─────────────────────────────────────────
|
||||
@@ -284,6 +306,18 @@ def areas_for_facility(facility_id):
|
||||
return jsonify([{'id': a.id, 'name': a.name} for a in areas])
|
||||
|
||||
|
||||
# ── AJAX: facilities for a given project/contract ────────────────────────────
|
||||
|
||||
@bp.route('/facilities_for_project/<int:project_id>')
|
||||
@login_required
|
||||
def facilities_for_project(project_id):
|
||||
facilities = (Facility.query
|
||||
.filter_by(active=True, project_id=project_id)
|
||||
.order_by(Facility.name)
|
||||
.all())
|
||||
return jsonify([{'id': f.id, 'name': f.name} for f in facilities])
|
||||
|
||||
|
||||
# ── Execute ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@bp.route('/<int:inspection_id>/execute', methods=['GET', 'POST'])
|
||||
|
||||
@@ -17,9 +17,22 @@
|
||||
{% 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", id="facilitySelect") }}
|
||||
{{ 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="d-grid">
|
||||
@@ -32,4 +45,61 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const projectSel = document.getElementById('projectSelect');
|
||||
const facilitySel = document.getElementById('facilitySelect');
|
||||
const spinner = document.getElementById('facilitySpinner');
|
||||
const emptyMsg = document.getElementById('facilityEmpty');
|
||||
|
||||
function loadFacilities(projectId, selectedFacilityId) {
|
||||
if (!projectId) return;
|
||||
spinner.classList.remove('d-none');
|
||||
emptyMsg.classList.add('d-none');
|
||||
facilitySel.disabled = true;
|
||||
|
||||
fetch(`{{ url_for('inspections.facilities_for_project', project_id=0) }}`.replace('/0', '/' + 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);
|
||||
});
|
||||
}
|
||||
})
|
||||
.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);
|
||||
});
|
||||
|
||||
// On page load: if project already selected (e.g. validation error or reinspect),
|
||||
// reload facility list preserving the currently selected facility value.
|
||||
const initProject = projectSel.value;
|
||||
const initFacility = facilitySel.value ? parseInt(facilitySel.value, 10) : null;
|
||||
if (initProject) {
|
||||
// Only reload if facility list looks like a placeholder (no real numeric id in options)
|
||||
const hasRealOptions = Array.from(facilitySel.options).some(o => parseInt(o.value, 10) > 0);
|
||||
if (!hasRealOptions) {
|
||||
loadFacilities(initProject, initFacility);
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -142,6 +142,7 @@ class ChecklistItemForm(FlaskForm):
|
||||
|
||||
class StartInspectionForm(FlaskForm):
|
||||
template_id = SelectField('Template', coerce=int, validators=[DataRequired()])
|
||||
project_id = SelectField('Contract', coerce=int, validators=[DataRequired()])
|
||||
facility_id = SelectField('Facility', coerce=int, validators=[DataRequired()])
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user