Jul 16 - Fill the gaps between Single-tenant mode and Multi-tenant mode - MT3
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Print QR Codes{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.qr-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; }
|
||||
.qr-item { position: relative; break-inside: avoid; page-break-inside: avoid;
|
||||
text-align: center; cursor: pointer; }
|
||||
.qr-item img { width: 220px; height: 220px; max-width: 100%; }
|
||||
.qr-item.qr-selected { outline: 3px solid #2563eb; outline-offset: -1px; }
|
||||
.qr-check { position: absolute; top: 10px; left: 10px; }
|
||||
.qr-check .form-check-input { width: 1.25rem; height: 1.25rem; }
|
||||
.qr-kind { font-size: .68rem; letter-spacing: .08em; }
|
||||
@media print {
|
||||
.no-print { display: none !important; }
|
||||
.navbar, nav, footer { display: none !important; }
|
||||
.qr-item { border: 1px dashed #bbb !important; cursor: default; }
|
||||
.qr-item.qr-selected { outline: none !important; }
|
||||
.qr-grid { gap: 8px; }
|
||||
/* When printing a selection, hide the unselected cards. */
|
||||
body.print-selected-only .qr-item:not(.qr-selected) { display: none !important; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-3 no-print">
|
||||
<div>
|
||||
<h2 class="h4 mb-0"><i class="bi bi-qr-code"></i> QR Codes</h2>
|
||||
<div class="text-muted small">
|
||||
{% if selected_contract %}Contract: {{ selected_contract.name }} — {% endif %}
|
||||
{{ facilities|length }} facilit{{ 'y' if facilities|length == 1 else 'ies' }}
|
||||
{% if include_areas %}(with areas){% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ url_for('facilities.list_facilities') }}" class="btn btn-outline-secondary btn-sm">
|
||||
<i class="bi bi-arrow-left"></i> Back
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{# ── Filter bar (GET reload) ─────────────────────────────────────────────── #}
|
||||
<form method="get" id="filterForm" class="card card-body mb-3 no-print">
|
||||
<div class="row g-2 align-items-end">
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-semibold mb-1">Contract</label>
|
||||
<select name="contract_id" class="form-select form-select-sm"
|
||||
onchange="document.getElementById('facilitySelect').value=''; this.form.submit();">
|
||||
<option value="">All Contracts</option>
|
||||
{% for c in contracts %}
|
||||
<option value="{{ c.id }}" {{ 'selected' if selected_contract_id == c.id }}>{{ c.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<label class="form-label small fw-semibold mb-1">Facility</label>
|
||||
<select name="facility_id" id="facilitySelect" class="form-select form-select-sm">
|
||||
<option value="">All Facilities</option>
|
||||
{% for f in facility_options %}
|
||||
<option value="{{ f.id }}" {{ 'selected' if selected_facility_id == f.id }}>{{ f.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" name="include_areas" value="1"
|
||||
id="includeAreas" {{ 'checked' if include_areas }}>
|
||||
<label class="form-check-label small" for="includeAreas">Include area QR codes</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary btn-sm w-100">
|
||||
<i class="bi bi-funnel"></i> Apply
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{# ── Selection toolbar + export form ─────────────────────────────────────── #}
|
||||
<form method="post" action="{{ url_for('facilities.qr_export_pdf') }}" id="qrForm">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3 no-print flex-wrap">
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="selectAllQr(true)">Select All</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="selectAllQr(false)">Clear</button>
|
||||
<span class="text-muted small" id="selCount">0 selected</span>
|
||||
<div class="ms-auto d-flex gap-2">
|
||||
<button type="button" class="btn btn-primary btn-sm" onclick="printSelected()">
|
||||
<i class="bi bi-printer"></i> Print Selected
|
||||
</button>
|
||||
<button type="submit" class="btn btn-danger btn-sm">
|
||||
<i class="bi bi-file-earmark-pdf"></i> Export Selected to PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if facilities %}
|
||||
<div class="qr-grid">
|
||||
{% for f in facilities %}
|
||||
{# Facility QR card #}
|
||||
<label class="qr-item card shadow-sm p-3 mb-0">
|
||||
<div class="qr-check no-print">
|
||||
<input type="checkbox" class="form-check-input qr-cb" name="facility_ids" value="{{ f.id }}">
|
||||
</div>
|
||||
<div class="text-muted text-uppercase qr-kind">Facility</div>
|
||||
<div class="fw-bold">{{ f.name }}</div>
|
||||
{% if f.project %}
|
||||
<div class="text-muted small mb-1">{{ f.project.name }}</div>
|
||||
{% endif %}
|
||||
<div>
|
||||
<img src="{{ url_for('facilities.facility_qr_png', facility_id=f.id) }}"
|
||||
alt="QR code for {{ f.name }}" loading="lazy">
|
||||
</div>
|
||||
<div class="small">Report a problem & view recent quality</div>
|
||||
</label>
|
||||
|
||||
{% if include_areas %}
|
||||
{% for a in areas_by_facility.get(f.id, []) %}
|
||||
{# Area QR card #}
|
||||
<label class="qr-item card shadow-sm p-3 mb-0">
|
||||
<div class="qr-check no-print">
|
||||
<input type="checkbox" class="form-check-input qr-cb" name="area_ids" value="{{ a.id }}">
|
||||
</div>
|
||||
<div class="text-muted text-uppercase qr-kind">Area</div>
|
||||
<div class="fw-bold">{{ a.name }}</div>
|
||||
<div class="text-muted small mb-1">{{ f.name }}</div>
|
||||
<div>
|
||||
<img src="{{ url_for('facilities.area_qr_png', area_id=a.id) }}"
|
||||
alt="QR code for {{ a.name }}" loading="lazy">
|
||||
</div>
|
||||
<div class="small">Report a problem & view recent quality</div>
|
||||
</label>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info">No facilities match the selected filters.</div>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
<p class="text-muted small mt-3 no-print">
|
||||
Tip: tick the codes you want, then <strong>Print Selected</strong> or
|
||||
<strong>Export Selected to PDF</strong>. With nothing ticked, Print Selected prints them all.
|
||||
</p>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function checkboxes() { return document.querySelectorAll('.qr-cb'); }
|
||||
|
||||
function updateCount() {
|
||||
var n = document.querySelectorAll('.qr-cb:checked').length;
|
||||
document.getElementById('selCount').textContent = n + ' selected';
|
||||
}
|
||||
|
||||
function syncCard(cb) {
|
||||
var card = cb.closest('.qr-item');
|
||||
if (card) { card.classList.toggle('qr-selected', cb.checked); }
|
||||
}
|
||||
|
||||
window.selectAllQr = function (state) {
|
||||
checkboxes().forEach(function (cb) { cb.checked = state; syncCard(cb); });
|
||||
updateCount();
|
||||
};
|
||||
|
||||
window.printSelected = function () {
|
||||
var anySelected = document.querySelectorAll('.qr-cb:checked').length > 0;
|
||||
if (anySelected) { document.body.classList.add('print-selected-only'); }
|
||||
window.print();
|
||||
setTimeout(function () {
|
||||
document.body.classList.remove('print-selected-only');
|
||||
}, 500);
|
||||
};
|
||||
|
||||
// Toggling a checkbox inside its <label> card also fires on the label click.
|
||||
checkboxes().forEach(function (cb) {
|
||||
cb.addEventListener('change', function () { syncCard(cb); updateCount(); });
|
||||
});
|
||||
|
||||
updateCount();
|
||||
}());
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user