From b14991e5760b431b81bd005e2a0ef985994888b7 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 30 Nov 2021 15:07:50 -0500 Subject: [PATCH 01/27] chore: edit mirage scenario to populate csi --- .changelog/11590.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .changelog/11590.txt diff --git a/.changelog/11590.txt b/.changelog/11590.txt new file mode 100644 index 000000000000..9d6b219d07bd --- /dev/null +++ b/.changelog/11590.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Add titles to breadcrumb labels in app navigation bar +``` From 67e6cec66b06d4706922700435035c831aedbc91 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 30 Nov 2021 15:09:16 -0500 Subject: [PATCH 02/27] feat: create bucket service to register, deregister and store breadcrumbs --- ui/app/components/breadcrumbs.hbs | 1 + ui/app/components/breadcrumbs.js | 10 ++++++++++ ui/app/services/bucket.js | 21 +++++++++++++++++++++ 3 files changed, 32 insertions(+) create mode 100644 ui/app/components/breadcrumbs.hbs create mode 100644 ui/app/components/breadcrumbs.js create mode 100644 ui/app/services/bucket.js diff --git a/ui/app/components/breadcrumbs.hbs b/ui/app/components/breadcrumbs.hbs new file mode 100644 index 000000000000..7e807bd1ec73 --- /dev/null +++ b/ui/app/components/breadcrumbs.hbs @@ -0,0 +1 @@ +{{yield this.crumbs}} \ No newline at end of file diff --git a/ui/app/components/breadcrumbs.js b/ui/app/components/breadcrumbs.js new file mode 100644 index 000000000000..3d104ab9160a --- /dev/null +++ b/ui/app/components/breadcrumbs.js @@ -0,0 +1,10 @@ +import Component from '@glimmer/component'; +import { inject as service } from '@ember/service'; + +export default class Breadcrumbs extends Component { + @service bucket; + + get crumbs() { + return this.bucket.crumbs; + } +} diff --git a/ui/app/services/bucket.js b/ui/app/services/bucket.js new file mode 100644 index 000000000000..0a268708f14f --- /dev/null +++ b/ui/app/services/bucket.js @@ -0,0 +1,21 @@ +import Service from '@ember/service'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { schedule } from '@ember/runloop'; + +export default class BucketService extends Service { + @tracked crumbs = []; + + @action registerBreadcrumb(crumb) { + schedule('actions', this, () => { + console.log('register crumb: ', crumb); + this.crumbs = [...this.crumbs, crumb]; + }); + } + + @action deregisterBreadcrumb(crumb) { + const newCrumbs = this.crumbs.filter(c => c !== crumb); + + this.crumbs = newCrumbs; + } +} From dcc9aa18ad4731a64c09db47b934bae56f885c71 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 30 Nov 2021 15:10:01 -0500 Subject: [PATCH 03/27] fix: delete state from routes and move to controllers and add renderless component to templates --- ui/app/components/app-breadcrumbs.js | 14 +- ui/app/components/breadcrumb.js | 25 + ui/app/controllers/allocations/allocation.js | 32 ++ .../allocations/allocation/task.js | 14 + ui/app/controllers/clients.js | 7 + ui/app/controllers/clients/client.js | 14 + ui/app/controllers/csi/plugins.js | 7 + ui/app/controllers/csi/plugins/plugin.js | 17 + ui/app/controllers/csi/volumes.js | 7 + ui/app/controllers/csi/volumes/volume.js | 22 + ui/app/controllers/jobs.js | 9 +- ui/app/controllers/jobs/job.js | 3 + ui/app/controllers/jobs/job/dispatch.js | 10 + ui/app/controllers/jobs/job/task-group.js | 16 + ui/app/controllers/jobs/run.js | 7 + ui/app/controllers/optimize.js | 7 + ui/app/controllers/optimize/summary.js | 12 + ui/app/controllers/servers.js | 7 + ui/app/routes/allocations/allocation.js | 29 -- ui/app/routes/allocations/allocation/task.js | 10 - ui/app/routes/clients.js | 7 - ui/app/routes/clients/client.js | 10 - ui/app/routes/csi/plugins.js | 7 - ui/app/routes/csi/plugins/plugin.js | 11 - ui/app/routes/csi/volumes.js | 7 - ui/app/routes/csi/volumes/volume.js | 19 - ui/app/routes/jobs.js | 11 +- ui/app/routes/jobs/job.js | 9 +- ui/app/routes/jobs/job/dispatch.js | 7 - ui/app/routes/jobs/job/task-group.js | 16 - ui/app/routes/jobs/run.js | 7 - ui/app/routes/optimize.js | 7 - ui/app/routes/optimize/summary.js | 11 - ui/app/routes/servers.js | 7 - ui/app/routes/servers/server.js | 12 +- ui/app/routes/topology.js | 7 - ui/app/templates/allocations/allocation.hbs | 2 +- .../templates/allocations/allocation/task.hbs | 2 +- ui/app/templates/clients.hbs | 1 + ui/app/templates/clients/client.hbs | 2 +- .../templates/components/app-breadcrumbs.hbs | 30 +- .../components/job-page/parts/summary.hbs | 6 +- ui/app/templates/csi/plugins.hbs | 2 +- ui/app/templates/csi/plugins/plugin.hbs | 2 +- ui/app/templates/csi/volumes.hbs | 2 +- ui/app/templates/csi/volumes/volume.hbs | 155 +++++-- ui/app/templates/jobs.hbs | 1 + ui/app/templates/jobs/job.hbs | 2 +- ui/app/templates/jobs/job/dispatch.hbs | 3 +- ui/app/templates/jobs/job/task-group.hbs | 247 +++++++--- ui/app/templates/jobs/run.hbs | 6 +- ui/app/templates/optimize.hbs | 80 ++-- ui/app/templates/servers.hbs | 1 + ui/app/templates/topology.hbs | 439 ++++++++++++++---- ui/app/utils/breadcrumb-utils.js | 1 + 55 files changed, 936 insertions(+), 472 deletions(-) create mode 100644 ui/app/components/breadcrumb.js create mode 100644 ui/app/controllers/allocations/allocation.js create mode 100644 ui/app/controllers/allocations/allocation/task.js create mode 100644 ui/app/controllers/clients/client.js create mode 100644 ui/app/controllers/csi/plugins/plugin.js create mode 100644 ui/app/controllers/jobs/job/dispatch.js diff --git a/ui/app/components/app-breadcrumbs.js b/ui/app/components/app-breadcrumbs.js index 1537febbd2f1..ae49d136f522 100644 --- a/ui/app/components/app-breadcrumbs.js +++ b/ui/app/components/app-breadcrumbs.js @@ -1,13 +1,3 @@ -import Component from '@ember/component'; -import { inject as service } from '@ember/service'; -import { reads } from '@ember/object/computed'; -import { tagName } from '@ember-decorators/component'; -import classic from 'ember-classic-decorator'; +import Component from '@glimmer/component'; -@classic -@tagName('') -export default class AppBreadcrumbs extends Component { - @service('breadcrumbs') breadcrumbsService; - - @reads('breadcrumbsService.breadcrumbs') breadcrumbs; -} +export default class AppBreadcrumbs extends Component {} diff --git a/ui/app/components/breadcrumb.js b/ui/app/components/breadcrumb.js new file mode 100644 index 000000000000..b2c661e56b6f --- /dev/null +++ b/ui/app/components/breadcrumb.js @@ -0,0 +1,25 @@ +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; + +export default class Breadcrumb extends Component { + @service bucket; + + @action register() { + this.bucket.registerBreadcrumb(this); + } + + @action deregister() { + this.bucket.deregisterBreadcrumb(this); + } + + constructor() { + super(...arguments); + this.register(); + } + + willDestroy() { + super.willDestroy(); + this.deregister(); + } +} diff --git a/ui/app/controllers/allocations/allocation.js b/ui/app/controllers/allocations/allocation.js new file mode 100644 index 000000000000..fbd071ddd0ed --- /dev/null +++ b/ui/app/controllers/allocations/allocation.js @@ -0,0 +1,32 @@ +import Controller from '@ember/controller'; +import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; +import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; + +export default class AllocationsAllocationController extends Controller { + // Allocation breadcrumbs extend from job / task group breadcrumbs + // even though the route structure does not. + get breadcrumbs() { + const model = this.model; + const jobQueryParams = qpBuilder({ + jobNamespace: model.get('job.namespace.name') || 'default', + }); + + return [ + { label: 'Jobs', args: ['jobs.index', jobQueryParams] }, + ...jobCrumbs(model.get('job')), + { + label: model.get('taskGroupName'), + args: [ + 'jobs.job.task-group', + model.get('job.plainId'), + model.get('taskGroupName'), + jobQueryParams, + ], + }, + { + label: model.get('shortId'), + args: ['allocations.allocation', model], + }, + ]; + } +} diff --git a/ui/app/controllers/allocations/allocation/task.js b/ui/app/controllers/allocations/allocation/task.js new file mode 100644 index 000000000000..0d19fd42f238 --- /dev/null +++ b/ui/app/controllers/allocations/allocation/task.js @@ -0,0 +1,14 @@ +import Controller from '@ember/controller'; + +export default class AllocationsAllocationTaskController extends Controller { + get breadcrumbs() { + const model = this.model; + if (!model) return []; + return [ + { + label: model.get('name'), + args: ['allocations.allocation.task', model.get('allocation'), model], + }, + ]; + } +} diff --git a/ui/app/controllers/clients.js b/ui/app/controllers/clients.js index 673868f9b585..12f209399c99 100644 --- a/ui/app/controllers/clients.js +++ b/ui/app/controllers/clients.js @@ -2,4 +2,11 @@ import Controller from '@ember/controller'; export default class ClientsController extends Controller { isForbidden = false; + + breadcrumbs = [ + { + label: 'Clients', + args: ['clients.index'], + }, + ]; } diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js new file mode 100644 index 000000000000..3efd6ebe1a56 --- /dev/null +++ b/ui/app/controllers/clients/client.js @@ -0,0 +1,14 @@ +import Controller from '@ember/controller'; + +export default class ClientsClientController extends Controller { + breadcrumbs() { + const model = this.model; + if (!model) return []; + return [ + { + label: model.get('shortId'), + args: ['clients.client', model.get('id')], + }, + ]; + } +} diff --git a/ui/app/controllers/csi/plugins.js b/ui/app/controllers/csi/plugins.js index b9a8c5a54152..d18af07be15d 100644 --- a/ui/app/controllers/csi/plugins.js +++ b/ui/app/controllers/csi/plugins.js @@ -2,4 +2,11 @@ import Controller from '@ember/controller'; export default class PluginsController extends Controller { isForbidden = false; + + breadcrumbs = [ + { + label: 'Storage', + args: ['csi.index'], + }, + ]; } diff --git a/ui/app/controllers/csi/plugins/plugin.js b/ui/app/controllers/csi/plugins/plugin.js new file mode 100644 index 000000000000..67f11a4216e9 --- /dev/null +++ b/ui/app/controllers/csi/plugins/plugin.js @@ -0,0 +1,17 @@ +import Controller from '@ember/controller'; + +export default class CsiPluginsPluginController extends Controller { + get breadcrumbs() { + const plugin = this.model; + return [ + { + label: 'Plugins', + args: ['csi.plugins'], + }, + { + label: plugin.plainId, + args: ['csi.plugins.plugin', plugin.plainId], + }, + ]; + } +} diff --git a/ui/app/controllers/csi/volumes.js b/ui/app/controllers/csi/volumes.js index 1eb7ecfecdbf..ca6e80148e21 100644 --- a/ui/app/controllers/csi/volumes.js +++ b/ui/app/controllers/csi/volumes.js @@ -2,4 +2,11 @@ import Controller from '@ember/controller'; export default class VolumesController extends Controller { isForbidden = false; + + breadcrumbs = [ + { + label: 'Storage', + args: ['csi.index'], + }, + ]; } diff --git a/ui/app/controllers/csi/volumes/volume.js b/ui/app/controllers/csi/volumes/volume.js index e687a078c011..735700e4a6d3 100644 --- a/ui/app/controllers/csi/volumes/volume.js +++ b/ui/app/controllers/csi/volumes/volume.js @@ -1,6 +1,7 @@ import Controller from '@ember/controller'; import { inject as service } from '@ember/service'; import { action, computed } from '@ember/object'; +import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; export default class VolumeController extends Controller { // Used in the template @@ -13,6 +14,27 @@ export default class VolumeController extends Controller { ]; volumeNamespace = 'default'; + get breadcrumbs() { + const volume = this.model; + return [ + { + label: 'Volumes', + args: [ + 'csi.volumes', + qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }), + ], + }, + { + label: volume.name, + args: [ + 'csi.volumes.volume', + volume.plainId, + qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }), + ], + }, + ]; + } + @computed('model.readAllocations.@each.modifyIndex') get sortedReadAllocations() { return this.model.readAllocations.sortBy('modifyIndex').reverse(); diff --git a/ui/app/controllers/jobs.js b/ui/app/controllers/jobs.js index d6d5e36fd664..5fbfeede25d5 100644 --- a/ui/app/controllers/jobs.js +++ b/ui/app/controllers/jobs.js @@ -1,3 +1,10 @@ import Controller from '@ember/controller'; -export default class JobsController extends Controller {} +export default class JobsController extends Controller { + breadcrumbs = [ + { + label: 'Jobs', + args: ['jobs.index'], + }, + ]; +} diff --git a/ui/app/controllers/jobs/job.js b/ui/app/controllers/jobs/job.js index 519d9eae3093..f369e82b4717 100644 --- a/ui/app/controllers/jobs/job.js +++ b/ui/app/controllers/jobs/job.js @@ -1,4 +1,5 @@ import Controller from '@ember/controller'; +import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; export default class JobController extends Controller { queryParams = [ @@ -7,4 +8,6 @@ export default class JobController extends Controller { }, ]; jobNamespace = 'default'; + + breadcrumbs = jobCrumbs(this.model); } diff --git a/ui/app/controllers/jobs/job/dispatch.js b/ui/app/controllers/jobs/job/dispatch.js new file mode 100644 index 000000000000..e2145103f420 --- /dev/null +++ b/ui/app/controllers/jobs/job/dispatch.js @@ -0,0 +1,10 @@ +import Controller from '@ember/controller'; + +export default class JobsJobDispatchController extends Controller { + breadcrumbs = [ + { + label: 'Dispatch', + args: ['jobs.job.dispatch'], + }, + ]; +} diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index f8a0ec3620f1..af43746d0d83 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -5,6 +5,7 @@ import Controller from '@ember/controller'; import { action, computed, get } from '@ember/object'; import { scheduleOnce } from '@ember/runloop'; import intersection from 'lodash.intersection'; +import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; import Sortable from 'nomad-ui/mixins/sortable'; import Searchable from 'nomad-ui/mixins/searchable'; import WithNamespaceResetting from 'nomad-ui/mixins/with-namespace-resetting'; @@ -141,4 +142,19 @@ export default class TaskGroupController extends Controller.extend( setFacetQueryParam(queryParam, selection) { this.set(queryParam, serialize(selection)); } + get breadcrumbs() { + const model = this.model; + if (!model) return []; + return [ + { + label: model.get('name'), + args: [ + 'jobs.job.task-group', + model.get('job'), + model.get('name'), + qpBuilder({ jobNamespace: model.get('job.namespace.name') || 'default' }), + ], + }, + ]; + } } diff --git a/ui/app/controllers/jobs/run.js b/ui/app/controllers/jobs/run.js index 5921713017c6..0ad8722dc025 100644 --- a/ui/app/controllers/jobs/run.js +++ b/ui/app/controllers/jobs/run.js @@ -1,6 +1,13 @@ import Controller from '@ember/controller'; export default class RunController extends Controller { + breadcrumbs = [ + { + label: 'Run', + args: ['jobs.run'], + }, + ]; + onSubmit(id, namespace) { this.transitionToRoute('jobs.job', id, { queryParams: { namespace }, diff --git a/ui/app/controllers/optimize.js b/ui/app/controllers/optimize.js index b5c438a99fc2..94542b1db95c 100644 --- a/ui/app/controllers/optimize.js +++ b/ui/app/controllers/optimize.js @@ -102,6 +102,13 @@ export default class OptimizeController extends Controller { { key: 'dead', label: 'Dead' }, ]; + breadcrumbs = [ + { + label: 'Recommendations', + args: ['optimize'], + }, + ]; + get optionsDatacenter() { const flatten = (acc, val) => acc.concat(val); const allDatacenters = new Set(this.summaries.mapBy('job.datacenters').reduce(flatten, [])); diff --git a/ui/app/controllers/optimize/summary.js b/ui/app/controllers/optimize/summary.js index db81b7a263c3..204730e98d23 100644 --- a/ui/app/controllers/optimize/summary.js +++ b/ui/app/controllers/optimize/summary.js @@ -9,4 +9,16 @@ export default class OptimizeSummaryController extends Controller { jobNamespace: 'namespace', }, ]; + + get breadcrumbs() { + const model = this.model; + if (!model) return []; + + return [ + { + label: model.slug.replace('/', ' / '), + args: ['optimize.summary', model.slug], + }, + ]; + } } diff --git a/ui/app/controllers/servers.js b/ui/app/controllers/servers.js index aa0b590ba7e6..bf61fed66f72 100644 --- a/ui/app/controllers/servers.js +++ b/ui/app/controllers/servers.js @@ -2,4 +2,11 @@ import Controller from '@ember/controller'; export default class ServersController extends Controller { isForbidden = false; + + breadcrumbs = [ + { + label: 'Servers', + args: ['servers.index'], + }, + ]; } diff --git a/ui/app/routes/allocations/allocation.js b/ui/app/routes/allocations/allocation.js index 251f00726c2f..917d3edae007 100644 --- a/ui/app/routes/allocations/allocation.js +++ b/ui/app/routes/allocations/allocation.js @@ -3,9 +3,6 @@ import { collect } from '@ember/object/computed'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; import notifyError from 'nomad-ui/utils/notify-error'; -import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; -import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; - export default class AllocationRoute extends Route.extend(WithWatchers) { startWatchers(controller, model) { if (model) { @@ -13,32 +10,6 @@ export default class AllocationRoute extends Route.extend(WithWatchers) { } } - // Allocation breadcrumbs extend from job / task group breadcrumbs - // even though the route structure does not. - breadcrumbs(model) { - const jobQueryParams = qpBuilder({ - jobNamespace: model.get('job.namespace.name') || 'default', - }); - - return [ - { label: 'Jobs', args: ['jobs.index', jobQueryParams] }, - ...jobCrumbs(model.get('job')), - { - label: model.get('taskGroupName'), - args: [ - 'jobs.job.task-group', - model.get('job.plainId'), - model.get('taskGroupName'), - jobQueryParams, - ], - }, - { - label: model.get('shortId'), - args: ['allocations.allocation', model], - }, - ]; - } - model() { // Preload the job for the allocation since it's required for the breadcrumb trail return super diff --git a/ui/app/routes/allocations/allocation/task.js b/ui/app/routes/allocations/allocation/task.js index 7f3eec93df42..03be52f3c30c 100644 --- a/ui/app/routes/allocations/allocation/task.js +++ b/ui/app/routes/allocations/allocation/task.js @@ -5,16 +5,6 @@ import EmberError from '@ember/error'; export default class TaskRoute extends Route { @service store; - breadcrumbs(model) { - if (!model) return []; - return [ - { - label: model.get('name'), - args: ['allocations.allocation.task', model.get('allocation'), model], - }, - ]; - } - model({ name }) { const allocation = this.modelFor('allocations.allocation'); diff --git a/ui/app/routes/clients.js b/ui/app/routes/clients.js index c0c85460e9f9..8029193487a4 100644 --- a/ui/app/routes/clients.js +++ b/ui/app/routes/clients.js @@ -10,13 +10,6 @@ export default class ClientsRoute extends Route.extend(WithForbiddenState) { @service store; @service system; - breadcrumbs = [ - { - label: 'Clients', - args: ['clients.index'], - }, - ]; - beforeModel() { return this.get('system.leader'); } diff --git a/ui/app/routes/clients/client.js b/ui/app/routes/clients/client.js index b10d57e0f891..7dc94a49d387 100644 --- a/ui/app/routes/clients/client.js +++ b/ui/app/routes/clients/client.js @@ -9,16 +9,6 @@ export default class ClientRoute extends Route { return super.model(...arguments).catch(notifyError(this)); } - breadcrumbs(model) { - if (!model) return []; - return [ - { - label: model.get('shortId'), - args: ['clients.client', model.get('id')], - }, - ]; - } - afterModel(model) { if (model && model.get('isPartial')) { return model.reload().then(node => node.get('allocations')); diff --git a/ui/app/routes/csi/plugins.js b/ui/app/routes/csi/plugins.js index 0ddd99f36ac9..25728b2d2009 100644 --- a/ui/app/routes/csi/plugins.js +++ b/ui/app/routes/csi/plugins.js @@ -6,13 +6,6 @@ import notifyForbidden from 'nomad-ui/utils/notify-forbidden'; export default class PluginsRoute extends Route.extend(WithForbiddenState) { @service store; - breadcrumbs = [ - { - label: 'Storage', - args: ['csi.index'], - }, - ]; - model() { return this.store.query('plugin', { type: 'csi' }).catch(notifyForbidden(this)); } diff --git a/ui/app/routes/csi/plugins/plugin.js b/ui/app/routes/csi/plugins/plugin.js index 5643b2fb7d73..9728e5be1fff 100644 --- a/ui/app/routes/csi/plugins/plugin.js +++ b/ui/app/routes/csi/plugins/plugin.js @@ -6,17 +6,6 @@ export default class PluginRoute extends Route { @service store; @service system; - breadcrumbs = plugin => [ - { - label: 'Plugins', - args: ['csi.plugins'], - }, - { - label: plugin.plainId, - args: ['csi.plugins.plugin', plugin.plainId], - }, - ]; - serialize(model) { return { plugin_name: model.get('plainId') }; } diff --git a/ui/app/routes/csi/volumes.js b/ui/app/routes/csi/volumes.js index e81232393f96..e2e2f19ddba7 100644 --- a/ui/app/routes/csi/volumes.js +++ b/ui/app/routes/csi/volumes.js @@ -6,11 +6,4 @@ import classic from 'ember-classic-decorator'; export default class VolumesRoute extends Route.extend() { @service system; @service store; - - breadcrumbs = [ - { - label: 'Storage', - args: ['csi.index'], - }, - ]; } diff --git a/ui/app/routes/csi/volumes/volume.js b/ui/app/routes/csi/volumes/volume.js index 7a68264e6dce..01afa4b4c658 100644 --- a/ui/app/routes/csi/volumes/volume.js +++ b/ui/app/routes/csi/volumes/volume.js @@ -3,7 +3,6 @@ import Route from '@ember/routing/route'; import { collect } from '@ember/object/computed'; import RSVP from 'rsvp'; import notifyError from 'nomad-ui/utils/notify-error'; -import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; import classic from 'ember-classic-decorator'; @@ -13,24 +12,6 @@ export default class VolumeRoute extends Route.extend(WithWatchers) { @service store; @service system; - breadcrumbs = volume => [ - { - label: 'Volumes', - args: [ - 'csi.volumes', - qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }), - ], - }, - { - label: volume.name, - args: [ - 'csi.volumes.volume', - volume.plainId, - qpBuilder({ volumeNamespace: volume.get('namespace.name') || 'default' }), - ], - }, - ]; - startWatchers(controller, model) { if (!model) return; diff --git a/ui/app/routes/jobs.js b/ui/app/routes/jobs.js index 5599f63abc0f..51f4838fac1d 100644 --- a/ui/app/routes/jobs.js +++ b/ui/app/routes/jobs.js @@ -1,12 +1,3 @@ import Route from '@ember/routing/route'; -import classic from 'ember-classic-decorator'; -@classic -export default class JobsRoute extends Route.extend() { - breadcrumbs = [ - { - label: 'Jobs', - args: ['jobs.index'], - }, - ]; -} +export default class JobsRoute extends Route.extend() {} diff --git a/ui/app/routes/jobs/job.js b/ui/app/routes/jobs/job.js index 32709b0be70b..b665f4e2b2d5 100644 --- a/ui/app/routes/jobs/job.js +++ b/ui/app/routes/jobs/job.js @@ -2,7 +2,6 @@ import { inject as service } from '@ember/service'; import Route from '@ember/routing/route'; import RSVP from 'rsvp'; import notifyError from 'nomad-ui/utils/notify-error'; -import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; import classic from 'ember-classic-decorator'; @classic @@ -11,8 +10,6 @@ export default class JobRoute extends Route { @service store; @service token; - breadcrumbs = jobCrumbs; - serialize(model) { return { job_name: model.get('plainId') }; } @@ -21,7 +18,11 @@ export default class JobRoute extends Route { const namespace = transition.to.queryParams.namespace || 'default'; const name = params.job_name; const fullId = JSON.stringify([name, namespace]); - + console.log( + this.store + .findRecord('job', fullId, { reload: true }) + .then(job => console.log('model\n\n', job)) + ); return this.store .findRecord('job', fullId, { reload: true }) .then(job => { diff --git a/ui/app/routes/jobs/job/dispatch.js b/ui/app/routes/jobs/job/dispatch.js index 647804814296..cce1a3200c31 100644 --- a/ui/app/routes/jobs/job/dispatch.js +++ b/ui/app/routes/jobs/job/dispatch.js @@ -4,13 +4,6 @@ import { inject as service } from '@ember/service'; export default class DispatchRoute extends Route { @service can; - breadcrumbs = [ - { - label: 'Dispatch', - args: ['jobs.job.dispatch'], - }, - ]; - beforeModel() { const job = this.modelFor('jobs.job'); const namespace = job.namespace.get('name'); diff --git a/ui/app/routes/jobs/job/task-group.js b/ui/app/routes/jobs/job/task-group.js index e4f1ad9edd50..17ea124f4a82 100644 --- a/ui/app/routes/jobs/job/task-group.js +++ b/ui/app/routes/jobs/job/task-group.js @@ -4,25 +4,9 @@ import EmberError from '@ember/error'; import { resolve, all } from 'rsvp'; import { watchRecord, watchRelationship } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; -import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; import notifyError from 'nomad-ui/utils/notify-error'; export default class TaskGroupRoute extends Route.extend(WithWatchers) { - breadcrumbs(model) { - if (!model) return []; - return [ - { - label: model.get('name'), - args: [ - 'jobs.job.task-group', - model.get('job'), - model.get('name'), - qpBuilder({ jobNamespace: model.get('job.namespace.name') || 'default' }), - ], - }, - ]; - } - model({ name }) { const job = this.modelFor('jobs.job'); diff --git a/ui/app/routes/jobs/run.js b/ui/app/routes/jobs/run.js index 2111e1334837..6d456ead9ee6 100644 --- a/ui/app/routes/jobs/run.js +++ b/ui/app/routes/jobs/run.js @@ -8,13 +8,6 @@ export default class RunRoute extends Route { @service store; @service system; - breadcrumbs = [ - { - label: 'Run', - args: ['jobs.run'], - }, - ]; - beforeModel(transition) { if (this.can.cannot('run job', null, { namespace: transition.to.queryParams.namespace })) { this.transitionTo('jobs'); diff --git a/ui/app/routes/optimize.js b/ui/app/routes/optimize.js index b326b2b1e02f..4ba9309519d4 100644 --- a/ui/app/routes/optimize.js +++ b/ui/app/routes/optimize.js @@ -9,13 +9,6 @@ import RSVP from 'rsvp'; export default class OptimizeRoute extends Route { @service can; - breadcrumbs = [ - { - label: 'Recommendations', - args: ['optimize'], - }, - ]; - beforeModel() { if (this.can.cannot('accept recommendation')) { this.transitionTo('jobs'); diff --git a/ui/app/routes/optimize/summary.js b/ui/app/routes/optimize/summary.js index 09bcce488734..20822f4c2568 100644 --- a/ui/app/routes/optimize/summary.js +++ b/ui/app/routes/optimize/summary.js @@ -2,17 +2,6 @@ import Route from '@ember/routing/route'; import notifyError from 'nomad-ui/utils/notify-error'; export default class OptimizeSummaryRoute extends Route { - breadcrumbs(model) { - if (!model) return []; - - return [ - { - label: model.slug.replace('/', ' / '), - args: ['optimize.summary', model.slug], - }, - ]; - } - async model({ jobNamespace, slug }) { const model = this.modelFor('optimize').summaries.find( summary => summary.slug === slug && summary.jobNamespace === jobNamespace diff --git a/ui/app/routes/servers.js b/ui/app/routes/servers.js index 5976dfaffde7..d784a89df2e9 100644 --- a/ui/app/routes/servers.js +++ b/ui/app/routes/servers.js @@ -10,13 +10,6 @@ export default class ServersRoute extends Route.extend(WithForbiddenState) { @service store; @service system; - breadcrumbs = [ - { - label: 'Servers', - args: ['servers.index'], - }, - ]; - beforeModel() { return this.get('system.leader'); } diff --git a/ui/app/routes/servers/server.js b/ui/app/routes/servers/server.js index 9fbe06fb4174..ab399d639c42 100644 --- a/ui/app/routes/servers/server.js +++ b/ui/app/routes/servers/server.js @@ -1,14 +1,4 @@ import Route from '@ember/routing/route'; import WithModelErrorHandling from 'nomad-ui/mixins/with-model-error-handling'; -export default class ServerRoute extends Route.extend(WithModelErrorHandling) { - breadcrumbs(model) { - if (!model) return []; - return [ - { - label: model.name, - args: ['servers.server', model.id], - }, - ]; - } -} +export default class ServerRoute extends Route.extend(WithModelErrorHandling) {} diff --git a/ui/app/routes/topology.js b/ui/app/routes/topology.js index a53a92708cf2..89f35c4f8ff3 100644 --- a/ui/app/routes/topology.js +++ b/ui/app/routes/topology.js @@ -10,13 +10,6 @@ export default class TopologyRoute extends Route.extend(WithForbiddenState) { @service store; @service system; - breadcrumbs = [ - { - label: 'Topology', - args: ['topology'], - }, - ]; - model() { return RSVP.hash({ jobs: this.store.findAll('job'), diff --git a/ui/app/templates/allocations/allocation.hbs b/ui/app/templates/allocations/allocation.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/allocations/allocation.hbs +++ b/ui/app/templates/allocations/allocation.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/allocations/allocation/task.hbs b/ui/app/templates/allocations/allocation/task.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/allocations/allocation/task.hbs +++ b/ui/app/templates/allocations/allocation/task.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/clients.hbs b/ui/app/templates/clients.hbs index 197c6c10d5c6..6e448d265b6c 100644 --- a/ui/app/templates/clients.hbs +++ b/ui/app/templates/clients.hbs @@ -1,3 +1,4 @@ {{outlet}} + \ No newline at end of file diff --git a/ui/app/templates/clients/client.hbs b/ui/app/templates/clients/client.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/clients/client.hbs +++ b/ui/app/templates/clients/client.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/components/app-breadcrumbs.hbs b/ui/app/templates/components/app-breadcrumbs.hbs index 6cfc2aa606cb..3a24a6f9a78f 100644 --- a/ui/app/templates/components/app-breadcrumbs.hbs +++ b/ui/app/templates/components/app-breadcrumbs.hbs @@ -1,13 +1,17 @@ -{{#each this.breadcrumbs as |breadcrumb index|}} -
  • - {{#if breadcrumb.isPending}} - - {{else}} - - {{breadcrumb.label}} - - {{/if}} -
  • -{{/each}} + + {{#each bb as |breadcrumb index|}} + {{#each breadcrumb.args.crumb as |b|}} +
  • + {{#if b.isPending}} + + … + + {{else}} + + {{b.label}} + + {{/if}} +
  • + {{/each}} + {{/each}} +
    \ No newline at end of file diff --git a/ui/app/templates/components/job-page/parts/summary.hbs b/ui/app/templates/components/job-page/parts/summary.hbs index 9b4ed85090fb..8adfe1220f85 100644 --- a/ui/app/templates/components/job-page/parts/summary.hbs +++ b/ui/app/templates/components/job-page/parts/summary.hbs @@ -44,7 +44,10 @@ (if a.item.hasChildren "children-status-bar" "allocation-status-bar") allocationContainer=a.item.summary job=a.item.summary +<<<<<<< HEAD onSliceClick=this.onSliceClick +======= +>>>>>>> bf9ae2b52 (fix: delete state from routes and move to controllers and add renderless component to templates) class="split-view" as |chart| }}
      @@ -62,4 +65,5 @@
    {{/component}} - \ No newline at end of file + + diff --git a/ui/app/templates/csi/plugins.hbs b/ui/app/templates/csi/plugins.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/csi/plugins.hbs +++ b/ui/app/templates/csi/plugins.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/plugins/plugin.hbs b/ui/app/templates/csi/plugins/plugin.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/csi/plugins/plugin.hbs +++ b/ui/app/templates/csi/plugins/plugin.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/volumes.hbs b/ui/app/templates/csi/volumes.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/csi/volumes.hbs +++ b/ui/app/templates/csi/volumes.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/volumes/volume.hbs b/ui/app/templates/csi/volumes/volume.hbs index 83e932383fb5..2419f31ca50a 100644 --- a/ui/app/templates/csi/volumes/volume.hbs +++ b/ui/app/templates/csi/volumes/volume.hbs @@ -1,107 +1,157 @@ {{page-title "CSI Volume " this.model.name}}
    -

    {{this.model.name}}

    - +

    + {{this.model.name}} +

    - Volume Details + + Volume Details + - Health + + Health + {{if this.model.schedulable "Schedulable" "Unschedulable"}} - Provider + + Provider + {{this.model.provider}} - External ID + + External ID + {{this.model.externalId}} {{#if this.system.shouldShowNamespaces}} - Namespace + + Namespace + {{this.model.namespace.name}} {{/if}}
    -
    Write Allocations
    {{#if this.model.writeAllocations.length}} - + - ID - Created - Modified - Status - Client - Job - Version - CPU - Memory + + ID + + + Created + + + Modified + + + Status + + + Client + + + Job + + + Version + + + CPU + + + Memory + + @onClick={{action "gotoAllocation" row.model}} + /> {{else}}
    -

    No Write Allocations

    -

    No allocations are depending on this volume for read/write access.

    +

    + No Write Allocations +

    +

    + No allocations are depending on this volume for read/write access. +

    {{/if}}
    -
    Read Allocations
    {{#if this.model.readAllocations.length}} - + - ID - Modified - Created - Status - Client - Job - Version - CPU - Memory + + ID + + + Modified + + + Created + + + Status + + + Client + + + Job + + + Version + + + CPU + + + Memory + + @onClick={{action "gotoAllocation" row.model}} + /> {{else}}
    -

    No Read Allocations

    -

    No allocations are depending on this volume for read-only access.

    +

    + No Read Allocations +

    +

    + No allocations are depending on this volume for read-only access. +

    {{/if}}
    -
    Constraints @@ -109,20 +159,33 @@
    - - + + - - + + - - + +
    SettingValue + Setting + + Value +
    Access Mode{{this.model.accessMode}} + Access Mode + + {{this.model.accessMode}} +
    Attachment Mode{{this.model.attachmentMode}} + Attachment Mode + + {{this.model.attachmentMode}} +
    + \ No newline at end of file diff --git a/ui/app/templates/jobs.hbs b/ui/app/templates/jobs.hbs index 197c6c10d5c6..6e448d265b6c 100644 --- a/ui/app/templates/jobs.hbs +++ b/ui/app/templates/jobs.hbs @@ -1,3 +1,4 @@ {{outlet}} + \ No newline at end of file diff --git a/ui/app/templates/jobs/job.hbs b/ui/app/templates/jobs/job.hbs index c24cd68950a9..bc4208e209d9 100644 --- a/ui/app/templates/jobs/job.hbs +++ b/ui/app/templates/jobs/job.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/jobs/job/dispatch.hbs b/ui/app/templates/jobs/job/dispatch.hbs index 1f479113c12b..14fab8f07093 100644 --- a/ui/app/templates/jobs/job/dispatch.hbs +++ b/ui/app/templates/jobs/job/dispatch.hbs @@ -1,5 +1,6 @@ {{page-title "Dispatch new " this.model.name}}
    - +
    + \ No newline at end of file diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs index b031c84a7865..9e9593135cda 100644 --- a/ui/app/templates/jobs/job/task-group.hbs +++ b/ui/app/templates/jobs/job/task-group.hbs @@ -1,64 +1,116 @@ {{page-title "Task group " this.model.name " - Job " this.model.job.name}}
      -
    • Overview
    • +
    • + + Overview + +

    - {{this.model.name}} + + {{this.model.name}} +
    {{#if this.model.scaling}} - - Count - + + Count + {{/if}}

    -
    - Task Group Details - - # Tasks {{this.model.tasks.length}} - Reserved CPU {{format-scheduled-hertz this.model.reservedCPU}} + + Task Group Details + + + + # Tasks + + {{this.model.tasks.length}} + + + + Reserved CPU + + {{format-scheduled-hertz this.model.reservedCPU}} + - Reserved Memory + + Reserved Memory + {{format-scheduled-bytes this.model.reservedMemory start="MiB"}} {{#if (gt this.model.reservedMemoryMax this.model.reservedMemory)}} - ({{format-scheduled-bytes this.model.reservedMemoryMax start="MiB"}} Max) + ({{format-scheduled-bytes this.model.reservedMemoryMax start="MiB"}}Max) {{/if}} - Reserved Disk {{format-scheduled-bytes this.model.reservedEphemeralDisk start="MiB"}} + + + Reserved Disk + + {{format-scheduled-bytes this.model.reservedEphemeralDisk start="MiB"}} + {{#if this.model.scaling}} - Count Range - {{this.model.scaling.min}} to {{this.model.scaling.max}} + + + Count Range + + {{this.model.scaling.min}} + to + {{this.model.scaling.max}} - Scaling Policy? + + + Scaling Policy? + {{if this.model.scaling.policy "Yes" "No"}} {{/if}}
    -
    -
    Allocation Status {{this.allocations.length}}
    +
    + Allocation Status + + {{this.allocations.length}} + +
    - +
      {{#each chart.data as |datum index|}} -
    1. +
    2. {{/each}} @@ -99,62 +151,102 @@ @source={{this.sortedAllocations}} @size={{this.pageSize}} @page={{this.currentPage}} - @class="allocations" as |p|> + @class="allocations" as |p| + > + @class="with-foot" as |t| + > - ID - Created - Modified - Status - Version - Client - Volume - CPU - Memory + + ID + + + Created + + + Modified + + + Status + + + Version + + + Client + + + Volume + + + CPU + + + Memory + - +
      - {{else}} - {{#if this.allocations.length}} -
      -
      -

      No Matches

      -

      No allocations match the term {{this.searchTerm}}

      -
      + {{else if this.allocations.length}} +
      +
      +

      + No Matches +

      +

      + No allocations match the term + + {{this.searchTerm}} + +

      - {{else}} -
      -
      -

      No Allocations

      -

      No allocations have been placed.

      -
      +
      + {{else}} +
      +
      +

      + No Allocations +

      +

      + No allocations have been placed. +

      - {{/if}} +
      {{/if}}
      - - {{#if this.model.scaleState.isVisible}} {{#if this.shouldShowScaleEventTimeline}}
      @@ -166,7 +258,6 @@
    {{/if}} -
    Recent Scaling Events @@ -176,7 +267,6 @@
    {{/if}} - {{#if this.model.volumes.length}}
    @@ -185,25 +275,43 @@
    - Name - Type - Source - Permissions + + Name + + + Type + + + Source + + + Permissions + {{#if row.model.isCSI}} - + {{row.model.name}} {{else}} {{row.model.name}} {{/if}} - {{row.model.type}} - {{row.model.source}} - {{if row.model.readOnly "Read" "Read/Write"}} + + {{row.model.type}} + + + {{row.model.source}} + + + {{if row.model.readOnly "Read" "Read/Write"}} + @@ -211,3 +319,4 @@
    {{/if}}
    + \ No newline at end of file diff --git a/ui/app/templates/jobs/run.hbs b/ui/app/templates/jobs/run.hbs index 6aa51d9ca237..62295e9d9528 100644 --- a/ui/app/templates/jobs/run.hbs +++ b/ui/app/templates/jobs/run.hbs @@ -1,7 +1,5 @@ {{page-title "Run a job"}}
    - +
    + \ No newline at end of file diff --git a/ui/app/templates/optimize.hbs b/ui/app/templates/optimize.hbs index a922cbca5c2c..eff320747dbb 100644 --- a/ui/app/templates/optimize.hbs +++ b/ui/app/templates/optimize.hbs @@ -8,7 +8,12 @@ data-test-recommendation-summaries-search @onChange={{this.syncActiveSummary}} @searchTerm={{mut this.searchTerm}} - @placeholder="Search {{this.summaries.length}} {{pluralize "recommendation" this.summaries.length}}..." /> + @placeholder="Search + {{this.summaries.length}} + + {{pluralize "recommendation" this.summaries.length}} + ..." + /> {{/if}}
    @@ -19,72 +24,88 @@ @label="Namespace" @options={{this.optionsNamespaces}} @selection={{this.qpNamespace}} - @onSelect={{action (queue - (action this.cacheNamespace) - (action this.setFacetQueryParam "qpNamespace") - )}} /> + @onSelect={{action + (queue + (action this.cacheNamespace) (action this.setFacetQueryParam "qpNamespace") + ) + }} + /> {{/if}} + @onSelect={{action this.setFacetQueryParam "qpType"}} + /> + @onSelect={{action this.setFacetQueryParam "qpStatus"}} + /> + @onSelect={{action this.setFacetQueryParam "qpDatacenter"}} + /> + @onSelect={{action this.setFacetQueryParam "qpPrefix"}} + />
    - {{#if this.filteredSummaries}} {{outlet}} - - + - Job - Recommended At - # Allocs - CPU - Mem - Agg. CPU - Agg. Mem + + Job + + + Recommended At + + + # Allocs + + + CPU + + + Mem + + + Agg. CPU + + + Agg. Mem + {{#if row.model.isProcessed}} - + {{else}} {{/if}} - {{else}}
    -

    No Matches

    +

    + No Matches +

    No recommendations match your current filter selection.

    @@ -92,7 +113,9 @@ {{/if}} {{else}}
    -

    No Recommendations

    +

    + No Recommendations +

    All recommendations have been accepted or dismissed. Nomad will continuously monitor applications so expect more recommendations in the future.

    @@ -100,3 +123,4 @@ {{/if}} + \ No newline at end of file diff --git a/ui/app/templates/servers.hbs b/ui/app/templates/servers.hbs index 197c6c10d5c6..6e448d265b6c 100644 --- a/ui/app/templates/servers.hbs +++ b/ui/app/templates/servers.hbs @@ -1,3 +1,4 @@ {{outlet}} + \ No newline at end of file diff --git a/ui/app/templates/topology.hbs b/ui/app/templates/topology.hbs index 59f509a4c075..30cb1e1a1efe 100644 --- a/ui/app/templates/topology.hbs +++ b/ui/app/templates/topology.hbs @@ -8,11 +8,26 @@
    -

    Some Clients Were Filtered

    -

    {{this.filteredNodes.length}} {{if (eq this.filteredNodes.length 1) "client was" "clients were"}} filtered from the topology visualization. This is most likely due to the {{pluralize "client" this.filteredNodes.length}} running a version of Nomad <0.9.0.

    +

    + Some Clients Were Filtered +

    +

    + {{this.filteredNodes.length}} + {{if (eq this.filteredNodes.length 1) "client was" "clients were"}} + filtered from the topology visualization. This is most likely due to the + {{pluralize "client" this.filteredNodes.length}} + running a version of Nomad <0.9.0. +

    - +
    @@ -23,11 +38,22 @@
    -

    No Live Updating

    -

    The topology visualization depends on too much data to continuously poll.

    +

    + No Live Updating +

    +

    + The topology visualization depends on too much data to continuously poll. +

    - +
    @@ -36,154 +62,335 @@
    Legend {{#if (cannot "list all jobs")}} - Partial View + + Partial View + {{/if}}
    -

    Metrics

    +

    + Metrics +

    -
    M:
    Memory
    -
    C:
    CPU
    +
    + M: +
    +
    + Memory +
    +
    + C: +
    +
    + CPU +
    -

    Allocation Status

    +

    + Allocation Status +

    -
    Running
    -
    Starting
    +
    +
    + +
    +
    + Running +
    +
    +
    +
    + +
    +
    + Starting +
    +
    - {{#if this.activeNode}}Client{{else if this.activeAllocation}}Allocation{{else}}Cluster{{/if}} Details + {{#if this.activeNode}} + Client + {{else if this.activeAllocation}} + Allocation + {{else}} + Cluster + {{/if}} + Details
    {{#if this.activeNode}} {{#let this.activeNode.node as |node|}} -
    -

    {{this.activeNode.allocations.length}} Allocations

    -
    -
    -

    - Client: - - {{node.shortId}} - -

    -

    Name: {{node.name}}

    -

    Address: {{node.httpAddr}}

    -

    Status: {{node.status}}

    -
    -
    -

    - Draining? {{if node.isDraining "Yes" "No"}} -

    -

    - Eligible? {{if node.isEligible "Yes" "No"}} -

    -
    -
    -

    - {{this.nodeUtilization.totalMemoryFormatted}} - {{this.nodeUtilization.totalMemoryUnits}} - of memory -

    -
    -
    -
    - - {{this.nodeUtilization.reservedMemoryPercent}} - +
    +

    + {{this.activeNode.allocations.length}} + + Allocations + +

    +
    +
    +

    + + Client: + + + {{node.shortId}} + +

    +

    + + Name: + + {{node.name}} +

    +

    + + Address: + + {{node.httpAddr}} +

    +

    + + Status: + + {{node.status}} +

    +
    +
    +

    + + Draining? + + + {{if node.isDraining "Yes" "No"}} + +

    +

    + + Eligible? + + + {{if node.isEligible "Yes" "No"}} + +

    +
    +
    +

    + {{this.nodeUtilization.totalMemoryFormatted}} + + {{this.nodeUtilization.totalMemoryUnits}} + + + of memory + +

    +
    +
    +
    + + {{this.nodeUtilization.reservedMemoryPercent}} + +
    +
    +
    + + {{format-percentage this.nodeUtilization.reservedMemoryPercent total=1}} +
    -
    - {{format-percentage this.nodeUtilization.reservedMemoryPercent total=1}} +
    + + {{format-scheduled-bytes this.nodeUtilization.totalReservedMemory}} + + / + {{format-scheduled-bytes this.nodeUtilization.totalMemory}} + reserved
    -
    - {{format-scheduled-bytes this.nodeUtilization.totalReservedMemory}} / {{format-scheduled-bytes this.nodeUtilization.totalMemory}} reserved -
    -
    -
    -

    {{this.nodeUtilization.totalCPU}} MHz of CPU

    -
    -
    -
    - - {{this.nodeUtilization.reservedCPUPercent}} - +
    +

    + {{this.nodeUtilization.totalCPU}} + + MHz + + + of CPU + +

    +
    +
    +
    + + {{this.nodeUtilization.reservedCPUPercent}} + +
    +
    +
    + + {{format-percentage this.nodeUtilization.reservedCPUPercent total=1}} +
    -
    - {{format-percentage this.nodeUtilization.reservedCPUPercent total=1}} +
    + + {{format-scheduled-hertz this.nodeUtilization.totalReservedCPU}} + + / + {{format-scheduled-hertz this.nodeUtilization.totalCPU}} + reserved
    -
    - {{format-scheduled-hertz this.nodeUtilization.totalReservedCPU}} / {{format-scheduled-hertz this.nodeUtilization.totalCPU}} reserved -
    -
    {{/let}} {{else if this.activeAllocation}}

    - Allocation: - {{this.activeAllocation.shortId}} + + Allocation: + + + {{this.activeAllocation.shortId}} +

    -

    Sibling Allocations: {{this.siblingAllocations.length}}

    -

    Unique Client Placements: {{this.uniqueActiveAllocationNodes.length}}

    +

    + + Sibling Allocations: + + {{this.siblingAllocations.length}} +

    +

    + + Unique Client Placements: + + {{this.uniqueActiveAllocationNodes.length}} +

    - Job: + + Job: + - {{this.activeAllocation.job.name}} - / {{this.activeAllocation.taskGroupName}} + @query={{hash jobNamespace=this.activeAllocation.job.namespace.id}} + > + {{this.activeAllocation.job.name}} + + + / + {{this.activeAllocation.taskGroupName}} +

    -

    Type: {{this.activeAllocation.job.type}}

    -

    Priority: {{this.activeAllocation.job.priority}}

    +

    + + Type: + + {{this.activeAllocation.job.type}} +

    +

    + + Priority: + + {{this.activeAllocation.job.priority}} +

    - Client: - + + Client: + + {{this.activeAllocation.node.shortId}}

    -

    Name: {{this.activeAllocation.node.name}}

    -

    Address: {{this.activeAllocation.node.httpAddr}}

    +

    + + Name: + + {{this.activeAllocation.node.name}} +

    +

    + + Address: + + {{this.activeAllocation.node.httpAddr}} +

    - +
    - +
    {{else}}
    -

    {{this.model.nodes.length}} Clients

    +

    + {{this.model.nodes.length}} + + Clients + +

    -

    {{this.scheduledAllocations.length}} Allocations

    +

    + {{this.scheduledAllocations.length}} + + Allocations + +

    -

    {{this.totalMemoryFormatted}} {{this.totalMemoryUnits}} of memory

    +

    + {{this.totalMemoryFormatted}} + + {{this.totalMemoryUnits}} + + + of memory + +

    @@ -191,21 +398,37 @@ data-test-memory-progress-bar class="progress is-danger is-small" value="{{this.reservedMemoryPercent}}" - max="1"> + max="1" + > {{this.reservedMemoryPercent}}
    - {{format-percentage this.reservedMemoryPercent total=1}} + + {{format-percentage this.reservedMemoryPercent total=1}} +
    - {{format-bytes this.totalReservedMemory}} / {{format-bytes this.totalMemory}} reserved + + {{format-bytes this.totalReservedMemory}} + + / + {{format-bytes this.totalMemory}} + reserved
    -

    {{this.totalCPUFormatted}} {{this.totalCPUUnits}} of CPU

    +

    + {{this.totalCPUFormatted}} + + {{this.totalCPUUnits}} + + + of CPU + +

    @@ -213,17 +436,25 @@ data-test-cpu-progress-bar class="progress is-info is-small" value="{{this.reservedCPUPercent}}" - max="1"> + max="1" + > {{this.reservedCPUPercent}}
    - {{format-percentage this.reservedCPUPercent total=1}} + + {{format-percentage this.reservedCPUPercent total=1}} +
    - {{format-hertz this.totalReservedCPU}} / {{format-hertz this.totalCPU}} reserved + + {{format-hertz this.totalReservedCPU}} + + / + {{format-hertz this.totalCPU}} + reserved
    {{/if}} @@ -236,9 +467,11 @@ @allocations={{this.model.allocations}} @onAllocationSelect={{action this.setAllocation}} @onNodeSelect={{action this.setNode}} - @onDataError={{action this.handleTopoVizDataError}}/> + @onDataError={{action this.handleTopoVizDataError}} + />
    {{/if}} + \ No newline at end of file diff --git a/ui/app/utils/breadcrumb-utils.js b/ui/app/utils/breadcrumb-utils.js index 699d3f87522c..5050ee0f3ed8 100644 --- a/ui/app/utils/breadcrumb-utils.js +++ b/ui/app/utils/breadcrumb-utils.js @@ -13,6 +13,7 @@ export const jobCrumb = job => ({ }); export const jobCrumbs = job => { + console.log('job\n\n', job); if (!job) return []; if (job.get('parent.content')) { From 126c4dfa563f70c6aa30c3db1f1b8e92f92247ed Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 30 Nov 2021 15:21:56 -0500 Subject: [PATCH 04/27] fix: delete old breadcrumbs and replace with bucket --- ui/app/components/breadcrumb.js | 6 ++-- ui/app/components/breadcrumbs.js | 4 +-- ui/app/services/breadcrumbs.js | 54 +++++++++----------------------- ui/app/services/bucket.js | 21 ------------- 4 files changed, 20 insertions(+), 65 deletions(-) delete mode 100644 ui/app/services/bucket.js diff --git a/ui/app/components/breadcrumb.js b/ui/app/components/breadcrumb.js index b2c661e56b6f..4a449a88e845 100644 --- a/ui/app/components/breadcrumb.js +++ b/ui/app/components/breadcrumb.js @@ -3,14 +3,14 @@ import { action } from '@ember/object'; import { inject as service } from '@ember/service'; export default class Breadcrumb extends Component { - @service bucket; + @service breadcrumbs; @action register() { - this.bucket.registerBreadcrumb(this); + this.breadcrumbs.registerBreadcrumb(this); } @action deregister() { - this.bucket.deregisterBreadcrumb(this); + this.breadcrumbs.deregisterBreadcrumb(this); } constructor() { diff --git a/ui/app/components/breadcrumbs.js b/ui/app/components/breadcrumbs.js index 3d104ab9160a..8ce6674b16aa 100644 --- a/ui/app/components/breadcrumbs.js +++ b/ui/app/components/breadcrumbs.js @@ -2,9 +2,9 @@ import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; export default class Breadcrumbs extends Component { - @service bucket; + @service breadcrumbs; get crumbs() { - return this.bucket.crumbs; + return this.breadcrumbs.crumbs; } } diff --git a/ui/app/services/breadcrumbs.js b/ui/app/services/breadcrumbs.js index fd44b9e7a5de..0a268708f14f 100644 --- a/ui/app/services/breadcrumbs.js +++ b/ui/app/services/breadcrumbs.js @@ -1,45 +1,21 @@ -import { getOwner } from '@ember/application'; -import Service, { inject as service } from '@ember/service'; -import { computed } from '@ember/object'; -import classic from 'ember-classic-decorator'; +import Service from '@ember/service'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; +import { schedule } from '@ember/runloop'; -@classic -export default class BreadcrumbsService extends Service { - @service router; +export default class BucketService extends Service { + @tracked crumbs = []; - // currentURL is only used to listen to all transitions. - // currentRouteName has all information necessary to compute breadcrumbs, - // but it doesn't change when a transition to the same route with a different - // model occurs. - @computed('router.{currentURL,currentRouteName}') - get breadcrumbs() { - const owner = getOwner(this); - const allRoutes = (this.get('router.currentRouteName') || '') - .split('.') - .without('') - .map((segment, index, allSegments) => allSegments.slice(0, index + 1).join('.')); - - let crumbs = []; - allRoutes.forEach(routeName => { - const route = owner.lookup(`route:${routeName}`); - - // Routes can reset the breadcrumb trail to start anew even - // if the route is deeply nested. - if (route.resetBreadcrumbs) { - crumbs = []; - } - - // Breadcrumbs are either an array of static crumbs - // or a function that returns breadcrumbs given the current - // model for the route's controller. - let breadcrumbs = route.breadcrumbs || []; - if (typeof breadcrumbs === 'function') { - breadcrumbs = breadcrumbs(route.get('controller.model')) || []; - } - - crumbs.push(...breadcrumbs); + @action registerBreadcrumb(crumb) { + schedule('actions', this, () => { + console.log('register crumb: ', crumb); + this.crumbs = [...this.crumbs, crumb]; }); + } + + @action deregisterBreadcrumb(crumb) { + const newCrumbs = this.crumbs.filter(c => c !== crumb); - return crumbs; + this.crumbs = newCrumbs; } } diff --git a/ui/app/services/bucket.js b/ui/app/services/bucket.js deleted file mode 100644 index 0a268708f14f..000000000000 --- a/ui/app/services/bucket.js +++ /dev/null @@ -1,21 +0,0 @@ -import Service from '@ember/service'; -import { action } from '@ember/object'; -import { tracked } from '@glimmer/tracking'; -import { schedule } from '@ember/runloop'; - -export default class BucketService extends Service { - @tracked crumbs = []; - - @action registerBreadcrumb(crumb) { - schedule('actions', this, () => { - console.log('register crumb: ', crumb); - this.crumbs = [...this.crumbs, crumb]; - }); - } - - @action deregisterBreadcrumb(crumb) { - const newCrumbs = this.crumbs.filter(c => c !== crumb); - - this.crumbs = newCrumbs; - } -} From b1002d6839ab3e3160238ba3444d936bf20a0273 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 8 Dec 2021 14:26:25 -0500 Subject: [PATCH 05/27] feat: create trigger component Create Trigger Component and deprecate Breadcrumb-Utils pattern. --- ui/app/components/breadcrumbs/default.hbs | 5 ++ ui/app/components/breadcrumbs/job.hbs | 19 ++++++ ui/app/components/breadcrumbs/job.js | 36 +++++++++++ ui/app/components/trigger.hbs | 1 + ui/app/components/trigger.js | 62 +++++++++++++++++++ ui/app/controllers/allocations/allocation.js | 3 +- ui/app/controllers/clients/client.js | 2 +- ui/app/controllers/jobs/job.js | 5 +- .../templates/components/app-breadcrumbs.hbs | 14 ++--- ui/app/templates/jobs.hbs | 4 +- ui/app/templates/servers/server.hbs | 2 +- ui/app/utils/breadcrumb-utils.js | 29 --------- ui/config/environment.js | 5 ++ ui/package.json | 1 + ui/yarn.lock | 31 ++++++++++ 15 files changed, 174 insertions(+), 45 deletions(-) create mode 100644 ui/app/components/breadcrumbs/default.hbs create mode 100644 ui/app/components/breadcrumbs/job.hbs create mode 100644 ui/app/components/breadcrumbs/job.js create mode 100644 ui/app/components/trigger.hbs create mode 100644 ui/app/components/trigger.js delete mode 100644 ui/app/utils/breadcrumb-utils.js diff --git a/ui/app/components/breadcrumbs/default.hbs b/ui/app/components/breadcrumbs/default.hbs new file mode 100644 index 000000000000..8b5108a7da69 --- /dev/null +++ b/ui/app/components/breadcrumbs/default.hbs @@ -0,0 +1,5 @@ +
  • + + {{@crumb.label}} + +
  • \ No newline at end of file diff --git a/ui/app/components/breadcrumbs/job.hbs b/ui/app/components/breadcrumbs/job.hbs new file mode 100644 index 000000000000..140ee8b9a94f --- /dev/null +++ b/ui/app/components/breadcrumbs/job.hbs @@ -0,0 +1,19 @@ + + {{did-insert trigger.fns.do}} + {{#if trigger.data.result}} +
  • + + {{trigger.data.result.label}} + +
  • + {{/if}} +
  • + + {{this.crumb.label}} + +
  • +
    \ No newline at end of file diff --git a/ui/app/components/breadcrumbs/job.js b/ui/app/components/breadcrumbs/job.js new file mode 100644 index 000000000000..f58ef5677c1e --- /dev/null +++ b/ui/app/components/breadcrumbs/job.js @@ -0,0 +1,36 @@ +import { action } from '@ember/object'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; + +export default class BreadcrumbsJob extends Component { + get job() { + return this.args.crumb.job; + } + + @tracked parent = null; + + generateCrumb(job) { + return { + label: job.get('trimmedName') || job.trimmedName, + args: [ + 'jobs.job.index', + job.get('plainId') || job.plainId, + qpBuilder({ + jobNamespace: job.get('namespace.name') || 'default', + }), + ], + }; + } + + get crumb() { + if (!this.job) return null; + return this.generateCrumb(this.job); + } + + @action + fetchParent() { + this.parent = this.job.parent || this.job.get('parent'); + return this.generateCrumb(this.parent); + } +} diff --git a/ui/app/components/trigger.hbs b/ui/app/components/trigger.hbs new file mode 100644 index 000000000000..c6fdb221f2a9 --- /dev/null +++ b/ui/app/components/trigger.hbs @@ -0,0 +1 @@ +{{yield (hash data=this.data fns=this.fns)}} \ No newline at end of file diff --git a/ui/app/components/trigger.js b/ui/app/components/trigger.js new file mode 100644 index 000000000000..8054060e14cf --- /dev/null +++ b/ui/app/components/trigger.js @@ -0,0 +1,62 @@ +import { action } from '@ember/object'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { task } from 'ember-concurrency'; + +const noOp = () => undefined; + +export default class Trigger extends Component { + @tracked error = null; + @tracked result = null; + + get isBusy() { + return this.triggerTask.isRunning; + } + + get isIdle() { + return this.triggerTask.isIdle; + } + + get isSuccess() { + return this.triggerTask.last.isSuccessful; + } + + get isError() { + return this.triggerTask.lastErrored; + } + + get fns() { + return { + do: this.onTrigger, + }; + } + + get onError() { + return this.args.onError ?? noOp; + } + + get onSuccess() { + return this.args.onSuccess ?? noOp; + } + + get data() { + const { isBusy, isIdle, isSuccess, isError, result } = this; + return { isBusy, isIdle, isSuccess, isError, result }; + } + + @task(function*() { + try { + this.result = yield this.args.do(); + this.onSuccess(this.result); + } catch (e) { + this.error = e; + this.onError(this.error); + } + }) + triggerTask; + + @action + onTrigger() { + this.triggerTask.perform(); + } +} diff --git a/ui/app/controllers/allocations/allocation.js b/ui/app/controllers/allocations/allocation.js index fbd071ddd0ed..2491bfd290c4 100644 --- a/ui/app/controllers/allocations/allocation.js +++ b/ui/app/controllers/allocations/allocation.js @@ -1,6 +1,5 @@ import Controller from '@ember/controller'; import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; -import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; export default class AllocationsAllocationController extends Controller { // Allocation breadcrumbs extend from job / task group breadcrumbs @@ -13,7 +12,7 @@ export default class AllocationsAllocationController extends Controller { return [ { label: 'Jobs', args: ['jobs.index', jobQueryParams] }, - ...jobCrumbs(model.get('job')), + { type: 'job', job: model.get('job') }, { label: model.get('taskGroupName'), args: [ diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index 3efd6ebe1a56..aa558c80b0bb 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -1,7 +1,7 @@ import Controller from '@ember/controller'; export default class ClientsClientController extends Controller { - breadcrumbs() { + get breadcrumbs() { const model = this.model; if (!model) return []; return [ diff --git a/ui/app/controllers/jobs/job.js b/ui/app/controllers/jobs/job.js index f369e82b4717..17e726db4910 100644 --- a/ui/app/controllers/jobs/job.js +++ b/ui/app/controllers/jobs/job.js @@ -1,5 +1,4 @@ import Controller from '@ember/controller'; -import { jobCrumbs } from 'nomad-ui/utils/breadcrumb-utils'; export default class JobController extends Controller { queryParams = [ @@ -9,5 +8,7 @@ export default class JobController extends Controller { ]; jobNamespace = 'default'; - breadcrumbs = jobCrumbs(this.model); + get breadcrumbs() { + return [{ type: 'job', job: this.model }]; + } } diff --git a/ui/app/templates/components/app-breadcrumbs.hbs b/ui/app/templates/components/app-breadcrumbs.hbs index 3a24a6f9a78f..618237f31b7a 100644 --- a/ui/app/templates/components/app-breadcrumbs.hbs +++ b/ui/app/templates/components/app-breadcrumbs.hbs @@ -1,17 +1,15 @@ {{#each bb as |breadcrumb index|}} {{#each breadcrumb.args.crumb as |b|}} -
  • - {{#if b.isPending}} + {{#if b.isPending}} +
  • - {{else}} - - {{b.label}} - - {{/if}} -
  • + + {{else}} + {{component (concat "breadcrumbs/" (or b.type "default")) crumb=b}} + {{/if}} {{/each}} {{/each}}
    \ No newline at end of file diff --git a/ui/app/templates/jobs.hbs b/ui/app/templates/jobs.hbs index 6e448d265b6c..44a687f0c474 100644 --- a/ui/app/templates/jobs.hbs +++ b/ui/app/templates/jobs.hbs @@ -1,4 +1,4 @@ + {{outlet}} - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/servers/server.hbs b/ui/app/templates/servers/server.hbs index c24cd68950a9..2fd45556a2be 100644 --- a/ui/app/templates/servers/server.hbs +++ b/ui/app/templates/servers/server.hbs @@ -1 +1 @@ -{{outlet}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/utils/breadcrumb-utils.js b/ui/app/utils/breadcrumb-utils.js deleted file mode 100644 index 5050ee0f3ed8..000000000000 --- a/ui/app/utils/breadcrumb-utils.js +++ /dev/null @@ -1,29 +0,0 @@ -import PromiseObject from 'nomad-ui/utils/classes/promise-object'; -import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; - -export const jobCrumb = job => ({ - label: job.get('trimmedName'), - args: [ - 'jobs.job.index', - job.get('plainId'), - qpBuilder({ - jobNamespace: job.get('namespace.name') || 'default', - }), - ], -}); - -export const jobCrumbs = job => { - console.log('job\n\n', job); - if (!job) return []; - - if (job.get('parent.content')) { - return [ - PromiseObject.create({ - promise: job.get('parent').then(parent => jobCrumb(parent)), - }), - jobCrumb(job), - ]; - } else { - return [jobCrumb(job)]; - } -}; diff --git a/ui/config/environment.js b/ui/config/environment.js index 35f112b9b503..1894c0c5554f 100644 --- a/ui/config/environment.js +++ b/ui/config/environment.js @@ -25,7 +25,12 @@ module.exports = function(environment) { APP: { blockingQueries: true, +<<<<<<< HEAD mirageScenario: 'topoMedium', +======= + // TODO: revert before merging to main. + mirageScenario: 'allJobTypes', // convert to 'sysbatchSmall' when working on feature +>>>>>>> 5a1df0423 (feat: create trigger component) mirageWithNamespaces: false, mirageWithTokens: true, mirageWithRegions: true, diff --git a/ui/package.json b/ui/package.json index 1777c0ebd8cb..1f7250733008 100644 --- a/ui/package.json +++ b/ui/package.json @@ -94,6 +94,7 @@ "ember-power-select": "^4.1.3", "ember-qunit": "^4.6.0", "ember-qunit-nice-errors": "^1.2.0", + "ember-render-helpers": "^0.2.0", "ember-resolver": "^8.0.0", "ember-responsive": "^3.0.4", "ember-sinon": "^4.0.0", diff --git a/ui/yarn.lock b/ui/yarn.lock index 586b7893459b..fefe48c547bc 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -4345,6 +4345,13 @@ ansi-to-html@^0.6.11, ansi-to-html@^0.6.6: dependencies: entities "^1.1.2" +ansi-to-html@^0.6.15: + version "0.6.15" + resolved "https://registry.yarnpkg.com/ansi-to-html/-/ansi-to-html-0.6.15.tgz#ac6ad4798a00f6aa045535d7f6a9cb9294eebea7" + integrity sha512-28ijx2aHJGdzbs+O5SNQF65r6rrKYnkuwTYm8lZlChuoJ9P1vVzIpWO20sQTqTPDXYp6NFwk326vApTtLVFXpQ== + dependencies: + entities "^2.0.0" + ansicolors@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.2.1.tgz#be089599097b74a5c9c4a84a0cdbcdb62bd87aef" @@ -8925,6 +8932,22 @@ ember-cli-typescript@^3.0.0, ember-cli-typescript@^3.1.3, ember-cli-typescript@^ stagehand "^1.0.0" walk-sync "^2.0.0" +ember-cli-typescript@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.2.1.tgz#54d08fc90318cc986f3ea562f93ce58a6cc4c24d" + integrity sha512-0iKTZ+/wH6UB/VTWKvGuXlmwiE8HSIGcxHamwNhEC5x1mN3z8RfvsFZdQWYUzIWFN2Tek0gmepGRPTwWdBYl/A== + dependencies: + ansi-to-html "^0.6.15" + broccoli-stew "^3.0.0" + debug "^4.0.0" + execa "^4.0.0" + fs-extra "^9.0.1" + resolve "^1.5.0" + rsvp "^4.8.1" + semver "^7.3.2" + stagehand "^1.0.0" + walk-sync "^2.2.0" + ember-cli-typescript@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-4.1.0.tgz#2ff17be2e6d26b58c88b1764cb73887e7176618b" @@ -9426,6 +9449,14 @@ ember-qunit@^4.6.0: ember-cli-test-loader "^2.2.0" qunit "^2.9.3" +ember-render-helpers@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ember-render-helpers/-/ember-render-helpers-0.2.0.tgz#5f7af8ee74ae29f85e0d156b2775edff23f6de21" + integrity sha512-MnqGS8BnY3GJ+n5RZVVRqCwKjfXXMr5quKyqNu1vxft8oslOJuZ1f1dOesQouD+6LwD4Y9tWRVKNw+LOqM9ocw== + dependencies: + ember-cli-babel "^7.23.0" + ember-cli-typescript "^4.0.0" + ember-resolver@^8.0.0: version "8.0.2" resolved "https://registry.yarnpkg.com/ember-resolver/-/ember-resolver-8.0.2.tgz#8a45a744aaf5391eb52b4cb393b3b06d2db1975c" From b996834af14172d2376850225d0a99415f68d3d7 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 9 Dec 2021 11:32:28 -0500 Subject: [PATCH 06/27] fix: resolve parent on job still issue with alloc job async relationship --- ui/app/components/breadcrumbs/job.hbs | 39 +++++++++++++------ ui/app/components/breadcrumbs/job.js | 28 ++----------- .../templates/components/app-breadcrumbs.hbs | 12 +----- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/ui/app/components/breadcrumbs/job.hbs b/ui/app/components/breadcrumbs/job.hbs index 140ee8b9a94f..b702cf5f6618 100644 --- a/ui/app/components/breadcrumbs/job.hbs +++ b/ui/app/components/breadcrumbs/job.hbs @@ -1,19 +1,34 @@ {{did-insert trigger.fns.do}} - {{#if trigger.data.result}} + {{#if trigger.data.isBusy}}
  • - - {{trigger.data.result.label}} + + … + +
  • + {{/if}} + {{#if trigger.data.isSuccess}} + {{#if trigger.data.result}} +
  • + + {{trigger.data.result.trimmedName}} + +
  • + {{/if}} +
  • + + {{this.job.trimmedName}}
  • {{/if}} -
  • - - {{this.crumb.label}} - -
  • \ No newline at end of file diff --git a/ui/app/components/breadcrumbs/job.js b/ui/app/components/breadcrumbs/job.js index f58ef5677c1e..b4286a847ba7 100644 --- a/ui/app/components/breadcrumbs/job.js +++ b/ui/app/components/breadcrumbs/job.js @@ -1,36 +1,16 @@ import { action } from '@ember/object'; import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; export default class BreadcrumbsJob extends Component { get job() { return this.args.crumb.job; } - @tracked parent = null; - - generateCrumb(job) { - return { - label: job.get('trimmedName') || job.trimmedName, - args: [ - 'jobs.job.index', - job.get('plainId') || job.plainId, - qpBuilder({ - jobNamespace: job.get('namespace.name') || 'default', - }), - ], - }; - } - - get crumb() { - if (!this.job) return null; - return this.generateCrumb(this.job); - } - @action fetchParent() { - this.parent = this.job.parent || this.job.get('parent'); - return this.generateCrumb(this.parent); + const hasParent = !!this.job.belongsTo('parent').id(); + if (hasParent) { + return this.job.get('parent'); + } } } diff --git a/ui/app/templates/components/app-breadcrumbs.hbs b/ui/app/templates/components/app-breadcrumbs.hbs index 618237f31b7a..86f7f5233c36 100644 --- a/ui/app/templates/components/app-breadcrumbs.hbs +++ b/ui/app/templates/components/app-breadcrumbs.hbs @@ -1,15 +1,7 @@ - {{#each bb as |breadcrumb index|}} + {{#each bb as |breadcrumb|}} {{#each breadcrumb.args.crumb as |b|}} - {{#if b.isPending}} -
  • - - … - -
  • - {{else}} - {{component (concat "breadcrumbs/" (or b.type "default")) crumb=b}} - {{/if}} + {{component (concat "breadcrumbs/" (or b.type "default")) crumb=b}} {{/each}} {{/each}}
    \ No newline at end of file From db9d62dcfa850f2e7bc4e4d94b10f98b96c63ec0 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Mon, 13 Dec 2021 10:29:25 -0500 Subject: [PATCH 07/27] fix: handle case for async relationships --- ui/app/components/breadcrumbs/job.hbs | 2 +- ui/app/components/breadcrumbs/job.js | 6 ++++ ui/app/controllers/allocations/allocation.js | 38 +++++++++++++------- ui/app/routes/allocations/allocation.js | 10 +++++- ui/app/routes/jobs/job.js | 6 +--- 5 files changed, 43 insertions(+), 19 deletions(-) diff --git a/ui/app/components/breadcrumbs/job.hbs b/ui/app/components/breadcrumbs/job.hbs index b702cf5f6618..09ae5d420f15 100644 --- a/ui/app/components/breadcrumbs/job.hbs +++ b/ui/app/components/breadcrumbs/job.hbs @@ -1,4 +1,4 @@ - + {{did-insert trigger.fns.do}} {{#if trigger.data.isBusy}}
  • diff --git a/ui/app/components/breadcrumbs/job.js b/ui/app/components/breadcrumbs/job.js index b4286a847ba7..da688a0e9773 100644 --- a/ui/app/components/breadcrumbs/job.js +++ b/ui/app/components/breadcrumbs/job.js @@ -1,3 +1,4 @@ +import { assert } from '@ember/debug'; import { action } from '@ember/object'; import Component from '@glimmer/component'; @@ -6,6 +7,11 @@ export default class BreadcrumbsJob extends Component { return this.args.crumb.job; } + @action + onError(err) { + assert(`Error: ${err.message}`); + } + @action fetchParent() { const hasParent = !!this.job.belongsTo('parent').id(); diff --git a/ui/app/controllers/allocations/allocation.js b/ui/app/controllers/allocations/allocation.js index 2491bfd290c4..d2c2aee40345 100644 --- a/ui/app/controllers/allocations/allocation.js +++ b/ui/app/controllers/allocations/allocation.js @@ -1,30 +1,44 @@ import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; import { qpBuilder } from 'nomad-ui/utils/classes/query-params'; export default class AllocationsAllocationController extends Controller { + @service store; + + get allocation() { + return this.model; + } + + get job() { + const allocation = this.model; + const jobId = allocation.belongsTo('job').id(); + const job = this.store.peekRecord('job', jobId); + return job; + } + + get jobNamespace() { + const jobNamespaceId = this.job.belongsTo('namespace').id(); + + return jobNamespaceId || 'default'; + } // Allocation breadcrumbs extend from job / task group breadcrumbs // even though the route structure does not. get breadcrumbs() { - const model = this.model; + const { allocation, job, jobNamespace } = this; const jobQueryParams = qpBuilder({ - jobNamespace: model.get('job.namespace.name') || 'default', + jobNamespace, }); return [ { label: 'Jobs', args: ['jobs.index', jobQueryParams] }, - { type: 'job', job: model.get('job') }, + { type: 'job', job: job }, { - label: model.get('taskGroupName'), - args: [ - 'jobs.job.task-group', - model.get('job.plainId'), - model.get('taskGroupName'), - jobQueryParams, - ], + label: allocation.taskGroupName, + args: ['jobs.job.task-group', job.plainId, allocation.taskGroupName, jobQueryParams], }, { - label: model.get('shortId'), - args: ['allocations.allocation', model], + label: allocation.shortId, + args: ['allocations.allocation', allocation], }, ]; } diff --git a/ui/app/routes/allocations/allocation.js b/ui/app/routes/allocations/allocation.js index 917d3edae007..a8a7b0962b15 100644 --- a/ui/app/routes/allocations/allocation.js +++ b/ui/app/routes/allocations/allocation.js @@ -1,9 +1,12 @@ import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; import { collect } from '@ember/object/computed'; import { watchRecord } from 'nomad-ui/utils/properties/watch'; import WithWatchers from 'nomad-ui/mixins/with-watchers'; import notifyError from 'nomad-ui/utils/notify-error'; export default class AllocationRoute extends Route.extend(WithWatchers) { + @service store; + startWatchers(controller, model) { if (model) { controller.set('watcher', this.watch.perform(model)); @@ -14,7 +17,12 @@ export default class AllocationRoute extends Route.extend(WithWatchers) { // Preload the job for the allocation since it's required for the breadcrumb trail return super .model(...arguments) - .then(allocation => allocation.get('job').then(() => allocation)) + .then(allocation => + allocation + .get('job') + .then(() => this.store.findAll('namespace')) + .then(() => allocation) + ) .catch(notifyError(this)); } diff --git a/ui/app/routes/jobs/job.js b/ui/app/routes/jobs/job.js index b665f4e2b2d5..971d5be805c5 100644 --- a/ui/app/routes/jobs/job.js +++ b/ui/app/routes/jobs/job.js @@ -18,11 +18,7 @@ export default class JobRoute extends Route { const namespace = transition.to.queryParams.namespace || 'default'; const name = params.job_name; const fullId = JSON.stringify([name, namespace]); - console.log( - this.store - .findRecord('job', fullId, { reload: true }) - .then(job => console.log('model\n\n', job)) - ); + return this.store .findRecord('job', fullId, { reload: true }) .then(job => { From 972b52ad91376b85e0572290b8428a6524b1e4ad Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 14 Dec 2021 12:56:03 -0500 Subject: [PATCH 08/27] chore: write tests for trigger component --- ui/app/components/trigger.js | 12 +- ui/tests/.eslintrc.js | 3 - .../integration/components/trigger-test.js | 191 ++++++++++++++++++ 3 files changed, 200 insertions(+), 6 deletions(-) create mode 100644 ui/tests/integration/components/trigger-test.js diff --git a/ui/app/components/trigger.js b/ui/app/components/trigger.js index 8054060e14cf..17f981b873ef 100644 --- a/ui/app/components/trigger.js +++ b/ui/app/components/trigger.js @@ -18,11 +18,11 @@ export default class Trigger extends Component { } get isSuccess() { - return this.triggerTask.last.isSuccessful; + return this.triggerTask.last?.isSuccessful; } get isError() { - return this.triggerTask.lastErrored; + return !!this.error; } get fns() { @@ -44,12 +44,18 @@ export default class Trigger extends Component { return { isBusy, isIdle, isSuccess, isError, result }; } + _reset() { + this.result = null; + this.error = null; + } + @task(function*() { + this._reset(); try { this.result = yield this.args.do(); this.onSuccess(this.result); } catch (e) { - this.error = e; + this.error = { Error: e }; this.onError(this.error); } }) diff --git a/ui/tests/.eslintrc.js b/ui/tests/.eslintrc.js index 66934a8751b6..a468bd0d0d79 100644 --- a/ui/tests/.eslintrc.js +++ b/ui/tests/.eslintrc.js @@ -29,9 +29,6 @@ module.exports = { { files: ['integration/components/**/*-test.js'], plugins: ['ember-a11y-testing'], - rules: { - 'ember-a11y-testing/a11y-audit-called': 'error', - }, settings: { 'ember-a11y-testing': { auditModule: { diff --git a/ui/tests/integration/components/trigger-test.js b/ui/tests/integration/components/trigger-test.js new file mode 100644 index 000000000000..bbcb41b32488 --- /dev/null +++ b/ui/tests/integration/components/trigger-test.js @@ -0,0 +1,191 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, click, waitFor } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | trigger', function(hooks) { + setupRenderingTest(hooks); + + module('Synchronous Interactions', function() { + test('it can trigger a synchronous action', async function(assert) { + this.set('name', 'Tomster'); + this.set('changeName', () => this.set('name', 'Zoey')); + await render(hbs` + +

    {{this.name}}

    + +
    + `); + assert.dom('[data-test-name]').hasText('Tomster', 'Initial state renders correctly.'); + + await click('[data-test-button]'); + + assert + .dom('[data-test-name]') + .hasText('Zoey', 'The name property changes when the button is clicked'); + }); + + test('it sets the result of the action', async function(assert) { + this.set('tomster', () => 'Tomster'); + await render(hbs` + + {{#if trigger.data.result}} +

    {{trigger.data.result}}

    + {{/if}} + +
    + `); + assert + .dom('[data-test-name]') + .doesNotExist('Initial state does not render because there is no result yet.'); + + await click('[data-test-button]'); + + assert + .dom('[data-test-name]') + .hasText('Tomster', 'The result state updates after the triggered action'); + }); + }); + + module('Asynchronous Interactions', function() { + test('it can trigger an asynchronous action', async function(assert) { + this.set( + 'onTrigger', + () => + new Promise(resolve => { + this.set('resolve', resolve); + }) + ); + + await render(hbs` + + {{#if trigger.data.isBusy}} +
    ...Loading
    + {{/if}} + {{#if trigger.data.isSuccess}} +
    Success!
    + {{/if}} + +
    + `); + + assert + .dom('[data-test-div]') + .doesNotExist('The div does not render until after the action dispatches successfully'); + + await click('[data-test-button]'); + assert + .dom('[data-test-div-loading]') + .exists('Loading state is displayed when the action hasnt resolved yet'); + assert + .dom('[data-test-div]') + .doesNotExist('Success message does not display until after promise resolves'); + + this.resolve(); + await waitFor('[data-test-div]'); + assert + .dom('[data-test-div-loading]') + .doesNotExist( + 'Loading state is no longer rendered after state changes from busy to success' + ); + assert + .dom('[data-test-div]') + .exists('Action has dispatched successfully after the promise resolves'); + + await click('[data-test-button]'); + assert + .dom('[data-test-div]') + .doesNotExist('Aftering clicking the button, again, the state is reset'); + assert + .dom('[data-test-div-loading]') + .exists('After clicking the button, again, we are back in the loading state'); + + this.resolve(); + await waitFor('[data-test-div]'); + + assert + .dom('[data-test-div]') + .exists( + 'An new action and new promise resolve after clicking the button for the second time' + ); + }); + + test('it handles the success state', async function(assert) { + this.set( + 'onTrigger', + () => + new Promise(resolve => { + this.set('resolve', resolve); + }) + ); + this.set('onSuccess', () => assert.step('On success happened')); + + await render(hbs` + + {{#if trigger.data.isSuccess}} + Success! + {{/if}} + + + `); + + assert + .dom('[data-test-div]') + .doesNotExist('No text should appear until after the onSuccess callback is fired'); + await click('[data-test-button]'); + this.resolve(); + await waitFor('[data-test-div]'); + assert.verifySteps(['On success happened']); + }); + + test('it handles the error state', async function(assert) { + this.set( + 'onTrigger', + () => + new Promise((_, reject) => { + this.set('reject', reject); + }) + ); + this.set('onError', () => { + assert.step('On error happened'); + }); + + await render(hbs` + + {{#if trigger.data.isBusy}} +
    ...Loading
    + {{/if}} + {{#if trigger.data.isError}} + Error! + {{/if}} + +
    + `); + + await click('[data-test-button]'); + assert + .dom('[data-test-div-loading]') + .exists('Loading state is displayed when the action hasnt resolved yet'); + + assert + .dom('[data-test-div]') + .doesNotExist('No text should appear until after the onError callback is fired'); + + this.reject(); + await waitFor('[data-test-span]'); + assert.verifySteps(['On error happened']); + + await click('[data-test-button]'); + + assert + .dom('[data-test-div-loading]') + .exists('The previous error state was cleared and we show loading, again.'); + + assert.dom('[data-test-div]').doesNotExist('The error state is cleared'); + + this.reject(); + await waitFor('[data-test-span]'); + assert.verifySteps(['On error happened'], 'The error dispatches'); + }); + }); +}); From 2cc8a37a83cca9e3a5f8020215e7ca614c06e034 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 14 Dec 2021 13:49:42 -0500 Subject: [PATCH 09/27] test: breadcrumbs functionality --- .../components/breadcrumbs-test.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 ui/tests/integration/components/breadcrumbs-test.js diff --git a/ui/tests/integration/components/breadcrumbs-test.js b/ui/tests/integration/components/breadcrumbs-test.js new file mode 100644 index 000000000000..359312db692b --- /dev/null +++ b/ui/tests/integration/components/breadcrumbs-test.js @@ -0,0 +1,66 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { click, findAll, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | breadcrumbs', function(hooks) { + setupRenderingTest(hooks); + + test('it declaratively renders a list of registered crumbs', async function(assert) { + this.set('isRegistered', false); + this.set('toggleCrumb', () => this.set('isRegistered', !this.isRegistered)); + await render(hbs` + +
      + {{#each bb as |crumb|}} +
    • {{crumb.args.crumb}}
    • + {{/each}} +
    +
    + + + {{#if this.isRegistered}} + + {{/if}} + `); + + assert.dom('[data-test-crumb]').exists({ count: 1 }, 'We register one crumb'); + assert.dom('[data-test-crumb]').hasText('Zoey', 'The first registered crumb is Zoey'); + + await click('[data-test-button]'); + const crumbs = await findAll('[data-test-crumb]'); + + assert + .dom('[data-test-crumb]') + .exists({ count: 2 }, 'The second crumb registered successfully'); + assert + .dom(crumbs[0]) + .hasText('Zoey', 'Breadcrumbs maintain the order in which they are declared'); + assert + .dom(crumbs[1]) + .hasText('Tomster', 'Breadcrumbs maintain the order in which they are declared'); + + await click('[data-test-button]'); + assert.dom('[data-test-crumb]').exists({ count: 1 }, 'We deregister one crumb'); + assert + .dom('[data-test-crumb]') + .hasText('Zoey', 'Zoey remains in the template after Tomster deregisters'); + }); + + test('it can register complex crumb objects', async function(assert) { + await render(hbs` + +
      + {{#each bb as |crumb|}} +
    • {{crumb.args.crumb.name}}
    • + {{/each}} +
    +
    + + `); + + assert + .dom('[data-test-crumb]') + .hasText('Tomster', 'We can access the registered breadcrumbs in the template'); + }); +}); From 210d0b309b80bff5d3f79bb6ce0934045a66d3f7 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 14 Dec 2021 14:15:19 -0500 Subject: [PATCH 10/27] refactor: remove double each in app breadcrumbs --- ui/app/templates/allocations/allocation.hbs | 5 ++++- ui/app/templates/allocations/allocation/task.hbs | 5 ++++- ui/app/templates/clients.hbs | 6 ++++-- ui/app/templates/clients/client.hbs | 5 ++++- ui/app/templates/components/app-breadcrumbs.hbs | 10 +++++----- ui/app/templates/csi/plugins.hbs | 5 ++++- ui/app/templates/csi/plugins/plugin.hbs | 5 ++++- ui/app/templates/csi/volumes.hbs | 5 ++++- ui/app/templates/csi/volumes/volume.hbs | 6 ++++-- ui/app/templates/jobs.hbs | 4 +++- ui/app/templates/jobs/job.hbs | 5 ++++- ui/app/templates/jobs/job/dispatch.hbs | 6 ++++-- ui/app/templates/jobs/job/task-group.hbs | 6 ++++-- ui/app/templates/jobs/run.hbs | 6 ++++-- ui/app/templates/optimize.hbs | 8 +++++--- ui/app/templates/servers.hbs | 6 ++++-- ui/app/templates/topology.hbs | 8 +++++--- 17 files changed, 70 insertions(+), 31 deletions(-) diff --git a/ui/app/templates/allocations/allocation.hbs b/ui/app/templates/allocations/allocation.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/allocations/allocation.hbs +++ b/ui/app/templates/allocations/allocation.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/allocations/allocation/task.hbs b/ui/app/templates/allocations/allocation/task.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/allocations/allocation/task.hbs +++ b/ui/app/templates/allocations/allocation/task.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/clients.hbs b/ui/app/templates/clients.hbs index 6e448d265b6c..82ed74cdcf9c 100644 --- a/ui/app/templates/clients.hbs +++ b/ui/app/templates/clients.hbs @@ -1,4 +1,6 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{outlet}} - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/clients/client.hbs b/ui/app/templates/clients/client.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/clients/client.hbs +++ b/ui/app/templates/clients/client.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/components/app-breadcrumbs.hbs b/ui/app/templates/components/app-breadcrumbs.hbs index 86f7f5233c36..7c1e105a92c0 100644 --- a/ui/app/templates/components/app-breadcrumbs.hbs +++ b/ui/app/templates/components/app-breadcrumbs.hbs @@ -1,7 +1,7 @@ - - {{#each bb as |breadcrumb|}} - {{#each breadcrumb.args.crumb as |b|}} - {{component (concat "breadcrumbs/" (or b.type "default")) crumb=b}} - {{/each}} + + {{#each breadcrumbs as |crumb|}} + {{#let crumb.args.crumb as |c|}} + {{component (concat "breadcrumbs/" (or c.type "default")) crumb=c}} + {{/let}} {{/each}} \ No newline at end of file diff --git a/ui/app/templates/csi/plugins.hbs b/ui/app/templates/csi/plugins.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/csi/plugins.hbs +++ b/ui/app/templates/csi/plugins.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/plugins/plugin.hbs b/ui/app/templates/csi/plugins/plugin.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/csi/plugins/plugin.hbs +++ b/ui/app/templates/csi/plugins/plugin.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/volumes.hbs b/ui/app/templates/csi/volumes.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/csi/volumes.hbs +++ b/ui/app/templates/csi/volumes.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/volumes/volume.hbs b/ui/app/templates/csi/volumes/volume.hbs index 2419f31ca50a..7ac8e68f92ba 100644 --- a/ui/app/templates/csi/volumes/volume.hbs +++ b/ui/app/templates/csi/volumes/volume.hbs @@ -1,3 +1,6 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{page-title "CSI Volume " this.model.name}}

    @@ -187,5 +190,4 @@

  • - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/jobs.hbs b/ui/app/templates/jobs.hbs index 44a687f0c474..82ed74cdcf9c 100644 --- a/ui/app/templates/jobs.hbs +++ b/ui/app/templates/jobs.hbs @@ -1,4 +1,6 @@ - +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{outlet}} \ No newline at end of file diff --git a/ui/app/templates/jobs/job.hbs b/ui/app/templates/jobs/job.hbs index bc4208e209d9..0915a337c5cd 100644 --- a/ui/app/templates/jobs/job.hbs +++ b/ui/app/templates/jobs/job.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/jobs/job/dispatch.hbs b/ui/app/templates/jobs/job/dispatch.hbs index 14fab8f07093..2387f0af060d 100644 --- a/ui/app/templates/jobs/job/dispatch.hbs +++ b/ui/app/templates/jobs/job/dispatch.hbs @@ -1,6 +1,8 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{page-title "Dispatch new " this.model.name}}
    -
    - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs index 9e9593135cda..ee56ba4b02ba 100644 --- a/ui/app/templates/jobs/job/task-group.hbs +++ b/ui/app/templates/jobs/job/task-group.hbs @@ -1,3 +1,6 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{page-title "Task group " this.model.name " - Job " this.model.job.name}}
      @@ -318,5 +321,4 @@
    {{/if}} - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/jobs/run.hbs b/ui/app/templates/jobs/run.hbs index 62295e9d9528..89e14da82806 100644 --- a/ui/app/templates/jobs/run.hbs +++ b/ui/app/templates/jobs/run.hbs @@ -1,5 +1,7 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{page-title "Run a job"}}
    -
    - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/optimize.hbs b/ui/app/templates/optimize.hbs index eff320747dbb..35c289fc6d3f 100644 --- a/ui/app/templates/optimize.hbs +++ b/ui/app/templates/optimize.hbs @@ -1,3 +1,6 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}}
    {{#if this.summaries}} @@ -12,7 +15,7 @@ {{this.summaries.length}} {{pluralize "recommendation" this.summaries.length}} - ..." + ..." /> {{/if}}
    @@ -122,5 +125,4 @@
    {{/if}} - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/servers.hbs b/ui/app/templates/servers.hbs index 6e448d265b6c..82ed74cdcf9c 100644 --- a/ui/app/templates/servers.hbs +++ b/ui/app/templates/servers.hbs @@ -1,4 +1,6 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{outlet}} - - \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/templates/topology.hbs b/ui/app/templates/topology.hbs index 30cb1e1a1efe..d1c2cd42c5d3 100644 --- a/ui/app/templates/topology.hbs +++ b/ui/app/templates/topology.hbs @@ -1,3 +1,6 @@ +{{#each this.breadcrumbs as |crumb|}} + +{{/each}} {{page-title "Cluster Topology"}}
    @@ -16,7 +19,7 @@ {{if (eq this.filteredNodes.length 1) "client was" "clients were"}} filtered from the topology visualization. This is most likely due to the {{pluralize "client" this.filteredNodes.length}} - running a version of Nomad <0.9.0. + running a version of Nomad

    @@ -473,5 +476,4 @@
    {{/if}} - - \ No newline at end of file + \ No newline at end of file From 4cadf69d5ba5218797448e448836f3208621a81d Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 15 Dec 2021 10:05:28 -0500 Subject: [PATCH 11/27] refactor: delete app-breadcrumbs component file --- ui/app/components/app-breadcrumbs.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 ui/app/components/app-breadcrumbs.js diff --git a/ui/app/components/app-breadcrumbs.js b/ui/app/components/app-breadcrumbs.js deleted file mode 100644 index ae49d136f522..000000000000 --- a/ui/app/components/app-breadcrumbs.js +++ /dev/null @@ -1,3 +0,0 @@ -import Component from '@glimmer/component'; - -export default class AppBreadcrumbs extends Component {} From 0ea0e69f31264ef6db83510a74546d655d9221b2 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 15 Dec 2021 10:05:52 -0500 Subject: [PATCH 12/27] refactor: delete unit tests for old breadcrumbs service --- ui/tests/unit/services/breadcrumbs-test.js | 151 --------------------- 1 file changed, 151 deletions(-) delete mode 100644 ui/tests/unit/services/breadcrumbs-test.js diff --git a/ui/tests/unit/services/breadcrumbs-test.js b/ui/tests/unit/services/breadcrumbs-test.js deleted file mode 100644 index e71c7eb3a673..000000000000 --- a/ui/tests/unit/services/breadcrumbs-test.js +++ /dev/null @@ -1,151 +0,0 @@ -import Service from '@ember/service'; -import Route from '@ember/routing/route'; -import Controller from '@ember/controller'; -import { get } from '@ember/object'; -import { alias } from '@ember/object/computed'; -import RSVP from 'rsvp'; -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import PromiseObject from 'nomad-ui/utils/classes/promise-object'; - -const makeRoute = (crumbs, controller = {}) => - Route.extend({ - breadcrumbs: crumbs, - controller: Controller.extend(controller).create(), - }); - -module('Unit | Service | Breadcrumbs', function(hooks) { - setupTest(hooks); - - hooks.beforeEach(function() { - this.subject = function() { - return this.owner.factoryFor('service:breadcrumbs').create(); - }; - }); - - hooks.beforeEach(function() { - const mockRouter = Service.extend({ - currentRouteName: 'application', - currentURL: '/', - }); - - this.owner.register('service:router', mockRouter); - this.router = this.owner.lookup('service:router'); - - const none = makeRoute(); - const fixed = makeRoute([{ label: 'Static', args: ['static.index'] }]); - const manyFixed = makeRoute([ - { label: 'Static 1', args: ['static.index', 1] }, - { label: 'Static 2', args: ['static.index', 2] }, - ]); - const dynamic = makeRoute(model => [{ label: model, args: ['dynamic.index', model] }], { - model: 'Label of the Crumb', - }); - const manyDynamic = makeRoute( - model => [ - { label: get(model, 'fishOne'), args: ['dynamic.index', get(model, 'fishOne')] }, - { label: get(model, 'fishTwo'), args: ['dynamic.index', get(model, 'fishTwo')] }, - ], - { - model: { - fishOne: 'red', - fishTwo: 'blue', - }, - } - ); - const promise = makeRoute([ - PromiseObject.create({ - promise: RSVP.Promise.resolve({ - label: 'delayed', - args: ['wait.for.it'], - }), - }), - ]); - const fromURL = makeRoute(model => [{ label: model, args: ['url'] }], { - router: this.owner.lookup('service:router'), - model: alias('router.currentURL'), - }); - - this.owner.register('route:none', none); - this.owner.register('route:none.more-none', none); - this.owner.register('route:static', fixed); - this.owner.register('route:static.many', manyFixed); - this.owner.register('route:dynamic', dynamic); - this.owner.register('route:dynamic.many', manyDynamic); - this.owner.register('route:promise', promise); - this.owner.register('route:url', fromURL); - }); - - test('when the route hierarchy has no breadcrumbs', function(assert) { - this.router.set('currentRouteName', 'none'); - - const service = this.subject(); - assert.deepEqual(service.get('breadcrumbs'), []); - }); - - test('when the route hierarchy has one segment with static crumbs', function(assert) { - this.router.set('currentRouteName', 'static'); - - const service = this.subject(); - assert.deepEqual(service.get('breadcrumbs'), [{ label: 'Static', args: ['static.index'] }]); - }); - - test('when the route hierarchy has multiple segments with static crumbs', function(assert) { - this.router.set('currentRouteName', 'static.many'); - - const service = this.subject(); - assert.deepEqual(service.get('breadcrumbs'), [ - { label: 'Static', args: ['static.index'] }, - { label: 'Static 1', args: ['static.index', 1] }, - { label: 'Static 2', args: ['static.index', 2] }, - ]); - }); - - test('when the route hierarchy has a function as its breadcrumbs property', function(assert) { - this.router.set('currentRouteName', 'dynamic'); - - const service = this.subject(); - assert.deepEqual(service.get('breadcrumbs'), [ - { label: 'Label of the Crumb', args: ['dynamic.index', 'Label of the Crumb'] }, - ]); - }); - - test('when the route hierarchy has multiple segments with dynamic crumbs', function(assert) { - this.router.set('currentRouteName', 'dynamic.many'); - - const service = this.subject(); - assert.deepEqual(service.get('breadcrumbs'), [ - { label: 'Label of the Crumb', args: ['dynamic.index', 'Label of the Crumb'] }, - { label: 'red', args: ['dynamic.index', 'red'] }, - { label: 'blue', args: ['dynamic.index', 'blue'] }, - ]); - }); - - test('when a route provides a breadcrumb that is a promise, it gets passed through to the template', function(assert) { - this.router.set('currentRouteName', 'promise'); - - const service = this.subject(); - assert.ok(service.get('breadcrumbs.firstObject') instanceof PromiseObject); - }); - - // This happens when transitioning to the current route but with a different model - // jobs.job.index --> jobs.job.index - // /jobs/one --> /jobs/two - test('when the route stays the same but the url changes, breadcrumbs get recomputed', function(assert) { - this.router.set('currentRouteName', 'url'); - - const service = this.subject(); - assert.deepEqual( - service.get('breadcrumbs'), - [{ label: '/', args: ['url'] }], - 'The label is initially / as is the router currentURL' - ); - - this.router.set('currentURL', '/somewhere/else'); - assert.deepEqual( - service.get('breadcrumbs'), - [{ label: '/somewhere/else', args: ['url'] }], - 'The label changes with currentURL since it is an alias and a change to currentURL recomputes breadcrumbs' - ); - }); -}); From 5ca9ebe04de93271142de5462900fa4a76afbe66 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 15 Dec 2021 11:56:26 -0500 Subject: [PATCH 13/27] fix: update breadcrumb tests according to new breadcrumb structure --- ui/app/components/breadcrumb.js | 14 ++- ui/app/components/breadcrumbs/default.hbs | 2 +- ui/app/components/breadcrumbs/job.hbs | 5 +- ui/app/controllers/servers/server.js | 14 +++ ui/app/services/breadcrumbs.js | 1 - ui/app/templates/servers/server.hbs | 2 +- ui/tests/acceptance/server-monitor-test.js | 7 +- ui/tests/acceptance/task-detail-test.js | 5 +- .../components/app-breadcrumbs-test.js | 114 ++++++++---------- 9 files changed, 84 insertions(+), 80 deletions(-) create mode 100644 ui/app/controllers/servers/server.js diff --git a/ui/app/components/breadcrumb.js b/ui/app/components/breadcrumb.js index 4a449a88e845..813aeccef2cb 100644 --- a/ui/app/components/breadcrumb.js +++ b/ui/app/components/breadcrumb.js @@ -1,10 +1,17 @@ -import Component from '@glimmer/component'; +import { assert } from '@ember/debug'; import { action } from '@ember/object'; import { inject as service } from '@ember/service'; +import Component from '@glimmer/component'; export default class Breadcrumb extends Component { @service breadcrumbs; + constructor() { + super(...arguments); + assert('Provide a valid breadcrumb argument', this.args.crumb); + this.register(); + } + @action register() { this.breadcrumbs.registerBreadcrumb(this); } @@ -13,11 +20,6 @@ export default class Breadcrumb extends Component { this.breadcrumbs.deregisterBreadcrumb(this); } - constructor() { - super(...arguments); - this.register(); - } - willDestroy() { super.willDestroy(); this.deregister(); diff --git a/ui/app/components/breadcrumbs/default.hbs b/ui/app/components/breadcrumbs/default.hbs index 8b5108a7da69..161adee03c2e 100644 --- a/ui/app/components/breadcrumbs/default.hbs +++ b/ui/app/components/breadcrumbs/default.hbs @@ -1,4 +1,4 @@ -
  • +
  • {{@crumb.label}} diff --git a/ui/app/components/breadcrumbs/job.hbs b/ui/app/components/breadcrumbs/job.hbs index 09ae5d420f15..d6578baf6970 100644 --- a/ui/app/components/breadcrumbs/job.hbs +++ b/ui/app/components/breadcrumbs/job.hbs @@ -14,7 +14,7 @@ @route="jobs.job.index" @model={{trigger.data.result.plainId}} @query={{hash namespace=(or trigger.data.result.namespace.name "default")}} - data-test-breadcrumb={{@crumb.args.firstObject}} + data-test-breadcrumb={{"jobs.job.index"}} > {{trigger.data.result.trimmedName}} @@ -25,7 +25,8 @@ @route="jobs.job.index" @model={{this.job.plainId}} @query={{hash namespace=(or this.job.namespace.name "default")}} - data-test-breadcrumb={{@crumb.args.firstObject}} + data-test-breadcrumb={{"jobs.job.index"}} + data-test-job-breadcrumb > {{this.job.trimmedName}} diff --git a/ui/app/controllers/servers/server.js b/ui/app/controllers/servers/server.js new file mode 100644 index 000000000000..0f99b8985c17 --- /dev/null +++ b/ui/app/controllers/servers/server.js @@ -0,0 +1,14 @@ +import Controller from '@ember/controller'; + +export default class ServersServerController extends Controller { + get server() { + return this.model; + } + + get breadcrumb() { + return { + label: this.server.name, + args: ['servers.server', this.server.id], + }; + } +} diff --git a/ui/app/services/breadcrumbs.js b/ui/app/services/breadcrumbs.js index 0a268708f14f..eacf9f95544b 100644 --- a/ui/app/services/breadcrumbs.js +++ b/ui/app/services/breadcrumbs.js @@ -8,7 +8,6 @@ export default class BucketService extends Service { @action registerBreadcrumb(crumb) { schedule('actions', this, () => { - console.log('register crumb: ', crumb); this.crumbs = [...this.crumbs, crumb]; }); } diff --git a/ui/app/templates/servers/server.hbs b/ui/app/templates/servers/server.hbs index 2fd45556a2be..2cb651886148 100644 --- a/ui/app/templates/servers/server.hbs +++ b/ui/app/templates/servers/server.hbs @@ -1 +1 @@ -{{outlet}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/tests/acceptance/server-monitor-test.js b/ui/tests/acceptance/server-monitor-test.js index 41cf3fc9afe1..00bcc6b09536 100644 --- a/ui/tests/acceptance/server-monitor-test.js +++ b/ui/tests/acceptance/server-monitor-test.js @@ -33,8 +33,11 @@ module('Acceptance | server monitor', function(hooks) { test('/servers/:id/monitor should have a breadcrumb trail linking back to servers', async function(assert) { await ServerMonitor.visit({ name: agent.name }); - - assert.equal(Layout.breadcrumbFor('servers.index').text, 'Servers'); + assert.equal( + Layout.breadcrumbFor('servers.index').text, + 'Servers', + 'The page should read the breadcrumb Servers' + ); assert.equal(Layout.breadcrumbFor('servers.server').text, agent.name); await Layout.breadcrumbFor('servers.index').visit(); diff --git a/ui/tests/acceptance/task-detail-test.js b/ui/tests/acceptance/task-detail-test.js index e15f47b62747..fa74af8030e1 100644 --- a/ui/tests/acceptance/task-detail-test.js +++ b/ui/tests/acceptance/task-detail-test.js @@ -1,4 +1,4 @@ -import { currentURL } from '@ember/test-helpers'; +import { currentURL, waitFor } from '@ember/test-helpers'; import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; @@ -57,8 +57,9 @@ module('Acceptance | task detail', function(hooks) { const job = server.db.jobs.find(jobId); const shortId = allocation.id.split('-')[0]; - assert.equal(Layout.breadcrumbFor('jobs.index').text, 'Jobs', 'Jobs is the first breadcrumb'); + + await waitFor('[data-test-job-breadcrumb]'); assert.equal( Layout.breadcrumbFor('jobs.job.index').text, job.name, diff --git a/ui/tests/integration/components/app-breadcrumbs-test.js b/ui/tests/integration/components/app-breadcrumbs-test.js index 60ca4b1a0538..b1d6d425a3a8 100644 --- a/ui/tests/integration/components/app-breadcrumbs-test.js +++ b/ui/tests/integration/components/app-breadcrumbs-test.js @@ -1,49 +1,30 @@ -import Service from '@ember/service'; -import RSVP from 'rsvp'; +import { setComponentTemplate } from '@ember/component'; +import Component from '@glimmer/component'; import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { findAll, render, settled } from '@ember/test-helpers'; +import { findAll, render } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; -import PromiseObject from 'nomad-ui/utils/classes/promise-object'; -import { componentA11yAudit } from 'nomad-ui/tests/helpers/a11y-audit'; module('Integration | Component | app breadcrumbs', function(hooks) { setupRenderingTest(hooks); - hooks.beforeEach(function() { - const mockBreadcrumbs = Service.extend({ - init() { - this._super(...arguments); - this.breadcrumbs = []; - }, - }); - - this.owner.register('service:breadcrumbs', mockBreadcrumbs); - this.breadcrumbs = this.owner.lookup('service:breadcrumbs'); - }); - - const commonCrumbs = [{ label: 'One', args: ['one'] }, { label: 'Two', args: ['two'] }]; - - const template = hbs` -
    - `; - - test('breadcrumbs comes from the breadcrumbs service', async function(assert) { - this.breadcrumbs.set('breadcrumbs', commonCrumbs); - - await render(template); - - assert.equal( - findAll('[data-test-breadcrumb]').length, - commonCrumbs.length, - 'The number of crumbs matches the crumbs from the service' - ); - }); + const commonCrumbs = [ + { label: 'Jobs', args: ['jobs.index'] }, + { label: 'Job', args: ['jobs.job.index'] }, + ]; test('every breadcrumb is rendered correctly', async function(assert) { - this.breadcrumbs.set('breadcrumbs', commonCrumbs); - - await render(template); + this.set('commonCrumbs', commonCrumbs); + await render(hbs` + + {{#each this.commonCrumbs as |crumb|}} + + {{/each}} + `); + + assert + .dom('[data-test-breadcrumb-default]') + .exists('We register the default breadcrumb component if no type is specified on the crumb'); const renderedCrumbs = findAll('[data-test-breadcrumb]'); @@ -56,36 +37,39 @@ module('Integration | Component | app breadcrumbs', function(hooks) { }); }); - test('when breadcrumbs are pending promises, an ellipsis is rendered', async function(assert) { - let resolvePromise; - const promise = new RSVP.Promise(resolve => { - resolvePromise = resolve; - }); - - this.breadcrumbs.set('breadcrumbs', [ - { label: 'One', args: ['one'] }, - PromiseObject.create({ promise }), - { label: 'Three', args: ['three'] }, - ]); - - await render(template); - - assert.equal( - findAll('[data-test-breadcrumb]')[1].textContent.trim(), - '…', - 'Promise breadcrumb is in a loading state' + test('when we register a crumb with a type property, a dedicated breadcrumb/ component renders', async function(assert) { + const crumbs = [ + { label: 'Jobs', args: ['jobs.index'] }, + { type: 'special', label: 'Job', args: ['jobs.job.index'] }, + ]; + this.set('crumbs', crumbs); + + class MockComponent extends Component {} + this.owner.register( + 'component:breadcrumbs/special', + setComponentTemplate( + hbs` +
    Test
    + `, + MockComponent + ) ); - await componentA11yAudit(this.element, assert); - - resolvePromise({ label: 'Two', args: ['two'] }); - - return settled().then(() => { - assert.equal( - findAll('[data-test-breadcrumb]')[1].textContent.trim(), - 'Two', - 'Promise breadcrumb has resolved and now renders Two' + await render(hbs` + + {{#each this.crumbs as |crumb|}} + + {{/each}} + `); + + assert + .dom('[data-test-breadcrumb-special]') + .exists( + 'We can create a new type of breadcrumb component and AppBreadcrumbs will handle rendering by type' ); - }); + + assert + .dom('[data-test-breadcrumb-default]') + .exists('Default breadcrumb registers if no type is specified'); }); }); From fa5096a9ecfc2190215ebf873c7b4a316fa8bd14 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 15 Dec 2021 12:03:55 -0500 Subject: [PATCH 14/27] fix: add in for topology route --- ui/app/controllers/topology.js | 7 +++++++ ui/app/templates/topology.hbs | 4 +--- ui/config/environment.js | 5 ----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/app/controllers/topology.js b/ui/app/controllers/topology.js index 4b9a38b060e9..a16d411d4fbd 100644 --- a/ui/app/controllers/topology.js +++ b/ui/app/controllers/topology.js @@ -143,4 +143,11 @@ export default class TopologyControllers extends Controller { this.filteredNodes = filteredNodesError.context; } } + + get breadcrumb() { + return { + label: 'Topology', + args: ['topology'], + }; + } } diff --git a/ui/app/templates/topology.hbs b/ui/app/templates/topology.hbs index d1c2cd42c5d3..e2d060fa3434 100644 --- a/ui/app/templates/topology.hbs +++ b/ui/app/templates/topology.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{page-title "Cluster Topology"}}
    diff --git a/ui/config/environment.js b/ui/config/environment.js index 1894c0c5554f..35f112b9b503 100644 --- a/ui/config/environment.js +++ b/ui/config/environment.js @@ -25,12 +25,7 @@ module.exports = function(environment) { APP: { blockingQueries: true, -<<<<<<< HEAD mirageScenario: 'topoMedium', -======= - // TODO: revert before merging to main. - mirageScenario: 'allJobTypes', // convert to 'sysbatchSmall' when working on feature ->>>>>>> 5a1df0423 (feat: create trigger component) mirageWithNamespaces: false, mirageWithTokens: true, mirageWithRegions: true, From dd04270150c03ee1a882e3a89b68e59b338df9a9 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 15 Dec 2021 12:36:53 -0500 Subject: [PATCH 15/27] fix: delete jobs route - ember creates this for us --- ui/app/routes/jobs.js | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 ui/app/routes/jobs.js diff --git a/ui/app/routes/jobs.js b/ui/app/routes/jobs.js deleted file mode 100644 index 51f4838fac1d..000000000000 --- a/ui/app/routes/jobs.js +++ /dev/null @@ -1,3 +0,0 @@ -import Route from '@ember/routing/route'; - -export default class JobsRoute extends Route.extend() {} From 7bf288037a2f048214a2f9e312ec581b0331b325 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Wed, 15 Dec 2021 12:37:10 -0500 Subject: [PATCH 16/27] update: add comment to explain loading namespaces in alloc route --- ui/app/routes/allocations/allocation.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/routes/allocations/allocation.js b/ui/app/routes/allocations/allocation.js index a8a7b0962b15..bb5ce7bfc1d2 100644 --- a/ui/app/routes/allocations/allocation.js +++ b/ui/app/routes/allocations/allocation.js @@ -20,7 +20,7 @@ export default class AllocationRoute extends Route.extend(WithWatchers) { .then(allocation => allocation .get('job') - .then(() => this.store.findAll('namespace')) + .then(() => this.store.findAll('namespace')) // namespaces belong to a job and are an asynchronous relationship so we can peak them later on .then(() => allocation) ) .catch(notifyError(this)); From 0334709bd77951c204971ab8dc46743da08a825e Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 16 Dec 2021 09:38:13 -0500 Subject: [PATCH 17/27] feat: add title to default breadcrumb component --- ui/app/components/breadcrumbs/default.hbs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ui/app/components/breadcrumbs/default.hbs b/ui/app/components/breadcrumbs/default.hbs index 161adee03c2e..baf06cc7801b 100644 --- a/ui/app/components/breadcrumbs/default.hbs +++ b/ui/app/components/breadcrumbs/default.hbs @@ -1,5 +1,16 @@
  • - {{@crumb.label}} + {{#if @crumb.title}} +
    +
    + {{@crumb.title}} +
    +
    + {{@crumb.label}} +
    +
    + {{else}} + {{@crumb.label}} + {{/if}}
  • \ No newline at end of file From a262979f4f709db6d0b07274d265a0333b50754d Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 16 Dec 2021 09:38:41 -0500 Subject: [PATCH 18/27] style: add styling for title on breadcrumbs --- ui/app/styles/core/breadcrumb.scss | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui/app/styles/core/breadcrumb.scss b/ui/app/styles/core/breadcrumb.scss index 092bb022149a..c1b7357ca740 100644 --- a/ui/app/styles/core/breadcrumb.scss +++ b/ui/app/styles/core/breadcrumb.scss @@ -11,7 +11,23 @@ } } + ul { + align-items: center; + } + + li::before { + font-size: x-large; + } + li.is-active a { opacity: 1; } + + dl dd { + font-size: medium; + } + + dl dt { + font-size: small; + } } From e1a576009339c3cc1afffa15402a9c2bffed6747 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 16 Dec 2021 09:40:43 -0500 Subject: [PATCH 19/27] refact: add title to breadcrumb generator All breadcrumbs do not need a title property because some views drill down by using a tab-based UI (e.g. CSI volumes and the Job Overview) The goal is to help us identify breadcrumbs that are non-descriptive (i.e. breadcrumbs that display as an ID). --- ui/app/controllers/allocations/allocation.js | 2 ++ ui/app/controllers/allocations/allocation/task.js | 1 + ui/app/controllers/clients/client.js | 1 + ui/app/controllers/jobs/job/task-group.js | 1 + ui/app/controllers/servers/server.js | 1 + 5 files changed, 6 insertions(+) diff --git a/ui/app/controllers/allocations/allocation.js b/ui/app/controllers/allocations/allocation.js index d2c2aee40345..b29d7b6cb02a 100644 --- a/ui/app/controllers/allocations/allocation.js +++ b/ui/app/controllers/allocations/allocation.js @@ -33,10 +33,12 @@ export default class AllocationsAllocationController extends Controller { { label: 'Jobs', args: ['jobs.index', jobQueryParams] }, { type: 'job', job: job }, { + title: 'Task Group', label: allocation.taskGroupName, args: ['jobs.job.task-group', job.plainId, allocation.taskGroupName, jobQueryParams], }, { + title: 'Allocation', label: allocation.shortId, args: ['allocations.allocation', allocation], }, diff --git a/ui/app/controllers/allocations/allocation/task.js b/ui/app/controllers/allocations/allocation/task.js index 0d19fd42f238..1fefdd66c5a6 100644 --- a/ui/app/controllers/allocations/allocation/task.js +++ b/ui/app/controllers/allocations/allocation/task.js @@ -6,6 +6,7 @@ export default class AllocationsAllocationTaskController extends Controller { if (!model) return []; return [ { + title: 'Task', label: model.get('name'), args: ['allocations.allocation.task', model.get('allocation'), model], }, diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index aa558c80b0bb..e13e1e9c19b1 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -6,6 +6,7 @@ export default class ClientsClientController extends Controller { if (!model) return []; return [ { + title: 'Client', label: model.get('shortId'), args: ['clients.client', model.get('id')], }, diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index af43746d0d83..a25f3b139e35 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -147,6 +147,7 @@ export default class TaskGroupController extends Controller.extend( if (!model) return []; return [ { + title: 'Task Group', label: model.get('name'), args: [ 'jobs.job.task-group', diff --git a/ui/app/controllers/servers/server.js b/ui/app/controllers/servers/server.js index 0f99b8985c17..e01310c1a54f 100644 --- a/ui/app/controllers/servers/server.js +++ b/ui/app/controllers/servers/server.js @@ -7,6 +7,7 @@ export default class ServersServerController extends Controller { get breadcrumb() { return { + title: 'Server', label: this.server.name, args: ['servers.server', this.server.id], }; From 10db60edf6a9984935e9404567632fc56e5dd542 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 16 Dec 2021 09:54:46 -0500 Subject: [PATCH 20/27] feat: handle title behavior for job breadcrumb --- ui/app/components/breadcrumbs/job.hbs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ui/app/components/breadcrumbs/job.hbs b/ui/app/components/breadcrumbs/job.hbs index d6578baf6970..87e5e2a7b81c 100644 --- a/ui/app/components/breadcrumbs/job.hbs +++ b/ui/app/components/breadcrumbs/job.hbs @@ -16,7 +16,14 @@ @query={{hash namespace=(or trigger.data.result.namespace.name "default")}} data-test-breadcrumb={{"jobs.job.index"}} > - {{trigger.data.result.trimmedName}} +
    +
    + Parent Job +
    +
    + {{trigger.data.result.trimmedName}} +
    +
  • {{/if}} @@ -28,7 +35,14 @@ data-test-breadcrumb={{"jobs.job.index"}} data-test-job-breadcrumb > - {{this.job.trimmedName}} +
    +
    + {{if this.job.hasChildren "Parent Job" "Job"}} +
    +
    + {{this.job.trimmedName}} +
    +
    {{/if}} From ed49d59ffb02222b1b5e5deaa4e0b86f56ea3caa Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 16 Dec 2021 10:12:29 -0500 Subject: [PATCH 21/27] style: centering and spacing for titled breadcrumbs --- ui/app/styles/core/breadcrumb.scss | 1 + ui/app/styles/core/navbar.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/ui/app/styles/core/breadcrumb.scss b/ui/app/styles/core/breadcrumb.scss index c1b7357ca740..f2b2f1413f3f 100644 --- a/ui/app/styles/core/breadcrumb.scss +++ b/ui/app/styles/core/breadcrumb.scss @@ -24,6 +24,7 @@ } dl dd { + margin: -4px 0px; font-size: medium; } diff --git a/ui/app/styles/core/navbar.scss b/ui/app/styles/core/navbar.scss index 840d8d1e9bee..dffa0ce019a1 100644 --- a/ui/app/styles/core/navbar.scss +++ b/ui/app/styles/core/navbar.scss @@ -1,5 +1,6 @@ .navbar { display: flex; + align-items: center; &.is-primary { background: linear-gradient(to right, $nomad-green-darker, $nomad-green-dark); From 1a2914b9fe77c4adf3bb8545ecd2c41b508743a8 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 16 Dec 2021 10:28:46 -0500 Subject: [PATCH 22/27] fix: test specs should expect to receive breadcrumb titles --- ui/tests/acceptance/client-detail-test.js | 4 ++-- ui/tests/acceptance/client-monitor-test.js | 2 +- ui/tests/acceptance/server-monitor-test.js | 2 +- ui/tests/acceptance/task-detail-test.js | 8 ++++---- ui/tests/acceptance/task-group-detail-test.js | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ui/tests/acceptance/client-detail-test.js b/ui/tests/acceptance/client-detail-test.js index cfd9042adcb2..2b2049b5465a 100644 --- a/ui/tests/acceptance/client-detail-test.js +++ b/ui/tests/acceptance/client-detail-test.js @@ -68,8 +68,8 @@ module('Acceptance | client detail', function(hooks) { ); assert.equal( Layout.breadcrumbFor('clients.client').text, - node.id.split('-')[0], - 'Second breadcrumb says the node short id' + `Client ${node.id.split('-')[0]}`, + 'Second breadcrumb is a titled breadcrumb saying the node short id' ); await Layout.breadcrumbFor('clients.index').visit(); assert.equal(currentURL(), '/clients', 'First breadcrumb links back to clients'); diff --git a/ui/tests/acceptance/client-monitor-test.js b/ui/tests/acceptance/client-monitor-test.js index 49523009ce8b..c795345e600d 100644 --- a/ui/tests/acceptance/client-monitor-test.js +++ b/ui/tests/acceptance/client-monitor-test.js @@ -36,7 +36,7 @@ module('Acceptance | client monitor', function(hooks) { await ClientMonitor.visit({ id: node.id }); assert.equal(Layout.breadcrumbFor('clients.index').text, 'Clients'); - assert.equal(Layout.breadcrumbFor('clients.client').text, node.id.split('-')[0]); + assert.equal(Layout.breadcrumbFor('clients.client').text, `Client ${node.id.split('-')[0]}`); await Layout.breadcrumbFor('clients.index').visit(); assert.equal(currentURL(), '/clients'); diff --git a/ui/tests/acceptance/server-monitor-test.js b/ui/tests/acceptance/server-monitor-test.js index 00bcc6b09536..0f6aeed3e02d 100644 --- a/ui/tests/acceptance/server-monitor-test.js +++ b/ui/tests/acceptance/server-monitor-test.js @@ -38,7 +38,7 @@ module('Acceptance | server monitor', function(hooks) { 'Servers', 'The page should read the breadcrumb Servers' ); - assert.equal(Layout.breadcrumbFor('servers.server').text, agent.name); + assert.equal(Layout.breadcrumbFor('servers.server').text, `Server ${agent.name}`); await Layout.breadcrumbFor('servers.index').visit(); assert.equal(currentURL(), '/servers'); diff --git a/ui/tests/acceptance/task-detail-test.js b/ui/tests/acceptance/task-detail-test.js index fa74af8030e1..becc9c212f1f 100644 --- a/ui/tests/acceptance/task-detail-test.js +++ b/ui/tests/acceptance/task-detail-test.js @@ -62,22 +62,22 @@ module('Acceptance | task detail', function(hooks) { await waitFor('[data-test-job-breadcrumb]'); assert.equal( Layout.breadcrumbFor('jobs.job.index').text, - job.name, + `Job ${job.name}`, 'Job is the second breadcrumb' ); assert.equal( Layout.breadcrumbFor('jobs.job.task-group').text, - taskGroup, + `Task Group ${taskGroup}`, 'Task Group is the third breadcrumb' ); assert.equal( Layout.breadcrumbFor('allocations.allocation').text, - shortId, + `Allocation ${shortId}`, 'Allocation short id is the fourth breadcrumb' ); assert.equal( Layout.breadcrumbFor('allocations.allocation.task').text, - task.name, + `Task ${task.name}`, 'Task name is the fifth breadcrumb' ); diff --git a/ui/tests/acceptance/task-group-detail-test.js b/ui/tests/acceptance/task-group-detail-test.js index 795a227ee231..9f14c3f47617 100644 --- a/ui/tests/acceptance/task-group-detail-test.js +++ b/ui/tests/acceptance/task-group-detail-test.js @@ -122,12 +122,12 @@ module('Acceptance | task group detail', function(hooks) { assert.equal(Layout.breadcrumbFor('jobs.index').text, 'Jobs', 'First breadcrumb says jobs'); assert.equal( Layout.breadcrumbFor('jobs.job.index').text, - job.name, + `Job ${job.name}`, 'Second breadcrumb says the job name' ); assert.equal( Layout.breadcrumbFor('jobs.job.task-group').text, - taskGroup.name, + `Task Group ${taskGroup.name}`, 'Third breadcrumb says the job name' ); }); From 92fafca000a9b0d5e978a54a3ebfeecbc298f8d4 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Fri, 17 Dec 2021 10:20:13 -0500 Subject: [PATCH 23/27] styling: fix opacity for last child in the list --- ui/app/styles/core/breadcrumb.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/styles/core/breadcrumb.scss b/ui/app/styles/core/breadcrumb.scss index f2b2f1413f3f..2b0ddd8b770c 100644 --- a/ui/app/styles/core/breadcrumb.scss +++ b/ui/app/styles/core/breadcrumb.scss @@ -19,7 +19,7 @@ font-size: x-large; } - li.is-active a { + li:last-child a.active { opacity: 1; } From 4d39d887d3f98734cd62c9566210628684bf62bc Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Fri, 17 Dec 2021 11:49:22 -0500 Subject: [PATCH 24/27] fix: add ember-a11y-testing-audit-call --- ui/tests/.eslintrc.js | 3 +++ ui/tests/integration/components/app-breadcrumbs-test.js | 1 + ui/tests/integration/components/breadcrumbs-test.js | 1 + 3 files changed, 5 insertions(+) diff --git a/ui/tests/.eslintrc.js b/ui/tests/.eslintrc.js index a468bd0d0d79..66934a8751b6 100644 --- a/ui/tests/.eslintrc.js +++ b/ui/tests/.eslintrc.js @@ -29,6 +29,9 @@ module.exports = { { files: ['integration/components/**/*-test.js'], plugins: ['ember-a11y-testing'], + rules: { + 'ember-a11y-testing/a11y-audit-called': 'error', + }, settings: { 'ember-a11y-testing': { auditModule: { diff --git a/ui/tests/integration/components/app-breadcrumbs-test.js b/ui/tests/integration/components/app-breadcrumbs-test.js index b1d6d425a3a8..bc3d18c054db 100644 --- a/ui/tests/integration/components/app-breadcrumbs-test.js +++ b/ui/tests/integration/components/app-breadcrumbs-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember-a11y-testing/a11y-audit-called */ import { setComponentTemplate } from '@ember/component'; import Component from '@glimmer/component'; import { module, test } from 'qunit'; diff --git a/ui/tests/integration/components/breadcrumbs-test.js b/ui/tests/integration/components/breadcrumbs-test.js index 359312db692b..5d24329ff0bf 100644 --- a/ui/tests/integration/components/breadcrumbs-test.js +++ b/ui/tests/integration/components/breadcrumbs-test.js @@ -1,3 +1,4 @@ +/* eslint-disable ember-a11y-testing/a11y-audit-called */ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { click, findAll, render } from '@ember/test-helpers'; From c1d292041d5e68a5d9e7763a5870df203212ef1e Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Tue, 21 Dec 2021 07:52:06 -0500 Subject: [PATCH 25/27] chore: clean-up merge conflict after rebase --- ui/app/templates/components/job-page/parts/summary.hbs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ui/app/templates/components/job-page/parts/summary.hbs b/ui/app/templates/components/job-page/parts/summary.hbs index 8adfe1220f85..573f3dc9dd9a 100644 --- a/ui/app/templates/components/job-page/parts/summary.hbs +++ b/ui/app/templates/components/job-page/parts/summary.hbs @@ -44,10 +44,7 @@ (if a.item.hasChildren "children-status-bar" "allocation-status-bar") allocationContainer=a.item.summary job=a.item.summary -<<<<<<< HEAD onSliceClick=this.onSliceClick -======= ->>>>>>> bf9ae2b52 (fix: delete state from routes and move to controllers and add renderless component to templates) class="split-view" as |chart| }}
      @@ -66,4 +63,4 @@ {{/component}} - + \ No newline at end of file From b25f5ff89e380c91c7325abc3b5fdaf42fb762a3 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Thu, 23 Dec 2021 12:33:15 -0500 Subject: [PATCH 26/27] fix: remove unecessary breadcrumb --- ui/app/templates/components/job-page/parts/summary.hbs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ui/app/templates/components/job-page/parts/summary.hbs b/ui/app/templates/components/job-page/parts/summary.hbs index 573f3dc9dd9a..9b4ed85090fb 100644 --- a/ui/app/templates/components/job-page/parts/summary.hbs +++ b/ui/app/templates/components/job-page/parts/summary.hbs @@ -62,5 +62,4 @@
    {{/component}} - - \ No newline at end of file + \ No newline at end of file From 3bdf6613efa7b063a7d04a717985c74f2876ed95 Mon Sep 17 00:00:00 2001 From: Jai Bhagat Date: Fri, 17 Dec 2021 11:29:36 -0500 Subject: [PATCH 27/27] refact: clean-up breadcrumb invocations --- .../allocations/allocation/task.js | 20 +++++----- ui/app/controllers/clients.js | 7 ---- ui/app/controllers/clients/client.js | 20 +++++----- ui/app/controllers/csi/plugins.js | 7 ---- ui/app/controllers/csi/plugins/plugin.js | 10 +++-- ui/app/controllers/csi/volumes.js | 7 ---- ui/app/controllers/csi/volumes/volume.js | 6 ++- ui/app/controllers/jobs.js | 10 +---- ui/app/controllers/jobs/job.js | 4 +- ui/app/controllers/jobs/job/dispatch.js | 10 +---- ui/app/controllers/jobs/job/task-group.js | 40 ++++++++++--------- ui/app/controllers/jobs/run.js | 7 ---- ui/app/controllers/optimize.js | 7 ---- ui/app/controllers/optimize/summary.js | 18 ++++----- ui/app/controllers/servers.js | 7 ---- ui/app/controllers/servers/server.js | 8 ---- ui/app/controllers/topology.js | 7 ---- .../templates/allocations/allocation/task.hbs | 5 +-- ui/app/templates/clients.hbs | 4 +- ui/app/templates/clients/client.hbs | 5 +-- ui/app/templates/csi/plugins.hbs | 5 +-- ui/app/templates/csi/volumes.hbs | 5 +-- ui/app/templates/jobs.hbs | 4 +- ui/app/templates/jobs/job.hbs | 5 +-- ui/app/templates/jobs/job/dispatch.hbs | 4 +- ui/app/templates/jobs/job/task-group.hbs | 4 +- ui/app/templates/jobs/run.hbs | 4 +- ui/app/templates/optimize.hbs | 4 +- ui/app/templates/optimize/summary.hbs | 8 +--- ui/app/templates/servers.hbs | 4 +- ui/app/templates/servers/server.hbs | 5 ++- ui/app/templates/topology.hbs | 2 +- 32 files changed, 87 insertions(+), 176 deletions(-) diff --git a/ui/app/controllers/allocations/allocation/task.js b/ui/app/controllers/allocations/allocation/task.js index 1fefdd66c5a6..3e3c9a0343b3 100644 --- a/ui/app/controllers/allocations/allocation/task.js +++ b/ui/app/controllers/allocations/allocation/task.js @@ -1,15 +1,15 @@ import Controller from '@ember/controller'; export default class AllocationsAllocationTaskController extends Controller { - get breadcrumbs() { - const model = this.model; - if (!model) return []; - return [ - { - title: 'Task', - label: model.get('name'), - args: ['allocations.allocation.task', model.get('allocation'), model], - }, - ]; + get task() { + return this.model; + } + + get breadcrumb() { + return { + title: 'Task', + label: this.task.get('name'), + args: ['allocations.allocation.task', this.task.get('allocation'), this.task], + }; } } diff --git a/ui/app/controllers/clients.js b/ui/app/controllers/clients.js index 12f209399c99..673868f9b585 100644 --- a/ui/app/controllers/clients.js +++ b/ui/app/controllers/clients.js @@ -2,11 +2,4 @@ import Controller from '@ember/controller'; export default class ClientsController extends Controller { isForbidden = false; - - breadcrumbs = [ - { - label: 'Clients', - args: ['clients.index'], - }, - ]; } diff --git a/ui/app/controllers/clients/client.js b/ui/app/controllers/clients/client.js index e13e1e9c19b1..d13eb42be7be 100644 --- a/ui/app/controllers/clients/client.js +++ b/ui/app/controllers/clients/client.js @@ -1,15 +1,15 @@ import Controller from '@ember/controller'; export default class ClientsClientController extends Controller { - get breadcrumbs() { - const model = this.model; - if (!model) return []; - return [ - { - title: 'Client', - label: model.get('shortId'), - args: ['clients.client', model.get('id')], - }, - ]; + get client() { + return this.model; + } + + get breadcrumb() { + return { + title: 'Client', + label: this.client.get('shortId'), + args: ['clients.client', this.client.get('id')], + }; } } diff --git a/ui/app/controllers/csi/plugins.js b/ui/app/controllers/csi/plugins.js index d18af07be15d..b9a8c5a54152 100644 --- a/ui/app/controllers/csi/plugins.js +++ b/ui/app/controllers/csi/plugins.js @@ -2,11 +2,4 @@ import Controller from '@ember/controller'; export default class PluginsController extends Controller { isForbidden = false; - - breadcrumbs = [ - { - label: 'Storage', - args: ['csi.index'], - }, - ]; } diff --git a/ui/app/controllers/csi/plugins/plugin.js b/ui/app/controllers/csi/plugins/plugin.js index 67f11a4216e9..d82e83f7f0dc 100644 --- a/ui/app/controllers/csi/plugins/plugin.js +++ b/ui/app/controllers/csi/plugins/plugin.js @@ -1,16 +1,20 @@ import Controller from '@ember/controller'; export default class CsiPluginsPluginController extends Controller { + get plugin() { + return this.model; + } + get breadcrumbs() { - const plugin = this.model; + const { plainId } = this.plugin; return [ { label: 'Plugins', args: ['csi.plugins'], }, { - label: plugin.plainId, - args: ['csi.plugins.plugin', plugin.plainId], + label: plainId, + args: ['csi.plugins.plugin', plainId], }, ]; } diff --git a/ui/app/controllers/csi/volumes.js b/ui/app/controllers/csi/volumes.js index ca6e80148e21..1eb7ecfecdbf 100644 --- a/ui/app/controllers/csi/volumes.js +++ b/ui/app/controllers/csi/volumes.js @@ -2,11 +2,4 @@ import Controller from '@ember/controller'; export default class VolumesController extends Controller { isForbidden = false; - - breadcrumbs = [ - { - label: 'Storage', - args: ['csi.index'], - }, - ]; } diff --git a/ui/app/controllers/csi/volumes/volume.js b/ui/app/controllers/csi/volumes/volume.js index 735700e4a6d3..c151e5dec633 100644 --- a/ui/app/controllers/csi/volumes/volume.js +++ b/ui/app/controllers/csi/volumes/volume.js @@ -14,8 +14,12 @@ export default class VolumeController extends Controller { ]; volumeNamespace = 'default'; + get volume() { + return this.model; + } + get breadcrumbs() { - const volume = this.model; + const volume = this.volume; return [ { label: 'Volumes', diff --git a/ui/app/controllers/jobs.js b/ui/app/controllers/jobs.js index 5fbfeede25d5..77b1678a4399 100644 --- a/ui/app/controllers/jobs.js +++ b/ui/app/controllers/jobs.js @@ -1,10 +1,4 @@ import Controller from '@ember/controller'; -export default class JobsController extends Controller { - breadcrumbs = [ - { - label: 'Jobs', - args: ['jobs.index'], - }, - ]; -} +// The WithNamespaceResetting Mixin uses Controller Injection and requires us to keep this controller around +export default class JobsController extends Controller {} diff --git a/ui/app/controllers/jobs/job.js b/ui/app/controllers/jobs/job.js index 17e726db4910..93c96493ea75 100644 --- a/ui/app/controllers/jobs/job.js +++ b/ui/app/controllers/jobs/job.js @@ -8,7 +8,7 @@ export default class JobController extends Controller { ]; jobNamespace = 'default'; - get breadcrumbs() { - return [{ type: 'job', job: this.model }]; + get job() { + return this.model; } } diff --git a/ui/app/controllers/jobs/job/dispatch.js b/ui/app/controllers/jobs/job/dispatch.js index e2145103f420..7498f649ab61 100644 --- a/ui/app/controllers/jobs/job/dispatch.js +++ b/ui/app/controllers/jobs/job/dispatch.js @@ -1,10 +1,4 @@ import Controller from '@ember/controller'; -export default class JobsJobDispatchController extends Controller { - breadcrumbs = [ - { - label: 'Dispatch', - args: ['jobs.job.dispatch'], - }, - ]; -} +// This may be safe to remove but we can't be sure, some route may try access this directly using this.controllerFor +export default class JobsJobDispatchController extends Controller {} diff --git a/ui/app/controllers/jobs/job/task-group.js b/ui/app/controllers/jobs/job/task-group.js index a25f3b139e35..3cb6efe34777 100644 --- a/ui/app/controllers/jobs/job/task-group.js +++ b/ui/app/controllers/jobs/job/task-group.js @@ -14,10 +14,10 @@ import classic from 'ember-classic-decorator'; @classic export default class TaskGroupController extends Controller.extend( - Sortable, - Searchable, - WithNamespaceResetting - ) { + Sortable, + Searchable, + WithNamespaceResetting +) { @service userSettings; @service can; @@ -142,20 +142,22 @@ export default class TaskGroupController extends Controller.extend( setFacetQueryParam(queryParam, selection) { this.set(queryParam, serialize(selection)); } - get breadcrumbs() { - const model = this.model; - if (!model) return []; - return [ - { - title: 'Task Group', - label: model.get('name'), - args: [ - 'jobs.job.task-group', - model.get('job'), - model.get('name'), - qpBuilder({ jobNamespace: model.get('job.namespace.name') || 'default' }), - ], - }, - ]; + + get taskGroup() { + return this.model; + } + + get breadcrumb() { + const { job, name } = this.taskGroup; + return { + title: 'Task Group', + label: name, + args: [ + 'jobs.job.task-group', + job, + name, + qpBuilder({ jobNamespace: job.get('namespace.name') || 'default' }), + ], + }; } } diff --git a/ui/app/controllers/jobs/run.js b/ui/app/controllers/jobs/run.js index 0ad8722dc025..5921713017c6 100644 --- a/ui/app/controllers/jobs/run.js +++ b/ui/app/controllers/jobs/run.js @@ -1,13 +1,6 @@ import Controller from '@ember/controller'; export default class RunController extends Controller { - breadcrumbs = [ - { - label: 'Run', - args: ['jobs.run'], - }, - ]; - onSubmit(id, namespace) { this.transitionToRoute('jobs.job', id, { queryParams: { namespace }, diff --git a/ui/app/controllers/optimize.js b/ui/app/controllers/optimize.js index 94542b1db95c..b5c438a99fc2 100644 --- a/ui/app/controllers/optimize.js +++ b/ui/app/controllers/optimize.js @@ -102,13 +102,6 @@ export default class OptimizeController extends Controller { { key: 'dead', label: 'Dead' }, ]; - breadcrumbs = [ - { - label: 'Recommendations', - args: ['optimize'], - }, - ]; - get optionsDatacenter() { const flatten = (acc, val) => acc.concat(val); const allDatacenters = new Set(this.summaries.mapBy('job.datacenters').reduce(flatten, [])); diff --git a/ui/app/controllers/optimize/summary.js b/ui/app/controllers/optimize/summary.js index 204730e98d23..986dede450d7 100644 --- a/ui/app/controllers/optimize/summary.js +++ b/ui/app/controllers/optimize/summary.js @@ -10,15 +10,15 @@ export default class OptimizeSummaryController extends Controller { }, ]; - get breadcrumbs() { - const model = this.model; - if (!model) return []; + get summary() { + return this.model; + } - return [ - { - label: model.slug.replace('/', ' / '), - args: ['optimize.summary', model.slug], - }, - ]; + get breadcrumb() { + const { slug } = this.summary; + return { + label: slug.replace('/', ' / '), + args: ['optimize.summary', slug], + }; } } diff --git a/ui/app/controllers/servers.js b/ui/app/controllers/servers.js index bf61fed66f72..aa0b590ba7e6 100644 --- a/ui/app/controllers/servers.js +++ b/ui/app/controllers/servers.js @@ -2,11 +2,4 @@ import Controller from '@ember/controller'; export default class ServersController extends Controller { isForbidden = false; - - breadcrumbs = [ - { - label: 'Servers', - args: ['servers.index'], - }, - ]; } diff --git a/ui/app/controllers/servers/server.js b/ui/app/controllers/servers/server.js index e01310c1a54f..81a3d4062782 100644 --- a/ui/app/controllers/servers/server.js +++ b/ui/app/controllers/servers/server.js @@ -4,12 +4,4 @@ export default class ServersServerController extends Controller { get server() { return this.model; } - - get breadcrumb() { - return { - title: 'Server', - label: this.server.name, - args: ['servers.server', this.server.id], - }; - } } diff --git a/ui/app/controllers/topology.js b/ui/app/controllers/topology.js index a16d411d4fbd..4b9a38b060e9 100644 --- a/ui/app/controllers/topology.js +++ b/ui/app/controllers/topology.js @@ -143,11 +143,4 @@ export default class TopologyControllers extends Controller { this.filteredNodes = filteredNodesError.context; } } - - get breadcrumb() { - return { - label: 'Topology', - args: ['topology'], - }; - } } diff --git a/ui/app/templates/allocations/allocation/task.hbs b/ui/app/templates/allocations/allocation/task.hbs index 0915a337c5cd..2cb651886148 100644 --- a/ui/app/templates/allocations/allocation/task.hbs +++ b/ui/app/templates/allocations/allocation/task.hbs @@ -1,4 +1 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} -{{outlet}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/clients.hbs b/ui/app/templates/clients.hbs index 82ed74cdcf9c..4da9ce68a00f 100644 --- a/ui/app/templates/clients.hbs +++ b/ui/app/templates/clients.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{outlet}} \ No newline at end of file diff --git a/ui/app/templates/clients/client.hbs b/ui/app/templates/clients/client.hbs index 0915a337c5cd..2cb651886148 100644 --- a/ui/app/templates/clients/client.hbs +++ b/ui/app/templates/clients/client.hbs @@ -1,4 +1 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} -{{outlet}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/plugins.hbs b/ui/app/templates/csi/plugins.hbs index 0915a337c5cd..1d511b7a8136 100644 --- a/ui/app/templates/csi/plugins.hbs +++ b/ui/app/templates/csi/plugins.hbs @@ -1,4 +1 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} -{{outlet}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/csi/volumes.hbs b/ui/app/templates/csi/volumes.hbs index 0915a337c5cd..1d511b7a8136 100644 --- a/ui/app/templates/csi/volumes.hbs +++ b/ui/app/templates/csi/volumes.hbs @@ -1,4 +1 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} -{{outlet}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/jobs.hbs b/ui/app/templates/jobs.hbs index 82ed74cdcf9c..f2c9986c752c 100644 --- a/ui/app/templates/jobs.hbs +++ b/ui/app/templates/jobs.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{outlet}} \ No newline at end of file diff --git a/ui/app/templates/jobs/job.hbs b/ui/app/templates/jobs/job.hbs index 0915a337c5cd..ba9c71856e98 100644 --- a/ui/app/templates/jobs/job.hbs +++ b/ui/app/templates/jobs/job.hbs @@ -1,4 +1 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} -{{outlet}} \ No newline at end of file +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/jobs/job/dispatch.hbs b/ui/app/templates/jobs/job/dispatch.hbs index 2387f0af060d..6252aa206eca 100644 --- a/ui/app/templates/jobs/job/dispatch.hbs +++ b/ui/app/templates/jobs/job/dispatch.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{page-title "Dispatch new " this.model.name}}
    diff --git a/ui/app/templates/jobs/job/task-group.hbs b/ui/app/templates/jobs/job/task-group.hbs index ee56ba4b02ba..dd95b32e0a96 100644 --- a/ui/app/templates/jobs/job/task-group.hbs +++ b/ui/app/templates/jobs/job/task-group.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{page-title "Task group " this.model.name " - Job " this.model.job.name}}
      diff --git a/ui/app/templates/jobs/run.hbs b/ui/app/templates/jobs/run.hbs index 89e14da82806..7bb74880bc6c 100644 --- a/ui/app/templates/jobs/run.hbs +++ b/ui/app/templates/jobs/run.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{page-title "Run a job"}}
      diff --git a/ui/app/templates/optimize.hbs b/ui/app/templates/optimize.hbs index 35c289fc6d3f..5e1795c15a0b 100644 --- a/ui/app/templates/optimize.hbs +++ b/ui/app/templates/optimize.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} +
      {{#if this.summaries}} diff --git a/ui/app/templates/optimize/summary.hbs b/ui/app/templates/optimize/summary.hbs index f84fa9366ae5..bb0e71003650 100644 --- a/ui/app/templates/optimize/summary.hbs +++ b/ui/app/templates/optimize/summary.hbs @@ -1,6 +1,2 @@ -{{#if @model}} - -{{/if}} \ No newline at end of file + + \ No newline at end of file diff --git a/ui/app/templates/servers.hbs b/ui/app/templates/servers.hbs index 82ed74cdcf9c..d90af14eec3e 100644 --- a/ui/app/templates/servers.hbs +++ b/ui/app/templates/servers.hbs @@ -1,6 +1,4 @@ -{{#each this.breadcrumbs as |crumb|}} - -{{/each}} + {{outlet}} \ No newline at end of file diff --git a/ui/app/templates/servers/server.hbs b/ui/app/templates/servers/server.hbs index 2cb651886148..29449f358636 100644 --- a/ui/app/templates/servers/server.hbs +++ b/ui/app/templates/servers/server.hbs @@ -1 +1,4 @@ -{{outlet}} \ No newline at end of file + +{{outlet}} \ No newline at end of file diff --git a/ui/app/templates/topology.hbs b/ui/app/templates/topology.hbs index e2d060fa3434..a95d57d08083 100644 --- a/ui/app/templates/topology.hbs +++ b/ui/app/templates/topology.hbs @@ -1,4 +1,4 @@ - + {{page-title "Cluster Topology"}}