diff --git a/.changelog/16378.txt b/.changelog/16378.txt
new file mode 100644
index 000000000..c82e4b8a1
--- /dev/null
+++ b/.changelog/16378.txt
@@ -0,0 +1,3 @@
+```release-note:improvement
+ui: added new keyboard commands for job start, stop, exec, and client metadata
+```
diff --git a/ui/app/components/job-page/parts/title.js b/ui/app/components/job-page/parts/title.js
index 09a9bb24b..1cff99459 100644
--- a/ui/app/components/job-page/parts/title.js
+++ b/ui/app/components/job-page/parts/title.js
@@ -1,3 +1,4 @@
+// @ts-check
import Component from '@ember/component';
import { task } from 'ember-concurrency';
import { inject as service } from '@ember/service';
@@ -16,12 +17,22 @@ export default class Title extends Component {
handleError() {}
- @task(function* () {
+ /**
+ * @param {boolean} withNotifications - Whether to show a toast on success, as when triggered by keyboard shortcut
+ */
+ @task(function* (withNotifications = false) {
try {
const job = this.job;
yield job.stop();
// Eagerly update the job status to avoid flickering
- this.job.set('status', 'dead');
+ job.set('status', 'dead');
+ if (withNotifications) {
+ this.notifications.add({
+ title: 'Job Stopped',
+ message: `${job.name} has been stopped`,
+ color: 'success',
+ });
+ }
} catch (err) {
this.handleError({
title: 'Could Not Stop Job',
@@ -37,7 +48,7 @@ export default class Title extends Component {
yield job.purge();
this.notifications.add({
title: 'Job Purged',
- message: `You have purged ${this.job.name}`,
+ message: `You have purged ${job.name}`,
color: 'success',
});
this.router.transitionTo('jobs');
@@ -50,7 +61,10 @@ export default class Title extends Component {
})
purgeJob;
- @task(function* () {
+ /**
+ * @param {boolean} withNotifications - Whether to show a toast on success, as when triggered by keyboard shortcut
+ */
+ @task(function* (withNotifications = false) {
const job = this.job;
const definition = yield job.fetchRawDefinition();
@@ -62,6 +76,13 @@ export default class Title extends Component {
yield job.update();
// Eagerly update the job status to avoid flickering
job.set('status', 'running');
+ if (withNotifications) {
+ this.notifications.add({
+ title: 'Job Started',
+ message: `${job.name} has started`,
+ color: 'success',
+ });
+ }
} catch (err) {
this.handleError({
title: 'Could Not Start Job',
diff --git a/ui/app/controllers/jobs/index.js b/ui/app/controllers/jobs/index.js
index d4600c65b..36b3864e5 100644
--- a/ui/app/controllers/jobs/index.js
+++ b/ui/app/controllers/jobs/index.js
@@ -2,7 +2,7 @@
import { inject as service } from '@ember/service';
import { alias, readOnly } from '@ember/object/computed';
import Controller from '@ember/controller';
-import { computed } from '@ember/object';
+import { computed, action } from '@ember/object';
import { scheduleOnce } from '@ember/runloop';
import intersection from 'lodash.intersection';
import Sortable from 'nomad-ui/mixins/sortable';
@@ -20,6 +20,7 @@ export default class IndexController extends Controller.extend(
) {
@service system;
@service userSettings;
+ @service router;
isForbidden = false;
@@ -254,4 +255,9 @@ export default class IndexController extends Controller.extend(
setFacetQueryParam(queryParam, selection) {
this.set(queryParam, serialize(selection));
}
+
+ @action
+ goToRun() {
+ this.router.transitionTo('jobs.run');
+ }
}
diff --git a/ui/app/services/keyboard.js b/ui/app/services/keyboard.js
index 5e9950bd2..5c05b30ee 100644
--- a/ui/app/services/keyboard.js
+++ b/ui/app/services/keyboard.js
@@ -342,7 +342,7 @@ export default class KeyboardService extends Service {
this.displayHints = true;
} else {
if (!DISALLOWED_KEYS.includes(key)) {
- this.addKeyToBuffer.perform(key, shifted);
+ this.addKeyToBuffer.perform(key, shifted, event);
}
}
} else if (type === 'release') {
@@ -382,7 +382,7 @@ export default class KeyboardService extends Service {
* @param {string} key
* @param {boolean} shifted
*/
- @restartableTask *addKeyToBuffer(key, shifted) {
+ @restartableTask *addKeyToBuffer(key, shifted, event) {
// Replace key with its unshifted equivalent if it's a number key
if (shifted && key in DIGIT_MAP) {
key = DIGIT_MAP[key];
@@ -411,6 +411,7 @@ export default class KeyboardService extends Service {
command.label === 'Show Keyboard Shortcuts' ||
command.label === 'Hide Keyboard Shortcuts'
) {
+ event.preventDefault();
command.action();
}
});
diff --git a/ui/app/templates/clients/client/index.hbs b/ui/app/templates/clients/client/index.hbs
index 2389ed1ec..fdf962818 100644
--- a/ui/app/templates/clients/client/index.hbs
+++ b/ui/app/templates/clients/client/index.hbs
@@ -879,6 +879,11 @@
type="button"
class="button is-primary"
{{on "click" (action (mut this.editingMetadata) true)}}
+ {{keyboard-shortcut
+ label="Add Dynamic Node Metadata"
+ pattern=(array "m" "e" "t" "a")
+ action=(action (mut this.editingMetadata) true)
+ }}
>
Add new Dynamic Metadata
diff --git a/ui/app/templates/components/exec/open-button.hbs b/ui/app/templates/components/exec/open-button.hbs
index 00156976a..1cb4d9c9b 100644
--- a/ui/app/templates/components/exec/open-button.hbs
+++ b/ui/app/templates/components/exec/open-button.hbs
@@ -5,7 +5,12 @@
class="button exec-button is-outline is-small {{if cannotExec "tooltip"}}"
disabled={{if cannotExec 'disabled'}}
aria-label={{if cannotExec "You don’t have permission to exec"}}
- {{action "open"}}>
+ {{action "open"}}
+ {{keyboard-shortcut
+ label="Exec"
+ pattern=(array "e" "x" "e" "c")
+ action=(action "open")
+ }}>
{{x-icon "console"}}
Exec
diff --git a/ui/app/templates/components/job-page/parts/title.hbs b/ui/app/templates/components/job-page/parts/title.hbs
index bfc8543b3..9d0704932 100644
--- a/ui/app/templates/components/job-page/parts/title.hbs
+++ b/ui/app/templates/components/job-page/parts/title.hbs
@@ -23,7 +23,12 @@
@confirmText="Yes, Stop Job"
@confirmationMessage="Are you sure you want to stop this job?"
@awaitingConfirmation={{this.stopJob.isRunning}}
- @onConfirm={{perform this.stopJob}} />
+ @onConfirm={{perform this.stopJob}}
+ {{keyboard-shortcut
+ label="Stop"
+ pattern=(array "s" "t" "o" "p")
+ action=(perform this.stopJob true)
+ }} />
{{else}}
+ @onConfirm={{perform this.purgeJob}}
+ {{keyboard-shortcut
+ label="Purge"
+ pattern=(array "p" "u" "r" "g" "e")
+ action=(perform this.purgeJob)
+ }}
+ />
+ @onConfirm={{perform this.startJob}}
+ {{keyboard-shortcut
+ label="Start"
+ pattern=(array "s" "t" "a" "r" "t")
+ action=(perform this.startJob true)
+ }}
+ />
{{/if}}
diff --git a/ui/app/templates/jobs/index.hbs b/ui/app/templates/jobs/index.hbs
index 3df484328..5ffc665ad 100644
--- a/ui/app/templates/jobs/index.hbs
+++ b/ui/app/templates/jobs/index.hbs
@@ -84,6 +84,11 @@
@query={{hash namespace=this.qpNamespace}}
data-test-run-job
class="button is-primary"
+ {{keyboard-shortcut
+ label="Run Job"
+ pattern=(array "r" "u" "n")
+ action=(action this.goToRun)
+ }}
>
Run Job