@andrian.yablonskyy/thub-coordinator 1.0.42 → 1.0.43

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@andrian.yablonskyy/thub-coordinator",
3
- "version": "1.0.42",
3
+ "version": "1.0.43",
4
4
  "description": "TestHub Coordinator — job queue, resource registry, scheduler, heartbeat monitor, log/artifact store and web dashboard",
5
5
  "main": "src/server.js",
6
6
  "engines": {
@@ -227,3 +227,111 @@
227
227
  animation: none;
228
228
  }
229
229
  }
230
+
231
+ /* Mobile / tablet menu (below lg, where the navbar collapses; layout.pug).
232
+ It drops down over the page instead of pushing it down, scrolls inside
233
+ itself on short screens, and is laid out for thumbs: pages as a two-column
234
+ grid, the tools in one row, the account in the next. public/js/actions.js
235
+ closes it on a tap outside, Esc, or choosing a page. */
236
+ .thub-nav-break {
237
+ display: none;
238
+ }
239
+
240
+ @media (max-width: 991.98px) {
241
+ .navbar .thub-nav {
242
+ position: absolute;
243
+ top: 100%;
244
+ left: 0;
245
+ right: 0;
246
+ z-index: 1035;
247
+ max-height: calc(100dvh - 4rem);
248
+ overflow-y: auto;
249
+ padding: 0.75rem 1rem 1rem;
250
+ background: var(--bs-tertiary-bg);
251
+ border-bottom: 1px solid var(--bs-border-color);
252
+ box-shadow: 0 0.75rem 1.5rem rgba(0, 0, 0, 0.15);
253
+ }
254
+
255
+ .thub-nav-pages {
256
+ display: grid;
257
+ grid-template-columns: repeat(2, minmax(0, 1fr));
258
+ gap: 0.375rem;
259
+ padding-bottom: 0.75rem;
260
+ border-bottom: 1px solid var(--bs-border-color);
261
+ }
262
+
263
+ .thub-nav-pages .nav-link {
264
+ display: flex;
265
+ align-items: center;
266
+ min-height: 2.75rem;
267
+ padding: 0.5rem 0.75rem;
268
+ border-radius: var(--bs-border-radius);
269
+ background: var(--bs-body-bg);
270
+ border: 1px solid var(--bs-border-color-translucent);
271
+ }
272
+
273
+ .thub-nav-pages .nav-link.active {
274
+ background: var(--bs-primary-bg-subtle);
275
+ border-color: var(--bs-primary-border-subtle);
276
+ color: var(--bs-primary-text-emphasis);
277
+ font-weight: 500;
278
+ }
279
+
280
+ .thub-nav-tools {
281
+ flex-direction: row;
282
+ flex-wrap: wrap;
283
+ align-items: center;
284
+ gap: 0.5rem 0.75rem;
285
+ padding-top: 0.75rem;
286
+ }
287
+
288
+ .thub-nav-tools > li {
289
+ margin: 0 !important;
290
+ }
291
+
292
+ /* Bigger tap targets for the icon buttons. */
293
+ .thub-nav-tools .btn-sm {
294
+ min-width: 2.5rem;
295
+ min-height: 2.5rem;
296
+ }
297
+
298
+ /* The account row: the profile link takes the line, sign-out at its end. */
299
+ .thub-nav-break {
300
+ display: block;
301
+ flex-basis: 100%;
302
+ height: 0;
303
+ }
304
+
305
+ .thub-nav-account {
306
+ flex: 1 1 auto;
307
+ min-height: 2.5rem;
308
+ }
309
+ }
310
+
311
+ /* Phones: wide tables scroll sideways inside their own box instead of
312
+ running off the page, with one-line headers (wrapped headers of squeezed
313
+ columns used to make the header row very tall). The resource card's tab
314
+ row scrolls sideways too rather than wrapping onto several lines. */
315
+ @media (max-width: 767.98px) {
316
+ main .table:not(.table-responsive .table) {
317
+ display: block;
318
+ max-width: 100%;
319
+ overflow-x: auto;
320
+ -webkit-overflow-scrolling: touch;
321
+ }
322
+
323
+ main .table th {
324
+ white-space: nowrap;
325
+ }
326
+
327
+ .thub-resource-card .nav-tabs {
328
+ flex-wrap: nowrap;
329
+ overflow-x: auto;
330
+ overflow-y: hidden;
331
+ scrollbar-width: thin;
332
+ }
333
+
334
+ .thub-resource-card .nav-tabs .nav-link {
335
+ white-space: nowrap;
336
+ }
337
+ }
@@ -39,6 +39,29 @@
39
39
  }
40
40
  });
41
41
 
42
+ // The collapsed (mobile) menu drops down over the page (thub.css
43
+ // .thub-nav): close it on a tap outside, on Esc, and once a page in it is
44
+ // chosen — over the content, it shouldn't stay open in the way.
45
+ const nav = document.getElementById('nav'),
46
+ closeNav = () => {
47
+ if (nav?.classList.contains('show')){
48
+ window.bootstrap?.Collapse.getOrCreateInstance(nav, { toggle: false }).hide();
49
+ }
50
+ };
51
+ document.addEventListener('click', (e) => {
52
+ if (!nav?.classList.contains('show')){
53
+ return;
54
+ }
55
+ if (!e.target.closest('#nav, .navbar-toggler') || e.target.closest('#nav a[href]')){
56
+ closeNav();
57
+ }
58
+ });
59
+ document.addEventListener('keydown', (e) => {
60
+ if (e.key === 'Escape'){
61
+ closeNav();
62
+ }
63
+ });
64
+
42
65
  // <form data-submit-busy>: disable its button and show a spinner while
43
66
  // the request runs (e.g. the navbar's check for updates).
44
67
  document.addEventListener('submit', (e) => {
@@ -16,5 +16,10 @@
16
16
  // Delegated (one instance per element, created on first hover/focus), so
17
17
  // content swapped in by live updates (public/js/live.js) gets tooltips too.
18
18
  (function (){
19
+ // On touch screens a tap would open a tooltip and leave it stuck there
20
+ // (no hover to end it) — over the mobile menu, too: no tooltips there.
21
+ if (!window.matchMedia('(hover: hover)').matches){
22
+ return;
23
+ }
19
24
  new bootstrap.Tooltip(document.body, { selector: '[data-bs-toggle="tooltip"]' });
20
25
  })();
package/views/layout.pug CHANGED
@@ -29,23 +29,18 @@ html(lang="en" data-bs-theme=(user && user.theme) || "auto")
29
29
  )= `v${updates.coordinatorUpdate} available`
30
30
  button.navbar-toggler(type="button" data-bs-toggle="collapse" data-bs-target="#nav")
31
31
  span.navbar-toggler-icon
32
- #nav.collapse.navbar-collapse
33
- ul.navbar-nav.me-auto
34
- if user
35
- li.nav-item
36
- a.nav-link(href="/" class=active === 'overview' ? 'active' : '') Overview
37
- li.nav-item
38
- a.nav-link(href="/resources" class=active === 'resources' ? 'active' : '') Resources
32
+ #nav.collapse.navbar-collapse.thub-nav
33
+ //- Below lg the menu drops down over the page (thub.css .thub-nav,
34
+ //- closed by public/js/actions.js): pages as a two-column grid with
35
+ //- icons, then the tools in a row, then the account row.
36
+ - const pages = user ? [['/', 'overview', 'Overview', 'speedometer2'], ['/resources', 'resources', 'Resources', 'hdd-rack'], ['/groups', 'groups', 'Groups', 'collection'], ['/jobs', 'jobs', 'Jobs', 'list-task']].concat(user.role === 'admin' ? [['/admin/agents', 'agents', 'Agents', 'person-badge'], ['/admin/settings', 'settings', 'Settings', 'gear']] : []) : []
37
+ ul.navbar-nav.me-auto.thub-nav-pages
38
+ each pg in pages
39
39
  li.nav-item
40
- a.nav-link(href="/groups" class=active === 'groups' ? 'active' : '') Groups
41
- li.nav-item
42
- a.nav-link(href="/jobs" class=active === 'jobs' ? 'active' : '') Jobs
43
- if user.role === 'admin'
44
- li.nav-item
45
- a.nav-link(href="/admin/agents" class=active === 'agents' ? 'active' : '') Agents
46
- li.nav-item
47
- a.nav-link(href="/admin/settings" class=active === 'settings' ? 'active' : '') Settings
48
- ul.navbar-nav
40
+ a.nav-link(href=pg[0] class=active === pg[1] ? 'active' : '' aria-current=active === pg[1] ? 'page' : false)
41
+ i.bi.me-2.d-lg-none(class=`bi-${pg[3]}` aria-hidden="true")
42
+ = pg[2]
43
+ ul.navbar-nav.thub-nav-tools
49
44
  if user && liveTopics
50
45
  //- Live-update status, driven by public/js/live.js.
51
46
  li.nav-item.d-flex.align-items-center.me-3
@@ -117,7 +112,8 @@ html(lang="en" data-bs-theme=(user && user.theme) || "auto")
117
112
  )
118
113
  i.bi.bi-circle-half
119
114
  if user
120
- li.nav-item.d-flex.align-items-center.me-3
115
+ li.thub-nav-break(aria-hidden="true")
116
+ li.nav-item.d-flex.align-items-center.me-3.thub-nav-account
121
117
  a.nav-link.d-flex.align-items-center.p-0(
122
118
  href="/profile"
123
119
  data-bs-toggle="tooltip"
@@ -130,6 +126,7 @@ html(lang="en" data-bs-theme=(user && user.theme) || "auto")
130
126
  span.rounded-circle.bg-secondary.text-white.d-inline-flex.align-items-center.justify-content-center.me-2(style="width: 24px; height: 24px; font-size: 12px;")
131
127
  = ((user.firstName ? user.firstName[0] : user.username[0]) + (user.lastName ? user.lastName[0] : '')).toUpperCase()
132
128
  span= user.firstName ? `${user.firstName} ${user.lastName || ''}`.trim() : user.username
129
+ span.small.text-body-secondary.ms-2.d-lg-none= `${user.role} · profile`
133
130
  li.nav-item.d-flex.align-items-center
134
131
  form(action="/logout" method="post")
135
132
  button.btn.btn-sm.btn-outline-danger(