[Perl] DAQ system for the FAPG.
refactor align with Bootstrap UI metaphors
Breadcrumbs: - Remove custom pill styling (bg-white border rounded-pill), use standard Bootstrap .breadcrumb Quick links: - Convert from list-inline to nav nav-pills Reading gauge cards: - Replace custom is-good/is-fair/is-poor/is-unknown classes with Bootstrap border-success/border-warning/border-danger utilities - Marker color remains set via inline style from JS Status page: - Rewrite using Bootstrap list-group-flush inside the card - Each status item is a flat list-group-item with marker, title, badge, and detail fields - Remove nested section/ol wrapper structure
Changed files
roles/dashboard/public/js/dashboard/quick-readings.js
@@ -76,6 +76,10 @@
76
76
return `rgb(${r}, ${g}, ${b})`;
77
77
}
78
78
79
Added:
function qualityBorderClass(state) {
80
Added:
return { good: "border-success", fair: "border-warning", poor: "border-danger", unknown: "" }[state] || "";
81
Added:
}
82
Added:
79
83
function updateItem(item, reading) {
80
84
const probe = item.dataset.readingQuickProbe;
81
85
const range = READING_RANGES[probe];
@@ -89,8 +93,9 @@
89
93
const age = timestamp ? formatAge(ageMs(timestamp)) : "unknown age";
90
94
91
95
configureRange(item, range, unit);
92
Removed:
item.classList.remove("is-good", "is-fair", "is-poor", "is-unknown");
93
Removed:
item.classList.add(`is-${state}`);
96
Added:
item.classList.remove("border-success", "border-warning", "border-danger");
97
Added:
const borderClass = qualityBorderClass(state);
98
Added:
if (borderClass) item.classList.add(borderClass);
94
99
95
100
const marker = item.querySelector("[data-reading-quick-marker]");
96
101
marker.style.left = `${percent}%`;
roles/dashboard/t/05-status-page.t
@@ -19,18 +19,18 @@
19
19
'nav[aria-label="Breadcrumb"] [aria-current="page"]', 'Status' )
20
20
->text_is( 'h1', 'DAQ Status' )
21
21
->element_exists('main.container')
22
Removed:
->element_exists('[data-status-page-groups]');
22
Added:
->element_exists('.list-group');
23
23
24
24
for my $item (qw(hub ph do orp ec)) {
25
25
$t->element_exists(
26
Removed:
qq{[data-status-page-probe="$item"] [data-status-page-item="$item"] [data-status-page-state]});
26
Added:
qq{[data-status-page-item="$item"] [data-status-page-state]});
27
27
}
28
28
29
29
for my $probe (qw(ph do orp ec)) {
30
30
$t->element_exists(
31
Removed:
qq{[data-status-page-probe="$probe"] h2 a[href="/probes/$probe"]});
31
Added:
qq{[data-status-page-item="$probe"] h2 a[href="/probes/$probe"]});
32
32
}
33
33
34
Removed:
$t->element_exists_not('[data-status-page-probe="hub"] h2 a');
34
Added:
$t->element_exists_not('[data-status-page-item="hub"] h2 a');
35
35
36
36
done_testing;
roles/dashboard/templates/dashboard/index.html.ep
@@ -9,10 +9,10 @@
9
9
<h1 class="display-5 fw-bold mb-1">FAPG DAQ Dashboard</h1>
10
10
<p class="text-muted mb-2">Live aquaponic sensor readings</p>
11
11
<nav aria-label="Quick links">
12
Removed:
<ul class="list-inline mb-0">
13
Removed:
<li class="list-inline-item"><a class="text-success fw-semibold text-decoration-none" href="/probes">Probes</a></li>
14
Removed:
<li class="list-inline-item"><a class="text-success fw-semibold text-decoration-none" href="/status">Status</a></li>
15
Removed:
<li class="list-inline-item"><a class="text-success fw-semibold text-decoration-none" href="/downloads">Downloads</a></li>
12
Added:
<ul class="nav nav-pills">
13
Added:
<li class="nav-item"><a class="nav-link" href="/probes">Probes</a></li>
14
Added:
<li class="nav-item"><a class="nav-link" href="/status">Status</a></li>
15
Added:
<li class="nav-item"><a class="nav-link" href="/downloads">Downloads</a></li>
16
16
</ul>
17
17
</nav>
18
18
</div>
@@ -24,7 +24,7 @@
24
24
<div class="col-12 col-sm-6 col-xl-3">
25
25
<a class="reading-quick-link text-decoration-none text-body d-block h-100" href="/probes/<%= $probe->{key} %>">
26
26
<article
27
Removed:
class="card h-100 reading-quick-item is-unknown"
27
Added:
class="card h-100 reading-quick-item"
28
28
data-reading-quick-probe="<%= $probe->{key} %>"
29
29
data-reading-quick-label="<%= $probe->{label} %>"
30
30
data-reading-quick-unit="<%= $probe->{unit} %>">
roles/dashboard/templates/dashboard/status.html.ep
@@ -9,42 +9,39 @@
9
9
<p class="text-muted small mb-0 mt-1">Current connection status for the hub and sensor probes.</p>
10
10
</div>
11
11
12
Removed:
<div class="card-body">
13
Removed:
<div class="d-grid gap-4" data-status-page-groups>
14
Removed:
% for my $item ($items->@*) {
15
Removed:
<section
16
Removed:
data-status-page-probe="<%= $item->{key} %>"
17
Removed:
aria-labelledby="status-probe-<%= $item->{key} %>">
18
Removed:
<h2 id="status-probe-<%= $item->{key} %>" class="h6 fw-semibold mb-2 text-uppercase text-muted" data-status-page-label>
19
Removed:
% if ($item->{key} ne 'hub') {
12
Added:
<div class="list-group list-group-flush">
13
Added:
% for my $item ($items->@*) {
14
Added:
<div class="list-group-item" data-status-page-item="<%= $item->{key} %>">
15
Added:
<div class="d-flex align-items-center gap-3">
16
Added:
<span class="status-node-marker is-unknown" aria-hidden="true"></span>
17
Added:
<div class="flex-grow-1 min-w-0">
18
Added:
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2">
19
Added:
<h2 class="h6 fw-semibold mb-0">
20
Added:
% if ($item->{key} ne 'hub') {
20
21
<a class="text-body text-decoration-none" href="/probes/<%= $item->{key} %>"><%= $item->{label} %></a>
21
Removed:
% } else {
22
Added:
% } else {
22
23
<%= $item->{label} %>
23
Removed:
% }
24
Removed:
</h2>
25
Removed:
<ol class="list-unstyled mb-0" aria-label="<%= $item->{label} %> nodes">
26
Removed:
<li class="d-flex gap-3 align-items-start" data-status-page-item="<%= $item->{key} %>">
27
Removed:
<span class="status-node-marker is-unknown flex-shrink-0 mt-2" aria-hidden="true"></span>
28
Removed:
<div class="border rounded p-3 flex-grow-1 bg-white">
29
Removed:
<div class="d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2">
30
Removed:
<p class="fw-bold mb-0 status-node-name" data-status-page-node>Checking…</p>
31
Removed:
<span class="badge status-pill is-unknown" data-status-page-state>Checking</span>
32
Removed:
</div>
33
Removed:
<dl class="row row-cols-1 row-cols-sm-2 g-2 mb-0 small">
34
Removed:
<div class="col">
35
Removed:
<dt class="text-muted text-uppercase fw-semibold status-node-dt">Last status</dt>
36
Removed:
<dd class="mb-0" data-status-page-last-seen>Checking…</dd>
37
Removed:
</div>
38
Removed:
<div class="col">
39
Removed:
<dt class="text-muted text-uppercase fw-semibold status-node-dt">Message</dt>
40
Removed:
<dd class="mb-0" data-status-page-message>Checking status…</dd>
41
Removed:
</div>
42
Removed:
</dl>
43
Removed:
</div>
44
Removed:
</li>
45
Removed:
</ol>
46
Removed:
</section>
47
Removed:
% }
24
Added:
% }
25
Added:
</h2>
26
Added:
<span class="badge status-pill is-unknown" data-status-page-state>Checking</span>
27
Added:
</div>
28
Added:
<div class="row row-cols-1 row-cols-sm-2 g-1 mt-1 small text-muted">
29
Added:
<div class="col">
30
Added:
<span class="fw-semibold text-uppercase status-node-dt">Node:</span>
31
Added:
<span data-status-page-node>Checking…</span>
32
Added:
</div>
33
Added:
<div class="col">
34
Added:
<span class="fw-semibold text-uppercase status-node-dt">Last seen:</span>
35
Added:
<span data-status-page-last-seen>Checking…</span>
36
Added:
</div>
37
Added:
<div class="col col-sm-12">
38
Added:
<span class="fw-semibold text-uppercase status-node-dt">Message:</span>
39
Added:
<span data-status-page-message>Checking status…</span>
40
Added:
</div>
41
Added:
</div>
42
Added:
</div>
43
Added:
</div>
48
44
</div>
45
Added:
% }
49
46
</div>
50
47
</div>
roles/dashboard/templates/dashboard/toolbar.html.ep
@@ -3,13 +3,13 @@
3
3
<div id="toolbar" class="d-flex flex-column flex-md-row align-items-start align-items-md-center justify-content-between gap-2 mb-3 toolbar">
4
4
% if ($breadcrumbs->@*) {
5
5
<nav aria-label="Breadcrumb">
6
Removed:
<ol class="breadcrumb mb-0 bg-white border rounded-pill px-3 py-2 small fw-bold">
6
Added:
<ol class="breadcrumb mb-0 small">
7
7
% for my $index (0 .. $breadcrumbs->$#*) {
8
8
% my $breadcrumb = $breadcrumbs->[$index];
9
9
% my $is_last = $index == $breadcrumbs->$#*;
10
10
<li class="breadcrumb-item<%= $is_last ? ' active' : '' %>">
11
11
% if (defined $breadcrumb->{href}) {
12
Removed:
<a class="text-success text-decoration-none fw-bold" href="<%= $breadcrumb->{href} %>"><%= $breadcrumb->{label} %></a>
12
Added:
<a href="<%= $breadcrumb->{href} %>"><%= $breadcrumb->{label} %></a>
13
13
% } else {
14
14
<span aria-current="page"><%= $breadcrumb->{label} %></span>
15
15
% }