mirror of
https://github.com/kemko/nomad.git
synced 2026-01-07 19:05:42 +03:00
Merge pull request #4533 from hashicorp/f-ui-refactor-page-chrome
UI: Refactor page chrome
This commit is contained in:
@@ -4,6 +4,7 @@ import { computed } from '@ember/object';
|
||||
|
||||
export default Component.extend({
|
||||
system: service(),
|
||||
router: service(),
|
||||
|
||||
sortedNamespaces: computed('system.namespaces.@each.name', function() {
|
||||
const namespaces = this.get('system.namespaces').toArray() || [];
|
||||
@@ -31,5 +32,11 @@ export default Component.extend({
|
||||
});
|
||||
}),
|
||||
|
||||
onNamespaceChange() {},
|
||||
gotoJobsForNamespace(namespace) {
|
||||
if (!namespace || !namespace.get('id')) return;
|
||||
|
||||
this.get('router').transitionTo('jobs', {
|
||||
queryParams: { namespace: namespace.get('id') },
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,7 +11,6 @@ export default Component.extend({
|
||||
sortDescending: null,
|
||||
|
||||
// Provide actions that require routing
|
||||
onNamespaceChange() {},
|
||||
gotoTaskGroup() {},
|
||||
gotoJob() {},
|
||||
|
||||
|
||||
5
ui/app/components/page-layout.js
Normal file
5
ui/app/components/page-layout.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['page-layout'],
|
||||
});
|
||||
@@ -18,12 +18,6 @@ export default Controller.extend({
|
||||
// But query param defaults can't be CPs: https://github.com/emberjs/ember.js/issues/9819
|
||||
syncNamespaceService: forwardNamespace('jobNamespace', 'system.activeNamespace'),
|
||||
syncNamespaceParam: forwardNamespace('system.activeNamespace', 'jobNamespace'),
|
||||
|
||||
actions: {
|
||||
refreshRoute() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function forwardNamespace(source, destination) {
|
||||
|
||||
@@ -58,9 +58,5 @@ export default Controller.extend(Sortable, Searchable, {
|
||||
gotoJob(job) {
|
||||
this.transitionToRoute('jobs.job', job.get('plainId'));
|
||||
},
|
||||
|
||||
refresh() {
|
||||
this.send('refreshRoute');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ export default Route.extend(WithForbiddenState, {
|
||||
|
||||
model() {
|
||||
return this.get('store')
|
||||
.findAll('job')
|
||||
.findAll('job', { reload: true })
|
||||
.catch(notifyForbidden(this));
|
||||
},
|
||||
|
||||
|
||||
@@ -10,10 +10,4 @@ export default Route.extend(WithWatchers, {
|
||||
|
||||
watch: watchAll('job'),
|
||||
watchers: collect('watch'),
|
||||
|
||||
actions: {
|
||||
refreshRoute() {
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
<div class="page-layout">
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#page-layout}}
|
||||
{{outlet}}
|
||||
</div>
|
||||
{{/page-layout}}
|
||||
|
||||
@@ -1,100 +1,98 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section">
|
||||
<h1 data-test-title class="title">
|
||||
Allocation {{model.name}}
|
||||
<span class="bumper-left tag {{model.statusClass}}">{{model.clientStatus}}</span>
|
||||
<span class="tag is-hollow is-small no-text-transform">{{model.id}}</span>
|
||||
</h1>
|
||||
<section class="section">
|
||||
<h1 data-test-title class="title">
|
||||
Allocation {{model.name}}
|
||||
<span class="bumper-left tag {{model.statusClass}}">{{model.clientStatus}}</span>
|
||||
<span class="tag is-hollow is-small no-text-transform">{{model.id}}</span>
|
||||
</h1>
|
||||
|
||||
<div class="boxed-section is-small">
|
||||
<div data-test-allocation-details class="boxed-section-body inline-definitions">
|
||||
<span class="label">Allocation Details</span>
|
||||
<span class="pair job-link"><span class="term">Job</span>
|
||||
{{#link-to "jobs.job" model.job (query-params jobNamespace=model.job.namespace.id) data-test-job-link}}{{model.job.name}}{{/link-to}}
|
||||
</span>
|
||||
<span class="pair node-link"><span class="term">Client</span>
|
||||
{{#link-to "clients.client" model.node data-test-client-link}}{{model.node.shortId}}{{/link-to}}
|
||||
</span>
|
||||
<div class="boxed-section is-small">
|
||||
<div data-test-allocation-details class="boxed-section-body inline-definitions">
|
||||
<span class="label">Allocation Details</span>
|
||||
<span class="pair job-link"><span class="term">Job</span>
|
||||
{{#link-to "jobs.job" model.job (query-params jobNamespace=model.job.namespace.id) data-test-job-link}}{{model.job.name}}{{/link-to}}
|
||||
</span>
|
||||
<span class="pair node-link"><span class="term">Client</span>
|
||||
{{#link-to "clients.client" model.node data-test-client-link}}{{model.node.shortId}}{{/link-to}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Tasks
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table
|
||||
source=sortedStates
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="is-striped" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-narrow"></th>
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="state"}}State{{/t.sort-by}}
|
||||
<th>Last Event</th>
|
||||
{{#t.sort-by prop="events.lastObject.time"}}Time{{/t.sort-by}}
|
||||
<th>Addresses</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr
|
||||
data-test-task-row={{row.model.task.name}}
|
||||
onclick={{action "taskClick" row.model.allocation row.model}}
|
||||
class="is-interactive">
|
||||
<td class="is-narrow">
|
||||
{{#if (not row.model.driverStatus.healthy)}}
|
||||
<span data-test-icon="unhealthy-driver" class="tooltip text-center" aria-label="{{row.model.driver}} is unhealthy">
|
||||
{{x-icon "warning" class="is-warning"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-name>
|
||||
{{#link-to "allocations.allocation.task" row.model.allocation row.model class="is-primary"}}
|
||||
{{row.model.name}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td data-test-state>{{row.model.state}}</td>
|
||||
<td data-test-message>
|
||||
{{#if row.model.events.lastObject.message}}
|
||||
{{row.model.events.lastObject.message}}
|
||||
{{else}}
|
||||
<em>No message</em>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-time>{{moment-format row.model.events.lastObject.time "MM/DD/YY HH:mm:ss"}}</td>
|
||||
<td data-test-ports>
|
||||
<ul>
|
||||
{{#with row.model.resources.networks.firstObject as |network|}}
|
||||
{{#each network.reservedPorts as |port|}}
|
||||
<li data-test-port>
|
||||
<strong>{{port.Label}}:</strong>
|
||||
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank">{{network.ip}}:{{port.Value}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{#each network.dynamicPorts as |port|}}
|
||||
<li>
|
||||
<strong>{{port.Label}}:</strong>
|
||||
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank">{{network.ip}}:{{port.Value}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{/with}}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if model.hasRescheduleEvents}}
|
||||
<div class="boxed-section" data-test-reschedule-events>
|
||||
<div class="boxed-section-head is-hollow">
|
||||
Reschedule Events
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{reschedule-event-timeline allocation=model}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Tasks
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table
|
||||
source=sortedStates
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="is-striped" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-narrow"></th>
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="state"}}State{{/t.sort-by}}
|
||||
<th>Last Event</th>
|
||||
{{#t.sort-by prop="events.lastObject.time"}}Time{{/t.sort-by}}
|
||||
<th>Addresses</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr
|
||||
data-test-task-row={{row.model.task.name}}
|
||||
onclick={{action "taskClick" row.model.allocation row.model}}
|
||||
class="is-interactive">
|
||||
<td class="is-narrow">
|
||||
{{#if (not row.model.driverStatus.healthy)}}
|
||||
<span data-test-icon="unhealthy-driver" class="tooltip text-center" aria-label="{{row.model.driver}} is unhealthy">
|
||||
{{x-icon "warning" class="is-warning"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-name>
|
||||
{{#link-to "allocations.allocation.task" row.model.allocation row.model class="is-primary"}}
|
||||
{{row.model.name}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td data-test-state>{{row.model.state}}</td>
|
||||
<td data-test-message>
|
||||
{{#if row.model.events.lastObject.message}}
|
||||
{{row.model.events.lastObject.message}}
|
||||
{{else}}
|
||||
<em>No message</em>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td data-test-time>{{moment-format row.model.events.lastObject.time "MM/DD/YY HH:mm:ss"}}</td>
|
||||
<td data-test-ports>
|
||||
<ul>
|
||||
{{#with row.model.resources.networks.firstObject as |network|}}
|
||||
{{#each network.reservedPorts as |port|}}
|
||||
<li data-test-port>
|
||||
<strong>{{port.Label}}:</strong>
|
||||
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank">{{network.ip}}:{{port.Value}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{#each network.dynamicPorts as |port|}}
|
||||
<li>
|
||||
<strong>{{port.Label}}:</strong>
|
||||
<a href="http://{{network.ip}}:{{port.Value}}" target="_blank">{{network.ip}}:{{port.Value}}</a>
|
||||
</li>
|
||||
{{/each}}
|
||||
{{/with}}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if model.hasRescheduleEvents}}
|
||||
<div class="boxed-section" data-test-reschedule-events>
|
||||
<div class="boxed-section-head is-hollow">
|
||||
Reschedule Events
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{reschedule-event-timeline allocation=model}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
@@ -1,85 +1,83 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
{{partial "allocations/allocation/task/subnav"}}
|
||||
<section class="section">
|
||||
<h1 class="title" data-test-title>
|
||||
{{model.name}}
|
||||
<span class="bumper-left tag {{model.stateClass}}" data-test-state>{{model.state}}</span>
|
||||
</h1>
|
||||
{{partial "allocations/allocation/task/subnav"}}
|
||||
<section class="section">
|
||||
<h1 class="title" data-test-title>
|
||||
{{model.name}}
|
||||
<span class="bumper-left tag {{model.stateClass}}" data-test-state>{{model.state}}</span>
|
||||
</h1>
|
||||
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Task Details</span>
|
||||
<span class="pair" data-test-started-at>
|
||||
<span class="term">Started At</span>
|
||||
{{moment-format model.startedAt "MM/DD/YY HH:mm:ss"}}
|
||||
</span>
|
||||
{{#if model.finishedAt}}
|
||||
<span class="pair">
|
||||
<span class="term">Finished At</span>
|
||||
{{moment-format model.finishedAt "MM/DD/YY HH:mm:ss"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Task Details</span>
|
||||
<span class="pair" data-test-started-at>
|
||||
<span class="term">Started At</span>
|
||||
{{moment-format model.startedAt "MM/DD/YY HH:mm:ss"}}
|
||||
</span>
|
||||
{{#if model.finishedAt}}
|
||||
<span class="pair">
|
||||
<span class="term">Driver</span>
|
||||
{{model.task.driver}}
|
||||
<span class="term">Finished At</span>
|
||||
{{moment-format model.finishedAt "MM/DD/YY HH:mm:ss"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<span class="pair">
|
||||
<span class="term">Driver</span>
|
||||
{{model.task.driver}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if ports.length}}
|
||||
<div class="boxed-section" data-test-task-addresses>
|
||||
<div class="boxed-section-head">
|
||||
Addresses
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table source=ports as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-1">Dynamic?</th>
|
||||
<th class="is-2">Name</th>
|
||||
<th>Address</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-task-address>
|
||||
<td data-test-task-address-is-dynamic>{{if row.model.isDynamic "Yes" "No"}}</td>
|
||||
<td data-test-task-address-name>{{row.model.name}}</td>
|
||||
<td data-test-task-address-address>
|
||||
<a href="http://{{network.ip}}:{{row.model.port}}" target="_blank">
|
||||
{{network.ip}}:{{row.model.port}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="boxed-section">
|
||||
{{#if ports.length}}
|
||||
<div class="boxed-section" data-test-task-addresses>
|
||||
<div class="boxed-section-head">
|
||||
Recent Events
|
||||
Addresses
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table source=(reverse model.events) class="is-striped" as |t|}}
|
||||
{{#list-table source=ports as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-3">Time</th>
|
||||
<th class="is-1">Type</th>
|
||||
<th>Description</th>
|
||||
<th class="is-1">Dynamic?</th>
|
||||
<th class="is-2">Name</th>
|
||||
<th>Address</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-task-event>
|
||||
<td data-test-task-event-time>{{moment-format row.model.time "MM/DD/YY HH:mm:ss"}}</td>
|
||||
<td data-test-task-event-type>{{row.model.type}}</td>
|
||||
<td data-test-task-event-message>
|
||||
{{#if row.model.message}}
|
||||
{{row.model.message}}
|
||||
{{else}}
|
||||
<em>No message</em>
|
||||
{{/if}}
|
||||
<tr data-test-task-address>
|
||||
<td data-test-task-address-is-dynamic>{{if row.model.isDynamic "Yes" "No"}}</td>
|
||||
<td data-test-task-address-name>{{row.model.name}}</td>
|
||||
<td data-test-task-address-address>
|
||||
<a href="http://{{network.ip}}:{{row.model.port}}" target="_blank">
|
||||
{{network.ip}}:{{row.model.port}}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{/if}}
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Recent Events
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table source=(reverse model.events) class="is-striped" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-3">Time</th>
|
||||
<th class="is-1">Type</th>
|
||||
<th>Description</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-task-event>
|
||||
<td data-test-task-event-time>{{moment-format row.model.time "MM/DD/YY HH:mm:ss"}}</td>
|
||||
<td data-test-task-event-type>{{row.model.type}}</td>
|
||||
<td data-test-task-event-message>
|
||||
{{#if row.model.message}}
|
||||
{{row.model.message}}
|
||||
{{else}}
|
||||
<em>No message</em>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
{{partial "allocations/allocation/task/subnav"}}
|
||||
<section class="section">
|
||||
{{task-log data-test-task-log allocation=model.allocation task=model.name}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{partial "allocations/allocation/task/subnav"}}
|
||||
<section class="section">
|
||||
{{task-log data-test-task-log allocation=model.allocation task=model.name}}
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
<div class="page-layout">
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#page-layout}}
|
||||
{{outlet}}
|
||||
</div>
|
||||
{{/page-layout}}
|
||||
|
||||
@@ -1,252 +1,250 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section">
|
||||
<h1 data-test-title class="title">
|
||||
<span data-test-node-status="{{model.compositeStatus}}" class="node-status-light {{model.compositeStatus}}"></span>
|
||||
{{or model.name model.shortId}}
|
||||
<span class="tag is-hollow is-small no-text-transform">{{model.id}}</span>
|
||||
</h1>
|
||||
<section class="section">
|
||||
<h1 data-test-title class="title">
|
||||
<span data-test-node-status="{{model.compositeStatus}}" class="node-status-light {{model.compositeStatus}}"></span>
|
||||
{{or model.name model.shortId}}
|
||||
<span class="tag is-hollow is-small no-text-transform">{{model.id}}</span>
|
||||
</h1>
|
||||
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Client Details</span>
|
||||
<span class="pair" data-test-status-definition>
|
||||
<span class="term">Status</span>
|
||||
<span class="status-text node-{{model.status}}">{{model.status}}</span>
|
||||
</span>
|
||||
<span class="pair" data-test-address-definition>
|
||||
<span class="term">Address</span>
|
||||
{{model.httpAddr}}
|
||||
</span>
|
||||
<span class="pair" data-test-draining>
|
||||
<span class="term">Draining</span>
|
||||
{{#if model.isDraining}}
|
||||
<span class="status-text is-info">true</span>
|
||||
{{else}}
|
||||
false
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="pair" data-test-eligibility>
|
||||
<span class="term">Eligibility</span>
|
||||
{{#if model.isEligible}}
|
||||
{{model.schedulingEligibility}}
|
||||
{{else}}
|
||||
<span class="status-text is-warning">{{model.schedulingEligibility}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="pair" data-test-datacenter-definition>
|
||||
<span class="term">Datacenter</span>
|
||||
{{model.datacenter}}
|
||||
</span>
|
||||
<span class="pair" data-test-driver-health>
|
||||
<span class="term">Drivers</span>
|
||||
{{#if model.unhealthyDrivers.length}}
|
||||
{{x-icon "warning" class="is-text is-warning"}}
|
||||
{{model.unhealthyDrivers.length}} of {{model.detectedDrivers.length}} {{pluralize "driver" model.detectedDrivers.length}} unhealthy
|
||||
{{else}}
|
||||
All healthy
|
||||
{{/if}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if model.drainStrategy}}
|
||||
<div class="boxed-section is-small is-info">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Client Details</span>
|
||||
<span class="pair" data-test-status-definition>
|
||||
<span class="term">Status</span>
|
||||
<span class="status-text node-{{model.status}}">{{model.status}}</span>
|
||||
</span>
|
||||
<span class="pair" data-test-address-definition>
|
||||
<span class="term">Address</span>
|
||||
{{model.httpAddr}}
|
||||
</span>
|
||||
<span class="pair" data-test-draining>
|
||||
<span class="term">Draining</span>
|
||||
{{#if model.isDraining}}
|
||||
<span class="status-text is-info">true</span>
|
||||
<span class="label">Drain Strategy</span>
|
||||
<span class="pair" data-test-drain-deadline>
|
||||
<span class="term">Deadline</span>
|
||||
{{#if model.drainStrategy.isForced}}
|
||||
<span class="badge is-danger">Forced Drain</span>
|
||||
{{else if model.drainStrategy.hasNoDeadline}}
|
||||
No deadline
|
||||
{{else}}
|
||||
false
|
||||
{{format-duration model.drainStrategy.deadline}}
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="pair" data-test-eligibility>
|
||||
<span class="term">Eligibility</span>
|
||||
{{#if model.isEligible}}
|
||||
{{model.schedulingEligibility}}
|
||||
{{else}}
|
||||
<span class="status-text is-warning">{{model.schedulingEligibility}}</span>
|
||||
{{/if}}
|
||||
</span>
|
||||
<span class="pair" data-test-datacenter-definition>
|
||||
<span class="term">Datacenter</span>
|
||||
{{model.datacenter}}
|
||||
</span>
|
||||
<span class="pair" data-test-driver-health>
|
||||
<span class="term">Drivers</span>
|
||||
{{#if model.unhealthyDrivers.length}}
|
||||
{{x-icon "warning" class="is-text is-warning"}}
|
||||
{{model.unhealthyDrivers.length}} of {{model.detectedDrivers.length}} {{pluralize "driver" model.detectedDrivers.length}} unhealthy
|
||||
{{else}}
|
||||
All healthy
|
||||
{{/if}}
|
||||
{{#if model.drainStrategy.forceDeadline}}
|
||||
<span class="pair" data-test-drain-forced-deadline>
|
||||
<span class="term">Forced Deadline</span>
|
||||
{{moment-format model.drainStrategy.forceDeadline "MM/DD/YY HH:mm:ss"}}
|
||||
({{moment-from-now model.drainStrategy.forceDeadline interval=1000}})
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="pair" data-test-drain-ignore-system-jobs>
|
||||
<span class="term">Ignore System Jobs?</span>
|
||||
{{if model.drainStrategy.ignoreSystemJobs "Yes" "No"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if model.drainStrategy}}
|
||||
<div class="boxed-section is-small is-info">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Drain Strategy</span>
|
||||
<span class="pair" data-test-drain-deadline>
|
||||
<span class="term">Deadline</span>
|
||||
{{#if model.drainStrategy.isForced}}
|
||||
<span class="badge is-danger">Forced Drain</span>
|
||||
{{else if model.drainStrategy.hasNoDeadline}}
|
||||
No deadline
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
<div>Allocations <span class="badge is-white">{{model.allocations.length}}</span></div>
|
||||
{{search-box
|
||||
searchTerm=(mut searchTerm)
|
||||
placeholder="Search allocations..."
|
||||
class="is-inline pull-right"
|
||||
inputClass="is-compact"}}
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-pagination
|
||||
source=sortedAllocations
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-narrow"></th>
|
||||
{{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
|
||||
{{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="job.name"}}Job{{/t.sort-by}}
|
||||
{{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
|
||||
<th>CPU</th>
|
||||
<th>Memory</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{allocation-row
|
||||
allocation=row.model
|
||||
context="node"
|
||||
onClick=(action "gotoAllocation" row.model)
|
||||
data-test-allocation=row.model.id}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedAllocations.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-test-client-events class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Client Events
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table source=sortedEvents class="is-striped" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-2">Time</th>
|
||||
<th class="is-2">Subsystem</th>
|
||||
<th>Message</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-client-event>
|
||||
<td data-test-client-event-time>{{moment-format row.model.time "MM/DD/YY HH:mm:ss"}}</td>
|
||||
<td data-test-client-event-subsystem>{{row.model.subsystem}}</td>
|
||||
<td data-test-client-event-message>
|
||||
{{#if row.model.message}}
|
||||
{{#if row.model.driver}}
|
||||
<span class="badge is-secondary is-small">{{row.model.driver}}</span>
|
||||
{{/if}}
|
||||
{{row.model.message}}
|
||||
{{else}}
|
||||
<em>No message</em>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-test-driver-status class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Driver Status
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{#list-accordion source=sortedDrivers key="name" as |a|}}
|
||||
{{#a.head buttonLabel="details" isExpandable=a.item.detected}}
|
||||
<div class="columns inline-definitions {{unless a.item.detected "is-faded"}}">
|
||||
<div class="column is-1">
|
||||
<span data-test-name>{{a.item.name}}</span>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
{{#if a.item.detected}}
|
||||
<span data-test-health>
|
||||
<span class="color-swatch {{a.item.healthClass}}"></span>
|
||||
{{if a.item.healthy "Healthy" "Unhealthy"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="column">
|
||||
<span class="pair">
|
||||
<span class="term">Detected</span>
|
||||
<span data-test-detected>{{if a.item.detected "Yes" "No"}}</span>
|
||||
</span>
|
||||
<span class="is-pulled-right">
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span data-test-last-updated>{{moment-from-now a.item.updateTime interval=1000}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/a.head}}
|
||||
{{#a.body}}
|
||||
<p data-test-health-description class="message">{{a.item.healthDescription}}</p>
|
||||
<div data-test-driver-attributes class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
{{capitalize a.item.name}} Attributes
|
||||
</div>
|
||||
{{#if a.item.attributes.attributesStructured}}
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{attributes-table
|
||||
attributes=a.item.attributesShort
|
||||
class="attributes-table"}}
|
||||
</div>
|
||||
{{else}}
|
||||
{{format-duration model.drainStrategy.deadline}}
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message">
|
||||
<h3 class="empty-message-headline">No Driver Attributes</h3>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</span>
|
||||
{{#if model.drainStrategy.forceDeadline}}
|
||||
<span class="pair" data-test-drain-forced-deadline>
|
||||
<span class="term">Forced Deadline</span>
|
||||
{{moment-format model.drainStrategy.forceDeadline "MM/DD/YY HH:mm:ss"}}
|
||||
({{moment-from-now model.drainStrategy.forceDeadline interval=1000}})
|
||||
</span>
|
||||
{{/if}}
|
||||
<span class="pair" data-test-drain-ignore-system-jobs>
|
||||
<span class="term">Ignore System Jobs?</span>
|
||||
{{if model.drainStrategy.ignoreSystemJobs "Yes" "No"}}
|
||||
</span>
|
||||
</div>
|
||||
{{/a.body}}
|
||||
{{/list-accordion}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Attributes
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{attributes-table
|
||||
data-test-attributes
|
||||
attributes=model.attributes.attributesStructured
|
||||
class="attributes-table"}}
|
||||
</div>
|
||||
<div class="boxed-section-head">
|
||||
Meta
|
||||
</div>
|
||||
{{#if model.meta.attributesStructured}}
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{attributes-table
|
||||
data-test-meta
|
||||
attributes=model.meta.attributesStructured
|
||||
class="attributes-table"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="boxed-section-body">
|
||||
<div data-test-empty-meta-message class="empty-message">
|
||||
<h3 class="empty-message-headline">No Meta Attributes</h3>
|
||||
<p class="empty-message-body">This client is configured with no meta attributes.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
<div>Allocations <span class="badge is-white">{{model.allocations.length}}</span></div>
|
||||
{{search-box
|
||||
searchTerm=(mut searchTerm)
|
||||
placeholder="Search allocations..."
|
||||
class="is-inline pull-right"
|
||||
inputClass="is-compact"}}
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-pagination
|
||||
source=sortedAllocations
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-narrow"></th>
|
||||
{{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
|
||||
{{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="job.name"}}Job{{/t.sort-by}}
|
||||
{{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
|
||||
<th>CPU</th>
|
||||
<th>Memory</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{allocation-row
|
||||
allocation=row.model
|
||||
context="node"
|
||||
onClick=(action "gotoAllocation" row.model)
|
||||
data-test-allocation=row.model.id}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedAllocations.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-test-client-events class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Client Events
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-table source=sortedEvents class="is-striped" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-2">Time</th>
|
||||
<th class="is-2">Subsystem</th>
|
||||
<th>Message</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-client-event>
|
||||
<td data-test-client-event-time>{{moment-format row.model.time "MM/DD/YY HH:mm:ss"}}</td>
|
||||
<td data-test-client-event-subsystem>{{row.model.subsystem}}</td>
|
||||
<td data-test-client-event-message>
|
||||
{{#if row.model.message}}
|
||||
{{#if row.model.driver}}
|
||||
<span class="badge is-secondary is-small">{{row.model.driver}}</span>
|
||||
{{/if}}
|
||||
{{row.model.message}}
|
||||
{{else}}
|
||||
<em>No message</em>
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-test-driver-status class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Driver Status
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{#list-accordion source=sortedDrivers key="name" as |a|}}
|
||||
{{#a.head buttonLabel="details" isExpandable=a.item.detected}}
|
||||
<div class="columns inline-definitions {{unless a.item.detected "is-faded"}}">
|
||||
<div class="column is-1">
|
||||
<span data-test-name>{{a.item.name}}</span>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
{{#if a.item.detected}}
|
||||
<span data-test-health>
|
||||
<span class="color-swatch {{a.item.healthClass}}"></span>
|
||||
{{if a.item.healthy "Healthy" "Unhealthy"}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="column">
|
||||
<span class="pair">
|
||||
<span class="term">Detected</span>
|
||||
<span data-test-detected>{{if a.item.detected "Yes" "No"}}</span>
|
||||
</span>
|
||||
<span class="is-pulled-right">
|
||||
<span class="pair">
|
||||
<span class="term">Last Updated</span>
|
||||
<span data-test-last-updated>{{moment-from-now a.item.updateTime interval=1000}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{{/a.head}}
|
||||
{{#a.body}}
|
||||
<p data-test-health-description class="message">{{a.item.healthDescription}}</p>
|
||||
<div data-test-driver-attributes class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
{{capitalize a.item.name}} Attributes
|
||||
</div>
|
||||
{{#if a.item.attributes.attributesStructured}}
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{attributes-table
|
||||
attributes=a.item.attributesShort
|
||||
class="attributes-table"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message">
|
||||
<h3 class="empty-message-headline">No Driver Attributes</h3>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/a.body}}
|
||||
{{/list-accordion}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Attributes
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{attributes-table
|
||||
data-test-attributes
|
||||
attributes=model.attributes.attributesStructured
|
||||
class="attributes-table"}}
|
||||
</div>
|
||||
<div class="boxed-section-head">
|
||||
Meta
|
||||
</div>
|
||||
{{#if model.meta.attributesStructured}}
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{attributes-table
|
||||
data-test-meta
|
||||
attributes=model.meta.attributesStructured
|
||||
class="attributes-table"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="boxed-section-body">
|
||||
<div data-test-empty-meta-message class="empty-message">
|
||||
<h3 class="empty-message-headline">No Meta Attributes</h3>
|
||||
<p class="empty-message-body">This client is configured with no meta attributes.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section">
|
||||
{{#if isForbidden}}
|
||||
{{partial "partials/forbidden-message"}}
|
||||
{{else}}
|
||||
{{#if nodes.length}}
|
||||
<div class="content">
|
||||
<div>{{search-box searchTerm=(mut searchTerm) placeholder="Search clients..."}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#list-pagination
|
||||
source=sortedNodes
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
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 prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="isDraining"}}Drain{{/t.sort-by}}
|
||||
{{#t.sort-by prop="schedulingEligibility"}}Eligibility{{/t.sort-by}}
|
||||
<th>Address</th>
|
||||
{{#t.sort-by prop="datacenter"}}Datacenter{{/t.sort-by}}
|
||||
<th># Allocs</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{client-node-row data-test-client-node-row node=row.model onClick=(action "gotoNode" row.model)}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination" data-test-pagination>
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedNodes.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty-message" data-test-empty-clients-list>
|
||||
{{#if (eq nodes.length 0)}}
|
||||
<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 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>{{searchTerm}}</strong></p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
<section class="section">
|
||||
{{#if isForbidden}}
|
||||
{{partial "partials/forbidden-message"}}
|
||||
{{else}}
|
||||
{{#if nodes.length}}
|
||||
<div class="content">
|
||||
<div>{{search-box searchTerm=(mut searchTerm) placeholder="Search clients..."}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{#list-pagination
|
||||
source=sortedNodes
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
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 prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="isDraining"}}Drain{{/t.sort-by}}
|
||||
{{#t.sort-by prop="schedulingEligibility"}}Eligibility{{/t.sort-by}}
|
||||
<th>Address</th>
|
||||
{{#t.sort-by prop="datacenter"}}Datacenter{{/t.sort-by}}
|
||||
<th># Allocs</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{client-node-row data-test-client-node-row node=row.model onClick=(action "gotoNode" row.model)}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination" data-test-pagination>
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedNodes.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="empty-message" data-test-empty-clients-list>
|
||||
{{#if (eq nodes.length 0)}}
|
||||
<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 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>{{searchTerm}}</strong></p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
{{/gutter-menu}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
|
||||
@@ -14,10 +14,7 @@
|
||||
selected=system.activeNamespace
|
||||
searchField="name"
|
||||
searchEnabled=(gt sortedNamespaces.length 10)
|
||||
onchange=(action (queue
|
||||
(action (mut system.activeNamespace))
|
||||
(action onNamespaceChange)
|
||||
))
|
||||
onchange=(action gotoJobsForNamespace)
|
||||
tagName="div"
|
||||
class="namespace-switcher"
|
||||
as |namespace|}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{job-page/parts/title job=job handleError=(action "handleError")}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{job-page/parts/title job=job title=job.trimmedName handleError=(action "handleError")}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{#job-page/parts/title job=job handleError=(action "handleError")}}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=onNamespaceChange}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
{{yield}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
{{yield}}
|
||||
</section>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{job-page/parts/title job=job title=job.trimmedName handleError=(action "handleError")}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{#job-page/parts/title job=job title=job.trimmedName handleError=(action "handleError")}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{job-page/parts/title job=job handleError=(action "handleError")}}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
{{job-page/parts/error errorMessage=errorMessage onDismiss=(action "clearErrorMessage")}}
|
||||
|
||||
{{job-page/parts/title job=job handleError=(action "handleError")}}
|
||||
|
||||
6
ui/app/templates/components/page-layout.hbs
Normal file
6
ui/app/templates/components/page-layout.hbs
Normal file
@@ -0,0 +1,6 @@
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#gutter-menu class="page-body"}}
|
||||
{{yield}}
|
||||
{{/gutter-menu}}
|
||||
@@ -1,6 +1,3 @@
|
||||
<div class="page-layout">
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#page-layout}}
|
||||
{{outlet}}
|
||||
</div>
|
||||
{{/page-layout}}
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "refresh")}}
|
||||
<section class="section">
|
||||
{{#if isForbidden}}
|
||||
{{partial "partials/forbidden-message"}}
|
||||
{{else}}
|
||||
{{#if filteredJobs.length}}
|
||||
<div class="content">
|
||||
<div>{{search-box data-test-jobs-search searchTerm=(mut searchTerm) placeholder="Search jobs..."}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#list-pagination
|
||||
source=sortedJobs
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="type"}}Type{{/t.sort-by}}
|
||||
{{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
|
||||
<th>Groups</th>
|
||||
<th class="is-3">Summary</th>
|
||||
{{/t.head}}
|
||||
{{#t.body key="model.id" as |row|}}
|
||||
{{job-row data-test-job-row job=row.model onClick=(action "gotoJob" row.model)}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedJobs.length}}
|
||||
{{#if searchTerm}}
|
||||
<em>({{dec sortedJobs.length filteredJobs.length}} hidden by search term)</em>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{else}}
|
||||
<div data-test-empty-jobs-list class="empty-message">
|
||||
{{#if (eq filteredJobs.length 0)}}
|
||||
<h3 data-test-empty-jobs-list-headline class="empty-message-headline">No Jobs</h3>
|
||||
<p class="empty-message-body">
|
||||
The cluster is currently empty.
|
||||
</p>
|
||||
{{else if searchTerm}}
|
||||
<h3 data-test-empty-jobs-list-headline class="empty-message-headline">No Matches</h3>
|
||||
<p class="empty-message-body">No jobs match the term <strong>{{searchTerm}}</strong></p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
<section class="section">
|
||||
{{#if isForbidden}}
|
||||
{{partial "partials/forbidden-message"}}
|
||||
{{else}}
|
||||
{{#if filteredJobs.length}}
|
||||
<div class="content">
|
||||
<div>{{search-box data-test-jobs-search searchTerm=(mut searchTerm) placeholder="Search jobs..."}}</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{#list-pagination
|
||||
source=sortedJobs
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="type"}}Type{{/t.sort-by}}
|
||||
{{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
|
||||
<th>Groups</th>
|
||||
<th class="is-3">Summary</th>
|
||||
{{/t.head}}
|
||||
{{#t.body key="model.id" as |row|}}
|
||||
{{job-row data-test-job-row job=row.model onClick=(action "gotoJob" row.model)}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedJobs.length}}
|
||||
{{#if searchTerm}}
|
||||
<em>({{dec sortedJobs.length filteredJobs.length}} hidden by search term)</em>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{else}}
|
||||
<div data-test-empty-jobs-list class="empty-message">
|
||||
{{#if (eq filteredJobs.length 0)}}
|
||||
<h3 data-test-empty-jobs-list-headline class="empty-message-headline">No Jobs</h3>
|
||||
<p class="empty-message-body">
|
||||
The cluster is currently empty.
|
||||
</p>
|
||||
{{else if searchTerm}}
|
||||
<h3 data-test-empty-jobs-list-headline class="empty-message-headline">No Matches</h3>
|
||||
<p class="empty-message-body">No jobs match the term <strong>{{searchTerm}}</strong></p>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
{{/if}}
|
||||
</section>
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
{{json-viewer data-test-definition-view json=model.definition}}
|
||||
</div>
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-body is-dark">
|
||||
{{json-viewer data-test-definition-view json=model.definition}}
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
{{job-deployments-stream deployments=model.deployments}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
{{job-deployments-stream deployments=model.deployments}}
|
||||
</section>
|
||||
|
||||
@@ -1,49 +1,46 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Evaluations
|
||||
</div>
|
||||
<div class="boxed-section-body {{if sortedEvaluations.length "is-full-bleed"}}">
|
||||
{{#if sortedEvaluations.length}}
|
||||
{{#list-table
|
||||
source=sortedEvaluations
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending as |t|}}
|
||||
{{#t.head}}
|
||||
<th>ID</th>
|
||||
{{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
|
||||
{{#t.sort-by prop="triggeredBy"}}Triggered By{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="hasPlacementFailures"}}Placement Failures{{/t.sort-by}}
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-evaluation="{{row.model.shortId}}">
|
||||
<td data-test-id>{{row.model.shortId}}</td>
|
||||
<td data-test-priority>{{row.model.priority}}</td>
|
||||
<td data-test-triggered-by>{{row.model.triggeredBy}}</td>
|
||||
<td data-test-status>{{row.model.status}}</td>
|
||||
<td data-test-blocked>
|
||||
{{#if (eq row.model.status "blocked")}}
|
||||
N/A - In Progress
|
||||
{{else if row.model.hasPlacementFailures}}
|
||||
True
|
||||
{{else}}
|
||||
False
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
{{else}}
|
||||
<div data-test-empty-evaluations-list class="empty-message">
|
||||
<h3 data-test-empty-evaluations-list-headline class="empty-message-headline">No Evaluations</h3>
|
||||
<p class="empty-message-body">This is most likely due to garbage collection.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Evaluations
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
|
||||
<div class="boxed-section-body {{if sortedEvaluations.length "is-full-bleed"}}">
|
||||
{{#if sortedEvaluations.length}}
|
||||
{{#list-table
|
||||
source=sortedEvaluations
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending as |t|}}
|
||||
{{#t.head}}
|
||||
<th>ID</th>
|
||||
{{#t.sort-by prop="priority"}}Priority{{/t.sort-by}}
|
||||
{{#t.sort-by prop="triggeredBy"}}Triggered By{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="hasPlacementFailures"}}Placement Failures{{/t.sort-by}}
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
<tr data-test-evaluation="{{row.model.shortId}}">
|
||||
<td data-test-id>{{row.model.shortId}}</td>
|
||||
<td data-test-priority>{{row.model.priority}}</td>
|
||||
<td data-test-triggered-by>{{row.model.triggeredBy}}</td>
|
||||
<td data-test-status>{{row.model.status}}</td>
|
||||
<td data-test-blocked>
|
||||
{{#if (eq row.model.status "blocked")}}
|
||||
N/A - In Progress
|
||||
{{else if row.model.hasPlacementFailures}}
|
||||
True
|
||||
{{else}}
|
||||
False
|
||||
{{/if}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
{{else}}
|
||||
<div data-test-empty-evaluations-list class="empty-message">
|
||||
<h3 data-test-empty-evaluations-list-headline class="empty-message-headline">No Evaluations</h3>
|
||||
<p class="empty-message-body">This is most likely due to garbage collection.</p>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -3,6 +3,5 @@
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
currentPage=currentPage
|
||||
onNamespaceChange=(action "gotoJobs")
|
||||
gotoJob=(action "gotoJob")
|
||||
gotoTaskGroup=(action "gotoTaskGroup")}}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
{{#gutter-menu class="page-body"}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
{{/gutter-menu}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
|
||||
@@ -1,109 +1,107 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
|
||||
<div class="tabs is-subnav">
|
||||
<ul>
|
||||
<li>{{#link-to "jobs.job.task-group" model.job model activeClass="is-active"}}Overview{{/link-to}}</li>
|
||||
</ul>
|
||||
<div class="tabs is-subnav">
|
||||
<ul>
|
||||
<li>{{#link-to "jobs.job.task-group" model.job model activeClass="is-active"}}Overview{{/link-to}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<section class="section">
|
||||
<h1 class="title">
|
||||
{{model.name}}
|
||||
</h1>
|
||||
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Task Group Details</span>
|
||||
|
||||
<span class="pair" data-test-task-group-tasks><span class="term"># Tasks</span> {{model.tasks.length}}</span>
|
||||
<span class="pair" data-test-task-group-cpu><span class="term">Reserved CPU</span> {{model.reservedCPU}} MHz</span>
|
||||
<span class="pair" data-test-task-group-mem><span class="term">Reserved Memory</span> {{model.reservedMemory}} MiB</span>
|
||||
<span class="pair" data-test-task-group-disk><span class="term">Reserved Disk</span> {{model.reservedEphemeralDisk}} MiB</span>
|
||||
</div>
|
||||
</div>
|
||||
<section class="section">
|
||||
<h1 class="title">
|
||||
{{model.name}}
|
||||
</h1>
|
||||
|
||||
<div class="boxed-section is-small">
|
||||
<div class="boxed-section-body inline-definitions">
|
||||
<span class="label">Task Group Details</span>
|
||||
|
||||
<span class="pair" data-test-task-group-tasks><span class="term"># Tasks</span> {{model.tasks.length}}</span>
|
||||
<span class="pair" data-test-task-group-cpu><span class="term">Reserved CPU</span> {{model.reservedCPU}} MHz</span>
|
||||
<span class="pair" data-test-task-group-mem><span class="term">Reserved Memory</span> {{model.reservedMemory}} MiB</span>
|
||||
<span class="pair" data-test-task-group-disk><span class="term">Reserved Disk</span> {{model.reservedEphemeralDisk}} MiB</span>
|
||||
</div>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
<div>Allocation Status <span class="badge is-white">{{allocations.length}}</span></div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
<div>Allocation Status <span class="badge is-white">{{allocations.length}}</span></div>
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{#allocation-status-bar allocationContainer=model.summary class="split-view" as |chart|}}
|
||||
<ol class="legend">
|
||||
{{#each chart.data as |datum index|}}
|
||||
<li class="{{datum.className}} {{if (eq datum.index chart.activeDatum.index) "is-active"}} {{if (eq datum.value 0) "is-empty"}}">
|
||||
<span class="color-swatch {{if datum.className datum.className (concat "swatch-" index)}}" />
|
||||
<span class="value">{{datum.value}}</span>
|
||||
<span class="label">
|
||||
{{datum.label}}
|
||||
</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
{{/allocation-status-bar}}
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
{{#allocation-status-bar allocationContainer=model.summary class="split-view" as |chart|}}
|
||||
<ol class="legend">
|
||||
{{#each chart.data as |datum index|}}
|
||||
<li class="{{datum.className}} {{if (eq datum.index chart.activeDatum.index) "is-active"}} {{if (eq datum.value 0) "is-empty"}}">
|
||||
<span class="color-swatch {{if datum.className datum.className (concat "swatch-" index)}}" />
|
||||
<span class="value">{{datum.value}}</span>
|
||||
<span class="label">
|
||||
{{datum.label}}
|
||||
</span>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ol>
|
||||
{{/allocation-status-bar}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Allocations
|
||||
{{search-box
|
||||
searchTerm=(mut searchTerm)
|
||||
placeholder="Search allocations..."
|
||||
class="is-inline pull-right"
|
||||
inputClass="is-compact"}}
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-pagination
|
||||
source=sortedAllocations
|
||||
size=pageSize
|
||||
page=currentPage
|
||||
class="allocations" as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-narrow"></th>
|
||||
{{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
|
||||
{{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
|
||||
{{#t.sort-by prop="node.shortId"}}Client{{/t.sort-by}}
|
||||
<th>CPU</th>
|
||||
<th>Memory</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{allocation-row data-test-allocation=row.model.id allocation=row.model context="job" onClick=(action "gotoAllocation" row.model)}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedAllocations.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
<div class="boxed-section">
|
||||
<div class="boxed-section-head">
|
||||
Allocations
|
||||
{{search-box
|
||||
searchTerm=(mut searchTerm)
|
||||
placeholder="Search allocations..."
|
||||
class="is-inline pull-right"
|
||||
inputClass="is-compact"}}
|
||||
</div>
|
||||
<div class="boxed-section-body is-full-bleed">
|
||||
{{#list-pagination
|
||||
source=sortedAllocations
|
||||
size=pageSize
|
||||
page=currentPage
|
||||
class="allocations" as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
<th class="is-narrow"></th>
|
||||
{{#t.sort-by prop="shortId"}}ID{{/t.sort-by}}
|
||||
{{#t.sort-by prop="modifyIndex" title="Modify Index"}}Modified{{/t.sort-by}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="statusIndex"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="jobVersion"}}Version{{/t.sort-by}}
|
||||
{{#t.sort-by prop="node.shortId"}}Client{{/t.sort-by}}
|
||||
<th>CPU</th>
|
||||
<th>Memory</th>
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{allocation-row data-test-allocation=row.model.id allocation=row.model context="job" onClick=(action "gotoAllocation" row.model)}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedAllocations.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if allocations.length}}
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message" data-test-empty-allocations-list>
|
||||
<h3 class="empty-message-headline" data-test-empty-allocations-list-headline>No Matches</h3>
|
||||
<p class="empty-message-body">No allocations match the term <strong>{{searchTerm}}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if allocations.length}}
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message" data-test-empty-allocations-list>
|
||||
<h3 class="empty-message-headline" data-test-empty-allocations-list-headline>No Matches</h3>
|
||||
<p class="empty-message-body">No allocations match the term <strong>{{searchTerm}}</strong></p>
|
||||
</div>
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message" data-test-empty-allocations-list>
|
||||
<h3 class="empty-message-headline" data-test-empty-allocations-list-headline>No Allocations</h3>
|
||||
<p class="empty-message-body">No allocations have been placed.</p>
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="boxed-section-body">
|
||||
<div class="empty-message" data-test-empty-allocations-list>
|
||||
<h3 class="empty-message-headline" data-test-empty-allocations-list-headline>No Allocations</h3>
|
||||
<p class="empty-message-body">No allocations have been placed.</p>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/list-pagination}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/list-pagination}}
|
||||
</div>
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
{{#gutter-menu class="page-body" onNamespaceChange=(action "gotoJobs")}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
{{job-versions-stream versions=model.versions verbose=true}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
{{partial "jobs/job/subnav"}}
|
||||
<section class="section">
|
||||
{{job-versions-stream versions=model.versions verbose=true}}
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1 @@
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
{{/gutter-menu}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
<div class="page-layout">
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
{{/gutter-menu}}
|
||||
</div>
|
||||
{{#page-layout}}
|
||||
<section class="section has-text-centered">{{partial "partials/loading-spinner"}}</section>
|
||||
{{/page-layout}}
|
||||
|
||||
@@ -1,46 +1,41 @@
|
||||
<div class="page-layout">
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#gutter-menu class="page-body"}}
|
||||
<section class="section">
|
||||
{{#if isForbidden}}
|
||||
{{partial "partials/forbidden-message"}}
|
||||
{{else}}
|
||||
{{#list-pagination
|
||||
source=sortedAgents
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="isLeader"}}Leader{{/t.sort-by}}
|
||||
{{#t.sort-by prop="address"}}Address{{/t.sort-by}}
|
||||
{{#t.sort-by prop="serfPort"}}port{{/t.sort-by}}
|
||||
{{#t.sort-by prop="datacenter"}}Datacenter{{/t.sort-by}}
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{server-agent-row data-test-server-agent-row agent=row.model}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedAgents.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
{{outlet}}
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/gutter-menu}}
|
||||
</div>
|
||||
{{#page-layout}}
|
||||
<section class="section">
|
||||
{{#if isForbidden}}
|
||||
{{partial "partials/forbidden-message"}}
|
||||
{{else}}
|
||||
{{#list-pagination
|
||||
source=sortedAgents
|
||||
size=pageSize
|
||||
page=currentPage as |p|}}
|
||||
{{#list-table
|
||||
source=p.list
|
||||
sortProperty=sortProperty
|
||||
sortDescending=sortDescending
|
||||
class="with-foot" as |t|}}
|
||||
{{#t.head}}
|
||||
{{#t.sort-by prop="name"}}Name{{/t.sort-by}}
|
||||
{{#t.sort-by prop="status"}}Status{{/t.sort-by}}
|
||||
{{#t.sort-by prop="isLeader"}}Leader{{/t.sort-by}}
|
||||
{{#t.sort-by prop="address"}}Address{{/t.sort-by}}
|
||||
{{#t.sort-by prop="serfPort"}}port{{/t.sort-by}}
|
||||
{{#t.sort-by prop="datacenter"}}Datacenter{{/t.sort-by}}
|
||||
{{/t.head}}
|
||||
{{#t.body as |row|}}
|
||||
{{server-agent-row data-test-server-agent-row agent=row.model}}
|
||||
{{/t.body}}
|
||||
{{/list-table}}
|
||||
<div class="table-foot">
|
||||
<nav class="pagination">
|
||||
<div class="pagination-numbers">
|
||||
{{p.startsAt}}–{{p.endsAt}} of {{sortedAgents.length}}
|
||||
</div>
|
||||
{{#p.prev class="pagination-previous"}} < {{/p.prev}}
|
||||
{{#p.next class="pagination-next"}} > {{/p.next}}
|
||||
<ul class="pagination-list"></ul>
|
||||
</nav>
|
||||
</div>
|
||||
{{/list-pagination}}
|
||||
{{outlet}}
|
||||
{{/if}}
|
||||
</section>
|
||||
{{/page-layout}}
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
<div class="page-layout">
|
||||
{{#global-header class="page-header"}}
|
||||
{{app-breadcrumbs}}
|
||||
{{/global-header}}
|
||||
{{#gutter-menu class="page-body"}}
|
||||
{{outlet}}
|
||||
{{/gutter-menu}}
|
||||
</div>
|
||||
{{#page-layout}}
|
||||
{{outlet}}
|
||||
{{/page-layout}}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
import { run } from '@ember/runloop';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { test, moduleForComponent } from 'ember-qunit';
|
||||
import { click, find, findAll } from 'ember-native-dom-helpers';
|
||||
import { find, findAll } from 'ember-native-dom-helpers';
|
||||
import wait from 'ember-test-helpers/wait';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
import sinon from 'sinon';
|
||||
import { clickTrigger } from 'ember-power-select/test-support/helpers';
|
||||
import { startMirage } from 'nomad-ui/initializers/ember-cli-mirage';
|
||||
|
||||
moduleForComponent('job-page/parts/body', 'Integration | Component | job-page/parts/body', {
|
||||
@@ -23,10 +21,9 @@ moduleForComponent('job-page/parts/body', 'Integration | Component | job-page/pa
|
||||
|
||||
test('includes a subnav for the job', function(assert) {
|
||||
this.set('job', {});
|
||||
this.set('onNamespaceChange', () => {});
|
||||
|
||||
this.render(hbs`
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
<div class="inner-content">Inner content</div>
|
||||
{{/job-page/parts/body}}
|
||||
`);
|
||||
@@ -48,10 +45,9 @@ test('the subnav includes the deployments link when the job is a service', funct
|
||||
});
|
||||
|
||||
this.set('job', job);
|
||||
this.set('onNamespaceChange', () => {});
|
||||
|
||||
this.render(hbs`
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
<div class="inner-content">Inner content</div>
|
||||
{{/job-page/parts/body}}
|
||||
`);
|
||||
@@ -76,10 +72,9 @@ test('the subnav does not include the deployments link when the job is not a ser
|
||||
});
|
||||
|
||||
this.set('job', job);
|
||||
this.set('onNamespaceChange', () => {});
|
||||
|
||||
this.render(hbs`
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
<div class="inner-content">Inner content</div>
|
||||
{{/job-page/parts/body}}
|
||||
`);
|
||||
@@ -94,44 +89,17 @@ test('the subnav does not include the deployments link when the job is not a ser
|
||||
|
||||
test('body yields content to a section after the subnav', function(assert) {
|
||||
this.set('job', {});
|
||||
this.set('onNamespaceChange', () => {});
|
||||
|
||||
this.render(hbs`
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
{{#job-page/parts/body job=job}}
|
||||
<div class="inner-content">Inner content</div>
|
||||
{{/job-page/parts/body}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
assert.ok(
|
||||
find('[data-test-page-content] .section > .inner-content'),
|
||||
'Content is rendered in a section in a gutter menu'
|
||||
);
|
||||
assert.ok(
|
||||
find('[data-test-subnav="job"] + .section > .inner-content'),
|
||||
'Content is rendered immediately after the subnav'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
test('onNamespaceChange action is called when the namespace changes in the nested gutter menu', function(assert) {
|
||||
const namespaceSpy = sinon.spy();
|
||||
|
||||
this.set('job', {});
|
||||
this.set('onNamespaceChange', namespaceSpy);
|
||||
|
||||
this.render(hbs`
|
||||
{{#job-page/parts/body job=job onNamespaceChange=onNamespaceChange}}
|
||||
<div class="inner-content">Inner content</div>
|
||||
{{/job-page/parts/body}}
|
||||
`);
|
||||
|
||||
return wait().then(() => {
|
||||
clickTrigger('[data-test-namespace-switcher]');
|
||||
click(findAll('.ember-power-select-option')[1]);
|
||||
|
||||
return wait().then(() => {
|
||||
assert.ok(namespaceSpy.calledOnce, 'Switching namespaces calls the onNamespaceChange action');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user