Upgrade Ember and friends 3.28 (#12215)
* chore: upgrade forward compatible packages * chore: v3.20.2...v3.24.0 * chore: silence string prototype extension deprecation * refact: don't test clicking disabled button job-list Recent test-helper upgrades will guard against clicking disabled buttons as this is not something that real users can do. We need to change our tests accordingly. * fix: await async test helper `expectError` We have to await this async test function otherwise the test's rendering context will be torn down before we run assertions against it. * fix: don't try to click disabled two-step-button Recent test-helper updates prohibit clicking disabled buttons. We need to adapt the tests accordingly. * fix: recommendation-accordion Use up-to-date semantics for handling list-accordion closing in recommendation-accordion. * fixes toggling recommendation-accordion toggle. * fix: simple-unless linting error application.hbs There's no reason to use unless here - we can use if instead. * fix: no-quoteless-attributes recommendation accordion * fix: no-quoteless-attributes recommendation-chart * fix: allow `unless` - global-header.hbs This is a valid use of unless in our opinion. * fix: allow unless in job-diff This is not a great use for unless but we don't want to change this behavior atm. * fix: no-attrs-in-components list-pager There is no need to use this.attrs in classic components. When we will convert to glimmer we will use `@`-instead. * fix: simple-unless job/definition We can convert to a simple if here. * fix: allow inline-styles stats-box component To make linter happy. * fix: disable no-action and no-invalid-interactive Will be adressed in follow-up PRs. * chore: update ember-classic-decorator to latest * chore: upgrade ember-can to latest * chore: upgrade ember-composable-helpers to latest * chore: upgrade ember-concurrency * fix: recomputation deprecation `Trigger` schedule `do` on actions queue to work around recomputation deprecation when triggering Trigger on `did-insert`. * chore: upgrade ember-cli-string-helpers * chore: upgrade ember-copy * chore: upgrade ember-data-model-fragments * chore: upgrade ember-deprecation-workflow * chore: upgrade ember-inline-svg * chore: upgrade ember-modifier * chore: upgrade ember-truth-helpers * chore: upgrade ember-moment & ember-cli-moment-shim * chore: upgrade ember-power-select * chore: upgrade ember-responsive * chore: upgrade ember-sinon * chore: upgrade ember-cli-mirage For now we will stay on 2.2 - upgrades > 2.3 break the build. * chore: upgrade 3.24.0 to 3.28.5 * fix: add missing classic decorators on adapters * fix: missing classic decorators to serializers * fix: don't reopen Ember.Object anymore * fix: remove unused useNativeEvents ember-cli-page-objects doesn't provide this method anymore * fix: add missing attributeBindings for test-selectors ember-test-selectors doesn't provides automatic bindings for data-test-* attributes anymore. * fix: classic decorator for application serializer test * fix: remove `removeContext` from tests. It is unneeded and ember-cli-page-objects doesn't provides this method anymore. * fix: remove deprecations `run.*`-invocations * fix: `collapseWhitespace` in optimize test * fix: make sure to load async relationship before access * fix: dependent keys for relationship computeds We need to add `*.isFulfilled` as dependent keys for computeds that access async relationships. * fix: `computed.read`-invocations use `read` instead * chore: prettify templates * fix: use map instead of mapBy ember-cli-page-object Doesn't work with updated ember-cli-page-object anymore. * fix: remove remaining deprecated `run.*`-calls * chore: add more deprecations deprecation-workflow * fix: `implicit-injection`-deprecation All routes that add watchers will need to inject the store-service as the store service is internally used in watchers. * fix: more implicit injection deprecations * chore: silence implicit-injection deprecation We can tackle the deprecation when we find the time. * fix: new linting errors after upgrade * fix: remove merge conflicts prettierignore * chore: upgrade to run node 12.22 when building binaries
This commit is contained in:
parent
5ae30849a9
commit
e096a0a5ab
|
@ -10,8 +10,8 @@ export NVM_DIR="${HOME}/.nvm"
|
|||
|
||||
# Install Node, Ember CLI, and Phantom for UI development
|
||||
# Use exact full version version (e.g. not 12) for reproducibility purposes
|
||||
nvm install 12.19.0
|
||||
nvm alias default 12.19.0
|
||||
nvm install 12.22.10
|
||||
nvm alias default 12.22.10
|
||||
npm install -g ember-cli
|
||||
|
||||
# Install Yarn for front-end dependency management
|
||||
|
|
|
@ -15,6 +15,8 @@ mirage/
|
|||
# misc
|
||||
/coverage/
|
||||
!.*
|
||||
.*/
|
||||
.eslintcache
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
|
|
|
@ -37,15 +37,16 @@ module.exports = {
|
|||
// node files
|
||||
{
|
||||
files: [
|
||||
'.eslintrc.js',
|
||||
'.prettierrc.js',
|
||||
'.template-lintrc.js',
|
||||
'ember-cli-build.js',
|
||||
'testem.js',
|
||||
'blueprints/*/index.js',
|
||||
'config/**/*.js',
|
||||
'lib/*/index.js',
|
||||
'server/**/*.js',
|
||||
'./.eslintrc.js',
|
||||
'./.prettierrc.js',
|
||||
'./.template-lintrc.js',
|
||||
'./ember-cli-build.js',
|
||||
'./testem.js',
|
||||
'./blueprints/*/index.js',
|
||||
'./config/**/*.js',
|
||||
'./lib/*/index.js',
|
||||
'./server/**/*.js',
|
||||
'./tests/.eslintrc.js',
|
||||
],
|
||||
parserOptions: {
|
||||
sourceType: 'script',
|
||||
|
@ -73,5 +74,10 @@ module.exports = {
|
|||
},
|
||||
plugins: ['node'],
|
||||
},
|
||||
{
|
||||
// Test files:
|
||||
files: ['tests/**/*-test.{js,ts}'],
|
||||
extends: ['plugin:qunit/recommended'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -3,37 +3,7 @@
|
|||
module.exports = {
|
||||
extends: 'recommended',
|
||||
rules: {
|
||||
// should definitely move to template only
|
||||
// glimmer components for this one
|
||||
'no-partial': false,
|
||||
|
||||
// these need to be looked into, but
|
||||
// may be a bigger change
|
||||
'no-invalid-interactive': false,
|
||||
'simple-unless': false,
|
||||
|
||||
'self-closing-void-elements': false,
|
||||
'no-unnecessary-concat': false,
|
||||
'no-quoteless-attributes': false,
|
||||
'no-nested-interactive': false,
|
||||
|
||||
// Only used in list-pager, which can be replaced with
|
||||
// an angle-bracket component
|
||||
'no-attrs-in-components': false,
|
||||
|
||||
// Used in practice with charts. Ideally this would be true
|
||||
// except for a whitelist of chart files.
|
||||
'no-inline-styles': false,
|
||||
|
||||
// not sure we'll ever want these on,
|
||||
// would be nice but if prettier isn't doing
|
||||
// it for us, then not sure it's worth it
|
||||
'attribute-indentation': false,
|
||||
'block-indentation': false,
|
||||
quotes: false,
|
||||
|
||||
// remove when moving from extending `recommended` to `octane`
|
||||
'no-curly-component-invocation': true,
|
||||
'no-implicit-this': true,
|
||||
'no-action': 'off',
|
||||
'no-invalid-interactive': 'off',
|
||||
},
|
||||
};
|
||||
|
|
33
ui/README.md
33
ui/README.md
|
@ -6,9 +6,9 @@ The official Nomad UI.
|
|||
|
||||
This is an [ember.js](https://emberjs.com/) project, and you will need the following tools installed on your computer.
|
||||
|
||||
* [Node.js v10](https://nodejs.org/)
|
||||
* [Yarn](https://yarnpkg.com)
|
||||
* [Ember CLI](https://ember-cli.com/)
|
||||
- [Node.js v10](https://nodejs.org/)
|
||||
- [Yarn](https://yarnpkg.com)
|
||||
- [Ember CLI](https://ember-cli.com/)
|
||||
|
||||
## Installation
|
||||
|
||||
|
@ -21,10 +21,10 @@ $ yarn
|
|||
|
||||
## Running / Development
|
||||
|
||||
UI in development mode defaults to using fake generated data, but you can configure it to proxy a live running nomad process by setting `USE_MIRAGE` environment variable to `false`. First, make sure nomad is running. The UI, in development mode, runs independently from Nomad, so this could be an official release or a dev branch. Likewise, Nomad can be running in server mode or dev mode. As long as the API is accessible, the UI will work as expected.
|
||||
UI in development mode defaults to using fake generated data, but you can configure it to proxy a live running nomad process by setting `USE_MIRAGE` environment variable to `false`. First, make sure nomad is running. The UI, in development mode, runs independently from Nomad, so this could be an official release or a dev branch. Likewise, Nomad can be running in server mode or dev mode. As long as the API is accessible, the UI will work as expected.
|
||||
|
||||
* `USE_MIRAGE=false ember serve`
|
||||
* Visit your app at [http://localhost:4200](http://localhost:4200).
|
||||
- `USE_MIRAGE=false ember serve`
|
||||
- Visit your app at [http://localhost:4200](http://localhost:4200).
|
||||
|
||||
You may need to reference the direct path to `ember`, typically in `./node_modules/.bin/ember`.
|
||||
|
||||
|
@ -38,8 +38,8 @@ All necessary tools for UI development are installed as part of the Vagrantfile.
|
|||
|
||||
That said, development with Vagrant is still possible, but the `ember serve` command requires two modifications:
|
||||
|
||||
* `--watch polling`: This allows the vm to notice file changes made in the host environment.
|
||||
* `--port 4201`: The default port 4200 is not forwarded, since local development is recommended.
|
||||
- `--watch polling`: This allows the vm to notice file changes made in the host environment.
|
||||
- `--port 4201`: The default port 4200 is not forwarded, since local development is recommended.
|
||||
|
||||
This makes the full command for running the UI in development mode in Vagrant:
|
||||
|
||||
|
@ -51,8 +51,8 @@ $ ember serve --watch polling --port 4201
|
|||
|
||||
Nomad UI tests can be run independently of Nomad golang tests.
|
||||
|
||||
* `ember test` (single run, headless browser)
|
||||
* `ember test --server` (watches for changes, runs in a full browser)
|
||||
- `ember test` (single run, headless browser)
|
||||
- `ember test --server` (watches for changes, runs in a full browser)
|
||||
|
||||
You can use `--filter <test name>` to run a targetted set of tests, e.g. `ember test --filter 'allocation detail'`.
|
||||
|
||||
|
@ -60,18 +60,15 @@ In the test environment, the fake data is generated with a random seed. If you w
|
|||
|
||||
### Linting
|
||||
|
||||
Linting should happen automatically in your editor and when committing changes, but it can also be invoked manually.
|
||||
|
||||
* `npm run lint:hbs`
|
||||
* `npm run lint:js`
|
||||
* `npm run lint:js -- --fix`
|
||||
- `yarn lint`
|
||||
- `yarn lint:fix`
|
||||
|
||||
### Building
|
||||
|
||||
Typically `make release` or `make dev-ui` will be the desired build workflow, but in the event that build artifacts need to be inspected, `ember build` will output compiled files in `ui/dist`.
|
||||
|
||||
* `ember build` (development)
|
||||
* `ember build --environment production` (production)
|
||||
- `ember build` (development)
|
||||
- `ember build --environment production` (production)
|
||||
|
||||
### Releasing
|
||||
|
||||
|
@ -79,7 +76,7 @@ Nomad UI releases are in lockstep with Nomad releases and are integrated into th
|
|||
|
||||
### Conventions
|
||||
|
||||
* UI branches should be prefix with `f-ui-` for feature work and `b-ui-` for bug fixes. This instructs CI to skip running nomad backend tests.
|
||||
- UI branches should be prefix with `f-ui-` for feature work and `b-ui-` for bug fixes. This instructs CI to skip running nomad backend tests.
|
||||
|
||||
### Storybook UI Library
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationAdapter from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class AgentAdapter extends ApplicationAdapter {
|
||||
pathForType = () => 'agent/members';
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Watchable from './watchable';
|
||||
import addToPath from 'nomad-ui/utils/add-to-path';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class AllocationAdapter extends Watchable {
|
||||
stop = adapterAction('/stop');
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Watchable from './watchable';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class DeploymentAdapter extends Watchable {
|
||||
fail(deployment) {
|
||||
const id = deployment.get('id');
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationAdapter from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class EvaluationAdapter extends ApplicationAdapter {
|
||||
handleResponse(_status, headers) {
|
||||
const result = super.handleResponse(...arguments);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import WatchableNamespaceIDs from './watchable-namespace-ids';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobScaleAdapter extends WatchableNamespaceIDs {
|
||||
urlForFindRecord(id, type, hash) {
|
||||
return super.urlForFindRecord(id, 'job', hash, 'scale');
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import WatchableNamespaceIDs from './watchable-namespace-ids';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobSummaryAdapter extends WatchableNamespaceIDs {
|
||||
urlForFindRecord(id, type, hash) {
|
||||
return super.urlForFindRecord(id, 'job', hash, 'summary');
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ApplicationAdapter from './application';
|
||||
import addToPath from 'nomad-ui/utils/add-to-path';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobVersionAdapter extends ApplicationAdapter {
|
||||
revertTo(jobVersion) {
|
||||
const jobAdapter = this.store.adapterFor('job');
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import WatchableNamespaceIDs from './watchable-namespace-ids';
|
||||
import addToPath from 'nomad-ui/utils/add-to-path';
|
||||
import { base64EncodeString } from 'nomad-ui/utils/encode';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobAdapter extends WatchableNamespaceIDs {
|
||||
relationshipFallbackLinks = {
|
||||
summary: '/summary',
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Watchable from './watchable';
|
||||
import codesForError from '../utils/codes-for-error';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class NamespaceAdapter extends Watchable {
|
||||
findRecord(store, modelClass, id) {
|
||||
return super.findRecord(...arguments).catch((error) => {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Watchable from './watchable';
|
||||
import addToPath from 'nomad-ui/utils/add-to-path';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class NodeAdapter extends Watchable {
|
||||
setEligible(node) {
|
||||
return this.setEligibility(node, true);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Watchable from './watchable';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class PluginAdapter extends Watchable {
|
||||
queryParamsToAttrs = {
|
||||
type: 'type',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { default as ApplicationAdapter, namespace } from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class PolicyAdapter extends ApplicationAdapter {
|
||||
namespace = namespace + '/acl';
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationAdapter from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class RecommendationSummaryAdapter extends ApplicationAdapter {
|
||||
pathForType = () => 'recommendations';
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { inject as service } from '@ember/service';
|
||||
import { default as ApplicationAdapter, namespace } from './application';
|
||||
import OTTExchangeError from '../utils/ott-exchange-error';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TokenAdapter extends ApplicationAdapter {
|
||||
@service store;
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import WatchableNamespaceIDs from './watchable-namespace-ids';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class VolumeAdapter extends WatchableNamespaceIDs {
|
||||
queryParamsToAttrs = {
|
||||
type: 'type',
|
||||
|
|
|
@ -5,7 +5,9 @@ import { AbortError } from '@ember-data/adapter/error';
|
|||
import queryString from 'query-string';
|
||||
import ApplicationAdapter from './application';
|
||||
import removeRecord from '../utils/remove-record';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class Watchable extends ApplicationAdapter {
|
||||
@service watchList;
|
||||
@service store;
|
||||
|
|
|
@ -4,16 +4,25 @@ import Component from '@ember/component';
|
|||
import { computed } from '@ember/object';
|
||||
import { computed as overridable } from 'ember-overridable-computed';
|
||||
import { alias } from '@ember/object/computed';
|
||||
import { run } from '@ember/runloop';
|
||||
import { scheduleOnce } from '@ember/runloop';
|
||||
import { task, timeout } from 'ember-concurrency';
|
||||
import { lazyClick } from '../helpers/lazy-click';
|
||||
import AllocationStatsTracker from 'nomad-ui/utils/classes/allocation-stats-tracker';
|
||||
import classic from 'ember-classic-decorator';
|
||||
import { classNames, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
|
||||
@classic
|
||||
@tagName('tr')
|
||||
@classNames('allocation-row', 'is-interactive')
|
||||
@attributeBindings(
|
||||
'data-test-allocation',
|
||||
'data-test-write-allocation',
|
||||
'data-test-read-allocation'
|
||||
)
|
||||
export default class AllocationRow extends Component {
|
||||
@service store;
|
||||
@service token;
|
||||
|
@ -56,7 +65,7 @@ export default class AllocationRow extends Component {
|
|||
const allocation = this.allocation;
|
||||
|
||||
if (allocation) {
|
||||
run.scheduleOnce('afterRender', this, qualifyAllocation);
|
||||
scheduleOnce('afterRender', this, qualifyAllocation);
|
||||
} else {
|
||||
this.fetchStats.cancelAll();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { computed } from '@ember/object';
|
||||
import DistributionBar from './distribution-bar';
|
||||
import { attributeBindings } from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@attributeBindings('data-test-allocation-status-bar')
|
||||
export default class AllocationStatusBar extends DistributionBar {
|
||||
layoutName = 'components/distribution-bar';
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
{{! template-lint-disable no-unknown-arguments-for-builtin-components }}
|
||||
<li data-test-breadcrumb-default>
|
||||
<LinkTo @params={{@crumb.args}} data-test-breadcrumb={{@crumb.args.firstObject}}>
|
||||
<LinkTo
|
||||
@params={{@crumb.args}}
|
||||
data-test-breadcrumb={{@crumb.args.firstObject}}
|
||||
>
|
||||
{{#if @crumb.title}}
|
||||
<dl>
|
||||
<dt>
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { computed } from '@ember/object';
|
||||
import DistributionBar from './distribution-bar';
|
||||
import classic from 'ember-classic-decorator';
|
||||
import { attributeBindings } from '@ember-decorators/component';
|
||||
|
||||
@classic
|
||||
@attributeBindings('data-test-children-status-bar')
|
||||
export default class ChildrenStatusBar extends DistributionBar {
|
||||
layoutName = 'components/distribution-bar';
|
||||
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
{{#if this.show}}
|
||||
<ListAccordion
|
||||
data-test-recommendation-accordion
|
||||
class="recommendation-accordion boxed-section {{if this.closing "closing"}}"
|
||||
class="recommendation-accordion boxed-section {{if this.closing 'closing'}}"
|
||||
@source={{array @summary}}
|
||||
@key="id"
|
||||
{{did-insert this.inserted}}
|
||||
as |a|>
|
||||
as |a|
|
||||
>
|
||||
{{#if a.isOpen}}
|
||||
<div class="animation-container" style={{this.animationContainerStyle}}>
|
||||
<Das::RecommendationCard
|
||||
@summary={{@summary}}
|
||||
@proceed={{this.proceed}}
|
||||
@onCollapse={{action (mut a.isOpen) false}}
|
||||
@skipReset=true
|
||||
@onCollapse={{a.close}}
|
||||
@skipReset={{true}}
|
||||
/>
|
||||
</div>
|
||||
{{else}}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
{{! template-lint-disable no-duplicate-landmark-elements}}
|
||||
{{#if this.interstitialComponent}}
|
||||
<section class="das-interstitial" style={{this.interstitialStyle}}>
|
||||
{{component (concat 'das/' this.interstitialComponent) proceed=this.proceedPromiseResolve error=this.error}}
|
||||
{{component
|
||||
(concat "das/" this.interstitialComponent)
|
||||
proceed=this.proceedPromiseResolve
|
||||
error=this.error
|
||||
}}
|
||||
</section>
|
||||
{{else if @summary.taskGroup}}
|
||||
<section
|
||||
...attributes
|
||||
data-test-task-group-recommendations
|
||||
class='recommendation-card'
|
||||
class="recommendation-card"
|
||||
{{did-insert this.cardInserted}}
|
||||
>
|
||||
|
||||
|
@ -14,11 +19,18 @@
|
|||
|
||||
<header class="overview inner-container">
|
||||
<h3 class="slug">
|
||||
<span class="job" data-test-job-name>{{@summary.taskGroup.job.name}}</span>
|
||||
<span class="group" data-test-task-group-name>{{@summary.taskGroup.name}}</span>
|
||||
<span
|
||||
class="job"
|
||||
data-test-job-name
|
||||
>{{@summary.taskGroup.job.name}}</span>
|
||||
<span
|
||||
class="group"
|
||||
data-test-task-group-name
|
||||
>{{@summary.taskGroup.name}}</span>
|
||||
</h3>
|
||||
<h4 class="namespace">
|
||||
<span class="namespace-label">Namespace:</span> <span data-test-namespace>{{@summary.jobNamespace}}</span>
|
||||
<span class="namespace-label">Namespace:</span>
|
||||
<span data-test-namespace>{{@summary.jobNamespace}}</span>
|
||||
</h4>
|
||||
</header>
|
||||
|
||||
|
@ -45,10 +57,16 @@
|
|||
<th class="toggle-cell">
|
||||
<Toggle
|
||||
data-test-cpu-toggle
|
||||
@isActive={{and this.allCpuToggleActive (not this.allCpuToggleDisabled)}}
|
||||
@isActive={{and
|
||||
this.allCpuToggleActive
|
||||
(not this.allCpuToggleDisabled)
|
||||
}}
|
||||
@isDisabled={{this.allCpuToggleDisabled}}
|
||||
@onToggle={{action this.toggleAllRecommendationsForResource 'CPU'}}
|
||||
title='Toggle CPU recommendations for all tasks'
|
||||
@onToggle={{action
|
||||
this.toggleAllRecommendationsForResource
|
||||
"CPU"
|
||||
}}
|
||||
title="Toggle CPU recommendations for all tasks"
|
||||
>
|
||||
<div class="label-wrapper">CPU</div>
|
||||
</Toggle>
|
||||
|
@ -56,10 +74,16 @@
|
|||
<th class="toggle-cell">
|
||||
<Toggle
|
||||
data-test-memory-toggle
|
||||
@isActive={{and this.allMemoryToggleActive (not this.allMemoryToggleDisabled)}}
|
||||
@isActive={{and
|
||||
this.allMemoryToggleActive
|
||||
(not this.allMemoryToggleDisabled)
|
||||
}}
|
||||
@isDisabled={{this.allMemoryToggleDisabled}}
|
||||
@onToggle={{action this.toggleAllRecommendationsForResource 'MemoryMB'}}
|
||||
title='Toggle memory recommendations for all tasks'
|
||||
@onToggle={{action
|
||||
this.toggleAllRecommendationsForResource
|
||||
"MemoryMB"
|
||||
}}
|
||||
title="Toggle memory recommendations for all tasks"
|
||||
>
|
||||
<div class="label-wrapper">Mem</div>
|
||||
</Toggle>
|
||||
|
@ -87,13 +111,27 @@
|
|||
</section>
|
||||
|
||||
<section class="actions overview inner-container">
|
||||
<button class='button is-primary' type='button' disabled={{this.cannotAccept}} data-test-accept {{on "click" this.accept}}>Accept</button>
|
||||
<button class='button is-light' type='button' data-test-dismiss {{on "click" this.dismiss}}>Dismiss</button>
|
||||
<button
|
||||
class="button is-primary"
|
||||
type="button"
|
||||
disabled={{this.cannotAccept}}
|
||||
data-test-accept
|
||||
{{on "click" this.accept}}
|
||||
>Accept</button>
|
||||
<button
|
||||
class="button is-light"
|
||||
type="button"
|
||||
data-test-dismiss
|
||||
{{on "click" this.dismiss}}
|
||||
>Dismiss</button>
|
||||
</section>
|
||||
|
||||
<section class="active-task-group" data-test-active-task>
|
||||
<section class="top active-task inner-container">
|
||||
<CopyButton data-test-copy-button @clipboardText={{this.copyButtonLink}}>
|
||||
<CopyButton
|
||||
data-test-copy-button
|
||||
@clipboardText={{this.copyButtonLink}}
|
||||
>
|
||||
{{@summary.taskGroup.job.name}}
|
||||
/
|
||||
{{@summary.taskGroup.name}}
|
||||
|
@ -104,7 +142,8 @@
|
|||
data-test-accordion-toggle
|
||||
class="button is-light is-compact pull-right accordion-toggle"
|
||||
{{on "click" @onCollapse}}
|
||||
type="button">
|
||||
type="button"
|
||||
>
|
||||
Collapse
|
||||
</button>
|
||||
{{/if}}
|
||||
|
@ -131,7 +170,10 @@
|
|||
@currentValue={{recommendation.currentValue}}
|
||||
@recommendedValue={{recommendation.value}}
|
||||
@stats={{recommendation.stats}}
|
||||
@disabled={{includes recommendation @summary.excludedRecommendations}}
|
||||
@disabled={{includes
|
||||
recommendation
|
||||
@summary.excludedRecommendations
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
{{/each}}
|
||||
|
|
|
@ -194,11 +194,12 @@ export default class DasRecommendationCardComponent extends Component {
|
|||
}
|
||||
|
||||
@action
|
||||
dismiss() {
|
||||
async dismiss() {
|
||||
this.storeCardHeight();
|
||||
this.args.summary.excludedRecommendations.pushObjects(
|
||||
this.args.summary.recommendations
|
||||
);
|
||||
const recommendations = await this.args.summary.recommendations;
|
||||
|
||||
this.args.summary.excludedRecommendations.pushObjects(recommendations);
|
||||
|
||||
this.args.summary
|
||||
.save()
|
||||
.then(
|
||||
|
|
|
@ -31,7 +31,13 @@
|
|||
</text>
|
||||
|
||||
{{#if this.center}}
|
||||
<line class="center" x1={{this.center.x1}} y1={{this.center.y1}} x2={{this.center.x2}} y2={{this.center.y2}} />
|
||||
<line
|
||||
class="center"
|
||||
x1={{this.center.x1}}
|
||||
y1={{this.center.y1}}
|
||||
x2={{this.center.x2}}
|
||||
y2={{this.center.y2}}
|
||||
></line>
|
||||
{{/if}}
|
||||
|
||||
{{#each this.statsShapes as |shapes|}}
|
||||
|
@ -55,7 +61,7 @@
|
|||
height={{shapes.rect.height}}
|
||||
{{on "mouseenter" (fn this.setActiveLegendRow shapes.text.label)}}
|
||||
{{on "mouseleave" this.unsetActiveLegendRow}}
|
||||
/>
|
||||
></rect>
|
||||
|
||||
<line
|
||||
class="stat {{shapes.class}}"
|
||||
|
@ -65,7 +71,7 @@
|
|||
y2={{shapes.line.y2}}
|
||||
{{on "mouseenter" (fn this.setActiveLegendRow shapes.text.label)}}
|
||||
{{on "mouseleave" this.unsetActiveLegendRow}}
|
||||
/>
|
||||
></line>
|
||||
{{/each}}
|
||||
|
||||
{{#unless @disabled}}
|
||||
|
@ -77,24 +83,24 @@
|
|||
y={{this.deltaRect.y}}
|
||||
width={{this.deltaRect.width}}
|
||||
height={{this.deltaRect.height}}
|
||||
/>
|
||||
></rect>
|
||||
|
||||
<polygon
|
||||
class="delta"
|
||||
style={{this.deltaTriangle.style}}
|
||||
points={{this.deltaTriangle.points}}
|
||||
/>
|
||||
></polygon>
|
||||
|
||||
<line
|
||||
class="changes delta"
|
||||
style={{this.deltaLines.delta.style}}
|
||||
x1=0
|
||||
x1={{0}}
|
||||
y1={{this.edgeTickY1}}
|
||||
x2=0
|
||||
x2={{0}}
|
||||
y2={{this.edgeTickY2}}
|
||||
{{on "mouseenter" (fn this.setActiveLegendRow "New")}}
|
||||
{{on "mouseleave" this.unsetActiveLegendRow}}
|
||||
/>
|
||||
></line>
|
||||
|
||||
<line
|
||||
class="changes"
|
||||
|
@ -104,7 +110,7 @@
|
|||
y2={{this.edgeTickY2}}
|
||||
{{on "mouseenter" (fn this.setActiveLegendRow "Current")}}
|
||||
{{on "mouseleave" this.unsetActiveLegendRow}}
|
||||
/>
|
||||
></line>
|
||||
|
||||
<text
|
||||
class="changes"
|
||||
|
@ -138,10 +144,19 @@
|
|||
{{/if}}
|
||||
{{/unless}}
|
||||
|
||||
<line class="zero" x1={{this.gutterWidthLeft}} y1={{this.edgeTickY1}} x2={{this.gutterWidthLeft}} y2={{this.edgeTickY2}} />
|
||||
<line
|
||||
class="zero"
|
||||
x1={{this.gutterWidthLeft}}
|
||||
y1={{this.edgeTickY1}}
|
||||
x2={{this.gutterWidthLeft}}
|
||||
y2={{this.edgeTickY2}}
|
||||
></line>
|
||||
</svg>
|
||||
|
||||
<div class="chart-tooltip {{if this.showLegend "active" "inactive"}}" style={{this.tooltipStyle}}>
|
||||
<div
|
||||
class="chart-tooltip {{if this.showLegend 'active' 'inactive'}}"
|
||||
style={{this.tooltipStyle}}
|
||||
>
|
||||
<ol>
|
||||
{{#each this.sortedStats as |stat|}}
|
||||
<li class={{if (eq this.activeLegendRow stat.label) "active"}}>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{#if @summary.taskGroup.allocations.length}}
|
||||
{{!-- Prevent storing aggregate diffs until allocation count is known --}}
|
||||
{{! Prevent storing aggregate diffs until allocation count is known }}
|
||||
<tr
|
||||
class="recommendation-row"
|
||||
...attributes
|
||||
|
@ -8,12 +8,13 @@
|
|||
>
|
||||
<td>
|
||||
<div data-test-slug>
|
||||
<span class='job'>{{@summary.taskGroup.job.name}}</span>
|
||||
<span class="job">{{@summary.taskGroup.job.name}}</span>
|
||||
/
|
||||
<span class='task-group'>{{@summary.taskGroup.name}}</span>
|
||||
<span class="task-group">{{@summary.taskGroup.name}}</span>
|
||||
</div>
|
||||
<div class='namespace'>
|
||||
Namespace: <span data-test-namespace>{{@summary.jobNamespace}}</span>
|
||||
<div class="namespace">
|
||||
Namespace:
|
||||
<span data-test-namespace>{{@summary.jobNamespace}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td data-test-date>
|
||||
|
@ -25,13 +26,13 @@
|
|||
<td data-test-cpu>
|
||||
{{#if this.cpu.delta}}
|
||||
{{this.cpu.signedDiff}}
|
||||
<span class='percent'>{{this.cpu.percentDiff}}</span>
|
||||
<span class="percent">{{this.cpu.percentDiff}}</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-memory>
|
||||
{{#if this.memory.delta}}
|
||||
{{this.memory.signedDiff}}
|
||||
<span class='percent'>{{this.memory.percentDiff}}</span>
|
||||
<span class="percent">{{this.memory.percentDiff}}</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-aggregate-cpu>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import Component from '@ember/component';
|
||||
import { computed, set } from '@ember/object';
|
||||
import { observes } from '@ember-decorators/object';
|
||||
import { run } from '@ember/runloop';
|
||||
import { run, once } from '@ember/runloop';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import { guidFor } from '@ember/object/internals';
|
||||
import { copy } from 'ember-copy';
|
||||
|
@ -190,6 +190,6 @@ export default class DistributionBar extends Component.extend(WindowResizable) {
|
|||
/* eslint-enable */
|
||||
|
||||
windowResizeHandler() {
|
||||
run.once(this, this.renderChart);
|
||||
once(this, this.renderChart);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { run } from '@ember/runloop';
|
||||
import { next } from '@ember/runloop';
|
||||
import { action } from '@ember/object';
|
||||
import { minIndex, max } from 'd3-array';
|
||||
|
||||
|
@ -14,7 +14,7 @@ export default class FlexMasonry extends Component {
|
|||
|
||||
@action
|
||||
reflow() {
|
||||
run.next(() => {
|
||||
next(() => {
|
||||
// There's nothing to do if there is no element
|
||||
if (!this.element) return;
|
||||
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
import { isEmpty } from '@ember/utils';
|
||||
import { classNames, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@tagName('nav')
|
||||
@classNames('breadcrumb')
|
||||
@attributeBindings('data-test-fs-breadcrumbs')
|
||||
export default class Breadcrumbs extends Component {
|
||||
'data-test-fs-breadcrumbs' = true;
|
||||
|
||||
|
|
|
@ -6,11 +6,12 @@ import { equal, gt } from '@ember/object/computed';
|
|||
import RSVP from 'rsvp';
|
||||
import Log from 'nomad-ui/utils/classes/log';
|
||||
import timeout from 'nomad-ui/utils/timeout';
|
||||
import { classNames } from '@ember-decorators/component';
|
||||
import { classNames, attributeBindings } from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@classNames('boxed-section', 'task-log')
|
||||
@attributeBindings('data-test-file-viewer')
|
||||
export default class File extends Component {
|
||||
@service token;
|
||||
@service system;
|
||||
|
|
|
@ -2,7 +2,7 @@ import Component from '@ember/component';
|
|||
import { computed } from '@ember/object';
|
||||
import { assert } from '@ember/debug';
|
||||
import { guidFor } from '@ember/object/internals';
|
||||
import { run } from '@ember/runloop';
|
||||
import { once } from '@ember/runloop';
|
||||
import d3Shape from 'd3-shape';
|
||||
import WindowResizable from 'nomad-ui/mixins/window-resizable';
|
||||
import { classNames } from '@ember-decorators/component';
|
||||
|
@ -88,6 +88,6 @@ export default class GaugeChart extends Component.extend(WindowResizable) {
|
|||
}
|
||||
|
||||
windowResizeHandler() {
|
||||
run.once(this, this.updateDimensions);
|
||||
once(this, this.updateDimensions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import Component from '@ember/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { attributeBindings } from '@ember-decorators/component';
|
||||
|
||||
@classic
|
||||
@attributeBindings('data-test-global-header')
|
||||
export default class GlobalHeader extends Component {
|
||||
@service config;
|
||||
@service system;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import Component from '@ember/component';
|
||||
import { classNames } from '@ember-decorators/component';
|
||||
import { classNames, attributeBindings } from '@ember-decorators/component';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { action, set } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { debounce, run } from '@ember/runloop';
|
||||
import { debounce, next } from '@ember/runloop';
|
||||
|
||||
const SLASH_KEY = '/';
|
||||
const MAXIMUM_RESULTS = 10;
|
||||
|
||||
@classNames('global-search-container')
|
||||
@attributeBindings('data-test-search-parent')
|
||||
export default class GlobalSearchControl extends Component {
|
||||
@service router;
|
||||
@service token;
|
||||
|
@ -223,7 +224,7 @@ export default class GlobalSearchControl extends Component {
|
|||
@action
|
||||
onCloseEvent(select, event) {
|
||||
if (event.key === 'Escape') {
|
||||
run.next(() => {
|
||||
next(() => {
|
||||
this.element.querySelector('.ember-power-select-trigger').blur();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
import Component from '@ember/component';
|
||||
import { computed } from '@ember/object';
|
||||
import { classNames, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@tagName('figure')
|
||||
@classNames('image-file')
|
||||
@attributeBindings('data-test-image-file')
|
||||
export default class ImageFile extends Component {
|
||||
'data-test-image-file' = true;
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import { computed } from '@ember/object';
|
||||
import DistributionBar from './distribution-bar';
|
||||
import { attributeBindings } from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@attributeBindings('data-test-job-client-status-bar')
|
||||
export default class JobClientStatusBar extends DistributionBar {
|
||||
layoutName = 'components/distribution-bar';
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import Component from '@ember/component';
|
||||
import { assert } from '@ember/debug';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import { computed, action } from '@ember/object';
|
||||
import { task } from 'ember-concurrency';
|
||||
import messageFromAdapterError from 'nomad-ui/utils/message-from-adapter-error';
|
||||
import localStorageProperty from 'nomad-ui/utils/properties/local-storage';
|
||||
import { attributeBindings } from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@attributeBindings('data-test-job-editor')
|
||||
export default class JobEditor extends Component {
|
||||
@service store;
|
||||
@service config;
|
||||
|
@ -33,6 +35,12 @@ export default class JobEditor extends Component {
|
|||
this.set('_context', value);
|
||||
}
|
||||
|
||||
@action updateCode(value) {
|
||||
if (!this.job.isDestroying && !this.job.isDestroyed) {
|
||||
this.job.set('_newDefinition', value);
|
||||
}
|
||||
}
|
||||
|
||||
_context = null;
|
||||
parseError = null;
|
||||
planError = null;
|
||||
|
|
|
@ -2,12 +2,17 @@ import Component from '@ember/component';
|
|||
import { action } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { lazyClick } from '../helpers/lazy-click';
|
||||
import { classNames, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@tagName('tr')
|
||||
@classNames('job-row', 'is-interactive')
|
||||
@attributeBindings('data-test-job-row')
|
||||
export default class JobRow extends Component {
|
||||
@service router;
|
||||
@service store;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { run } from '@ember/runloop';
|
||||
import { schedule, next } from '@ember/runloop';
|
||||
import d3 from 'd3-selection';
|
||||
import d3Scale from 'd3-scale';
|
||||
import d3Axis from 'd3-axis';
|
||||
|
@ -235,7 +235,7 @@ export default class LineChart extends Component {
|
|||
const mouseX = d3.pointer(ev, this)[0];
|
||||
chart.latestMouseX = mouseX;
|
||||
updateActiveDatum(mouseX);
|
||||
run.schedule('afterRender', chart, () => (chart.isActive = true));
|
||||
schedule('afterRender', chart, () => (chart.isActive = true));
|
||||
});
|
||||
|
||||
canvas.on('mousemove', function (ev) {
|
||||
|
@ -245,7 +245,7 @@ export default class LineChart extends Component {
|
|||
});
|
||||
|
||||
canvas.on('mouseleave', () => {
|
||||
run.schedule('afterRender', this, () => (this.isActive = false));
|
||||
schedule('afterRender', this, () => (this.isActive = false));
|
||||
this.activeDatum = null;
|
||||
this.activeData = [];
|
||||
});
|
||||
|
@ -338,7 +338,7 @@ export default class LineChart extends Component {
|
|||
// svg elements
|
||||
this.mountD3Elements();
|
||||
|
||||
run.next(() => {
|
||||
next(() => {
|
||||
// Since each axis depends on the dimension of the other
|
||||
// axis, the axes themselves are recomputed and need to
|
||||
// be re-rendered.
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import Component from '@ember/component';
|
||||
import { classNames, classNameBindings } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
classNameBindings,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@classNames('accordion-head')
|
||||
@classNameBindings('isOpen::is-light', 'isExpandable::is-inactive')
|
||||
@attributeBindings('data-test-accordion-head')
|
||||
export default class AccordionHead extends Component {
|
||||
'data-test-accordion-head' = true;
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ export default class ListTable extends Component {
|
|||
@overridable(() => []) source;
|
||||
|
||||
// Plan for a future with metadata (e.g., isSelected)
|
||||
@computed('source.[]')
|
||||
@computed('source.{[],isFulfilled}')
|
||||
get decoratedSource() {
|
||||
return (this.source || []).map((row) => ({
|
||||
model: row,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Component from '@ember/component';
|
||||
import { action } from '@ember/object';
|
||||
import { computed as overridable } from 'ember-overridable-computed';
|
||||
import { run } from '@ember/runloop';
|
||||
import { scheduleOnce } from '@ember/runloop';
|
||||
import { classNames } from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
|
@ -33,7 +33,7 @@ export default class MultiSelectDropdown extends Component {
|
|||
super.didReceiveAttrs();
|
||||
const dropdown = this.dropdown;
|
||||
if (this.isOpen && dropdown) {
|
||||
run.scheduleOnce('afterRender', this, this.repositionDropdown);
|
||||
scheduleOnce('afterRender', this, this.repositionDropdown);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import AllocationRow from 'nomad-ui/components/allocation-row';
|
||||
import classic from 'ember-classic-decorator';
|
||||
import { attributeBindings } from '@ember-decorators/component';
|
||||
|
||||
@classic
|
||||
@attributeBindings(
|
||||
'data-test-controller-allocation',
|
||||
'data-test-node-allocation'
|
||||
)
|
||||
export default class PluginAllocationRow extends AllocationRow {
|
||||
pluginAllocation = null;
|
||||
allocation = null;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Component from '@ember/component';
|
||||
import { action } from '@ember/object';
|
||||
import { run } from '@ember/runloop';
|
||||
import { scheduleOnce } from '@ember/runloop';
|
||||
import { classNames } from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
|
@ -35,7 +35,7 @@ export default class PopoverMenu extends Component {
|
|||
super.didReceiveAttrs();
|
||||
const dropdown = this.dropdown;
|
||||
if (this.isOpen && dropdown) {
|
||||
run.scheduleOnce('afterRender', this, this.repositionDropdown);
|
||||
scheduleOnce('afterRender', this, this.repositionDropdown);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import Component from '@ember/component';
|
||||
import { run } from '@ember/runloop';
|
||||
import { scheduleOnce, once } from '@ember/runloop';
|
||||
import { task } from 'ember-concurrency';
|
||||
import WindowResizable from 'nomad-ui/mixins/window-resizable';
|
||||
import { classNames, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
const A_KEY = 65;
|
||||
|
@ -10,6 +14,7 @@ const A_KEY = 65;
|
|||
@classic
|
||||
@tagName('pre')
|
||||
@classNames('cli-window')
|
||||
@attributeBindings('data-test-log-cli')
|
||||
export default class StreamingFile extends Component.extend(WindowResizable) {
|
||||
'data-test-log-cli' = true;
|
||||
|
||||
|
@ -27,7 +32,7 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
|
|||
return;
|
||||
}
|
||||
|
||||
run.scheduleOnce('actions', this, this.performTask);
|
||||
scheduleOnce('actions', this, this.performTask);
|
||||
}
|
||||
|
||||
performTask() {
|
||||
|
@ -100,7 +105,7 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
|
|||
}
|
||||
|
||||
windowResizeHandler() {
|
||||
run.once(this, this.fillAvailableHeight);
|
||||
once(this, this.fillAvailableHeight);
|
||||
}
|
||||
|
||||
fillAvailableHeight() {
|
||||
|
@ -115,7 +120,7 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
|
|||
|
||||
@task(function* () {
|
||||
yield this.get('logger.gotoHead').perform();
|
||||
run.scheduleOnce('afterRender', this, this.scrollToTop);
|
||||
scheduleOnce('afterRender', this, this.scrollToTop);
|
||||
})
|
||||
head;
|
||||
|
||||
|
@ -144,7 +149,7 @@ export default class StreamingFile extends Component.extend(WindowResizable) {
|
|||
stream;
|
||||
|
||||
scheduleScrollSynchronization() {
|
||||
run.scheduleOnce('afterRender', this, this.synchronizeScrollPosition);
|
||||
scheduleOnce('afterRender', this, this.synchronizeScrollPosition);
|
||||
}
|
||||
|
||||
willDestroy() {
|
||||
|
|
|
@ -5,12 +5,17 @@ import { computed } from '@ember/object';
|
|||
import { alias } from '@ember/object/computed';
|
||||
import { task, timeout } from 'ember-concurrency';
|
||||
import { lazyClick } from '../helpers/lazy-click';
|
||||
import { classNames, tagName } from '@ember-decorators/component';
|
||||
import {
|
||||
classNames,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
@tagName('tr')
|
||||
@classNames('task-row', 'is-interactive')
|
||||
@attributeBindings('data-test-task-row')
|
||||
export default class TaskRow extends Component {
|
||||
@service store;
|
||||
@service token;
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
classNames,
|
||||
classNameBindings,
|
||||
tagName,
|
||||
attributeBindings,
|
||||
} from '@ember-decorators/component';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
|
@ -10,6 +11,7 @@ import classic from 'ember-classic-decorator';
|
|||
@tagName('label')
|
||||
@classNames('toggle')
|
||||
@classNameBindings('isDisabled:is-disabled', 'isActive:is-active')
|
||||
@attributeBindings('data-test-label')
|
||||
export default class Toggle extends Component {
|
||||
'data-test-label' = true;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import Component from '@glimmer/component';
|
|||
import { tracked } from '@glimmer/tracking';
|
||||
import { action, set } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { run } from '@ember/runloop';
|
||||
import { next } from '@ember/runloop';
|
||||
import { scaleLinear } from 'd3-scale';
|
||||
import { extent, deviation, mean } from 'd3-array';
|
||||
import { line, curveBasis } from 'd3-shape';
|
||||
|
@ -268,7 +268,7 @@ export default class TopoViz extends Component {
|
|||
@action
|
||||
computedActiveEdges() {
|
||||
// Wait a render cycle
|
||||
run.next(() => {
|
||||
next(() => {
|
||||
const path = line().curve(curveBasis);
|
||||
// 1. Get the active element
|
||||
const allocation = this.activeAllocation.allocation;
|
||||
|
|
|
@ -2,6 +2,7 @@ import { action } from '@ember/object';
|
|||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { task } from 'ember-concurrency';
|
||||
import { schedule } from '@ember/runloop';
|
||||
|
||||
const noOp = () => undefined;
|
||||
|
||||
|
@ -63,6 +64,8 @@ export default class Trigger extends Component {
|
|||
|
||||
@action
|
||||
onTrigger() {
|
||||
this.triggerTask.perform();
|
||||
schedule('actions', () => {
|
||||
this.triggerTask.perform();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import classic from 'ember-classic-decorator';
|
|||
@classic
|
||||
export default class IndexController extends Controller.extend(Sortable) {
|
||||
@service token;
|
||||
@service store;
|
||||
|
||||
queryParams = [
|
||||
{
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable ember/no-observers */
|
||||
import { inject as service } from '@ember/service';
|
||||
import Controller from '@ember/controller';
|
||||
import { run } from '@ember/runloop';
|
||||
import { next } from '@ember/runloop';
|
||||
import { observes } from '@ember-decorators/object';
|
||||
import { computed } from '@ember/object';
|
||||
import Ember from 'ember';
|
||||
|
@ -14,6 +14,7 @@ import classic from 'ember-classic-decorator';
|
|||
export default class ApplicationController extends Controller {
|
||||
@service config;
|
||||
@service system;
|
||||
@service token;
|
||||
|
||||
queryParams = [
|
||||
{
|
||||
|
@ -70,11 +71,11 @@ export default class ApplicationController extends Controller {
|
|||
@observes('error')
|
||||
throwError() {
|
||||
if (this.get('config.isDev')) {
|
||||
run.next(() => {
|
||||
next(() => {
|
||||
throw this.error;
|
||||
});
|
||||
} else if (!Ember.testing) {
|
||||
run.next(() => {
|
||||
next(() => {
|
||||
// eslint-disable-next-line
|
||||
console.warn('UNRECOVERABLE ERROR:', this.error);
|
||||
});
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
deserializedQueryParam as selection,
|
||||
} from 'nomad-ui/utils/qp-serialize';
|
||||
import classic from 'ember-classic-decorator';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
@classic
|
||||
export default class ClientsController extends Controller.extend(
|
||||
|
@ -20,6 +21,8 @@ export default class ClientsController extends Controller.extend(
|
|||
Searchable,
|
||||
WithNamespaceResetting
|
||||
) {
|
||||
@service store;
|
||||
|
||||
queryParams = [
|
||||
{
|
||||
currentPage: 'page',
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Mixin from '@ember/object/mixin';
|
||||
import { run } from '@ember/runloop';
|
||||
import { scheduleOnce } from '@ember/runloop';
|
||||
import { assert } from '@ember/debug';
|
||||
import { on } from '@ember/object/evented';
|
||||
|
||||
|
@ -13,7 +13,7 @@ export default Mixin.create({
|
|||
},
|
||||
|
||||
setupWindowResize: on('didInsertElement', function () {
|
||||
run.scheduleOnce('afterRender', this, this.addResizeListener);
|
||||
scheduleOnce('afterRender', this, this.addResizeListener);
|
||||
}),
|
||||
|
||||
addResizeListener() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { computed } from '@ember/object';
|
||||
import { reads } from '@ember/object/computed';
|
||||
import Fragment from 'ember-data-model-fragments/fragment';
|
||||
import { attr } from '@ember-data/model';
|
||||
import {
|
||||
|
@ -19,6 +19,6 @@ export default class TaskGroupScale extends Fragment {
|
|||
|
||||
@fragmentArray('scale-event') events;
|
||||
|
||||
@computed.reads('events.length')
|
||||
@reads('events.length')
|
||||
isVisible;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ export default class TaskGroup extends Fragment {
|
|||
return this.tasks.mapBy('driver').uniq();
|
||||
}
|
||||
|
||||
@computed('job.allocations.@each.taskGroup', 'name')
|
||||
@computed('job.allocations.{@each.taskGroup,isFulfilled}', 'name')
|
||||
get allocations() {
|
||||
return maybe(this.get('job.allocations')).filterBy(
|
||||
'taskGroupName',
|
||||
|
|
|
@ -2,8 +2,11 @@ import Route from '@ember/routing/route';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchAll } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class IndexRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
startWatchers(controller) {
|
||||
controller.set('watcher', this.watch.perform());
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@ import Route from '@ember/routing/route';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchQuery } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class IndexRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
startWatchers(controller) {
|
||||
controller.set('modelWatch', this.watch.perform({ type: 'csi' }));
|
||||
}
|
||||
|
|
|
@ -2,8 +2,11 @@ import Route from '@ember/routing/route';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchRecord } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class AllocationsRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
startWatchers(controller, model) {
|
||||
if (!model) return;
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ import Route from '@ember/routing/route';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchRecord } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class IndexRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
startWatchers(controller, model) {
|
||||
if (!model) return;
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ import Route from '@ember/routing/route';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchRelationship } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class AllocationsRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
const job = this.modelFor('jobs.job');
|
||||
return job && job.get('allocations').then(() => job);
|
||||
|
|
|
@ -10,6 +10,7 @@ import { collect } from '@ember/object/computed';
|
|||
|
||||
export default class ClientsRoute extends Route.extend(WithWatchers) {
|
||||
@service can;
|
||||
@service store;
|
||||
|
||||
beforeModel() {
|
||||
if (this.can.cannot('read client')) {
|
||||
|
|
|
@ -3,8 +3,11 @@ import RSVP from 'rsvp';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchRelationship } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class DeploymentsRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
const job = this.modelFor('jobs.job');
|
||||
return (
|
||||
|
|
|
@ -2,8 +2,11 @@ import Route from '@ember/routing/route';
|
|||
import { collect } from '@ember/object/computed';
|
||||
import { watchRelationship } from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class EvaluationsRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
const job = this.modelFor('jobs.job');
|
||||
return job && job.get('evaluations').then(() => job);
|
||||
|
|
|
@ -11,6 +11,7 @@ import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
|||
|
||||
export default class IndexRoute extends Route.extend(WithWatchers) {
|
||||
@service can;
|
||||
@service store;
|
||||
|
||||
async model() {
|
||||
return this.modelFor('jobs.job');
|
||||
|
|
|
@ -8,8 +8,11 @@ import {
|
|||
} from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import notifyError from 'nomad-ui/utils/notify-error';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class TaskGroupRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
model({ name }) {
|
||||
const job = this.modelFor('jobs.job');
|
||||
|
||||
|
|
|
@ -5,8 +5,11 @@ import {
|
|||
watchRelationship,
|
||||
} from 'nomad-ui/utils/properties/watch';
|
||||
import WithWatchers from 'nomad-ui/mixins/with-watchers';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class VersionsRoute extends Route.extend(WithWatchers) {
|
||||
@service store;
|
||||
|
||||
model() {
|
||||
const job = this.modelFor('jobs.job');
|
||||
return job && job.get('versions').then(() => job);
|
||||
|
|
|
@ -8,6 +8,7 @@ import RSVP from 'rsvp';
|
|||
@classic
|
||||
export default class OptimizeRoute extends Route {
|
||||
@service can;
|
||||
@service store;
|
||||
|
||||
beforeModel() {
|
||||
if (this.can.cannot('accept recommendation')) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import AdapterError from '@ember-data/adapter/error';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class AgentSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
datacenter: 'dc',
|
||||
|
|
|
@ -6,7 +6,9 @@ import JSONSerializer from '@ember-data/serializer/json';
|
|||
import { pluralize, singularize } from 'ember-inflector';
|
||||
import removeRecord from '../utils/remove-record';
|
||||
import { assign } from '@ember/polyfills';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class Application extends JSONSerializer {
|
||||
primaryKey = 'ID';
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class DrainStrategy extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
// TODO API: finishedAt is always marshaled as a date even when unset.
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class Fragment extends ApplicationSerializer {}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import { get } from '@ember/object';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobPlan extends ApplicationSerializer {
|
||||
mapToArray = ['FailedTGAllocs'];
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobScale extends ApplicationSerializer {
|
||||
mapToArray = [{ beforeName: 'TaskGroups', afterName: 'TaskGroupScales' }];
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { get } from '@ember/object';
|
||||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobSummary extends ApplicationSerializer {
|
||||
normalize(modelClass, hash) {
|
||||
hash.PlainJobId = hash.JobID;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { assign } from '@ember/polyfills';
|
||||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobVersionSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
number: 'Version',
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { assign } from '@ember/polyfills';
|
||||
import ApplicationSerializer from './application';
|
||||
import queryString from 'query-string';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class JobSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
parameterized: 'ParameterizedJob',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class Namespace extends ApplicationSerializer {
|
||||
primaryKey = 'Name';
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import isIp from 'is-ip';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class NetworkSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
cidr: 'CIDR',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class NodeEventSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
time: 'Timestamp',
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
// Convert a map[string]interface{} into an array of objects
|
||||
// where the key becomes a property at propKey.
|
||||
|
@ -14,6 +15,7 @@ const unmap = (hash, propKey) =>
|
|||
return record;
|
||||
});
|
||||
|
||||
@classic
|
||||
export default class Plugin extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
hash.PlainId = hash.ID;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class Policy extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
hash.ID = hash.Name;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import isIp from 'is-ip';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class PortSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
hostIp: 'HostIP',
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import ApplicationSerializer from './application';
|
||||
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class RescheduleEvent extends ApplicationSerializer {
|
||||
separateNanos = ['Time'];
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class ResourcesSerializer extends ApplicationSerializer {
|
||||
arrayNullOverrides = ['Ports', 'Networks'];
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class ScaleEventSerializer extends ApplicationSerializer {
|
||||
separateNanos = ['Time'];
|
||||
objectNullOverrides = ['Meta'];
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class ServiceSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
connect: 'Connect',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class StructuredAttributes extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
return super.normalize(typeHash, { Raw: hash });
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TaskEventSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
message: 'DisplayMessage',
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TaskGroupDeploymentSummary extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
hash.PlacedCanaryAllocations = hash.PlacedCanaries || [];
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TaskGroupScaleSerializer extends ApplicationSerializer {
|
||||
arrayNullOverrides = ['Events'];
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { copy } from 'ember-copy';
|
||||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TaskGroup extends ApplicationSerializer {
|
||||
arrayNullOverrides = ['Services'];
|
||||
mapToArray = ['Volumes'];
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TaskState extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
// TODO API: finishedAt is always marshaled as a date even when unset.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class Task extends ApplicationSerializer {
|
||||
normalize(typeHash, hash) {
|
||||
// Lift the reserved resource numbers out of the Resources object
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { copy } from 'ember-copy';
|
||||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class TokenSerializer extends ApplicationSerializer {
|
||||
primaryKey = 'AccessorID';
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { set, get } from '@ember/object';
|
||||
import ApplicationSerializer from './application';
|
||||
import classic from 'ember-classic-decorator';
|
||||
|
||||
@classic
|
||||
export default class VolumeSerializer extends ApplicationSerializer {
|
||||
attrs = {
|
||||
externalId: 'ExternalID',
|
||||
|
|
|
@ -1,14 +1,18 @@
|
|||
{{page-title (if this.system.shouldShowRegions (concat this.system.activeRegion " - ")) "Nomad" separator=" - "}}
|
||||
{{page-title
|
||||
(if this.system.shouldShowRegions (concat this.system.activeRegion " - "))
|
||||
"Nomad"
|
||||
separator=" - "
|
||||
}}
|
||||
<SvgPatterns />
|
||||
{{#unless this.error}}
|
||||
{{outlet}}
|
||||
{{else}}
|
||||
{{#if this.error}}
|
||||
<div class="error-container">
|
||||
<div data-test-error class="error-message">
|
||||
{{#if this.isNoLeader}}
|
||||
<h1 data-test-error-title class="title is-spaced">No Cluster Leader</h1>
|
||||
<p data-test-error-message class="subtitle">
|
||||
The cluster has no leader. <a href="https://www.nomadproject.io/guides/outage.html"> Read about Outage Recovery.</a>
|
||||
The cluster has no leader.
|
||||
<a href="https://www.nomadproject.io/guides/outage.html">
|
||||
Read about Outage Recovery.</a>
|
||||
</p>
|
||||
{{else if this.isOTTExchange}}
|
||||
<h1 data-test-error-title class="title is-spaced">Token Exchange Error</h1>
|
||||
|
@ -17,16 +21,24 @@
|
|||
</p>
|
||||
{{else if this.is500}}
|
||||
<h1 data-test-error-title class="title is-spaced">Server Error</h1>
|
||||
<p data-test-error-message class="subtitle">A server error prevented data from being sent to the client.</p>
|
||||
<p data-test-error-message class="subtitle">A server error prevented
|
||||
data from being sent to the client.</p>
|
||||
{{else if this.is404}}
|
||||
<h1 data-test-error-title class="title is-spaced">Not Found</h1>
|
||||
<p data-test-error-message class="subtitle">What you're looking for couldn't be found. It either doesn't exist or you are not authorized to see it.</p>
|
||||
<p data-test-error-message class="subtitle">What you're looking for
|
||||
couldn't be found. It either doesn't exist or you are not authorized
|
||||
to see it.</p>
|
||||
{{else if this.is403}}
|
||||
<h1 data-test-error-title class="title is-spaced">Not Authorized</h1>
|
||||
{{#if this.token.secret}}
|
||||
<p data-test-error-message class="subtitle">Your <LinkTo @route="settings.tokens" data-test-error-acl-link>ACL token</LinkTo> does not provide the required permissions. Contact your administrator if this is an error.</p>
|
||||
<p data-test-error-message class="subtitle">Your
|
||||
<LinkTo @route="settings.tokens" data-test-error-acl-link>ACL token</LinkTo>
|
||||
does not provide the required permissions. Contact your
|
||||
administrator if this is an error.</p>
|
||||
{{else}}
|
||||
<p data-test-error-message class="subtitle">Provide an <LinkTo @route="settings.tokens" data-test-error-acl-link>ACL token</LinkTo> with requisite permissions to view this.</p>
|
||||
<p data-test-error-message class="subtitle">Provide an
|
||||
<LinkTo @route="settings.tokens" data-test-error-acl-link>ACL token</LinkTo>
|
||||
with requisite permissions to view this.</p>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<h1 data-test-error-title class="title is-spaced">Error</h1>
|
||||
|
@ -37,8 +49,15 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
<div class="error-links">
|
||||
<LinkTo @route="jobs" data-test-error-jobs-link class="button is-white">Go to Jobs</LinkTo>
|
||||
<LinkTo @route="clients" data-test-error-clients-link class="button is-white">Go to Clients</LinkTo>
|
||||
<LinkTo @route="jobs" data-test-error-jobs-link class="button is-white">Go
|
||||
to Jobs</LinkTo>
|
||||
<LinkTo
|
||||
@route="clients"
|
||||
data-test-error-clients-link
|
||||
class="button is-white"
|
||||
>Go to Clients</LinkTo>
|
||||
</div>
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{else}}
|
||||
{{outlet}}
|
||||
{{/if}}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
<SearchBox
|
||||
@searchTerm={{mut this.searchTerm}}
|
||||
@onChange={{action this.resetPagination}}
|
||||
@placeholder="Search clients..." />
|
||||
@placeholder="Search clients..."
|
||||
/>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="toolbar-item is-right-aligned is-mobile-full-width">
|
||||
|
@ -19,31 +20,36 @@
|
|||
@label="Class"
|
||||
@options={{this.optionsClass}}
|
||||
@selection={{this.selectionClass}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpClass"}} />
|
||||
@onSelect={{action this.setFacetQueryParam "qpClass"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-state-facet
|
||||
@label="State"
|
||||
@options={{this.optionsState}}
|
||||
@selection={{this.selectionState}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpState"}} />
|
||||
@onSelect={{action this.setFacetQueryParam "qpState"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-datacenter-facet
|
||||
@label="Datacenter"
|
||||
@options={{this.optionsDatacenter}}
|
||||
@selection={{this.selectionDatacenter}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpDatacenter"}} />
|
||||
@onSelect={{action this.setFacetQueryParam "qpDatacenter"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-version-facet
|
||||
@label="Version"
|
||||
@options={{this.optionsVersion}}
|
||||
@selection={{this.selectionVersion}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpVersion"}} />
|
||||
@onSelect={{action this.setFacetQueryParam "qpVersion"}}
|
||||
/>
|
||||
<MultiSelectDropdown
|
||||
data-test-volume-facet
|
||||
@label="Volume"
|
||||
@options={{this.optionsVolume}}
|
||||
@selection={{this.selectionVolume}}
|
||||
@onSelect={{action this.setFacetQueryParam "qpVolume"}} />
|
||||
@onSelect={{action this.setFacetQueryParam "qpVolume"}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -51,16 +57,23 @@
|
|||
<ListPagination
|
||||
@source={{this.sortedNodes}}
|
||||
@size={{this.pageSize}}
|
||||
@page={{this.currentPage}} as |p|>
|
||||
@page={{this.currentPage}}
|
||||
as |p|
|
||||
>
|
||||
<ListTable
|
||||
@source={{p.list}}
|
||||
@sortProperty={{this.sortProperty}}
|
||||
@sortDescending={{this.sortDescending}}
|
||||
@class="with-foot" as |t|>
|
||||
@class="with-foot"
|
||||
as |t|
|
||||
>
|
||||
<t.head>
|
||||
<th class="is-narrow"></th>
|
||||
<t.sort-by @prop="id">ID</t.sort-by>
|
||||
<t.sort-by @class="is-200px is-truncatable" @prop="name">Name</t.sort-by>
|
||||
<t.sort-by
|
||||
@class="is-200px is-truncatable"
|
||||
@prop="name"
|
||||
>Name</t.sort-by>
|
||||
<t.sort-by @prop="compositeStatus">State</t.sort-by>
|
||||
<th class="is-200px is-truncatable">Address</th>
|
||||
<t.sort-by @prop="datacenter">Datacenter</t.sort-by>
|
||||
|
@ -69,16 +82,24 @@
|
|||
<th># Allocs</th>
|
||||
</t.head>
|
||||
<t.body as |row|>
|
||||
<ClientNodeRow data-test-client-node-row @node={{row.model}} @onClick={{action "gotoNode" row.model}} />
|
||||
<ClientNodeRow
|
||||
data-test-client-node-row
|
||||
@node={{row.model}}
|
||||
@onClick={{action "gotoNode" row.model}}
|
||||
/>
|
||||
</t.body>
|
||||
</ListTable>
|
||||
<div class="table-foot">
|
||||
<PageSizeSelect @onChange={{action this.resetPagination}} />
|
||||
<nav class="pagination" data-test-pagination>
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{this.sortedNodes.length}}
|
||||
{{p.startsAt}}–{{p.endsAt}}
|
||||
of
|
||||
{{this.sortedNodes.length}}
|
||||
</div>
|
||||
<p.prev @class="pagination-previous">{{x-icon "chevron-left"}}</p.prev>
|
||||
<p.prev @class="pagination-previous">{{x-icon
|
||||
"chevron-left"
|
||||
}}</p.prev>
|
||||
<p.next @class="pagination-next">{{x-icon "chevron-right"}}</p.next>
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
|
@ -87,18 +108,28 @@
|
|||
{{else}}
|
||||
<div class="empty-message" data-test-empty-clients-list>
|
||||
{{#if (eq this.nodes.length 0)}}
|
||||
<h3 class="empty-message-headline" data-test-empty-clients-list-headline>No Clients</h3>
|
||||
<h3
|
||||
class="empty-message-headline"
|
||||
data-test-empty-clients-list-headline
|
||||
>No Clients</h3>
|
||||
<p class="empty-message-body">
|
||||
The cluster currently has no client nodes.
|
||||
</p>
|
||||
{{else if (eq this.filteredNodes.length 0)}}
|
||||
<h3 data-test-empty-clients-list-headline class="empty-message-headline">No Matches</h3>
|
||||
<h3
|
||||
data-test-empty-clients-list-headline
|
||||
class="empty-message-headline"
|
||||
>No Matches</h3>
|
||||
<p class="empty-message-body">
|
||||
No clients match your current filter selection.
|
||||
</p>
|
||||
{{else if this.searchTerm}}
|
||||
<h3 class="empty-message-headline" data-test-empty-clients-list-headline>No Matches</h3>
|
||||
<p class="empty-message-body">No clients match the term <strong>{{this.searchTerm}}</strong></p>
|
||||
<h3
|
||||
class="empty-message-headline"
|
||||
data-test-empty-clients-list-headline
|
||||
>No Matches</h3>
|
||||
<p class="empty-message-body">No clients match the term
|
||||
<strong>{{this.searchTerm}}</strong></p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue