@andrian.yablonskyy/thub-coordinator 1.0.21 → 1.0.22

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.21",
3
+ "version": "1.0.22",
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": {
@@ -28,3 +28,28 @@
28
28
  from { transform: scaleX(1); }
29
29
  to { transform: scaleX(0); }
30
30
  }
31
+
32
+ /* Hints (Bootstrap tooltips, public/js/tooltips.js): left-aligned — many
33
+ are multi-line sentences — and 0.9 of Bootstrap's .875rem font size. */
34
+ .tooltip {
35
+ --bs-tooltip-font-size: calc(0.875rem * 0.9);
36
+ }
37
+
38
+ .tooltip-inner {
39
+ text-align: left;
40
+ }
41
+
42
+ /* Resource card (views/mixins/resource.pug): 30% wider than Bootstrap's
43
+ default 500px modal. Only applies from 576px up, like Bootstrap's own
44
+ width — below that the modal fills the screen as before. */
45
+ .modal.thub-resource-card {
46
+ --bs-modal-width: calc(500px * 1.3);
47
+ }
48
+
49
+ /* A status badge inside a page heading (e.g. the job page's state): badges
50
+ are 0.75em of their parent, which in an h4 makes them oversized — keep
51
+ it close to the badges in tables, centered on the heading text. */
52
+ .thub-heading-badge {
53
+ font-size: 0.55em;
54
+ vertical-align: middle;
55
+ }
package/src/web/routes.js CHANGED
@@ -408,7 +408,7 @@ function createWebRouter({ services, config }){
408
408
  }
409
409
 
410
410
  parts.push(s.coordinatorUpdate
411
- ? `Coordinator v${s.coordinatorVersion} → v${s.coordinatorUpdate} available (Update app button).`
411
+ ? `Coordinator v${s.coordinatorVersion} → v${s.coordinatorUpdate} available.`
412
412
  : `Coordinator is up to date (v${s.coordinatorVersion}).`);
413
413
 
414
414
  const connected = services.registry.list().filter((r) => r.status !== RESOURCE_STATES.OUT_OF_SERVICE && r.last_heartbeat_at),
@@ -435,7 +435,7 @@ function createWebRouter({ services, config }){
435
435
  parts.push(`Some checks failed: ${s.error}`);
436
436
  }
437
437
  const behind = s.coordinatorUpdate || outdated.length || unknown.length;
438
- return { type: behind || s.error ? 'warning' : 'success', text: parts.join(' ') };
438
+ return { type: behind || s.error ? 'warning' : 'success', text: parts.join('\n') };
439
439
  }));
440
440
 
441
441
  router.post('/updates/coordinator', requireAdminRole, updateAction(async (req) => {
@@ -48,7 +48,7 @@ block content
48
48
  td: +jobBadge(j.state)
49
49
  td= j.resource ? j.resource.name : j.resource_name ? `${j.resource_name} (removed)` : '—'
50
50
  td= fmtDate(j.created_at)
51
- td(title=j.finished_at ? `Finished ${fmtDate(j.finished_at)}` : null)= j.duration_sec != null ? fmtDuration(j.duration_sec * 1000) : '—'
51
+ td(data-bs-toggle=j.finished_at ? 'tooltip' : null data-bs-title=j.finished_at ? `Finished ${fmtDate(j.finished_at)}` : null)= j.duration_sec != null ? fmtDuration(j.duration_sec * 1000) : '—'
52
52
  if canManage
53
53
  td.text-end
54
54
  if j.active
@@ -59,7 +59,7 @@ block content
59
59
  onsubmit="return confirm(this.dataset.confirm)"
60
60
  )
61
61
  input(type="hidden" name="returnTo" value=returnTo)
62
- button.btn.btn-sm.btn-outline-danger(type="submit" aria-label="Cancel" title="Cancel this job")
62
+ button.btn.btn-sm.btn-outline-danger(type="submit" aria-label="Cancel" data-bs-toggle="tooltip" data-bs-title="Cancel this job")
63
63
  i.bi.bi-x-circle
64
64
 
65
65
  if canManage
@@ -7,7 +7,7 @@ block content
7
7
  h1.h4.mb-0
8
8
  = job.id
9
9
  |
10
- +jobBadge(job.state)
10
+ +jobBadge(job.state)(class="thub-heading-badge ms-1")
11
11
  if canCancel
12
12
  form(method="post" action=`/jobs/${job.id}/cancel` onsubmit=`return confirm('Cancel job ${job.id}?')`)
13
13
  button.btn.btn-outline-danger.btn-sm(type="submit") Cancel
@@ -29,7 +29,7 @@ block content
29
29
  dd.col-sm-10= fmtDate(job.created_at)
30
30
  if job.duration_sec != null
31
31
  dt.col-sm-2 Duration
32
- dd.col-sm-10(title=`Finished ${fmtDate(job.finished_at)}`)= fmtDuration(job.duration_sec * 1000)
32
+ dd.col-sm-10(data-bs-toggle="tooltip" data-bs-title=`Finished ${fmtDate(job.finished_at)}`)= fmtDuration(job.duration_sec * 1000)
33
33
  if job.message
34
34
  dt.col-sm-2 Message
35
35
  dd.col-sm-10= job.message
@@ -226,7 +226,7 @@ mixin resourceCard(r, groupsById, returnTo)
226
226
  - const isLoopback = (a) => /^127\./.test(a) || a === '::1' || a === 'localhost'
227
227
  - const local = ((r.host_info && r.host_info.addresses) || []).filter((a) => !isLoopback(a.address))
228
228
  - const external = r.remote_addr && !isLoopback(r.remote_addr) ? r.remote_addr : null
229
- .modal.fade(id=`resource-card-${r.id}` tabindex="-1" aria-labelledby=`resource-card-${r.id}-title` aria-hidden="true")
229
+ .modal.fade.thub-resource-card(id=`resource-card-${r.id}` tabindex="-1" aria-labelledby=`resource-card-${r.id}-title` aria-hidden="true")
230
230
  .modal-dialog.modal-dialog-centered
231
231
  .modal-content
232
232
  .modal-header
@@ -279,7 +279,7 @@ mixin resourceCard(r, groupsById, returnTo)
279
279
  dd.col-sm-8
280
280
  if r.group_ids.length
281
281
  each gid in r.group_ids
282
- span.badge.text-bg-info.me-1(title=gid)= groupsById[gid] ? groupsById[gid].name : `(unknown: ${gid})`
282
+ span.badge.text-bg-info.me-1(data-bs-toggle="tooltip" data-bs-title=gid)= groupsById[gid] ? groupsById[gid].name : `(unknown: ${gid})`
283
283
  else
284
284
  span.text-body-secondary —
285
285
  dt.col-sm-4 Busy source / reason
@@ -11,4 +11,4 @@ mixin resourceBadge(status, removing)
11
11
 
12
12
  mixin jobBadge(state)
13
13
  - const map = { QUEUED: 'secondary', ASSIGNED: 'info', PREPARING: 'info', RUNNING: 'primary', PASSED: 'success', FAILED: 'danger', ERROR: 'danger', TIMEOUT: 'warning', CANCELED: 'secondary', LOST: 'danger' }
14
- span.badge(class=`text-bg-${map[state] || 'secondary'}`)= state
14
+ span.badge(class=`text-bg-${map[state] || 'secondary'}`)&attributes(attributes)= state
@@ -42,7 +42,7 @@ block content
42
42
  td
43
43
  if r.group_ids.length
44
44
  each gid in r.group_ids
45
- span.badge.text-bg-info.me-1(title=gid)= groupsById[gid] ? groupsById[gid].name : `(unknown: ${gid})`
45
+ span.badge.text-bg-info.me-1(data-bs-toggle="tooltip" data-bs-title=gid)= groupsById[gid] ? groupsById[gid].name : `(unknown: ${gid})`
46
46
  else
47
47
  span.text-body-secondary —
48
48
  td= r.busy_source ? `${r.busy_source}${r.busy_reason ? ' — ' + r.busy_reason : ''}` : '—'