[Perl] DAQ system for the FAPG.
feat add contextual toolbar
- add conventional breadcrumbs to every page through a shared toolbar - move graph controls and the homepage status summary into page-specific toolbar items - align pill-shaped toolbar controls across desktop and mobile layouts and expand page coverage
Changed files
- roles/dashboard/lib/FAPG/DAQ/Dashboard/Controller/Dashboard.pm
- roles/dashboard/public/css/app.css
- roles/dashboard/t/00-homepage.t
- roles/dashboard/t/01-graph-page.t
- roles/dashboard/t/05-status-page.t
- roles/dashboard/t/06-downloads.t
- roles/dashboard/templates/dashboard/graph.html.ep
- roles/dashboard/templates/dashboard/index.html.ep
- roles/dashboard/templates/dashboard/probes.html.ep
- roles/dashboard/templates/dashboard/toolbar.html.ep
- roles/dashboard/templates/layouts/default.html.ep
roles/dashboard/lib/FAPG/DAQ/Dashboard/Controller/Dashboard.pm
@@ -7,22 +7,29 @@
7
7
8
8
sub index ($self) {
9
9
$self->render(
10
Removed:
template => 'dashboard/index',
11
Removed:
probes => $self->probes,
10
Added:
template => 'dashboard/index',
11
Added:
probes => $self->probes,
12
Added:
breadcrumbs => [ { label => 'Home' } ],
13
Added:
show_overview_status => 1,
12
14
);
13
15
}
14
16
15
17
sub status ($self) {
16
18
$self->render(
17
Removed:
template => 'dashboard/status',
18
Removed:
items => $self->status_items,
19
Added:
template => 'dashboard/status',
20
Added:
items => $self->status_items,
21
Added:
breadcrumbs =>
22
Added:
[ { label => 'Home', href => '/' }, { label => 'Status' }, ],
19
23
);
20
24
}
21
25
22
26
sub probe_list ($self) {
23
27
$self->render(
24
Removed:
template => 'dashboard/probes',
25
Removed:
probes => $self->probes,
28
Added:
template => 'dashboard/probes',
29
Added:
probes => $self->probes,
30
Added:
breadcrumbs =>
31
Added:
[ { label => 'Home', href => '/' }, { label => 'Probes' }, ],
32
Added:
graph_control_aria_label => 'All probe chart timeframes',
26
33
);
27
34
}
28
35
@@ -49,12 +56,14 @@
49
56
$oldest =~ s/T.*\z//;
50
57
51
58
$self->render(
52
Removed:
template => 'dashboard/downloads',
53
Removed:
from => $from,
54
Removed:
month_from => $month_from,
55
Removed:
to => $to,
56
Removed:
oldest => $oldest,
57
Removed:
probes => $self->probes,
59
Added:
template => 'dashboard/downloads',
60
Added:
from => $from,
61
Added:
month_from => $month_from,
62
Added:
to => $to,
63
Added:
oldest => $oldest,
64
Added:
probes => $self->probes,
65
Added:
breadcrumbs =>
66
Added:
[ { label => 'Home', href => '/' }, { label => 'Downloads' }, ],
58
67
);
59
68
}
60
69
@@ -65,8 +74,14 @@
65
74
return $self->reply->not_found unless defined $probe;
66
75
67
76
$self->render(
68
Removed:
template => 'dashboard/graph',
69
Removed:
probe => $probe,
77
Added:
template => 'dashboard/graph',
78
Added:
probe => $probe,
79
Added:
breadcrumbs => [
80
Added:
{ label => 'Home', href => '/' },
81
Added:
{ label => 'Probes', href => '/probes' },
82
Added:
{ label => $probe->{label} },
83
Added:
],
84
Added:
graph_control_aria_label => "$probe->{label} chart timeframe",
70
85
);
71
86
}
72
87
roles/dashboard/public/css/app.css
@@ -57,7 +57,7 @@
57
57
justify-content: flex-start;
58
58
gap: 1rem;
59
59
width: 100%;
60
Removed:
margin: 0 0 1.5rem;
60
Added:
margin: 0 0 0.75rem;
61
61
border: 1px solid var(--border);
62
62
border-radius: 0 0 0.5rem 0.5rem;
63
63
padding: 0.7rem 0.85rem;
@@ -65,6 +65,92 @@
65
65
box-shadow: 0 8px 24px rgba(23, 33, 27, 0.08);
66
66
}
67
67
68
Added:
.toolbar {
69
Added:
--toolbar-item-height: 2.75rem;
70
Added:
71
Added:
display: flex;
72
Added:
align-items: center;
73
Added:
justify-content: space-between;
74
Added:
gap: 1rem;
75
Added:
width: min(1100px, calc(100% - 2rem));
76
Added:
margin: 0 auto 1rem;
77
Added:
}
78
Added:
79
Added:
.breadcrumbs {
80
Added:
display: inline-flex;
81
Added:
align-self: flex-start;
82
Added:
align-items: center;
83
Added:
max-width: 100%;
84
Added:
min-height: var(--toolbar-item-height);
85
Added:
min-width: 0;
86
Added:
border: 1px solid var(--border);
87
Added:
border-radius: 999px;
88
Added:
padding: 0.45rem 0.8rem;
89
Added:
background: var(--card);
90
Added:
color: var(--muted);
91
Added:
font-size: 0.88rem;
92
Added:
}
93
Added:
94
Added:
.breadcrumbs ol {
95
Added:
display: flex;
96
Added:
flex-wrap: wrap;
97
Added:
gap: 0.4rem;
98
Added:
align-items: center;
99
Added:
margin: 0;
100
Added:
padding: 0;
101
Added:
list-style: none;
102
Added:
}
103
Added:
104
Added:
.breadcrumbs li {
105
Added:
display: inline-flex;
106
Added:
gap: 0.4rem;
107
Added:
align-items: center;
108
Added:
}
109
Added:
110
Added:
.breadcrumbs li + li::before {
111
Added:
color: var(--muted);
112
Added:
content: "/";
113
Added:
font-weight: 700;
114
Added:
}
115
Added:
116
Added:
.breadcrumbs a {
117
Added:
color: var(--accent);
118
Added:
font-weight: 700;
119
Added:
text-decoration: none;
120
Added:
}
121
Added:
122
Added:
.breadcrumbs a:hover {
123
Added:
text-decoration: underline;
124
Added:
}
125
Added:
126
Added:
.breadcrumbs [aria-current="page"] {
127
Added:
color: var(--text);
128
Added:
font-weight: 700;
129
Added:
}
130
Added:
131
Added:
.toolbar-items {
132
Added:
display: flex;
133
Added:
flex: 0 1 auto;
134
Added:
align-items: center;
135
Added:
justify-content: flex-end;
136
Added:
min-height: var(--toolbar-item-height);
137
Added:
min-width: 0;
138
Added:
margin-left: auto;
139
Added:
}
140
Added:
141
Added:
.toolbar .graph-control,
142
Added:
.toolbar .status-pill,
143
Added:
.toolbar .timeframe-control,
144
Added:
.toolbar .smoothing-control {
145
Added:
min-height: var(--toolbar-item-height);
146
Added:
}
147
Added:
148
Added:
.toolbar .status-pill,
149
Added:
.toolbar .smoothing-control {
150
Added:
align-items: center;
151
Added:
justify-content: center;
152
Added:
}
153
Added:
68
154
.page-brand {
69
155
color: var(--accent);
70
156
font-size: 1.15rem;
@@ -578,10 +664,10 @@
578
664
.graph-control {
579
665
display: flex;
580
666
flex-wrap: wrap;
581
Removed:
justify-content: center;
667
Added:
justify-content: flex-end;
582
668
align-items: center;
583
669
gap: 0.75rem;
584
Removed:
margin-bottom: 1rem;
670
Added:
margin: 0;
585
671
}
586
672
587
673
.graph-control .timeframe-option {
@@ -851,10 +937,27 @@
851
937
margin-bottom: 1rem;
852
938
}
853
939
940
Added:
.toolbar {
941
Added:
flex-direction: column;
942
Added:
align-items: stretch;
943
Added:
width: min(100% - 1rem, 1100px);
944
Added:
margin-bottom: 1rem;
945
Added:
}
946
Added:
947
Added:
.toolbar-items {
948
Added:
width: 100%;
949
Added:
margin-left: 0;
950
Added:
}
951
Added:
952
Added:
.toolbar-items .graph-control {
953
Added:
width: 100%;
954
Added:
justify-content: flex-start;
955
Added:
}
956
Added:
854
957
.page-header {
855
958
align-items: center;
856
959
width: 100%;
857
Removed:
margin: 0 0 1rem;
960
Added:
margin: 0 0 0.75rem;
858
961
border-right: 0;
859
962
border-left: 0;
860
963
border-radius: 0 0 0.5rem 0.5rem;
roles/dashboard/t/00-homepage.t
@@ -14,11 +14,21 @@
14
14
$t->get_ok('/')
15
15
->status_is(200)
16
16
->element_exists('body > header.page-header')
17
Added:
->element_exists(
18
Added:
'body > header.page-header + div.toolbar + main.site-main')
17
19
->element_exists_not('main.site-main > header.page-header')
20
Added:
->text_is( 'nav.breadcrumbs [aria-current="page"]', 'Home' )
21
Added:
->element_exists_not('nav.breadcrumbs a[href="/"]')
18
22
->element_exists('input.nav-toggle[type="checkbox"]')
19
23
->text_is( 'a.page-brand[href="/"]', 'FAPG' )
20
Removed:
->element_exists('a.header-status-link[href="/status"][data-header-status]')
21
Removed:
->element_exists('a.status-summary[href="/status"][data-overview-status]')
24
Added:
->element_exists(
25
Added:
'a.header-status-link[href="/status"][data-header-status]')
26
Added:
->element_exists(
27
Added:
'div.toolbar-items a.status-summary[href="/status"][data-overview-status]'
28
Added:
)
29
Added:
->element_exists_not(
30
Added:
'main.site-main a.status-summary[data-overview-status]')
31
Added:
->element_exists_not('div.toolbar div.graph-control')
22
32
->element_exists('a.header-status-link span.header-status-led')
23
33
->element_exists('[data-header-status-item="hub"]')
24
34
->element_exists('[data-header-status-item="ph"]')
roles/dashboard/t/01-graph-page.t
@@ -13,8 +13,13 @@
13
13
14
14
$t->get_ok('/probes/ph')
15
15
->status_is(200)
16
Removed:
->text_is( 'a.page-brand[href="/"]', 'FAPG' )
17
16
->element_exists(
17
Added:
'body > header.page-header + div.toolbar + main.site-main')
18
Added:
->text_is( 'nav.breadcrumbs a[href="/"]', 'Home' )
19
Added:
->text_is( 'nav.breadcrumbs a[href="/probes"]', 'Probes' )
20
Added:
->text_is( 'nav.breadcrumbs [aria-current="page"]', 'pH' )
21
Added:
->text_is( 'a.page-brand[href="/"]', 'FAPG' )
22
Added:
->element_exists(
18
23
'a.header-status-link[href="/status"][data-header-status]')
19
24
->element_exists('a.header-status-link span.header-status-led')
20
25
->element_exists('[data-header-status-item="hub"]')
@@ -43,7 +48,9 @@
43
48
->content_like(qr/Weekly/)
44
49
->content_like(qr/Monthly/)
45
50
->content_like(qr/Yearly/)
46
Removed:
->element_exists('div.graph-control > div.timeframe-control')
51
Added:
->element_exists(
52
Added:
'div.toolbar-items div.graph-control > div.timeframe-control')
53
Added:
->element_exists_not('main.site-main div.graph-control')
47
54
->element_exists('[data-timeframe-option="week"]')
48
55
->element_exists('[data-timeframe-option="month"]')
49
56
->element_exists(
@@ -61,6 +68,9 @@
61
68
62
69
$t->get_ok('/probes')
63
70
->status_is(200)
71
Added:
->text_is( 'nav.breadcrumbs a[href="/"]', 'Home' )
72
Added:
->text_is( 'nav.breadcrumbs [aria-current="page"]', 'Probes' )
73
Added:
->element_exists_not('nav.breadcrumbs a[href="/probes"]')
64
74
->element_exists('div.card > div.card-heading')
65
75
->element_exists('div.card > div.card-body')
66
76
->text_is( 'h1 a[href="/probes/ph"]', 'pH' )
@@ -80,7 +90,8 @@
80
90
->element_exists('a[data-raw-download][data-probe="orp"]')
81
91
->element_exists('a[data-raw-download][data-probe="ec"]')
82
92
->element_exists('div.probes-grid')
83
Removed:
->element_exists('div.graph-control > div.timeframe-control')
93
Added:
->element_exists(
94
Added:
'div.toolbar-items div.graph-control > div.timeframe-control')
84
95
->element_exists_not('div.card div.timeframe-control')
85
96
->element_exists('[data-timeframe-option="week"]')
86
97
->element_exists(
roles/dashboard/t/05-status-page.t
@@ -13,10 +13,16 @@
13
13
14
14
$t->get_ok('/status')
15
15
->status_is(200)
16
Removed:
->text_is('h1', 'DAQ Status')
16
Added:
->element_exists(
17
Added:
'body > header.page-header + div.toolbar + main.site-main')
18
Added:
->text_is( 'nav.breadcrumbs a[href="/"]', 'Home' )
19
Added:
->text_is( 'nav.breadcrumbs [aria-current="page"]', 'Status' )
20
Added:
->element_exists_not('div.toolbar-items')
21
Added:
->text_is( 'h1', 'DAQ Status' )
17
22
->element_exists('div.card.status-page-card > div.card-heading')
18
23
->element_exists('div.card.status-page-card > div.card-body')
19
Removed:
->element_exists('a.header-status-link[href="/status"][data-header-status]')
24
Added:
->element_exists(
25
Added:
'a.header-status-link[href="/status"][data-header-status]')
20
26
->element_exists('table.status-table')
21
27
->element_exists('tr[data-status-page-item="hub"]')
22
28
->element_exists('tr[data-status-page-item="ph"]')
roles/dashboard/t/06-downloads.t
@@ -14,6 +14,11 @@
14
14
15
15
$t->get_ok('/downloads')
16
16
->status_is(200)
17
Added:
->element_exists(
18
Added:
'body > header.page-header + div.toolbar + main.site-main')
19
Added:
->text_is( 'nav.breadcrumbs a[href="/"]', 'Home' )
20
Added:
->text_is( 'nav.breadcrumbs [aria-current="page"]', 'Downloads' )
21
Added:
->element_exists_not('div.toolbar-items')
17
22
->text_is( 'div.nav-links a[href="/probes"]', 'Probes' )
18
23
->text_is( 'div.nav-links a[href="/downloads"]', 'Downloads' )
19
24
->element_exists(
roles/dashboard/templates/dashboard/graph.html.ep
@@ -3,8 +3,6 @@
3
3
% layout 'default';
4
4
% title "FAPG DAQ $probe->{label}";
5
5
6
Removed:
%= include 'dashboard/graph-control', aria_label => "$probe->{label} chart timeframe"
7
Removed:
8
6
%= include 'dashboard/probe-card', probe => $probe
9
7
10
8
<section class="card message-log" aria-label="Last ten reading messages for <%= $probe->{label} %>">
roles/dashboard/templates/dashboard/index.html.ep
@@ -5,10 +5,6 @@
5
5
6
6
<section class="status-band" aria-label="DAQ status overview">
7
7
<div class="status-overview">
8
Removed:
<a class="status-pill status-summary is-unknown" href="/status" data-overview-status>
9
Removed:
<span data-status-text>Checking sensors</span>
10
Removed:
</a>
11
Removed:
12
8
% for my $probe ($probes->@*) {
13
9
<span
14
10
hidden
roles/dashboard/templates/dashboard/probes.html.ep
@@ -3,8 +3,6 @@
3
3
% layout 'default';
4
4
% title 'FAPG DAQ Probes';
5
5
6
Removed:
%= include 'dashboard/graph-control', aria_label => 'All probe chart timeframes'
7
Removed:
8
6
<div class="probes-grid">
9
7
% for my $probe ($probes->@*) {
10
8
%= include 'dashboard/probe-card', probe => $probe
roles/dashboard/templates/dashboard/toolbar.html.ep
@@ -0,0 +1,30 @@
1
Added:
<!-- -*- mode: web; -*- -->
2
Added:
3
Added:
<div class="toolbar">
4
Added:
<nav class="breadcrumbs" aria-label="Breadcrumb">
5
Added:
<ol>
6
Added:
% for my $index (0 .. $breadcrumbs->$#*) {
7
Added:
% my $breadcrumb = $breadcrumbs->[$index];
8
Added:
<li>
9
Added:
% if (defined $breadcrumb->{href}) {
10
Added:
<a href="<%= $breadcrumb->{href} %>"><%= $breadcrumb->{label} %></a>
11
Added:
% } else {
12
Added:
<span aria-current="page"><%= $breadcrumb->{label} %></span>
13
Added:
% }
14
Added:
</li>
15
Added:
% }
16
Added:
</ol>
17
Added:
</nav>
18
Added:
% if (defined $graph_control_aria_label || $show_overview_status) {
19
Added:
<div class="toolbar-items">
20
Added:
% if (defined $graph_control_aria_label) {
21
Added:
%= include 'dashboard/graph-control', aria_label => $graph_control_aria_label
22
Added:
% }
23
Added:
% if ($show_overview_status) {
24
Added:
<a class="status-pill status-summary is-unknown" href="/status" data-overview-status>
25
Added:
<span data-status-text>Checking sensors</span>
26
Added:
</a>
27
Added:
% }
28
Added:
</div>
29
Added:
% }
30
Added:
</div>
roles/dashboard/templates/layouts/default.html.ep
@@ -39,6 +39,7 @@
39
39
data-header-status-label="<%= $item->{label} %>"></span>
40
40
% }
41
41
</header>
42
Added:
%= include 'dashboard/toolbar', breadcrumbs => (stash('breadcrumbs') // []), graph_control_aria_label => stash('graph_control_aria_label'), show_overview_status => stash('show_overview_status')
42
43
<main class="site-main">
43
44
<%= content %>
44
45
</main>