Aug 5 - Update (new design)

This commit is contained in:
2026-08-05 19:06:07 -04:00
parent 22b3afdb02
commit eba535487e
6 changed files with 96 additions and 24 deletions
+27 -13
View File
@@ -30,8 +30,9 @@ body.jqc-modern {
--jqc-faint: #93A1AB; --jqc-faint: #93A1AB;
--jqc-border: #E3E8EC; --jqc-border: #E3E8EC;
--jqc-border-2: #CFD9E0; --jqc-border-2: #CFD9E0;
--jqc-surface: #F5F8FA; --jqc-surface: #F5F8FA; /* subtle fill — hovers, muted rows (theme.css) */
--jqc-surface-2: #EEF3F6; --jqc-surface-2: #EEF3F6;
--jqc-card-bg: #FFFFFF; /* raised surfaces — cards, KPI tiles, sidebar */
--jqc-accent: #155F82; --jqc-accent: #155F82;
--jqc-accent-700: #0F4A66; --jqc-accent-700: #0F4A66;
--jqc-accent-50: #DCEBF5; --jqc-accent-50: #DCEBF5;
@@ -164,7 +165,7 @@ body.jqc-modern {
bottom: 0; bottom: 0;
left: 0; left: 0;
width: var(--jqc-sidebar-w); width: var(--jqc-sidebar-w);
background: #fff; background: var(--jqc-card-bg);
border-right: 1px solid var(--jqc-border); border-right: 1px solid var(--jqc-border);
overflow-y: auto; overflow-y: auto;
z-index: 1030; z-index: 1030;
@@ -274,8 +275,11 @@ body.jqc-modern {
letter-spacing: -.02em; letter-spacing: -.02em;
color: var(--jqc-ink); color: var(--jqc-ink);
margin: 0; margin: 0;
text-align: center;
} }
/* Opt-in centring — the default is left-aligned. `.center` on the head block
centres the title and its sub-line together. */
.jqc-modern .jqc-page-head.center,
.jqc-modern .jqc-page-title.center { text-align: center; }
.jqc-modern .jqc-page-sub { .jqc-modern .jqc-page-sub {
color: var(--jqc-muted); color: var(--jqc-muted);
font-size: .95rem; font-size: .95rem;
@@ -289,18 +293,18 @@ body.jqc-modern {
box-shadow: var(--jqc-shadow); box-shadow: var(--jqc-shadow);
} }
.jqc-modern .card-header { .jqc-modern .card-header {
background: #fff; background: var(--jqc-card-bg);
border-bottom: 1px solid var(--jqc-border); border-bottom: 1px solid var(--jqc-border);
color: var(--jqc-ink); color: var(--jqc-ink);
font-weight: 700; font-weight: 700;
padding: .9rem 1.15rem; padding: .9rem 1.15rem;
} }
.jqc-modern .card-header.bg-light, .jqc-modern .card-header.bg-light,
.jqc-modern .card-header.bg-white { background: #fff !important; } .jqc-modern .card-header.bg-white { background: var(--jqc-card-bg) !important; }
.jqc-modern .card-body { padding: 1.15rem; } .jqc-modern .card-body { padding: 1.15rem; }
.jqc-modern .jqc-card { .jqc-modern .jqc-card {
background: #fff; background: var(--jqc-card-bg);
border: 1px solid var(--jqc-border); border: 1px solid var(--jqc-border);
border-radius: 16px; border-radius: 16px;
box-shadow: var(--jqc-shadow); box-shadow: var(--jqc-shadow);
@@ -333,7 +337,7 @@ body.jqc-modern {
/* KPI tiles */ /* KPI tiles */
.jqc-modern .jqc-kpi { .jqc-modern .jqc-kpi {
background: #fff; background: var(--jqc-card-bg);
border: 1px solid var(--jqc-border); border: 1px solid var(--jqc-border);
border-radius: 16px; border-radius: 16px;
box-shadow: var(--jqc-shadow); box-shadow: var(--jqc-shadow);
@@ -379,7 +383,7 @@ a.jqc-kpi:hover { box-shadow: var(--jqc-shadow-md); transform: translateY(-1px);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
background: #fff; background: var(--jqc-card-bg);
border: 1px solid var(--jqc-border); border: 1px solid var(--jqc-border);
border-radius: 16px; border-radius: 16px;
box-shadow: var(--jqc-shadow); box-shadow: var(--jqc-shadow);
@@ -399,17 +403,27 @@ a.jqc-kpi:hover { box-shadow: var(--jqc-shadow-md); transform: translateY(-1px);
/* ── 7. Tables — dark teal header, as in the deck ────────────────────────── */ /* ── 7. Tables — dark teal header, as in the deck ────────────────────────── */
.jqc-modern .table { --bs-table-border-color: var(--jqc-border); margin-bottom: 0; } .jqc-modern .table { --bs-table-border-color: var(--jqc-border); margin-bottom: 0; }
/* Recoloured via Bootstrap's own table CSS variables rather than !important, so
a page that deliberately wants a different header (table-dark, a tinted
report header) can still override it with a normal rule. */
.jqc-modern .table > thead > tr > th, .jqc-modern .table > thead > tr > th,
.jqc-modern .table thead.table-light > tr > th, .jqc-modern .table thead.table-light > tr > th,
.jqc-modern .table > thead th { .jqc-modern .table > thead th {
background: var(--jqc-brand) !important; --bs-table-bg: var(--jqc-brand);
color: #fff !important; --bs-table-color: #fff;
border-color: var(--jqc-brand-700) !important; background-color: var(--jqc-brand);
color: #fff;
border-color: var(--jqc-brand-700);
font-weight: 600; font-weight: 600;
font-size: .88rem; font-size: .88rem;
vertical-align: middle; vertical-align: middle;
white-space: nowrap;
} }
/* nowrap only where the column set is known-narrow (dashboard panels); wide
tables such as the issues list and audit trail must be free to wrap rather
than force a horizontal scroll on iPad portrait. */
.jqc-modern .jqc-card .table > thead th { white-space: nowrap; }
.jqc-modern .table > tbody > tr > td { vertical-align: middle; font-size: .92rem; } .jqc-modern .table > tbody > tr > td { vertical-align: middle; font-size: .92rem; }
.jqc-modern .table-hover > tbody > tr:hover > * { background-color: var(--jqc-brand-025); } .jqc-modern .table-hover > tbody > tr:hover > * { background-color: var(--jqc-brand-025); }
.jqc-modern .jqc-table-wrap { .jqc-modern .jqc-table-wrap {
@@ -447,7 +461,7 @@ a.jqc-kpi:hover { box-shadow: var(--jqc-shadow-md); transform: translateY(-1px);
/* Filter bar — the rounded pill row from the deck */ /* Filter bar — the rounded pill row from the deck */
.jqc-modern .jqc-filter-bar { .jqc-modern .jqc-filter-bar {
background: #fff; background: var(--jqc-card-bg);
border: 1px solid var(--jqc-border); border: 1px solid var(--jqc-border);
border-radius: 16px; border-radius: 16px;
box-shadow: var(--jqc-shadow); box-shadow: var(--jqc-shadow);
+5 -2
View File
@@ -93,10 +93,13 @@
<span class="jqc-brand-sub">By L.T. Services, Inc</span> <span class="jqc-brand-sub">By L.T. Services, Inc</span>
</a> </a>
{# Scoped to inspection ID only — the placeholder says so explicitly so
nobody types a facility name and assumes the search is broken. #}
<form class="jqc-search" method="GET" action="{{ url_for('inspections.index') }}" role="search"> <form class="jqc-search" method="GET" action="{{ url_for('inspections.index') }}" role="search">
<i class="bi bi-search"></i> <i class="bi bi-search"></i>
<input type="text" name="inspection_id" class="form-control" <input type="search" name="inspection_id" class="form-control" inputmode="numeric"
placeholder="Search inspection number" aria-label="Search inspection number"> placeholder="Inspection # (e.g. 1423)" aria-label="Search by inspection number"
title="Search by inspection number">
</form> </form>
<div class="jqc-topbar-actions"> <div class="jqc-topbar-actions">
+61 -6
View File
@@ -14,7 +14,7 @@
{# ── Header ───────────────────────────────────────────────────────────── #} {# ── Header ───────────────────────────────────────────────────────────── #}
<div class="d-flex flex-wrap justify-content-between align-items-end mb-4 gap-2"> <div class="d-flex flex-wrap justify-content-between align-items-end mb-4 gap-2">
<div> <div>
<div class="jqc-page-title" style="text-align:left;">Welcome, {{ current_user.display_name }}</div> <div class="jqc-page-title">Welcome, {{ current_user.display_name }}</div>
<div class="jqc-page-sub">{{ current_user.role.replace('_',' ')|title }}</div> <div class="jqc-page-sub">{{ current_user.role.replace('_',' ')|title }}</div>
</div> </div>
<div class="text-muted">{{ now_display }}</div> <div class="text-muted">{{ now_display }}</div>
@@ -242,8 +242,15 @@
</thead> </thead>
<tbody> <tbody>
{% for insp in recent_inspections %} {% for insp in recent_inspections %}
<tr style="cursor:pointer;" onclick="window.location='{{ url_for('inspections.view', inspection_id=insp.id) }}'"> {# The row stays click-anywhere for the mouse, but the date is a real
<td><small>{{ insp.inspection_date.strftime('%b %d, %Y') }}</small></td> link so the row is keyboard-reachable and openable in a new tab.
The guard stops the row handler from double-firing on that link. #}
<tr style="cursor:pointer;"
onclick="if(!event.target.closest('a')) window.location='{{ url_for('inspections.view', inspection_id=insp.id) }}'">
<td>
<a href="{{ url_for('inspections.view', inspection_id=insp.id) }}"
class="text-decoration-none"><small>{{ insp.inspection_date.strftime('%b %d, %Y') }}</small></a>
</td>
<td>{{ insp.facility.name }}</td> <td>{{ insp.facility.name }}</td>
<td>{{ insp.area.name if insp.area else '—' }}</td> <td>{{ insp.area.name if insp.area else '—' }}</td>
{% if current_user.role != 'inspector' %}<td>{{ insp.inspector.display_name }}</td>{% endif %} {% if current_user.role != 'inspector' %}<td>{{ insp.inspector.display_name }}</td>{% endif %}
@@ -383,10 +390,16 @@
<span class="jqc-tile-icon"><i class="bi bi-building"></i></span>Your Facilities <span class="jqc-tile-icon"><i class="bi bi-building"></i></span>Your Facilities
<span class="badge bg-secondary rounded-pill ms-2">{{ customer_facilities|length }}</span> <span class="badge bg-secondary rounded-pill ms-2">{{ customer_facilities|length }}</span>
</div> </div>
<div class="row g-3"> {% if customer_facilities|length > 6 %}
<div class="mb-3">
<input type="text" id="facilitySearch" class="form-control form-control-sm"
placeholder="Search facilities…" aria-label="Search facilities">
</div>
{% endif %}
<div class="row g-3" id="facilityGrid">
{% for f in customer_facilities %} {% for f in customer_facilities %}
<div class="col-12 col-sm-6 col-lg-4"> <div class="col-12 col-sm-6 col-lg-4 facility-col">
<div class="border rounded-3 p-3 h-100 d-flex flex-column"> <div class="border rounded-3 p-3 h-100 d-flex flex-column facility-card">
<div class="fw-semibold mb-1">{{ f.name }}</div> <div class="fw-semibold mb-1">{{ f.name }}</div>
<div class="text-muted" style="font-size:.82rem;">{{ f.address or '—' }}</div> <div class="text-muted" style="font-size:.82rem;">{{ f.address or '—' }}</div>
<div class="my-2"> <div class="my-2">
@@ -402,7 +415,49 @@
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
{% if customer_facilities|length > 9 %}
<div id="facilityShowMore" class="text-center mt-3">
<button class="btn btn-sm btn-link text-muted" id="toggleFacilities">
Show all {{ customer_facilities|length }} facilities <i class="bi bi-chevron-down"></i>
</button>
</div> </div>
{% endif %} {% endif %}
</div>
{# Same VISIBLE = 9 / search > 6 thresholds as the classic dashboard (rule 65). #}
<script>
(function () {
{% if customer_facilities|length > 9 %}
var VISIBLE = 9;
var cols = document.querySelectorAll('#facilityGrid .facility-col');
var btn = document.getElementById('toggleFacilities');
var expanded = false;
cols.forEach(function (c, i) { if (i >= VISIBLE) c.style.display = 'none'; });
btn.addEventListener('click', function () {
expanded = !expanded;
cols.forEach(function (c, i) {
if (i >= VISIBLE) c.style.display = expanded ? '' : 'none';
});
btn.innerHTML = expanded
? 'Show fewer <i class="bi bi-chevron-up"></i>'
: 'Show all {{ customer_facilities|length }} facilities <i class="bi bi-chevron-down"></i>';
});
{% endif %}
{% if customer_facilities|length > 6 %}
document.getElementById('facilitySearch').addEventListener('input', function () {
var q = this.value.toLowerCase();
document.querySelectorAll('#facilityGrid .facility-col').forEach(function (col) {
var match = col.querySelector('.facility-card').textContent.toLowerCase().includes(q);
col.style.display = match ? '' : 'none';
});
var more = document.getElementById('facilityShowMore');
if (more) more.style.display = this.value ? 'none' : '';
});
{% endif %}
})();
</script>
{% endif %}
{% endblock %} {% endblock %}
+1 -1
View File
@@ -10,7 +10,7 @@
#} #}
{% block content %} {% block content %}
<div class="jqc-page-head"> <div class="jqc-page-head center">
<div class="jqc-page-title">Facilities</div> <div class="jqc-page-title">Facilities</div>
<div class="jqc-page-sub text-center">Manage facility records, statistics and QR access</div> <div class="jqc-page-sub text-center">Manage facility records, statistics and QR access</div>
</div> </div>
+1 -1
View File
@@ -4,7 +4,7 @@
{# About Us — new page (design A/B test). Linked from the modern sidebar. #} {# About Us — new page (design A/B test). Linked from the modern sidebar. #}
{% block content %} {% block content %}
<div class="jqc-page-head"> <div class="jqc-page-head center">
<div class="jqc-page-title">About Us</div> <div class="jqc-page-title">About Us</div>
<div class="jqc-page-sub text-center">Janitorial Quality Control by L.T. Services, Inc.</div> <div class="jqc-page-sub text-center">Janitorial Quality Control by L.T. Services, Inc.</div>
</div> </div>
+1 -1
View File
@@ -8,7 +8,7 @@
#} #}
{% block content %} {% block content %}
<div class="jqc-page-head"> <div class="jqc-page-head center">
<div class="jqc-page-title">Support</div> <div class="jqc-page-title">Support</div>
<div class="jqc-page-sub text-center">JQC Features — find answers and how-to guides</div> <div class="jqc-page-sub text-center">JQC Features — find answers and how-to guides</div>
</div> </div>