[Perl] DAQ system for the FAPG.
refactor replace breadcrumbs with inline page headers
Remove the breadcrumb toolbar bar; pages now render their own sticky title and controls. Navbar active state provides sufficient context; aligns all pages with the weather/insights pattern.
Changed files
- roles/dashboard/lib/FAPG/DAQ/Dashboard/Controller/Pages.pm
- roles/dashboard/t/01-graph-page.t
- roles/dashboard/t/05-status-page.t
- roles/dashboard/t/06-downloads.t
- roles/dashboard/t/08-insights.t
- roles/dashboard/templates/dashboard/downloads.html.ep
- roles/dashboard/templates/dashboard/graph.html.ep
- roles/dashboard/templates/dashboard/probes.html.ep
- roles/dashboard/templates/dashboard/status.html.ep
- roles/dashboard/templates/dashboard/toolbar.html.ep
roles/dashboard/lib/FAPG/DAQ/Dashboard/Controller/Pages.pm
@@ -5,33 +5,24 @@
5
5
6
6
sub index ($self) {
7
7
$self->render(
8
Removed:
template => 'dashboard/index',
9
Removed:
probes => $self->probes,
10
Removed:
breadcrumbs => [],
8
Added:
template => 'dashboard/index',
9
Added:
probes => $self->probes,
11
10
);
12
11
}
13
12
14
13
sub status ($self) {
15
14
$self->render(
16
Removed:
template => 'dashboard/status',
17
Removed:
items => $self->status_items,
18
Removed:
nav_page => 'probes',
19
Removed:
breadcrumbs => [
20
Removed:
{ label => 'Home', href => '/' },
21
Removed:
{ label => 'Probes', href => '/probes' },
22
Removed:
{ label => 'Status' },
23
Removed:
],
15
Added:
template => 'dashboard/status',
16
Added:
items => $self->status_items,
17
Added:
nav_page => 'probes',
24
18
);
25
19
}
26
20
27
21
sub probe_list ($self) {
28
22
$self->render(
29
Removed:
template => 'dashboard/probes',
30
Removed:
probes => $self->probes,
31
Removed:
nav_page => 'probes',
32
Removed:
breadcrumbs =>
33
Removed:
[ { label => 'Home', href => '/' }, { label => 'Probes' }, ],
34
Removed:
graph_control_aria_label => 'All probe chart timeframes',
23
Added:
template => 'dashboard/probes',
24
Added:
probes => $self->probes,
25
Added:
nav_page => 'probes',
35
26
);
36
27
}
37
28
@@ -46,34 +37,28 @@
46
37
$oldest =~ s/T.*\z//;
47
38
48
39
$self->render(
49
Removed:
template => 'dashboard/downloads',
50
Removed:
from => $from,
51
Removed:
month_from => $month_from,
52
Removed:
to => $to,
53
Removed:
oldest => $oldest,
54
Removed:
probes => $self->probes,
55
Removed:
nav_page => 'downloads',
56
Removed:
breadcrumbs =>
57
Removed:
[ { label => 'Home', href => '/' }, { label => 'Downloads' }, ],
40
Added:
template => 'dashboard/downloads',
41
Added:
from => $from,
42
Added:
month_from => $month_from,
43
Added:
to => $to,
44
Added:
oldest => $oldest,
45
Added:
probes => $self->probes,
46
Added:
nav_page => 'downloads',
58
47
);
59
48
}
60
49
61
50
sub insights ($self) {
62
51
$self->render(
63
Removed:
template => 'dashboard/insights',
64
Removed:
probes => $self->probes,
65
Removed:
nav_page => 'insights',
66
Removed:
breadcrumbs =>
67
Removed:
[ { label => 'Home', href => '/' }, { label => 'Insights' }, ],
52
Added:
template => 'dashboard/insights',
53
Added:
probes => $self->probes,
54
Added:
nav_page => 'insights',
68
55
);
69
56
}
70
57
71
58
sub weather ($self) {
72
59
$self->render(
73
Removed:
template => 'dashboard/weather',
74
Removed:
nav_page => 'weather',
75
Removed:
breadcrumbs =>
76
Removed:
[ { label => 'Home', href => '/' }, { label => 'Weather' }, ],
60
Added:
template => 'dashboard/weather',
61
Added:
nav_page => 'weather',
77
62
);
78
63
}
79
64
@@ -88,12 +73,6 @@
88
73
probe => $probe,
89
74
nav_page => 'probes',
90
75
single_probe => 1,
91
Removed:
breadcrumbs => [
92
Removed:
{ label => 'Home', href => '/' },
93
Removed:
{ label => 'Probes', href => '/probes' },
94
Removed:
{ label => $probe->{label} },
95
Removed:
],
96
Removed:
graph_control_aria_label => "$probe->{label} chart timeframe",
97
76
);
98
77
}
99
78
roles/dashboard/t/01-graph-page.t
@@ -11,19 +11,12 @@
11
11
12
12
$t->get_ok('/probes/ph')
13
13
->status_is(200)
14
Removed:
->element_exists(
15
Removed:
'nav[aria-label="Breadcrumb"] a[href="/"]')
16
Removed:
->text_is( 'nav[aria-label="Breadcrumb"] a[href="/"]', 'Home' )
17
Removed:
->text_is( 'nav[aria-label="Breadcrumb"] a[href="/probes"]', 'Probes' )
18
Removed:
->text_is(
19
Removed:
'nav[aria-label="Breadcrumb"] [aria-current="page"]', 'pH' )
14
Added:
->text_is( 'h1', 'pH' )
15
Added:
->element_exists('.sticky-controls .graph-control')
20
16
->text_is( '.card-header h1 a[href="/probes/ph"]', 'pH' )
21
17
->element_exists('.navbar-nav a.nav-link.active.dropdown-toggle')
22
18
->element_exists('section#probe-ph[data-panel="ph"] canvas#chart-ph')
23
19
->element_exists_not('[data-status="ph"]')
24
Removed:
->element_exists(
25
Removed:
'div.toolbar-items div.graph-control .timeframe-control[role="group"]'
26
Removed:
)
27
20
->text_is( 'a[data-raw-download][data-probe="ph"]', 'Download data' )
28
21
->element_exists(
29
22
'button[data-timeframe="day"].timeframe-button.btn-outline-secondary'
@@ -47,13 +40,10 @@
47
40
48
41
$t->get_ok('/probes')
49
42
->status_is(200)
50
Removed:
->element_exists(
51
Removed:
'nav[aria-label="Breadcrumb"] a[href="/"]')
52
Removed:
->text_is(
53
Removed:
'nav[aria-label="Breadcrumb"] [aria-current="page"]', 'Probes' )
43
Added:
->text_is( 'h1', 'Probes' )
44
Added:
->element_exists('.sticky-controls .graph-control')
54
45
->element_exists('main.container')
55
46
->element_exists('div.probes-grid')
56
Removed:
->element_exists('div.toolbar-items div.graph-control')
57
47
->element_exists('.navbar-nav a.nav-link.active.dropdown-toggle');
58
48
59
49
for my $probe (qw(ph do orp ec)) {
roles/dashboard/t/05-status-page.t
@@ -11,11 +11,7 @@
11
11
12
12
$t->get_ok('/probes/status')
13
13
->status_is(200)
14
Removed:
->element_exists(
15
Removed:
'nav[aria-label="Breadcrumb"] a[href="/"]')
16
Removed:
->text_is(
17
Removed:
'nav[aria-label="Breadcrumb"] [aria-current="page"]', 'Status' )
18
Removed:
->text_is( 'h1', 'DAQ Status' )
14
Added:
->text_is( 'h1', 'Status' )
19
15
->element_exists('main.container')
20
16
->element_exists('.list-group');
21
17
roles/dashboard/t/06-downloads.t
@@ -11,11 +11,7 @@
11
11
12
12
$t->get_ok('/downloads')
13
13
->status_is(200)
14
Removed:
->element_exists(
15
Removed:
'nav[aria-label="Breadcrumb"] a[href="/"]')
16
Removed:
->text_is(
17
Removed:
'nav[aria-label="Breadcrumb"] [aria-current="page"]', 'Downloads' )
18
Removed:
->text_is( 'h1', 'Downloads' )
14
Added:
->text_is( 'h1', 'Downloads' )
19
15
->element_exists('.navbar-nav a.nav-link.active[href="/downloads"][aria-current="page"]')
20
16
->element_exists('form.downloads-form[action="/downloads/readings.csv"]')
21
17
->element_exists('[data-downloads-error]')
roles/dashboard/t/08-insights.t
@@ -13,8 +13,7 @@
13
13
$t->get_ok('/insights')
14
14
->status_is(200)
15
15
->text_is('title', 'FAPG DAQ Insights')
16
Removed:
->element_exists('nav[aria-label="Breadcrumb"]')
17
Removed:
->text_like('.breadcrumb-item.active span', qr/Insights/)
16
Added:
->text_is('h1', 'Insights')
18
17
->element_exists('.navbar-nav a.nav-link.active[href="/insights"]')
19
18
->element_exists('[data-default-timeframe="day"]');
20
19
};
roles/dashboard/templates/dashboard/downloads.html.ep
@@ -1,12 +1,14 @@
1
1
% layout 'default';
2
2
% title 'FAPG DAQ Downloads';
3
3
4
Removed:
<div class="card">
5
Removed:
<div class="card-header">
6
Removed:
<h1 class="h5 mb-0">Downloads</h1>
7
Removed:
<p class="text-body-secondary small mb-0 mt-1">Download recorded DAQ readings as a CSV or Excel file.</p>
4
Added:
<section class="mb-4" aria-label="Data downloads">
5
Added:
<div class="mb-3">
6
Added:
<h1 class="h4 fw-bold mb-1">Downloads</h1>
7
Added:
<p class="text-body-secondary small mb-0">Download recorded DAQ readings as a CSV or Excel file.</p>
8
8
</div>
9
9
10
Added:
<div class="card">
11
Added:
10
12
<div class="card-body">
11
13
<div class="alert alert-danger d-none" role="alert" id="downloads-error" data-downloads-error></div>
12
14
<form action="/downloads/readings.csv" method="get" class="downloads-form d-flex flex-column gap-3">
@@ -71,5 +73,7 @@
71
73
</form>
72
74
</div>
73
75
</div>
76
Added:
77
Added:
</section>
74
78
75
79
<script src="/js/downloads.js"></script>
roles/dashboard/templates/dashboard/graph.html.ep
@@ -2,6 +2,12 @@
2
2
% title "FAPG DAQ $probe->{label}";
3
3
% stash use_charts => 1;
4
4
5
Added:
<section class="mb-4" aria-label="<%= $probe->{label} %> probe detail">
6
Added:
<div class="d-flex flex-column flex-sm-row align-items-start align-items-sm-center justify-content-between gap-2 mb-3 sticky-controls">
7
Added:
<h1 class="h4 fw-bold mb-0"><%= $probe->{label} %></h1>
8
Added:
%= include 'dashboard/graph-control', aria_label => "$probe->{label} chart timeframe", default_timeframe => 'day'
9
Added:
</div>
10
Added:
5
11
%= include 'dashboard/probe-card', probe => $probe
6
12
7
13
<div class="card mt-3" aria-label="Last ten reading messages for <%= $probe->{label} %>">
@@ -29,3 +35,5 @@
29
35
</div>
30
36
</div>
31
37
</div>
38
Added:
39
Added:
</section>
roles/dashboard/templates/dashboard/probes.html.ep
@@ -2,10 +2,17 @@
2
2
% title 'FAPG DAQ Probes';
3
3
% stash use_charts => 1;
4
4
5
Removed:
<div class="row g-3 probes-grid">
6
Removed:
% for my $probe ($probes->@*) {
7
Removed:
<div class="col-12 col-md-6 col-xl-3">
8
Removed:
%= include 'dashboard/probe-card', probe => $probe
5
Added:
<section class="mb-4" aria-label="Probe readings">
6
Added:
<div class="d-flex flex-column flex-sm-row align-items-start align-items-sm-center justify-content-between gap-2 mb-3 sticky-controls">
7
Added:
<h1 class="h4 fw-bold mb-0">Probes</h1>
8
Added:
%= include 'dashboard/graph-control', aria_label => 'All probe chart timeframes', default_timeframe => 'day'
9
9
</div>
10
Removed:
% }
11
Removed:
</div>
10
Added:
11
Added:
<div class="row g-3 probes-grid">
12
Added:
% for my $probe ($probes->@*) {
13
Added:
<div class="col-12 col-md-6 col-xl-3">
14
Added:
%= include 'dashboard/probe-card', probe => $probe
15
Added:
</div>
16
Added:
% }
17
Added:
</div>
18
Added:
</section>
roles/dashboard/templates/dashboard/status.html.ep
@@ -1,12 +1,14 @@
1
1
% layout 'default';
2
2
% title 'FAPG DAQ Status';
3
3
4
Removed:
<div class="card">
5
Removed:
<div class="card-header">
6
Removed:
<h1 class="h5 mb-0">DAQ Status</h1>
7
Removed:
<p class="text-body-secondary small mb-0 mt-1">Current connection status for the hub and sensor probes.</p>
4
Added:
<section class="mb-4" aria-label="DAQ status">
5
Added:
<div class="mb-3">
6
Added:
<h1 class="h4 fw-bold mb-1">Status</h1>
7
Added:
<p class="text-body-secondary small mb-0">Current connection status for the hub and sensor probes.</p>
8
8
</div>
9
9
10
Added:
<div class="card">
11
Added:
10
12
<div class="list-group list-group-flush">
11
13
% for my $item ($items->@*) {
12
14
<div class="list-group-item" data-status-page-item="<%= $item->{key} %>">
@@ -43,3 +45,5 @@
43
45
% }
44
46
</div>
45
47
</div>
48
Added:
49
Added:
</section>
roles/dashboard/templates/dashboard/toolbar.html.ep
@@ -1,24 +1,7 @@
1
Removed:
<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">
2
Removed:
% if ($breadcrumbs->@*) {
3
Removed:
<nav aria-label="Breadcrumb">
4
Removed:
<ol class="breadcrumb mb-0 small">
5
Removed:
% for my $index (0 .. $breadcrumbs->$#*) {
6
Removed:
% my $breadcrumb = $breadcrumbs->[$index];
7
Removed:
% my $is_last = $index == $breadcrumbs->$#*;
8
Removed:
<li class="breadcrumb-item<%= $is_last ? ' active' : '' %>">
9
Removed:
% if (defined $breadcrumb->{href}) {
10
Removed:
<a href="<%= $breadcrumb->{href} %>"><%= $breadcrumb->{label} %></a>
11
Removed:
% } else {
12
Removed:
<span aria-current="page"><%= $breadcrumb->{label} %></span>
13
Removed:
% }
14
Removed:
</li>
15
Removed:
% }
16
Removed:
</ol>
17
Removed:
</nav>
18
Removed:
% }
19
Removed:
% if (defined $graph_control_aria_label) {
1
Added:
<% if (defined $graph_control_aria_label) { %>
2
Added:
<div id="toolbar" class="d-flex align-items-center justify-content-end mb-3">
20
3
<div class="toolbar-items d-flex align-items-center gap-2 ms-md-auto overflow-x-auto mw-100">
21
4
%= include 'dashboard/graph-control', aria_label => $graph_control_aria_label
22
5
</div>
23
Removed:
% }
24
6
</div>
7
Added:
<% } %>