Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
cb04435
feat: ember 6.4
aklkv Mar 15, 2026
b582eca
ui: remove ember-fetch and stabilize async tests
aklkv Mar 15, 2026
ec07db8
ui: add missing copyright headers
aklkv Mar 15, 2026
205ad09
ui: stabilize Ember Data 4.12 token/job flows
aklkv Mar 16, 2026
2d10653
ui: restore deep-link redirect and diff sync
aklkv Mar 16, 2026
e699969
feat: upgrade ember-cli (no linting yet)
aklkv Mar 16, 2026
1dd02bb
format the world 💅
aklkv Mar 16, 2026
68d86c3
ui: stabilize route-model and test regressions
aklkv Mar 16, 2026
d6af1d6
fix the world
aklkv Mar 16, 2026
213cc25
ui: migrate (action ...) helper to modern patterns in components
aklkv Feb 17, 2026
eba5baf
ui: refactor image-file to use @action wrapper instead of init+bind
aklkv Feb 17, 2026
15bb0bb
refactor(ui): finish Ember deprecation and action-helper migration
aklkv Mar 17, 2026
03574db
ember 6.10 LTS
aklkv Mar 17, 2026
dba257c
ui: migrate breadcrumbs chain to gjs components
aklkv Mar 18, 2026
efb196a
refactor(ui): migrate placement-failure chain to gjs
aklkv Mar 18, 2026
ea40372
refactor(ui): migrate list-table chain to gjs
aklkv Mar 18, 2026
ea71344
refactor(ui): migrate list-pagination chain to gjs
aklkv Mar 18, 2026
57fa76f
chore(ui): migrate topo-viz and flex-masonry to gjs
aklkv Mar 19, 2026
68ead07
chore(ui): migrate plugin-subnav to gjs
aklkv Mar 19, 2026
5bcb326
chore(ui): migrate storage-subnav to gjs
aklkv Mar 19, 2026
ec4c3da
chore(ui): migrate loading-spinner to gjs
aklkv Mar 19, 2026
202dcda
chore(ui): migrate server-subnav to gjs
aklkv Mar 19, 2026
b774e7d
chore(ui): migrate actions-flyout-global-button to gjs
aklkv Mar 19, 2026
a4897f8
chore(ui): migrate task-subnav to gjs
aklkv Mar 19, 2026
b3101ae
chore(ui): migrate region-switcher to gjs
aklkv Mar 19, 2026
0ad9887
chore(ui): migrate conditional-link-to to gjs
aklkv Mar 19, 2026
12d2fa3
chore(ui): migrate allocation-stat to gjs
aklkv Mar 19, 2026
ddfc529
chore(ui): migrate job-service-row to gjs
aklkv Mar 19, 2026
77c535c
chore(ui): migrate actions-flyout to gjs
aklkv Mar 19, 2026
cc0dbb7
chore(ui): migrate job-diff-fields-and-objects to gjs
aklkv Mar 19, 2026
0527561
chore(ui): migrate hamburger-menu to gjs
aklkv Mar 19, 2026
5ea9a07
chore(ui): migrate service-status-indicator to gjs
aklkv Mar 19, 2026
6d19e5e
chore(ui): migrate image-file and integration test to gjs
aklkv Mar 19, 2026
39c5dc7
chore(ui): migrate trigger and integration test to gjs
aklkv Mar 19, 2026
082991e
chore(ui): migrate copy-button and integration test to gjs
aklkv Mar 19, 2026
348d4bb
chore(ui): migrate breadcrumbs and integration test to gjs
aklkv Mar 19, 2026
152b4d4
chore(ui): migrate job-search-box and integration test to gjs
aklkv Mar 19, 2026
894e5d9
chore(ui): migrate das dismissed and integration test to gjs
aklkv Mar 19, 2026
eaefa9c
chore(ui): migrate popover-menu and integration test to gjs
aklkv Mar 19, 2026
496acad
chore(ui): migrate agent-monitor and integration test to gjs
aklkv Mar 19, 2026
63cd2bb
chore(ui): migrate resize mixin components to gjs
aklkv Mar 19, 2026
92a7e74
chore(ui): migrate stepper-input to gjs
aklkv Mar 19, 2026
82e78e4
chore(ui): migrate toggle to gjs
aklkv Mar 19, 2026
c58601d
chore(ui): migrate two-step-button to gjs
aklkv Mar 19, 2026
cfbb944
chore(ui): migrate allocation-service-sidebar to gjs
aklkv Mar 19, 2026
3dd88a8
chore(ui): migrate policy-editor to gjs
aklkv Mar 19, 2026
285a121
chore(ui): migrate page-layout to gjs
aklkv Mar 19, 2026
dbf011b
chore(ui): migrate global-header and gutter-menu to gjs
aklkv Mar 19, 2026
a9e072d
chore(ui): migrate template-only body and attributes-table to gjs
aklkv Mar 19, 2026
8450133
chore(ui): migrate template-only failed-or-lost to gjs
aklkv Mar 19, 2026
8c032f1
chore(ui): migrate template-only scale-events-accordion to gjs
aklkv Mar 19, 2026
8a7c050
chore(ui): migrate template-only reschedule-event-timeline to gjs
aklkv Mar 19, 2026
32cec89
chore(ui): migrate template-only job-page/service to gjs
aklkv Mar 19, 2026
3b03ee5
chore(ui): migrate template-only status and das components to gjs
aklkv Mar 19, 2026
a11c728
chore(ui): migrate deployment details and children to gjs
aklkv Mar 19, 2026
7681a57
chore(ui): migrate template-only parameterized and summary pieces to gjs
aklkv Mar 19, 2026
e6b1f84
chore(ui): migrate template-only batch sysbatch and system to gjs
aklkv Mar 19, 2026
eac38af
chore(ui): migrate template-only periodic-child to gjs
aklkv Mar 19, 2026
c7cf05c
chore(ui): migrate template-only logo and utility components to gjs
aklkv Mar 19, 2026
3b74457
chore(ui): migrate evaluation and variable relation components to gjs
aklkv Mar 19, 2026
ab541f2
chore(ui): migrate job editor read and edit views to gjs
aklkv Mar 19, 2026
6a798fa
chore(ui): migrate remaining status bars and job-editor pieces to gjs
aklkv Mar 19, 2026
7c5473b
refactor(ui): migrate drain-popover and primary-metric components/tes…
aklkv Mar 19, 2026
a2d9138
refactor(ui): migrate task-sub-row and variable-paths to gjs
aklkv Mar 19, 2026
a67de4d
refactor(ui): migrate scale-events-chart component and test to gjs
aklkv Mar 19, 2026
668ee38
ui: fix test regressions in tracker and task row actions
aklkv Mar 19, 2026
7d035df
ui: fix client metadata edit/add reactivity
aklkv Mar 19, 2026
469eb3c
ui: finish remaining SFC migration updates
aklkv Mar 19, 2026
419c694
ui: migrate subnav and page-size components to SFC
aklkv Mar 19, 2026
cb2071e
chore(ui): migrate exec components to SFC
aklkv Mar 19, 2026
fb5a6f6
chore(ui): migrate chart-primitives components to SFC
aklkv Mar 19, 2026
06a9e89
chore(ui): migrate das components to SFC
aklkv Mar 19, 2026
7761e1d
chore(ui): migrate fs components to SFC
aklkv Mar 19, 2026
528a272
chore(ui): migrate list-accordion components to SFC
aklkv Mar 19, 2026
cab81e8
chore(ui): migrate variable form support components to SFC
aklkv Mar 19, 2026
ce0bb06
ui: migrate classic components/tests to gjs and fix regressions
aklkv Mar 20, 2026
89eb93d
ui: convert job diff/subnav/profile/proxy to gjs
aklkv Mar 20, 2026
021919e
ui: convert child/deployment/json/reschedule components to gjs
aklkv Mar 20, 2026
383dbdf
ui: convert additional components to gjs
aklkv Mar 20, 2026
1f8085a
chore(ui): migrate classic components to gjs
aklkv Mar 20, 2026
9fd540d
chore(ui): normalize did-update imports
aklkv Mar 20, 2026
bbfd78f
chore(ui): finish gjs migration fixes and regression updates
aklkv Mar 20, 2026
d964947
fix(ui): avoid EmberObject get in logger helper
aklkv Mar 20, 2026
7b778c6
update lock file
aklkv May 5, 2026
be9a96f
update render modifers
aklkv May 5, 2026
fe1b2f8
update see to run all lints
aklkv May 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 2 additions & 4 deletions .github/workflows/test-ui.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: ./.github/actions/setup-js
- name: lint:js
run: pnpm lint:js
- name: lint:hbs
run: pnpm lint:hbs
- name: Lint
run: pnpm lint
- id: nonce
name: nonce
run: echo "nonce=${{ github.run_id }}-$(date +%s)" >> "$GITHUB_OUTPUT"
Expand Down
707 changes: 297 additions & 410 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

242 changes: 242 additions & 0 deletions ui/app/components/action-card.gjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/**
* Copyright IBM Corp. 2015, 2025
* SPDX-License-Identifier: BUSL-1.1
*/

import Component from '@glimmer/component';
import { capitalize } from '@ember/string';
import { didUpdate } from '@ember/render-modifiers';
import { fn } from '@ember/helper';
import { LinkTo } from '@ember/routing';
import { on } from '@ember/modifier';
import { service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { eq } from 'ember-truth-helpers';
import {
HdsBadge,
HdsButton,
HdsButtonSet,
HdsCopyButton,
HdsPageHeader,
HdsReveal,
} from '@hashicorp/design-system-components/components';
import formatTs from 'nomad-ui/helpers/format-ts';

export default class ActionCard extends Component {
@service nomadActions;

@tracked selectedPeer = null;
@tracked hasBeenAnchored = false;

get instance() {
return this.selectedPeer || this.args.instance;
}

get peers() {
const peerID = this.instance?.peerID;
if (!peerID) {
return [];
}
return this.nomadActions.actionsQueue.filter(
(peer) => peer.peerID === peerID,
);
}

get hasRunningPeers() {
return this.peers.some((peer) => peer.state === 'running');
}

get stateText() {
return capitalize(this.instance?.state || '');
}

get stateColor() {
const instance = this.instance;
switch (instance.state) {
case 'starting':
return 'neutral';
case 'running':
return 'highlight';
case 'complete':
return 'success';
case 'error':
return 'critical';
default:
return 'neutral';
}
}

get completedSecondsFloat() {
const started = this.instance?.createdAt;
const ended = this.instance?.completedAt;
if (!started || !ended) {
return null;
}
return (new Date(ended).getTime() - new Date(started).getTime()) / 1000;
}

get completedSecondsInt() {
const seconds = this.completedSecondsFloat;
if (seconds == null) {
return null;
}
return Math.trunc(seconds);
}

get completedLongerThanOneSecond() {
return (this.completedSecondsInt ?? 0) > 1;
}

stop = () => {
this.instance.socket.close();
};

stopAll = () => {
this.nomadActions.stopPeers(this.instance.peerID);
};

selectPeer = (peer) => {
this.selectedPeer = peer;
};

anchorToBottom = (element) => {
if (this.hasBeenAnchored) return;
const parentHeight = element.parentElement.clientHeight;
const elementHeight = element.clientHeight;
if (elementHeight > parentHeight) {
this.hasBeenAnchored = true;
element.parentElement.scroll(0, elementHeight);
}
};

<template>
<div class="action-card" ...attributes>
<HdsPageHeader class="action-card-header" as |PH|>
<PH.Title>
<span class="action-card-title">
<span>{{this.instance.action.name}}</span>
<LinkTo
class="job-name"
@route="jobs.job"
@model={{this.instance.action.task.taskGroup.job}}
>
{{this.instance.action.task.taskGroup.job.name}}
</LinkTo>
</span>
<HdsBadge
@text={{this.stateText}}
@color={{this.stateColor}}
@size="medium"
/>
</PH.Title>
<PH.Actions>
{{#if this.instance.peerID}}
{{#if (eq this.instance.state "running")}}
<HdsButton
@text="Stop"
@color="critical"
@size="medium"
{{on "click" this.stop}}
/>
{{/if}}
{{#if this.hasRunningPeers}}
<HdsButton
@text="Stop All"
@color="critical"
@size="medium"
{{on "click" this.stopAll}}
/>
{{else}}
<HdsButton
@text="Clear"
@color="secondary"
{{on
"click"
(fn this.nomadActions.clearActionInstance this.instance)
}}
/>
{{/if}}
{{else}}
{{#if (eq this.instance.state "running")}}
<HdsButton
@text="Stop"
@color="critical"
@size="medium"
{{on "click" this.stop}}
/>
{{else}}
<HdsButton
@text="Clear"
@color="secondary"
{{on
"click"
(fn this.nomadActions.clearActionInstance this.instance)
}}
/>
{{/if}}
{{/if}}
</PH.Actions>
</HdsPageHeader>

{{#if this.instance.peerID}}
<HdsButtonSet class="peers">
{{#each this.peers as |peer|}}
<HdsButton
class="peer"
@icon={{if (eq peer.state "running") "loading" null}}
@iconPosition="trailing"
@text={{peer.allocShortID}}
@color={{if (eq this.instance.id peer.id) "primary" "secondary"}}
{{on "click" (fn this.selectPeer peer)}}
/>
{{/each}}
</HdsButtonSet>
{{/if}}

<div class="messages">
{{#if this.instance.error}}
<code><pre>Error: {{this.instance.error}}</pre></code>
{{/if}}
{{#if this.instance.messages.length}}
<code tabindex="0">
<HdsCopyButton
class="copy-button"
@text="Copy"
@isIconOnly={{true}}
@textToCopy={{this.instance.messages}}
/>
<pre {{didUpdate this.anchorToBottom this.instance.messages}}>
{{this.instance.messages}}
</pre>
<div class="anchor" />
</code>
{{else}}
{{#if (eq this.instance.state "complete")}}
<p class="no-messages">Action completed with no output</p>
{{/if}}
{{/if}}
</div>

<footer>
<HdsReveal @text="Action Info">
<ul>
<li><span>Task:</span> {{this.instance.action.task.name}}</li>
<li><span>Job:</span>
{{this.instance.action.task.taskGroup.job.name}}</li>
<li><span>Allocation:</span> {{this.instance.allocID}}</li>
<li><span>Created:</span> {{formatTs this.instance.createdAt}}</li>
{{#if this.instance.completedAt}}
{{#if this.completedLongerThanOneSecond}}
<li>Completed after {{this.completedSecondsInt}} seconds</li>
{{else}}
<li>Completed in {{this.completedSecondsFloat}} seconds</li>
{{/if}}
{{/if}}
</ul>
</HdsReveal>
</footer>

{{yield}}
</div>
</template>
}
Loading
Loading