mirror of https://github.com/hashicorp/consul
ui: Add Route component / routlet service (#9813)
* Add Routlet service and Route Component * Add ember-assign-helper (already an indirect dependency) * Use EventListeners for is-href instead of observing * Don't include :active in '-intent' stylespull/9851/head
parent
15e8b13891
commit
61eef053db
|
@ -9,7 +9,6 @@
|
||||||
@extend %main-nav-horizontal-action;
|
@extend %main-nav-horizontal-action;
|
||||||
}
|
}
|
||||||
%main-nav-horizontal .popover-menu [type='checkbox']:checked + label > *,
|
%main-nav-horizontal .popover-menu [type='checkbox']:checked + label > *,
|
||||||
%main-nav-horizontal > ul > li > a:active,
|
|
||||||
%main-nav-horizontal > ul > li.is-active > a,
|
%main-nav-horizontal > ul > li.is-active > a,
|
||||||
%main-nav-horizontal > ul > li.is-active > label > * {
|
%main-nav-horizontal > ul > li.is-active > label > * {
|
||||||
@extend %main-nav-horizontal-action-active;
|
@extend %main-nav-horizontal-action-active;
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
%main-nav-vertical > ul > li > span {
|
%main-nav-vertical > ul > li > span {
|
||||||
@extend %main-nav-vertical-action;
|
@extend %main-nav-vertical-action;
|
||||||
}
|
}
|
||||||
%main-nav-vertical > ul > li > a:active,
|
|
||||||
%main-nav-vertical > ul > li.is-active > a {
|
%main-nav-vertical > ul > li.is-active > a {
|
||||||
@extend %main-nav-vertical-action-active;
|
@extend %main-nav-vertical-action-active;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
{{did-insert this.connect}}
|
{{did-insert this.connect}}
|
||||||
{{will-destroy this.disconnect}}
|
{{will-destroy this.disconnect}}
|
||||||
<section
|
<section
|
||||||
|
{{did-insert (fn this.attributeChanged 'element')}}
|
||||||
class="outlet"
|
class="outlet"
|
||||||
data-outlet={{@name}}
|
data-outlet={{@name}}
|
||||||
data-route={{this.route}}
|
data-route={{this.routeName}}
|
||||||
data-state={{this.state.name}}
|
data-state={{this.state.name}}
|
||||||
data-transition={{concat this.previousState.name ' ' this.state.name}}
|
data-transition={{concat this.previousState.name ' ' this.state.name}}
|
||||||
|
{{on 'transitionend' this.transitionEnd}}
|
||||||
>
|
>
|
||||||
{{yield (hash
|
{{yield (hash
|
||||||
state=this.state
|
state=this.state
|
||||||
|
|
|
@ -12,56 +12,18 @@ class State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Outlets {
|
|
||||||
constructor() {
|
|
||||||
this.map = new Map();
|
|
||||||
}
|
|
||||||
sort() {
|
|
||||||
this.sorted = [...this.map.keys()];
|
|
||||||
this.sorted.sort((a, b) => {
|
|
||||||
const al = a.split('.').length;
|
|
||||||
const bl = b.split('.').length;
|
|
||||||
switch (true) {
|
|
||||||
case al > bl:
|
|
||||||
return -1;
|
|
||||||
case al < bl:
|
|
||||||
return 1;
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
set(name, value) {
|
|
||||||
this.map.set(name, value);
|
|
||||||
this.sort();
|
|
||||||
}
|
|
||||||
get(name) {
|
|
||||||
return this.map.get(name);
|
|
||||||
}
|
|
||||||
delete(name) {
|
|
||||||
this.map.delete(name);
|
|
||||||
this.sort();
|
|
||||||
}
|
|
||||||
keys() {
|
|
||||||
return this.sorted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const outlets = new Outlets();
|
|
||||||
|
|
||||||
export default class Outlet extends Component {
|
export default class Outlet extends Component {
|
||||||
|
@service('routlet') routlet;
|
||||||
@service('router') router;
|
@service('router') router;
|
||||||
@service('dom') dom;
|
|
||||||
|
|
||||||
@tracked route;
|
@tracked element;
|
||||||
|
@tracked routeName;
|
||||||
@tracked state;
|
@tracked state;
|
||||||
@tracked previousState;
|
@tracked previousState;
|
||||||
|
@tracked endTransition;
|
||||||
|
|
||||||
constructor() {
|
get model() {
|
||||||
super(...arguments);
|
return this.args.model || {};
|
||||||
if (this.args.name === 'application') {
|
|
||||||
this.setAppState('loading');
|
|
||||||
this.setAppRoute(this.router.currentRouteName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAppRoute(name) {
|
setAppRoute(name) {
|
||||||
|
@ -70,7 +32,7 @@ export default class Outlet extends Component {
|
||||||
name = name.substr(nspace.length);
|
name = name.substr(nspace.length);
|
||||||
}
|
}
|
||||||
if (name !== 'loading') {
|
if (name !== 'loading') {
|
||||||
const doc = this.dom.root();
|
const doc = this.element.ownerDocument.documentElement;
|
||||||
if (doc.classList.contains('ember-loading')) {
|
if (doc.classList.contains('ember-loading')) {
|
||||||
doc.classList.remove('ember-loading');
|
doc.classList.remove('ember-loading');
|
||||||
}
|
}
|
||||||
|
@ -80,31 +42,43 @@ export default class Outlet extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
setAppState(state) {
|
setAppState(state) {
|
||||||
this.dom.root().dataset.state = state;
|
const doc = this.element.ownerDocument.documentElement;
|
||||||
|
doc.dataset.state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
setOutletRoutes(route) {
|
@action
|
||||||
const keys = [...outlets.keys()];
|
attributeChanged(prop, value) {
|
||||||
const pos = keys.indexOf(this.name);
|
switch (prop) {
|
||||||
const key = pos + 1;
|
case 'element':
|
||||||
const parent = outlets.get(keys[key]);
|
this.element = value;
|
||||||
parent.route = this.args.name;
|
if (this.args.name === 'application') {
|
||||||
|
this.setAppState('loading');
|
||||||
|
this.setAppRoute(this.router.currentRouteName);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.route = route;
|
@action transitionEnd($el) {
|
||||||
|
if (typeof this.endTransition === 'function') {
|
||||||
|
this.endTransition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
startLoad(transition) {
|
startLoad(transition) {
|
||||||
const keys = [...outlets.keys()];
|
const outlet = this.routlet.findOutlet(transition.to.name) || 'application';
|
||||||
|
|
||||||
const outlet =
|
|
||||||
keys.find(item => {
|
|
||||||
return transition.to.name.indexOf(item) !== -1;
|
|
||||||
}) || 'application';
|
|
||||||
|
|
||||||
if (this.args.name === outlet) {
|
if (this.args.name === outlet) {
|
||||||
this.previousState = this.state;
|
this.previousState = this.state;
|
||||||
this.state = new State('loading');
|
this.state = new State('loading');
|
||||||
|
this.endTransition = this.routlet.transition();
|
||||||
|
// if we have no transition-duration set immediately end the transition
|
||||||
|
const duration = window
|
||||||
|
.getComputedStyle(this.element)
|
||||||
|
.getPropertyValue('transition-duration');
|
||||||
|
if (parseFloat(duration) === 0) {
|
||||||
|
this.endTransition();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.args.name === 'application') {
|
if (this.args.name === 'application') {
|
||||||
this.setAppState('loading');
|
this.setAppState('loading');
|
||||||
|
@ -114,8 +88,6 @@ export default class Outlet extends Component {
|
||||||
@action
|
@action
|
||||||
endLoad(transition) {
|
endLoad(transition) {
|
||||||
if (this.state.matches('loading')) {
|
if (this.state.matches('loading')) {
|
||||||
this.setOutletRoutes(transition.to.name);
|
|
||||||
|
|
||||||
this.previousState = this.state;
|
this.previousState = this.state;
|
||||||
this.state = new State('idle');
|
this.state = new State('idle');
|
||||||
}
|
}
|
||||||
|
@ -126,7 +98,7 @@ export default class Outlet extends Component {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
connect() {
|
connect() {
|
||||||
outlets.set(this.args.name, this);
|
this.routlet.addOutlet(this.args.name, this);
|
||||||
this.previousState = this.state = new State('idle');
|
this.previousState = this.state = new State('idle');
|
||||||
this.router.on('routeWillChange', this.startLoad);
|
this.router.on('routeWillChange', this.startLoad);
|
||||||
this.router.on('routeDidChange', this.endLoad);
|
this.router.on('routeDidChange', this.endLoad);
|
||||||
|
@ -134,7 +106,7 @@ export default class Outlet extends Component {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
disconnect() {
|
disconnect() {
|
||||||
outlets.delete(this.args.name);
|
this.routlet.removeOutlet(this.args.name);
|
||||||
this.router.off('routeWillChange', this.startLoad);
|
this.router.off('routeWillChange', this.startLoad);
|
||||||
this.router.off('routeDidChange', this.endLoad);
|
this.router.off('routeDidChange', this.endLoad);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{did-insert this.connect}}
|
||||||
|
{{will-destroy this.disconnect}}
|
||||||
|
{{yield (hash
|
||||||
|
model=model
|
||||||
|
)}}
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Component from '@glimmer/component';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { action } from '@ember/object';
|
||||||
|
import { tracked } from '@glimmer/tracking';
|
||||||
|
|
||||||
|
export default class RouteComponent extends Component {
|
||||||
|
@service('routlet') routlet;
|
||||||
|
|
||||||
|
@tracked model;
|
||||||
|
|
||||||
|
@action
|
||||||
|
connect() {
|
||||||
|
this.routlet.addRoute(this.args.name, this);
|
||||||
|
}
|
||||||
|
@action
|
||||||
|
disconnect() {
|
||||||
|
this.routlet.removeRoute(this.args.name, this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,31 @@
|
||||||
/*eslint ember/no-observers: "warn"*/
|
|
||||||
// TODO: Remove ^
|
|
||||||
import Helper from '@ember/component/helper';
|
import Helper from '@ember/component/helper';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { observes } from '@ember-decorators/object';
|
import { action } from '@ember/object';
|
||||||
|
|
||||||
export default class IsHrefHelper extends Helper {
|
export default class IsHrefHelper extends Helper {
|
||||||
@service('router') router;
|
@service('router') router;
|
||||||
|
init() {
|
||||||
|
super.init(...arguments);
|
||||||
|
this.router.on('routeWillChange', this.routeWillChange);
|
||||||
|
}
|
||||||
|
|
||||||
compute([targetRouteName, ...rest]) {
|
compute([targetRouteName, ...rest]) {
|
||||||
if (this.router.currentRouteName.startsWith('nspace.') && targetRouteName.startsWith('dc.')) {
|
if (this.router.currentRouteName.startsWith('nspace.') && targetRouteName.startsWith('dc.')) {
|
||||||
targetRouteName = `nspace.${targetRouteName}`;
|
targetRouteName = `nspace.${targetRouteName}`;
|
||||||
}
|
}
|
||||||
|
if (typeof this.next !== 'undefined' && this.next !== 'loading') {
|
||||||
|
return this.next.startsWith(targetRouteName);
|
||||||
|
}
|
||||||
return this.router.isActive(...[targetRouteName, ...rest]);
|
return this.router.isActive(...[targetRouteName, ...rest]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@observes('router.currentURL')
|
@action
|
||||||
onURLChange() {
|
routeWillChange(transition) {
|
||||||
|
this.next = transition.to.name.replace('.index', '');
|
||||||
this.recompute();
|
this.recompute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
willDestroy() {
|
||||||
|
this.router.off('routeWillChange', this.routeWillChange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,18 @@ export default class DcRoute extends Route {
|
||||||
dc: params.dc,
|
dc: params.dc,
|
||||||
nspace: get(nspace || {}, 'Name'),
|
nspace: get(nspace || {}, 'Name'),
|
||||||
});
|
});
|
||||||
|
// the model here is actually required for the entire application
|
||||||
|
// but we need to wait until we are in this route so we know what the dc
|
||||||
|
// and or nspace is if the below changes please revisit the comments
|
||||||
|
// in routes/application:model
|
||||||
|
// We do this here instead of in setupController to prevent timing issues
|
||||||
|
// in lower routes
|
||||||
|
this.controllerFor('application').setProperties({
|
||||||
|
dc,
|
||||||
|
nspace,
|
||||||
|
token,
|
||||||
|
permissions,
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
dc,
|
dc,
|
||||||
nspace,
|
nspace,
|
||||||
|
@ -55,15 +67,6 @@ export default class DcRoute extends Route {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
setupController(controller, model) {
|
|
||||||
super.setupController(...arguments);
|
|
||||||
// the model here is actually required for the entire application
|
|
||||||
// but we need to wait until we are in this route so we know what the dc
|
|
||||||
// and or nspace is if the below changes please revists the comments
|
|
||||||
// in routes/application:model
|
|
||||||
this.controllerFor('application').setProperties(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: This will eventually be deprecated please see
|
// TODO: This will eventually be deprecated please see
|
||||||
// https://deprecations.emberjs.com/v3.x/#toc_deprecate-router-events
|
// https://deprecations.emberjs.com/v3.x/#toc_deprecate-router-events
|
||||||
@action
|
@action
|
||||||
|
|
|
@ -22,11 +22,11 @@ export default class IndexRoute extends Route {
|
||||||
async model(params, transition) {
|
async model(params, transition) {
|
||||||
const nspace = this.modelFor('nspace').nspace.substr(1);
|
const nspace = this.modelFor('nspace').nspace.substr(1);
|
||||||
const dc = this.modelFor('dc').dc.Name;
|
const dc = this.modelFor('dc').dc.Name;
|
||||||
const items = await this.data.source(uri => uri`/${nspace}/${dc}/services`);
|
const items = this.data.source(uri => uri`/${nspace}/${dc}/services`);
|
||||||
return {
|
return {
|
||||||
dc,
|
dc,
|
||||||
nspace,
|
nspace,
|
||||||
items,
|
items: await items,
|
||||||
searchProperties: this.queryParams.searchproperty.empty[0],
|
searchProperties: this.queryParams.searchproperty.empty[0],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,12 @@ export default class RoutingRoute extends Route {
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.join('.');
|
.join('.');
|
||||||
const model = this.modelFor(parent);
|
const model = this.modelFor(parent);
|
||||||
|
const chain = this.data.source(
|
||||||
|
uri => uri`/${model.nspace}/${model.dc.Name}/discovery-chain/${model.slug}`
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...model,
|
...model,
|
||||||
chain: await this.data.source(
|
chain: await chain,
|
||||||
uri => uri`/${model.nspace}/${model.dc.Name}/discovery-chain/${model.slug}`
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import Route from 'consul-ui/routing/route';
|
import Route from 'consul-ui/routing/route';
|
||||||
|
|
||||||
export default class TagsRoute extends Route {
|
export default class TagsRoute extends Route {
|
||||||
model() {
|
async model() {
|
||||||
const parent = this.routeName
|
const parent = this.routeName
|
||||||
.split('.')
|
.split('.')
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
import Service from '@ember/service';
|
||||||
|
import { schedule } from '@ember/runloop';
|
||||||
|
|
||||||
|
class Outlets {
|
||||||
|
constructor() {
|
||||||
|
this.map = new Map();
|
||||||
|
this.sorted = [];
|
||||||
|
}
|
||||||
|
sort() {
|
||||||
|
this.sorted = [...this.map.keys()];
|
||||||
|
this.sorted.sort((a, b) => {
|
||||||
|
if (a === 'application') {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (b === 'application') {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
const al = a.split('.').length;
|
||||||
|
const bl = b.split('.').length;
|
||||||
|
switch (true) {
|
||||||
|
case al > bl:
|
||||||
|
return -1;
|
||||||
|
case al < bl:
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
set(name, value) {
|
||||||
|
this.map.set(name, value);
|
||||||
|
// TODO: find, splice to insert at the correct index instead of sorting
|
||||||
|
// all the time
|
||||||
|
this.sort();
|
||||||
|
}
|
||||||
|
get(name) {
|
||||||
|
return this.map.get(name);
|
||||||
|
}
|
||||||
|
delete(name) {
|
||||||
|
// TODO: find, splice to delete at the correct index instead of sorting
|
||||||
|
// all the time
|
||||||
|
this.map.delete(name);
|
||||||
|
this.sort();
|
||||||
|
}
|
||||||
|
keys() {
|
||||||
|
return this.sorted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const outlets = new Outlets();
|
||||||
|
export default class RoutletService extends Service {
|
||||||
|
ready() {
|
||||||
|
return this._transition;
|
||||||
|
}
|
||||||
|
|
||||||
|
transition() {
|
||||||
|
let endTransition;
|
||||||
|
this._transition = new Promise(resolve => {
|
||||||
|
endTransition = resolve;
|
||||||
|
});
|
||||||
|
return endTransition;
|
||||||
|
}
|
||||||
|
|
||||||
|
findOutlet(name) {
|
||||||
|
const keys = [...outlets.keys()];
|
||||||
|
const key = keys.find(item => name.indexOf(item) !== -1);
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
addOutlet(name, outlet) {
|
||||||
|
outlets.set(name, outlet);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeOutlet(name) {
|
||||||
|
outlets.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// modelFor gets the model for Outlet specified by `name`, not the Route
|
||||||
|
modelFor(name) {
|
||||||
|
const outlet = outlets.get(name);
|
||||||
|
if (typeof outlet !== 'undefined') {
|
||||||
|
return outlet.model || {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
addRoute(name, route) {
|
||||||
|
const keys = [...outlets.keys()];
|
||||||
|
const pos = keys.indexOf(name);
|
||||||
|
const key = pos + 1;
|
||||||
|
const outlet = outlets.get(keys[key]);
|
||||||
|
if (typeof outlet !== 'undefined') {
|
||||||
|
route.model = outlet.model;
|
||||||
|
// TODO: Try to avoid the double computation bug
|
||||||
|
schedule('afterRender', () => {
|
||||||
|
outlet.routeName = route.args.name;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeRoute(name, route) {}
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
>
|
||||||
<HeadLayout />
|
<HeadLayout />
|
||||||
{{page-title 'Consul' separator=' - '}}
|
{{page-title 'Consul' separator=' - '}}
|
||||||
|
|
||||||
|
@ -35,3 +38,4 @@ as |source|>
|
||||||
<Consul::Loader class="view-loader" />
|
<Consul::Loader class="view-loader" />
|
||||||
</HashicorpConsul>
|
</HashicorpConsul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
<Outlet
|
<Route
|
||||||
@name={{routeName}}
|
@name={{routeName}}
|
||||||
as |o|>
|
as |route|>
|
||||||
{{outlet}}
|
<Outlet
|
||||||
</Outlet>
|
@name={{routeName}}
|
||||||
|
@model={{route.model}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
>
|
||||||
{{page-title 'Services'}}
|
{{page-title 'Services'}}
|
||||||
|
|
||||||
<EventSource @src={{items}} />
|
<EventSource @src={{items}} />
|
||||||
|
@ -108,3 +111,4 @@ as |sort filters items|}}
|
||||||
</AppView>
|
</AppView>
|
||||||
|
|
||||||
{{/let}}
|
{{/let}}
|
||||||
|
</Route>
|
||||||
|
|
|
@ -1,114 +1,118 @@
|
||||||
{{#let items.firstObject as |item|}}
|
<Route
|
||||||
{{page-title item.Service.Service}}
|
@name={{routeName}}
|
||||||
<DataLoader as |loader|>
|
as |route|>
|
||||||
|
{{#let items.firstObject as |item|}}
|
||||||
|
{{page-title item.Service.Service}}
|
||||||
|
<DataLoader as |loader|>
|
||||||
|
|
||||||
<BlockSlot @name="data">
|
<BlockSlot @name="data">
|
||||||
<EventSource @src={{items}} @onerror={{action loader.dispatchError}} />
|
<EventSource @src={{items}} @onerror={{action loader.dispatchError}} />
|
||||||
{{#if (not loader.error)}}
|
{{#if (not loader.error)}}
|
||||||
<EventSource @src={{proxies}} />
|
<EventSource @src={{proxies}} />
|
||||||
<EventSource @src={{chain}} />
|
<EventSource @src={{chain}} />
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
|
|
||||||
<BlockSlot @name="error">
|
|
||||||
<AppError @error={{loader.error}} />
|
|
||||||
</BlockSlot>
|
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
|
||||||
{{#if (eq loader.error.status "404")}}
|
|
||||||
<Notification @sticky={{true}}>
|
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
|
||||||
<strong>Warning!</strong>
|
|
||||||
This service has been deregistered and no longer exists in the catalog.
|
|
||||||
</p>
|
|
||||||
</Notification>
|
|
||||||
{{else if (eq loader.error.status "403")}}
|
|
||||||
<Notification @sticky={{true}}>
|
|
||||||
<p data-notification role="alert" class="error notification-update">
|
|
||||||
<strong>Error!</strong>
|
|
||||||
You no longer have access to this service
|
|
||||||
</p>
|
|
||||||
</Notification>
|
|
||||||
{{else}}
|
|
||||||
<Notification @sticky={{true}}>
|
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
|
||||||
<strong>Warning!</strong>
|
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
|
||||||
</p>
|
|
||||||
</Notification>
|
|
||||||
{{/if}}
|
|
||||||
</BlockSlot>
|
|
||||||
|
|
||||||
<BlockSlot @name="loaded">
|
|
||||||
<AppView>
|
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<TopologyMetrics::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="breadcrumbs">
|
|
||||||
<ol>
|
|
||||||
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
|
||||||
</ol>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h1>
|
|
||||||
{{item.Service.Service}}
|
|
||||||
</h1>
|
|
||||||
<Consul::ExternalSource @item={{item.Service}} />
|
|
||||||
<Consul::Kind @item={{item.Service}} @withInfo={{true}} />
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="nav">
|
|
||||||
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
|
||||||
<TabNav @items={{
|
|
||||||
compact
|
|
||||||
(array
|
|
||||||
(if (and dc.MeshEnabled item.IsMeshOrigin (or (gt proxies.length 0) (eq item.Service.Kind 'ingress-gateway')))
|
|
||||||
(hash label="Topology" href=(href-to "dc.services.show.topology") selected=(is-href "dc.services.show.topology"))
|
|
||||||
'')
|
|
||||||
(if (eq item.Service.Kind 'terminating-gateway')
|
|
||||||
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
|
||||||
'')
|
|
||||||
(if (eq item.Service.Kind 'ingress-gateway')
|
|
||||||
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
|
||||||
'')
|
|
||||||
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
|
||||||
(if (not-eq item.Service.Kind 'terminating-gateway')
|
|
||||||
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
|
||||||
'')
|
|
||||||
(if (and dc.MeshEnabled item.IsOrigin)
|
|
||||||
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
|
||||||
'')
|
|
||||||
(if (not item.Service.Kind)
|
|
||||||
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
|
||||||
'')
|
|
||||||
)
|
|
||||||
}}/>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="actions">
|
|
||||||
{{#if urls.service}}
|
<BlockSlot @name="error">
|
||||||
<a href={{render-template urls.service (hash
|
<AppError @error={{loader.error}} />
|
||||||
Datacenter=dc.Name
|
</BlockSlot>
|
||||||
Service=(hash Name=item.Service.Service)
|
|
||||||
)}}
|
<BlockSlot @name="disconnected" as |Notification|>
|
||||||
target="_blank"
|
{{#if (eq loader.error.status "404")}}
|
||||||
rel="noopener noreferrer"
|
<Notification @sticky={{true}}>
|
||||||
data-test-dashboard-anchor>
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
Open Dashboard
|
<strong>Warning!</strong>
|
||||||
</a>
|
This service has been deregistered and no longer exists in the catalog.
|
||||||
{{/if}}
|
</p>
|
||||||
</BlockSlot>
|
</Notification>
|
||||||
<BlockSlot @name="content">
|
{{else if (eq loader.error.status "403")}}
|
||||||
<Outlet
|
<Notification @sticky={{true}}>
|
||||||
@name={{routeName}}
|
<p data-notification role="alert" class="error notification-update">
|
||||||
as |o|>
|
<strong>Error!</strong>
|
||||||
{{outlet}}
|
You no longer have access to this service
|
||||||
</Outlet>
|
</p>
|
||||||
</BlockSlot>
|
</Notification>
|
||||||
</AppView>
|
{{else}}
|
||||||
</BlockSlot>
|
<Notification @sticky={{true}}>
|
||||||
</DataLoader>
|
<p data-notification role="alert" class="warning notification-update">
|
||||||
{{/let}}
|
<strong>Warning!</strong>
|
||||||
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
|
</p>
|
||||||
|
</Notification>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
|
||||||
|
<BlockSlot @name="loaded">
|
||||||
|
<AppView>
|
||||||
|
<BlockSlot @name="notification" as |status type item error|>
|
||||||
|
<TopologyMetrics::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@error={{error}}
|
||||||
|
/>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="breadcrumbs">
|
||||||
|
<ol>
|
||||||
|
<li><a data-test-back href={{href-to 'dc.services'}}>All Services</a></li>
|
||||||
|
</ol>
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="header">
|
||||||
|
<h1>
|
||||||
|
{{item.Service.Service}}
|
||||||
|
</h1>
|
||||||
|
<Consul::ExternalSource @item={{item.Service}} />
|
||||||
|
<Consul::Kind @item={{item.Service}} @withInfo={{true}} />
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="nav">
|
||||||
|
{{#if (not-eq item.Service.Kind 'mesh-gateway')}}
|
||||||
|
<TabNav @items={{
|
||||||
|
compact
|
||||||
|
(array
|
||||||
|
(if (and dc.MeshEnabled item.IsMeshOrigin (or (gt proxies.length 0) (eq item.Service.Kind 'ingress-gateway')))
|
||||||
|
(hash label="Topology" href=(href-to "dc.services.show.topology") selected=(is-href "dc.services.show.topology"))
|
||||||
|
'')
|
||||||
|
(if (eq item.Service.Kind 'terminating-gateway')
|
||||||
|
(hash label="Linked Services" href=(href-to "dc.services.show.services") selected=(is-href "dc.services.show.services"))
|
||||||
|
'')
|
||||||
|
(if (eq item.Service.Kind 'ingress-gateway')
|
||||||
|
(hash label="Upstreams" href=(href-to "dc.services.show.upstreams") selected=(is-href "dc.services.show.upstreams"))
|
||||||
|
'')
|
||||||
|
(hash label="Instances" href=(href-to "dc.services.show.instances") selected=(is-href "dc.services.show.instances"))
|
||||||
|
(if (not-eq item.Service.Kind 'terminating-gateway')
|
||||||
|
(hash label="Intentions" href=(href-to "dc.services.show.intentions") selected=(is-href "dc.services.show.intentions"))
|
||||||
|
'')
|
||||||
|
(if (and dc.MeshEnabled item.IsOrigin)
|
||||||
|
(hash label="Routing" href=(href-to "dc.services.show.routing") selected=(is-href "dc.services.show.routing"))
|
||||||
|
'')
|
||||||
|
(if (not item.Service.Kind)
|
||||||
|
(hash label="Tags" href=(href-to "dc.services.show.tags") selected=(is-href "dc.services.show.tags"))
|
||||||
|
'')
|
||||||
|
)
|
||||||
|
}}/>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="actions">
|
||||||
|
{{#if urls.service}}
|
||||||
|
<a href={{render-template urls.service (hash
|
||||||
|
Datacenter=dc.Name
|
||||||
|
Service=(hash Name=item.Service.Service)
|
||||||
|
)}}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
data-test-dashboard-anchor>
|
||||||
|
Open Dashboard
|
||||||
|
</a>
|
||||||
|
{{/if}}
|
||||||
|
</BlockSlot>
|
||||||
|
<BlockSlot @name="content">
|
||||||
|
<Outlet
|
||||||
|
@name={{routeName}}
|
||||||
|
as |o|>
|
||||||
|
{{outlet}}
|
||||||
|
</Outlet>
|
||||||
|
</BlockSlot>
|
||||||
|
</AppView>
|
||||||
|
</BlockSlot>
|
||||||
|
</DataLoader>
|
||||||
|
{{/let}}
|
||||||
|
</Route>
|
|
@ -1,3 +1,6 @@
|
||||||
|
<Route
|
||||||
|
@name={{routeName}}
|
||||||
|
>
|
||||||
<div class="tab-section">
|
<div class="tab-section">
|
||||||
{{#let
|
{{#let
|
||||||
|
|
||||||
|
@ -66,3 +69,4 @@ as |sort filters items|}}
|
||||||
</DataCollection>
|
</DataCollection>
|
||||||
{{/let}}
|
{{/let}}
|
||||||
</div>
|
</div>
|
||||||
|
</Route>
|
||||||
|
|
|
@ -87,6 +87,7 @@
|
||||||
"d3-selection": "^2.0.0",
|
"d3-selection": "^2.0.0",
|
||||||
"d3-shape": "^2.0.0",
|
"d3-shape": "^2.0.0",
|
||||||
"dayjs": "^1.9.3",
|
"dayjs": "^1.9.3",
|
||||||
|
"ember-assign-helper": "^0.3.0",
|
||||||
"ember-auto-import": "^1.5.3",
|
"ember-auto-import": "^1.5.3",
|
||||||
"ember-can": "^3.0.0",
|
"ember-can": "^3.0.0",
|
||||||
"ember-changeset-conditional-validations": "^0.6.0",
|
"ember-changeset-conditional-validations": "^0.6.0",
|
||||||
|
|
Loading…
Reference in New Issue