mirror of https://github.com/status-im/consul.git
ui: Improved filtering and sorting (#8591)
This commit is contained in:
parent
a9df6ac50b
commit
31ad6e39ca
|
@ -0,0 +1,74 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
@position="left"
|
||||
@onchange={{action onfilter.access}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Permissions
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option @value="allow" @selected={{contains 'allow' filter.accesses}}>Allow</Option>
|
||||
<Option @value="deny" @selected={{contains 'deny' filter.accesses}}>Deny</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Action:asc" "Allow to Deny")
|
||||
(array "Action:desc" "Deny to Allow")
|
||||
(array "SourceName:asc" "Source: A to Z")
|
||||
(array "SourceName:desc" "Source: Z to A")
|
||||
(array "DestinationName:asc" "Destination: A to Z")
|
||||
(array "DestinationName:desc" "Destination: Z to A")
|
||||
(array "Precedence:asc" "Precedence: Ascending")
|
||||
(array "Precedence:desc" "Precedence: Descending")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Permission">
|
||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Source">
|
||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Destination">
|
||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Precedence">
|
||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,65 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
class="type-status"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.status}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Health Status
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option class="value-passing" @value="passing" @selected={{contains 'passing' filter.statuses}}>Passing</Option>
|
||||
<Option class="value-warning" @value="warning" @selected={{contains 'warning' filter.statuses}}>Warning</Option>
|
||||
<Option class="value-critical" @value="critical" @selected={{contains 'critical' filter.statuses}}>Failing</Option>
|
||||
<Option class="value-empty" @value="empty" @selected={{contains 'empty' filter.statuses}}>No checks</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Node:asc" "A to Z")
|
||||
(array "Node:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Service Name">
|
||||
<Option @value="Node:asc" @selected={{eq "Node:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Node:desc" @selected={{eq "Node:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
class="select-dcs"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.dc}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Datacenters
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
{{#each dcs as |dc|}}
|
||||
<Option @value={{dc.Name}} @selected={{contains dc.Name filter.dcs}}>{{dc.Name}}</Option>
|
||||
{{/each}}
|
||||
{{/let}}
|
||||
<DataSource @src="/*/*/datacenters" @loading="lazy" @onchange={{action (mut dcs) value="data"}} />
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
<PopoverSelect
|
||||
class="select-type"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.type}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Type
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option @value="global-management" @selected={{contains 'global-management' filter.types}}>Global Management</Option>
|
||||
<Option @value="standard" @selected={{contains 'standard' filter.types}}>Standard</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,3 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({});
|
|
@ -0,0 +1,43 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "CreateIndex:desc" "Newest to oldest")
|
||||
(array "CreateIndex:asc" "Oldest to newest")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Creation">
|
||||
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" sort}}>Newest to oldest</Option>
|
||||
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" sort}}>Oldest to newest</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,3 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({});
|
|
@ -0,0 +1,86 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
class="type-status"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.status}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Health Status
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option class="value-passing" @value="passing" @selected={{contains 'passing' filter.statuses}}>Passing</Option>
|
||||
<Option class="value-warning" @value="warning" @selected={{contains 'warning' filter.statuses}}>Warning</Option>
|
||||
<Option class="value-critical" @value="critical" @selected={{contains 'critical' filter.statuses}}>Failing</Option>
|
||||
<Option class="value-empty" @value="empty" @selected={{contains 'empty' filter.statuses}}>No checks</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
{{#if (gt sources.length 0)}}
|
||||
<PopoverSelect
|
||||
class="type-source"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.source}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Source
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
{{#each sources as |source|}}
|
||||
<Option class={{source}} @value={{source}} @selected={{contains source filter.sources}}>{{source}}</Option>
|
||||
{{/each}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Service Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,111 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
class="type-status"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.status}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Health Status
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option class="value-passing" @value="passing" @selected={{contains 'passing' filter.statuses}}>Passing</Option>
|
||||
<Option class="value-warning" @value="warning" @selected={{contains 'warning' filter.statuses}}>Warning</Option>
|
||||
<Option class="value-critical" @value="critical" @selected={{contains 'critical' filter.statuses}}>Failing</Option>
|
||||
<Option class="value-empty" @value="empty" @selected={{contains 'empty' filter.statuses}}>No checks</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
<PopoverSelect
|
||||
@position="left"
|
||||
@onchange={{action onfilter.type}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Service Type
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option @value="service" @selected={{contains 'service' filter.types}}>Service</Option>
|
||||
<Optgroup @label="Gateway">
|
||||
<Option @value="ingress-gateway" @selected={{contains 'ingress-gateway' filter.types}}>Ingress Gateway</Option>
|
||||
<Option @value="terminating-gateway" @selected={{contains 'terminating-gateway' filter.types}}>Terminating Gateway</Option>
|
||||
<Option @value="mesh-gateway" @selected={{contains 'mesh-gateway' filter.types}}>Mesh Gateway</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Mesh">
|
||||
<Option @value="mesh-enabled" @selected={{contains 'mesh-enabled' filter.types}}>In service mesh</Option>
|
||||
<Option @value="mesh-disabled" @selected={{contains 'mesh-disabled' filter.types}}>Not in service mesh</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
{{#if (gt sources.length 0)}}
|
||||
<PopoverSelect
|
||||
class="type-source"
|
||||
@position="left"
|
||||
@onchange={{action onfilter.source}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Source
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
{{#each sources as |source|}}
|
||||
<Option class={{source}} @value={{source}} @selected={{contains source filter.sources}}>{{source}}</Option>
|
||||
{{/each}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Service Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
@position="left"
|
||||
@onchange={{action onfilter.type}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Type
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option @value="global-management" @selected={{contains 'global-management' filter.types}}>Global Management</Option>
|
||||
<Option @value="global" @selected={{contains 'global' filter.types}}>Global</Option>
|
||||
<Option @value="local" @selected={{contains 'local' filter.types}}>Local</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "CreateTime:desc" "Newest to oldest")
|
||||
(array "CreateTime:asc" "Oldest to newest")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Creation">
|
||||
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" sort}}>Newest to oldest</Option>
|
||||
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" sort}}>Oldest to newest</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,3 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({});
|
|
@ -0,0 +1,62 @@
|
|||
<form class="filter-bar">
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="filters">
|
||||
<PopoverSelect
|
||||
@position="left"
|
||||
@onchange={{action onfilter.instance}}
|
||||
@multiple={{true}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
Type
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Option @value="registered" @selected={{contains 'registered' filter.instances}}>Registered</Option>
|
||||
<Option @value="not-registered" @selected={{contains 'not-registered' filter.instances}}>Not Registered</Option>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
data-test-sort-control
|
||||
@position="right"
|
||||
@onchange={{action onsort}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Service Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -6,7 +6,7 @@ export default Component.extend(Slotted, {
|
|||
tagName: '',
|
||||
dom: service('dom'),
|
||||
multiple: false,
|
||||
subtractive: true,
|
||||
subtractive: false,
|
||||
onchange: function() {},
|
||||
addOption: function(option) {
|
||||
if (typeof this._options === 'undefined') {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{{#let components.MenuItem as |MenuItem|}}
|
||||
<MenuItem
|
||||
class={{if selected 'is-active'}}
|
||||
...attributes
|
||||
@onclick={{action 'click'}}
|
||||
@selected={{selected}}
|
||||
>
|
||||
|
|
|
@ -2,6 +2,8 @@ import Controller from '@ember/controller';
|
|||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
dc: 'dc',
|
||||
type: 'type',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import Controller from '@ember/controller';
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
access: 'access',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
|
|
|
@ -3,6 +3,7 @@ import Controller from '@ember/controller';
|
|||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
status: 'status',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
|
|
|
@ -4,6 +4,9 @@ import { computed } from '@ember/object';
|
|||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
status: 'status',
|
||||
source: 'source',
|
||||
type: 'type',
|
||||
search: {
|
||||
as: 'filter',
|
||||
},
|
||||
|
@ -13,4 +16,11 @@ export default Controller.extend({
|
|||
return item.Kind !== 'connect-proxy';
|
||||
});
|
||||
}),
|
||||
externalSources: computed('services', function() {
|
||||
const sources = this.services.reduce(function(prev, item) {
|
||||
return prev.concat(item.ExternalSources || []);
|
||||
}, []);
|
||||
// unique, non-empty values, alpha sort
|
||||
return [...new Set(sources)].filter(Boolean).sort();
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
status: 'status',
|
||||
source: 'source',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
externalSources: computed('items', function() {
|
||||
const sources = this.items.reduce(function(prev, item) {
|
||||
return prev.concat(item.ExternalSources || []);
|
||||
}, []);
|
||||
// unique, non-empty values, alpha sort
|
||||
return [...new Set(sources)].filter(Boolean).sort();
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import Controller from '@ember/controller';
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
instance: 'instance',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,12 @@
|
|||
import Controller from '@ember/controller';
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
instance: 'instance',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
export default () => ({ accesses = [] }) => item => {
|
||||
if (accesses.length > 0) {
|
||||
if (accesses.includes(item.Action)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
export default () => ({ statuses = [] }) => {
|
||||
return item => {
|
||||
if (statuses.length > 0 && !statuses.includes(item.Status)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
import setHelpers from 'mnemonist/set';
|
||||
export default () => ({ dcs = [], types = [] }) => {
|
||||
const typeIncludes = ['global-management', 'standard'].reduce((prev, item) => {
|
||||
prev[item] = types.includes(item);
|
||||
return prev;
|
||||
}, {});
|
||||
const selectedDcs = new Set(dcs);
|
||||
return item => {
|
||||
let type = true;
|
||||
let dc = true;
|
||||
if (types.length > 0) {
|
||||
type = false;
|
||||
if (typeIncludes['global-management'] && item.isGlobalManagement) {
|
||||
type = true;
|
||||
}
|
||||
if (typeIncludes['standard'] && !item.isGlobalManagement) {
|
||||
type = true;
|
||||
}
|
||||
}
|
||||
if (dcs.length > 0) {
|
||||
// if datacenters is undefined it means the policy is applicable to all datacenters
|
||||
dc =
|
||||
typeof item.Datacenters === 'undefined' ||
|
||||
setHelpers.intersectionSize(selectedDcs, new Set(item.Datacenters)) > 0;
|
||||
}
|
||||
return type && dc;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,19 @@
|
|||
import setHelpers from 'mnemonist/set';
|
||||
export default () => ({ sources = [], statuses = [] }) => {
|
||||
const uniqueSources = new Set(sources);
|
||||
return item => {
|
||||
if (statuses.length > 0) {
|
||||
if (statuses.includes(item.Status)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (sources.length > 0) {
|
||||
if (setHelpers.intersectionSize(uniqueSources, new Set(item.ExternalSources || [])) !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
import setHelpers from 'mnemonist/set';
|
||||
export default () => ({ instances = [], sources = [], statuses = [], types = [] }) => {
|
||||
const uniqueSources = new Set(sources);
|
||||
const typeIncludes = [
|
||||
'ingress-gateway',
|
||||
'terminating-gateway',
|
||||
'mesh-gateway',
|
||||
'service',
|
||||
'mesh-enabled',
|
||||
'mesh-disabled',
|
||||
].reduce((prev, item) => {
|
||||
prev[item] = types.includes(item);
|
||||
return prev;
|
||||
}, {});
|
||||
const instanceIncludes = ['registered', 'not-registered'].reduce((prev, item) => {
|
||||
prev[item] = instances.includes(item);
|
||||
return prev;
|
||||
}, {});
|
||||
return item => {
|
||||
if (statuses.length > 0) {
|
||||
if (statuses.includes(item.MeshStatus)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (instances.length > 0) {
|
||||
if (item.InstanceCount > 0) {
|
||||
if (instanceIncludes['registered']) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (instanceIncludes['not-registered']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (types.length > 0) {
|
||||
if (typeIncludes['ingress-gateway'] && item.Kind === 'ingress-gateway') {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['terminating-gateway'] && item.Kind === 'terminating-gateway') {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['mesh-gateway'] && item.Kind === 'mesh-gateway') {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['service'] && typeof item.Kind === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['mesh-enabled'] && typeof item.Proxy !== 'undefined') {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['mesh-disabled'] && typeof item.Proxy === 'undefined') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (sources.length > 0) {
|
||||
if (setHelpers.intersectionSize(uniqueSources, new Set(item.ExternalSources || [])) !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,21 @@
|
|||
export default () => ({ types = [] }) => {
|
||||
const typeIncludes = ['global-management', 'global', 'local'].reduce((prev, item) => {
|
||||
prev[item] = types.includes(item);
|
||||
return prev;
|
||||
}, {});
|
||||
return item => {
|
||||
if (types.length > 0) {
|
||||
if (typeIncludes['global-management'] && item.isGlobalManagement) {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['global'] && !item.Local) {
|
||||
return true;
|
||||
}
|
||||
if (typeIncludes['local'] && item.Local) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
import Helper from '@ember/component/helper';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Helper.extend({
|
||||
filter: service('filter'),
|
||||
compute([type, filters], hash) {
|
||||
return this.filter.predicate(type)(filters);
|
||||
},
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
import { get } from '@ember/object';
|
||||
|
||||
const MANAGEMENT_ID = '00000000-0000-0000-0000-000000000001';
|
||||
import { MANAGEMENT_ID } from 'consul-ui/models/policy';
|
||||
|
||||
export default helper(function policyGroup([items] /*, hash*/) {
|
||||
return items.reduce(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import service from 'consul-ui/sort/comparators/service';
|
||||
import serviceInstance from 'consul-ui/sort/comparators/service-instance';
|
||||
import kv from 'consul-ui/sort/comparators/kv';
|
||||
import check from 'consul-ui/sort/comparators/check';
|
||||
import intention from 'consul-ui/sort/comparators/intention';
|
||||
|
@ -13,6 +14,7 @@ export function initialize(container) {
|
|||
const Sort = container.resolveRegistration('service:sort');
|
||||
const comparators = {
|
||||
service: service(),
|
||||
serviceInstance: serviceInstance(),
|
||||
kv: kv(),
|
||||
check: check(),
|
||||
intention: intention(),
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
export const PRIMARY_KEY = 'uid';
|
||||
export const SLUG_KEY = 'ID';
|
||||
|
||||
export const MANAGEMENT_ID = '00000000-0000-0000-0000-000000000001';
|
||||
|
||||
export default Model.extend({
|
||||
[PRIMARY_KEY]: attr('string'),
|
||||
[SLUG_KEY]: attr('string'),
|
||||
|
@ -19,6 +22,9 @@ export default Model.extend({
|
|||
// frontend only for ordering where CreateIndex can't be used
|
||||
CreateTime: attr('date', { defaultValue: 0 }),
|
||||
//
|
||||
isGlobalManagement: computed('ID', function() {
|
||||
return this.ID === MANAGEMENT_ID;
|
||||
}),
|
||||
Datacenter: attr('string'),
|
||||
Namespace: attr('string'),
|
||||
SyncTime: attr('number'),
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
import { belongsTo } from 'ember-data/relationships';
|
||||
import { filter, alias } from '@ember/object/computed';
|
||||
import { computed } from '@ember/object';
|
||||
import { or, filter, alias } from '@ember/object/computed';
|
||||
|
||||
export const PRIMARY_KEY = 'uid';
|
||||
export const SLUG_KEY = 'Node.Node,Service.ID';
|
||||
|
@ -19,13 +20,52 @@ export default Model.extend({
|
|||
Checks: attr(),
|
||||
SyncTime: attr('number'),
|
||||
meta: attr(),
|
||||
Name: or('Service.ID', 'Service.Service'),
|
||||
Tags: alias('Service.Tags'),
|
||||
Meta: alias('Service.Meta'),
|
||||
Namespace: alias('Service.Namespace'),
|
||||
ServiceChecks: filter('Checks', function(item, i, arr) {
|
||||
ExternalSources: computed('Service.Meta', function() {
|
||||
const sources = Object.entries(this.Service.Meta || {})
|
||||
.filter(([key, value]) => key === 'external-source')
|
||||
.map(([key, value]) => {
|
||||
return value;
|
||||
});
|
||||
return [...new Set(sources)];
|
||||
}),
|
||||
ServiceChecks: filter('Checks.[]', function(item, i, arr) {
|
||||
return item.ServiceID !== '';
|
||||
}),
|
||||
NodeChecks: filter('Checks', function(item, i, arr) {
|
||||
NodeChecks: filter('Checks.[]', function(item, i, arr) {
|
||||
return item.ServiceID === '';
|
||||
}),
|
||||
Status: computed('ChecksPassing', 'ChecksWarning', 'ChecksCritical', function() {
|
||||
switch (true) {
|
||||
case this.ChecksCritical.length !== 0:
|
||||
return 'critical';
|
||||
case this.ChecksWarning.length !== 0:
|
||||
return 'warning';
|
||||
case this.ChecksPassing.length !== 0:
|
||||
return 'passing';
|
||||
default:
|
||||
return 'empty';
|
||||
}
|
||||
}),
|
||||
ChecksPassing: computed('Checks.[]', function() {
|
||||
return this.Checks.filter(item => item.Status === 'passing');
|
||||
}),
|
||||
ChecksWarning: computed('Checks.[]', function() {
|
||||
return this.Checks.filter(item => item.Status === 'warning');
|
||||
}),
|
||||
ChecksCritical: computed('Checks.[]', function() {
|
||||
return this.Checks.filter(item => item.Status === 'critical');
|
||||
}),
|
||||
PercentageChecksPassing: computed('Checks.[]', 'ChecksPassing', function() {
|
||||
return (this.ChecksPassing.length / this.Checks.length) * 100;
|
||||
}),
|
||||
PercentageChecksWarning: computed('Checks.[]', 'ChecksWarning', function() {
|
||||
return (this.ChecksWarning.length / this.Checks.length) * 100;
|
||||
}),
|
||||
PercentageChecksCritical: computed('Checks.[]', 'ChecksCritical', function() {
|
||||
return (this.ChecksCritical.length / this.Checks.length) * 100;
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
import { computed } from '@ember/object';
|
||||
import { MANAGEMENT_ID } from 'consul-ui/models/policy';
|
||||
|
||||
export const PRIMARY_KEY = 'uid';
|
||||
export const SLUG_KEY = 'AccessorID';
|
||||
|
@ -24,6 +26,9 @@ export default Model.extend({
|
|||
Datacenter: attr('string'),
|
||||
Namespace: attr('string'),
|
||||
Local: attr('boolean'),
|
||||
isGlobalManagement: computed('Policies.[]', function() {
|
||||
return (this.Policies || []).find(item => item.ID === MANAGEMENT_ID);
|
||||
}),
|
||||
Policies: attr({
|
||||
defaultValue: function() {
|
||||
return [];
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
import Service from '@ember/service';
|
||||
|
||||
import service from 'consul-ui/filter/predicates/service';
|
||||
import serviceInstance from 'consul-ui/filter/predicates/service-instance';
|
||||
import node from 'consul-ui/filter/predicates/node';
|
||||
import intention from 'consul-ui/filter/predicates/intention';
|
||||
import token from 'consul-ui/filter/predicates/token';
|
||||
import policy from 'consul-ui/filter/predicates/policy';
|
||||
|
||||
const predicates = {
|
||||
service: service(),
|
||||
serviceInstance: serviceInstance(),
|
||||
node: node(),
|
||||
intention: intention(),
|
||||
token: token(),
|
||||
policy: policy(),
|
||||
};
|
||||
|
||||
export default Service.extend({
|
||||
predicate: function(type) {
|
||||
return predicates[type];
|
||||
},
|
||||
});
|
|
@ -16,7 +16,7 @@ export default RepositoryService.extend({
|
|||
const query = {
|
||||
dc: dc,
|
||||
nspace: nspace,
|
||||
filter: `SourceName == "${slug}" or DestinationName == "${slug}"`,
|
||||
filter: `SourceName == "${slug}" or DestinationName == "${slug}" or SourceName == "*" or DestinationName == "*"`,
|
||||
};
|
||||
if (typeof configuration.cursor !== 'undefined') {
|
||||
query.index = configuration.cursor;
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
export default () => key => {
|
||||
if (key.startsWith('Status:')) {
|
||||
const [, dir] = key.split(':');
|
||||
const props = [
|
||||
'PercentageChecksPassing',
|
||||
'PercentageChecksWarning',
|
||||
'PercentageChecksCritical',
|
||||
];
|
||||
if (dir === 'asc') {
|
||||
props.reverse();
|
||||
}
|
||||
return function(a, b) {
|
||||
for (let i in props) {
|
||||
let prop = props[i];
|
||||
if (a[prop] === b[prop]) {
|
||||
continue;
|
||||
}
|
||||
return a[prop] > b[prop] ? -1 : 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
return key;
|
||||
};
|
|
@ -1,5 +1,2 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
||||
%sort-button {
|
||||
@extend %split-button;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
width: 16px;
|
||||
height: 16px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
%popover-menu-toggle:checked + label > *::after {
|
||||
@extend %with-chevron-up-mask;
|
||||
|
|
|
@ -948,6 +948,16 @@
|
|||
mask-image: $logo-bitbucket-monochrome-svg;
|
||||
}
|
||||
|
||||
%with-logo-consul-color-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $consul-logo-color-svg;
|
||||
}
|
||||
%with-logo-consul-color-mask {
|
||||
@extend %with-mask;
|
||||
-webkit-mask-image: $consul-logo-color-svg;
|
||||
mask-image: $consul-logo-color-svg;
|
||||
}
|
||||
|
||||
%with-logo-gcp-color-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $logo-gcp-color-svg;
|
||||
|
@ -1047,6 +1057,15 @@
|
|||
-webkit-mask-image: $logo-microsoft-color-svg;
|
||||
mask-image: $logo-microsoft-color-svg;
|
||||
}
|
||||
%with-logo-nomad-color-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $nomad-logo-color-svg;
|
||||
}
|
||||
%with-logo-nomad-color-mask {
|
||||
@extend %with-mask;
|
||||
-webkit-mask-image: $nomad-logo-color-svg;
|
||||
mask-image: $nomad-logo-color-svg;
|
||||
}
|
||||
|
||||
%with-logo-okta-color-icon {
|
||||
@extend %with-icon;
|
||||
|
@ -1098,6 +1117,15 @@
|
|||
mask-image: $logo-slack-monochrome-svg;
|
||||
}
|
||||
|
||||
%with-logo-terraform-color-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $terraform-logo-color-svg;
|
||||
}
|
||||
%with-logo-terraform-color-mask {
|
||||
@extend %with-mask;
|
||||
-webkit-mask-image: $terraform-logo-color-svg;
|
||||
mask-image: $terraform-logo-color-svg;
|
||||
}
|
||||
%with-logo-vmware-color-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $logo-vmware-color-svg;
|
||||
|
|
|
@ -72,10 +72,10 @@ main {
|
|||
display: none;
|
||||
}
|
||||
#toolbar-toggle:checked + * {
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
html.template-service.template-show #toolbar-toggle + * {
|
||||
display: block;
|
||||
display: flex;
|
||||
padding: 4px;
|
||||
}
|
||||
html.template-service.template-show .actions {
|
||||
|
|
|
@ -3,14 +3,18 @@
|
|||
.filter-bar {
|
||||
@extend %filter-bar;
|
||||
}
|
||||
%filter-bar {
|
||||
%filter-bar .popover-select {
|
||||
height: 35px;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
%filter-bar:not(.with-sort) {
|
||||
%filter-bar [role='menuitem'] {
|
||||
justify-content: normal !important;
|
||||
}
|
||||
html.template-acl.template-list .filter-bar {
|
||||
@extend %filter-bar-reversed;
|
||||
}
|
||||
%filter-bar [role='radiogroup'] {
|
||||
html.template-acl.template-list .filter-bar [role='radiogroup'] {
|
||||
@extend %expanded-single-select;
|
||||
}
|
||||
%filter-bar span::before {
|
||||
|
@ -19,6 +23,11 @@
|
|||
margin-left: -2px;
|
||||
}
|
||||
|
||||
%filter-bar .popover-menu > [type='checkbox']:checked + label button {
|
||||
color: $blue-500;
|
||||
background-color: $gray-100;
|
||||
}
|
||||
|
||||
%filter-bar .value-passing span::before {
|
||||
@extend %with-check-circle-fill-icon, %as-pseudo;
|
||||
}
|
||||
|
|
|
@ -5,34 +5,50 @@
|
|||
margin-top: 0 !important;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
%filter-bar .filters {
|
||||
display: flex;
|
||||
margin-right: 12px;
|
||||
}
|
||||
%filter-bar .filters > *:not(:last-child) {
|
||||
margin-right: 6px;
|
||||
}
|
||||
%filter-bar + :not(.notice) {
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
flex: 0 1 auto;
|
||||
width: auto;
|
||||
}
|
||||
%filter-bar-reversed {
|
||||
flex-direction: row-reverse;
|
||||
padding: 4px;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
flex: 0 1 auto;
|
||||
%filter-bar-reversed fieldset {
|
||||
min-width: 210px;
|
||||
width: auto;
|
||||
}
|
||||
%filter-bar fieldset:first-child:not(:last-child) {
|
||||
flex: 1 1 auto;
|
||||
margin-right: 12px;
|
||||
}
|
||||
%filter-bar-reversed fieldset:first-child:not(:last-child) {
|
||||
flex: 0 1 auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
%filter-bar-reversed fieldset {
|
||||
min-width: 210px;
|
||||
width: auto;
|
||||
}
|
||||
%filter-bar-reversed > *:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
@media #{$--horizontal-filters} {
|
||||
%filter-bar fieldset:first-child:not(:last-child) {
|
||||
flex: 1 1 auto;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
@media #{$--lt-horizontal-filters} {
|
||||
%filter-bar {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
flex: 0 1 100%;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
%filter-bar-reversed > *:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,6 @@
|
|||
}
|
||||
}
|
||||
@media #{$--lt-horizontal-selects} {
|
||||
%filter-bar label:not(:last-child) {
|
||||
border-bottom: $decor-border-100;
|
||||
}
|
||||
}
|
||||
%filter-bar [role='radiogroup'] label {
|
||||
cursor: pointer;
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
right: auto;
|
||||
top: 28px !important;
|
||||
}
|
||||
%main-nav-horizontal .popover-menu > label > button::after {
|
||||
top: 2px;
|
||||
}
|
||||
@media #{$--horizontal-nav} {
|
||||
%main-nav-horizontal > ul,
|
||||
%main-nav-horizontal-panel {
|
||||
|
|
|
@ -1,6 +1,64 @@
|
|||
.popover-select {
|
||||
@extend %popover-select;
|
||||
}
|
||||
%popover-select label {
|
||||
height: 100%;
|
||||
}
|
||||
%popover-select label > * {
|
||||
@extend %button;
|
||||
padding: 0 8px !important;
|
||||
height: 100% !important;
|
||||
justify-content: space-between !important;
|
||||
min-width: auto !important;
|
||||
}
|
||||
%popover-select label > *::after {
|
||||
margin-left: 6px;
|
||||
}
|
||||
%popover-select.type-sort label > * {
|
||||
@extend %sort-button;
|
||||
}
|
||||
|
||||
%popover-select.type-access button::before,
|
||||
%popover-select.type-source button::before,
|
||||
%popover-select.type-status button::before {
|
||||
margin-right: 10px;
|
||||
}
|
||||
%popover-select .value-allow button::before,
|
||||
%popover-select .value-passing button::before {
|
||||
@extend %with-check-circle-fill-mask, %as-pseudo;
|
||||
color: $green-500;
|
||||
}
|
||||
%popover-select .value-warning button::before {
|
||||
@extend %with-alert-triangle-mask, %as-pseudo;
|
||||
color: $orange-500;
|
||||
}
|
||||
%popover-select .value-deny button::before,
|
||||
%popover-select .value-critical button::before {
|
||||
@extend %with-cancel-square-fill-mask, %as-pseudo;
|
||||
color: $red-500;
|
||||
}
|
||||
%popover-select .value-empty button::before {
|
||||
@extend %with-minus-square-fill-mask, %as-pseudo;
|
||||
color: $gray-400;
|
||||
}
|
||||
%popover-select.type-source li button {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
%popover-select.type-source li.aws button {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
%popover-select .aws button::before {
|
||||
@extend %with-logo-aws-color-icon, %as-pseudo;
|
||||
}
|
||||
%popover-select .kubernetes button::before {
|
||||
@extend %with-logo-kubernetes-color-icon, %as-pseudo;
|
||||
}
|
||||
%popover-select .consul button::before {
|
||||
@extend %with-logo-consul-color-icon, %as-pseudo;
|
||||
}
|
||||
%popover-select .nomad button::before {
|
||||
@extend %with-logo-nomad-color-icon, %as-pseudo;
|
||||
}
|
||||
%popover-select .terraform button::before {
|
||||
@extend %with-logo-terraform-color-icon, %as-pseudo;
|
||||
}
|
||||
|
|
|
@ -3,115 +3,100 @@
|
|||
{{else}}
|
||||
{{title 'Access Controls'}}
|
||||
{{/if}}
|
||||
{{#let (hash
|
||||
types=(if type (split type ',') undefined)
|
||||
dcs=(if dc (split dc ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<AppView
|
||||
@class="policy list"
|
||||
@loading={{isLoading}}
|
||||
@authorized={{isAuthorized}}
|
||||
@enabled={{isEnabled}}
|
||||
>
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/acls/policies/notifications'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Access Controls
|
||||
</h1>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="nav">
|
||||
{{#if isAuthorized }}
|
||||
{{partial 'dc/acls/nav'}}
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="disabled">
|
||||
{{partial 'dc/acls/disabled'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="authorization">
|
||||
{{partial 'dc/acls/authorization'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<ConsulPolicySearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<AppView
|
||||
@class="policy list"
|
||||
@loading={{isLoading}}
|
||||
@authorized={{isAuthorized}}
|
||||
@enabled={{isEnabled}}
|
||||
>
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/acls/policies/notifications'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Access Controls
|
||||
</h1>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="nav">
|
||||
{{#if isAuthorized }}
|
||||
{{partial 'dc/acls/nav'}}
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="disabled">
|
||||
{{partial 'dc/acls/disabled'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="authorization">
|
||||
{{partial 'dc/acls/authorization'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'policy' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'policy' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulPolicyList
|
||||
@items={{filtered}}
|
||||
@ondelete={{action send 'delete'}}
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
dc=(action (mut dc) value="target.selectedItems")
|
||||
type=(action (mut type) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No policies found
|
||||
{{else}}
|
||||
Welcome to Policies
|
||||
{{/if}}
|
||||
</h2>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (filter (filter-predicate 'policy' filters) items) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'policy' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'policy' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulPolicyList
|
||||
@items={{searched}}
|
||||
@ondelete={{action send 'delete'}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
{{#if (gt items.length 0)}}
|
||||
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any policies, or you may not have access to view policies yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No policies found
|
||||
{{else}}
|
||||
Welcome to Policies
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
{{#if (gt items.length 0)}}
|
||||
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any policies, or you may not have access to view policies yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<li class="docs-link">
|
||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
|
||||
</li>
|
||||
<li class="learn-link">
|
||||
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<li class="docs-link">
|
||||
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
|
||||
</li>
|
||||
<li class="learn-link">
|
||||
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
|
@ -35,46 +35,13 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
<ConsulRoleSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "CreateIndex:desc" "Newest to oldest")
|
||||
(array "CreateIndex:asc" "Oldest to newest")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Creation">
|
||||
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" sort}}>Newest to oldest</Option>
|
||||
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" sort}}>Oldest to newest</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
|
|
|
@ -4,113 +4,97 @@
|
|||
{{title 'Access Controls'}}
|
||||
{{/if}}
|
||||
|
||||
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
||||
<AppView
|
||||
@class="token list"
|
||||
@loading={{isLoading}}
|
||||
@authorized={{isAuthorized}}
|
||||
@enabled={{isEnabled}}
|
||||
>
|
||||
<BlockSlot @name="notification" as |status type subject|>
|
||||
{{partial 'dc/acls/tokens/notifications'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Access Controls
|
||||
</h1>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="nav">
|
||||
{{#if isAuthorized }}
|
||||
{{partial 'dc/acls/nav'}}
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="disabled">
|
||||
{{partial 'dc/acls/disabled'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="authorization">
|
||||
{{partial 'dc/acls/authorization'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0)}}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "CreateTime:desc" "Newest to oldest")
|
||||
(array "CreateTime:asc" "Oldest to newest")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Creation">
|
||||
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" sort}}>Newest to oldest</Option>
|
||||
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" sort}}>Oldest to newest</Option>
|
||||
</Optgroup>
|
||||
{{#let (hash
|
||||
types=(if type (split type ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
||||
<AppView
|
||||
@class="token list"
|
||||
@loading={{isLoading}}
|
||||
@authorized={{isAuthorized}}
|
||||
@enabled={{isEnabled}}
|
||||
>
|
||||
<BlockSlot @name="notification" as |status type subject|>
|
||||
{{partial 'dc/acls/tokens/notifications'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Access Controls
|
||||
</h1>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="nav">
|
||||
{{#if isAuthorized }}
|
||||
{{partial 'dc/acls/nav'}}
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="disabled">
|
||||
{{partial 'dc/acls/disabled'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="authorization">
|
||||
{{partial 'dc/acls/authorization'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0)}}
|
||||
<ConsulTokenSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
type=(action (mut type) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (token/is-legacy items)}}
|
||||
<p data-test-notification-update class="notice info"><strong>Update.</strong> We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||
{{/if}}
|
||||
{{#let (filter (filter-predicate 'token' filters) items) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'token' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'token' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulTokenList
|
||||
@items={{searched}}
|
||||
@token={{token}}
|
||||
@onuse={{action send 'use'}}
|
||||
@ondelete={{action send 'delete'}}
|
||||
@onlogout={{action send 'logout'}}
|
||||
@onclone={{action send 'clone'}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No tokens found
|
||||
{{else}}
|
||||
Welcome to ACL Tokens
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
{{#if (gt items.length 0)}}
|
||||
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any tokens, or you may not have access to view tokens yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (token/is-legacy items)}}
|
||||
<p data-test-notification-update class="notice info"><strong>Update.</strong> We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||
{{/if}}
|
||||
{{#let (sort-by (comparator 'token' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'token' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulTokenList
|
||||
@items={{filtered}}
|
||||
@token={{token}}
|
||||
@onuse={{action send 'use'}}
|
||||
@ondelete={{action send 'delete'}}
|
||||
@onlogout={{action send 'logout'}}
|
||||
@onclone={{action send 'clone'}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No tokens found
|
||||
{{else}}
|
||||
Welcome to ACL Tokens
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
{{#if (gt items.length 0)}}
|
||||
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any tokens, or you may not have access to view tokens yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
|
|
|
@ -6,11 +6,15 @@
|
|||
</BlockSlot>
|
||||
|
||||
<BlockSlot @name="loaded">
|
||||
{{#let api.data as |items|}}
|
||||
{{#let (hash
|
||||
accesses=(if access (split access ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Action:asc") as |sort|}}
|
||||
<AppView @class="intention list">
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Intentions <em>{{format-number api.data.length}} total</em>
|
||||
Intentions <em>{{format-number items.length}} total</em>
|
||||
</h1>
|
||||
<label for="toolbar-toggle"></label>
|
||||
</BlockSlot>
|
||||
|
@ -18,73 +22,34 @@
|
|||
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt api.data.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
{{#if (gt items.length 0) }}
|
||||
<ConsulIntentionSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Action:asc" "Allow to Deny")
|
||||
(array "Action:desc" "Deny to Allow")
|
||||
(array "SourceName:asc" "Source: A to Z")
|
||||
(array "SourceName:desc" "Source: Z to A")
|
||||
(array "DestinationName:asc" "Destination: A to Z")
|
||||
(array "DestinationName:desc" "Destination: Z to A")
|
||||
(array "Precedence:asc" "Precedence: Ascending")
|
||||
(array "Precedence:desc" "Precedence: Descending")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Permission">
|
||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Source">
|
||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Destination">
|
||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Precedence">
|
||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
access=(action (mut access) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'intention' sort) api.data) as |sorted|}}
|
||||
{{#let (filter (filter-predicate 'intention' filters) items) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'intention' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="content" as |filtered|>
|
||||
<BlockSlot @name="content" as |searched|>
|
||||
<ConsulIntentionList
|
||||
@items={{filtered}}
|
||||
@items={{searched}}
|
||||
@ondelete={{refresh-route}}
|
||||
>
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>
|
||||
{{#if (gt api.data.length 0)}}
|
||||
{{#if (gt items.length 0)}}
|
||||
No intentions found
|
||||
{{else}}
|
||||
Welcome to Intentions
|
||||
|
@ -93,7 +58,7 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
{{#if (gt api.data.length 0)}}
|
||||
{{#if (gt items.length 0)}}
|
||||
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
||||
|
@ -112,9 +77,12 @@
|
|||
</ConsulIntentionList>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</DataLoader>
|
|
@ -23,46 +23,48 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Key:asc" "A to Z")
|
||||
(array "Key:desc" "Z to A")
|
||||
(array "isFolder:desc" "Folders to Keys")
|
||||
(array "isFolder:asc" "Keys to Folders")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Key:asc" @selected={{eq "Key:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Key:desc" @selected={{eq "Key:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Type">
|
||||
<Option @value="isFolder:desc" @selected={{eq "isFolder:desc" sort}}>Folders to Keys</Option>
|
||||
<Option @value="isFolder:asc" @selected={{eq "isFolder:asc" sort}}>Keys to Folders</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
<form class="filter-bar with-sort">
|
||||
<FreetextFilter
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@value={{search}}
|
||||
@placeholder="Search"
|
||||
/>
|
||||
<div class="sort">
|
||||
<PopoverSelect
|
||||
class="type-sort"
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Key:asc" "A to Z")
|
||||
(array "Key:desc" "Z to A")
|
||||
(array "isFolder:desc" "Folders to Keys")
|
||||
(array "isFolder:asc" "Keys to Folders")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Key:asc" @selected={{eq "Key:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Key:desc" @selected={{eq "Key:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Type">
|
||||
<Option @value="isFolder:desc" @selected={{eq "isFolder:desc" sort}}>Folders to Keys</Option>
|
||||
<Option @value="isFolder:asc" @selected={{eq "isFolder:asc" sort}}>Keys to Folders</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</div>
|
||||
</form>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{{title 'Nodes'}}
|
||||
<EventSource @src={{items}} />
|
||||
{{#let (hash
|
||||
statuses=(if status (split status ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Node:asc") as |sort|}}
|
||||
<AppView @class="node list">
|
||||
<BlockSlot @name="header">
|
||||
|
@ -10,53 +13,26 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Node:asc" "A to Z")
|
||||
(array "Node:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Node:asc" @selected={{eq "Node:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Node:desc" @selected={{eq "Node:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
<ConsulNodeSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
status=(action (mut status) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'node' sort) items) as |sorted|}}
|
||||
{{#let (filter (filter-predicate 'node' filters) items) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'node' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'node' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulNodeList @items={{filtered}} @leader={{leader}} />
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulNodeList @items={{searched}} @leader={{leader}} />
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState>
|
||||
|
@ -68,7 +44,9 @@
|
|||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
{{/let}}
|
|
@ -15,40 +15,14 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0)}}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
<ConsulNspaceSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
|
|
|
@ -1,65 +1,46 @@
|
|||
{{title 'Services'}}
|
||||
<EventSource @src={{items}} />
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
{{#let (hash
|
||||
statuses=(if status (split status ',') undefined)
|
||||
types=(if type (split type ',') undefined)
|
||||
sources=(if source (split source ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<AppView @class="service list">
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/services/notifications'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Services <em>{{format-number services.length}} total</em>
|
||||
Services <em>{{format-number services.length}} total</em>
|
||||
</h1>
|
||||
<label for="toolbar-toggle"></label>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt services.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
<ConsulServiceSearchBar
|
||||
@sources={{externalSources}}
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Service Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
status=(action (mut status) value="target.selectedItems")
|
||||
type=(action (mut type) value="target.selectedItems")
|
||||
source=(action (mut source) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'service' sort) services) as |sorted|}}
|
||||
{{#let (filter (filter-predicate 'service' filters) services) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'service' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceList @items={{filtered}}/>
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulServiceList @items={{searched}} />
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
|
@ -92,7 +73,9 @@
|
|||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
|
|
|
@ -1,15 +1,32 @@
|
|||
<div id="instances" class="tab-section">
|
||||
<div role="tabpanel">
|
||||
{{#if (gt items.length 0) }}
|
||||
{{#let (hash
|
||||
statuses=(if status (split status ',') undefined)
|
||||
sources=(if source (split source ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
{{#if (gt items.length 0) }}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
<ConsulServiceInstanceSearchBar
|
||||
@sources={{externalSources}}
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable 'serviceInstance' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{filtered}}/>
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
status=(action (mut status) value="target.selectedItems")
|
||||
source=(action (mut source) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#let (filter (filter-predicate 'serviceInstance' filters) items) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'serviceInstance' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'serviceInstance' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{searched}}/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState>
|
||||
|
@ -21,5 +38,9 @@
|
|||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,87 +3,55 @@
|
|||
<ErrorState @error={{api.error}} />
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="loaded">
|
||||
{{#let api.data as |items|}}
|
||||
{{#let (hash
|
||||
accesses=(if access (split access ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Action:asc") as |sort|}}
|
||||
<div id="intentions" class="tab-section">
|
||||
<div role="tabpanel">
|
||||
<Portal @target="app-view-actions">
|
||||
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
|
||||
</Portal>
|
||||
{{#if (gt api.data.length 0) }}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Action:asc" "Allow to Deny")
|
||||
(array "Action:desc" "Deny to Allow")
|
||||
(array "SourceName:asc" "Source: A to Z")
|
||||
(array "SourceName:desc" "Source: Z to A")
|
||||
(array "DestinationName:asc" "Destination: A to Z")
|
||||
(array "DestinationName:desc" "Destination: Z to A")
|
||||
(array "Precedence:asc" "Precedence: Ascending")
|
||||
(array "Precedence:desc" "Precedence: Descending")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Permission">
|
||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Source">
|
||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Destination">
|
||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Precedence">
|
||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
{{#let (sort-by (comparator 'intention' sort) api.data) as |sorted|}}
|
||||
{{#if (gt items.length 0) }}
|
||||
<ConsulIntentionSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
access=(action (mut access) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#let (filter (filter-predicate 'intention' filters) items) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'intention' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="content" as |filtered|>
|
||||
<BlockSlot @name="content" as |searched|>
|
||||
<ConsulIntentionList
|
||||
@items={{filtered}}
|
||||
@items={{searched}}
|
||||
@ondelete={{refresh-route}}
|
||||
@routeName="dc.services.show.intentions.edit"
|
||||
>
|
||||
<EmptyState>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There are no intentions {{if (gt intentions.length 0) 'found '}} for this service.
|
||||
There are no intentions {{if (gt items.length 0) 'found '}} for this service.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</ConsulIntentionList>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</DataLoader>
|
||||
|
|
|
@ -1,22 +1,50 @@
|
|||
<div id="services" class="tab-section">
|
||||
<div role="tabpanel">
|
||||
{{#if (gt gatewayServices.length 0)}}
|
||||
<p>
|
||||
The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our
|
||||
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/terminating-gateway" target="_blank" rel="noopener noreferrer">step-by-step guide</a>.
|
||||
</p>
|
||||
<ConsulServiceList
|
||||
@items={{gatewayServices}}
|
||||
@nspace={{nspace}}
|
||||
/>
|
||||
{{else}}
|
||||
<EmptyState>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There are no linked services.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
{{/if}}
|
||||
{{#let (hash
|
||||
instances=(if instance (split instance ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
{{#if (gt gatewayServices.length 0)}}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<ConsulUpstreamSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
instance=(action (mut instance) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
<p>
|
||||
The following services may receive traffic from external services through this gateway. Learn more about configuring gateways in our
|
||||
<a href="{{env 'CONSUL_DOCS_URL'}}/connect/terminating-gateway" target="_blank" rel="noopener noreferrer">step-by-step guide</a>.
|
||||
</p>
|
||||
{{#let (filter (filter-predicate 'service' filters) gatewayServices) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'service' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulServiceList
|
||||
@items={{searched}}
|
||||
@nspace={{nspace}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There are no linked services.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,18 +1,49 @@
|
|||
<div id="upstreams" class="tab-section">
|
||||
<div role="tabpanel">
|
||||
{{#if (gt gatewayServices.length 0)}}
|
||||
{{#let (hash
|
||||
instances=(if instance (split instance ',') undefined)
|
||||
) as |filters|}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
{{#if (gt gatewayServices.length 0)}}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<ConsulUpstreamSearchBar
|
||||
@search={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
|
||||
@sort={{sort}}
|
||||
@onsort={{action (mut sortBy) value="target.selected"}}
|
||||
|
||||
@filter={{filters}}
|
||||
@onfilter={{hash
|
||||
instance=(action (mut instance) value="target.selectedItems")
|
||||
}}
|
||||
/>
|
||||
{{/if}}
|
||||
<p>
|
||||
Upstreams are services that may receive traffic from this gateway. Learn more about configuring gateways in our <a href="{{env 'CONSUL_DOCS_URL'}}/connect/ingress-gateway" target="_blank" rel="noopener noreferrer">documentation</a>.
|
||||
</p>
|
||||
<ConsulUpstreamList @items={{gatewayServices}} @dc={{dc}} @nspace={{nspace}} />
|
||||
{{else}}
|
||||
<EmptyState>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There are no upstreams.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
{{/if}}
|
||||
{{#let (filter (filter-predicate 'service' filters) gatewayServices) as |filtered|}}
|
||||
{{#let (sort-by (comparator 'service' sort) filtered) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |searched|>
|
||||
<ConsulServiceList
|
||||
@items={{searched}}
|
||||
@nspace={{nspace}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There are no upstreams.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / acls / policies / sorting
|
||||
Scenario: Sorting Policies
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 4 policy models from yaml
|
||||
---
|
||||
- Name: "system-A"
|
||||
- Name: "system-D"
|
||||
- Name: "system-C"
|
||||
- Name: "system-B"
|
||||
---
|
||||
When I visit the policies page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/acls/policies
|
||||
Then I see 4 policy models
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the policies vertically like yaml
|
||||
---
|
||||
- "system-D"
|
||||
- "system-C"
|
||||
- "system-B"
|
||||
- "system-A"
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the policies vertically like yaml
|
||||
---
|
||||
- "system-A"
|
||||
- "system-B"
|
||||
- "system-C"
|
||||
- "system-D"
|
||||
---
|
||||
|
||||
@setupApplicationTest
|
||||
Feature: dc / acls / policies / sorting
|
||||
Scenario: Sorting Policies
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 4 policy models from yaml
|
||||
---
|
||||
- Name: "system-A"
|
||||
- Name: "system-D"
|
||||
- Name: "system-C"
|
||||
- Name: "system-B"
|
||||
---
|
||||
When I visit the policies page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
Then the url should be /dc-1/acls/policies
|
||||
Then I see 4 policy models
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the policies vertically like yaml
|
||||
---
|
||||
- "system-D"
|
||||
- "system-C"
|
||||
- "system-B"
|
||||
- "system-A"
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the policies vertically like yaml
|
||||
---
|
||||
- "system-A"
|
||||
- "system-B"
|
||||
- "system-C"
|
||||
- "system-D"
|
||||
---
|
||||
|
||||
|
|
|
@ -1,73 +1,73 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / nodes / sorting
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 6 node models from yaml
|
||||
---
|
||||
- Node: Node-A
|
||||
Checks:
|
||||
- Status: critical
|
||||
- Node: Node-B
|
||||
Checks:
|
||||
- Status: passing
|
||||
- Node: Node-C
|
||||
Checks:
|
||||
- Status: warning
|
||||
- Node: Node-D
|
||||
Checks:
|
||||
- Status: critical
|
||||
- Node: Node-E
|
||||
Checks:
|
||||
- Status: critical
|
||||
- Node: Node-F
|
||||
Checks:
|
||||
- Status: warning
|
||||
---
|
||||
When I visit the nodes page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.3.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-B
|
||||
- Node-C
|
||||
- Node-F
|
||||
- Node-A
|
||||
- Node-D
|
||||
- Node-E
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.2.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-A
|
||||
- Node-D
|
||||
- Node-E
|
||||
- Node-C
|
||||
- Node-F
|
||||
- Node-B
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-A
|
||||
- Node-B
|
||||
- Node-C
|
||||
- Node-D
|
||||
- Node-E
|
||||
- Node-F
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-F
|
||||
- Node-E
|
||||
- Node-D
|
||||
- Node-C
|
||||
- Node-B
|
||||
- Node-A
|
||||
---
|
||||
@setupApplicationTest
|
||||
Feature: dc / nodes / sorting
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 6 node models from yaml
|
||||
---
|
||||
- Node: Node-A
|
||||
Checks:
|
||||
- Status: critical
|
||||
- Node: Node-B
|
||||
Checks:
|
||||
- Status: passing
|
||||
- Node: Node-C
|
||||
Checks:
|
||||
- Status: warning
|
||||
- Node: Node-D
|
||||
Checks:
|
||||
- Status: critical
|
||||
- Node: Node-E
|
||||
Checks:
|
||||
- Status: critical
|
||||
- Node: Node-F
|
||||
Checks:
|
||||
- Status: warning
|
||||
---
|
||||
When I visit the nodes page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-A
|
||||
- Node-D
|
||||
- Node-E
|
||||
- Node-C
|
||||
- Node-F
|
||||
- Node-B
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-B
|
||||
- Node-C
|
||||
- Node-F
|
||||
- Node-A
|
||||
- Node-D
|
||||
- Node-E
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.2.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-A
|
||||
- Node-B
|
||||
- Node-C
|
||||
- Node-D
|
||||
- Node-E
|
||||
- Node-F
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.3.button on the sort
|
||||
Then I see name on the nodes vertically like yaml
|
||||
---
|
||||
- Node-F
|
||||
- Node-E
|
||||
- Node-D
|
||||
- Node-C
|
||||
- Node-B
|
||||
- Node-A
|
||||
---
|
||||
|
|
|
@ -28,6 +28,7 @@ Feature: dc / services / show / upstreams
|
|||
---
|
||||
And the title should be "ingress-gateway-1 - Consul"
|
||||
When I click upstreams on the tabs
|
||||
And I see upstreamsIsSelected on the tabs
|
||||
Then I see 3 service models on the tabs.upstreamsTab component
|
||||
Scenario: Don't see the Upstreams tab
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
|
|
|
@ -40,15 +40,28 @@ Feature: dc / services / sorting
|
|||
dc: dc-1
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.3.button on the sort
|
||||
# unhealthy / healthy
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-B
|
||||
- Service-C
|
||||
- Service-A
|
||||
- Service-D
|
||||
- Service-F
|
||||
- Service-E
|
||||
---
|
||||
When I click selected on the sort
|
||||
# healthy / unhealthy
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-E
|
||||
- Service-F
|
||||
- Service-D
|
||||
- Service-A
|
||||
- Service-C
|
||||
- Service-B
|
||||
- Service-A
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.2.button on the sort
|
||||
|
@ -62,24 +75,13 @@ Feature: dc / services / sorting
|
|||
- Service-F
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
When I click options.3.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-B
|
||||
- Service-C
|
||||
- Service-A
|
||||
- Service-D
|
||||
- Service-F
|
||||
- Service-E
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-E
|
||||
- Service-F
|
||||
- Service-D
|
||||
- Service-A
|
||||
- Service-C
|
||||
- Service-B
|
||||
- Service-A
|
||||
---
|
||||
|
|
|
@ -2,6 +2,6 @@ export default function(visitable, creatable, policies, popoverSelect) {
|
|||
return creatable({
|
||||
visit: visitable('/:dc/acls/policies'),
|
||||
policies: policies(),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ export default function(visitable, creatable, roles, popoverSelect) {
|
|||
return {
|
||||
visit: visitable('/:dc/acls/roles'),
|
||||
roles: roles(),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
...creatable(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ export default function(visitable, creatable, text, tokens, popoverSelect) {
|
|||
visit: visitable('/:dc/acls/tokens'),
|
||||
update: text('[data-test-notification-update]'),
|
||||
tokens: tokens(),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
...creatable(),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ export default function(visitable, creatable, clickable, intentions, popoverSele
|
|||
return creatable({
|
||||
visit: visitable('/:dc/intentions'),
|
||||
intentions: intentions(),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
create: clickable('[data-test-create]'),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -8,6 +8,6 @@ export default function(visitable, text, clickable, attribute, collection, popov
|
|||
visit: visitable('/:dc/nodes'),
|
||||
nodes: collection('.consul-node-list [data-test-list-row]', node),
|
||||
home: clickable('[data-test-home]'),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,6 @@ export default function(visitable, creatable, nspaces, popoverSelect) {
|
|||
return creatable({
|
||||
visit: visitable('/:dc/namespaces'),
|
||||
nspaces: nspaces(),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,6 +10,6 @@ export default function(visitable, clickable, text, attribute, present, collecti
|
|||
visit: visitable('/:dc/services'),
|
||||
services: collection('.consul-service-list > ul > li:not(:first-child)', service),
|
||||
home: clickable('[data-test-home]'),
|
||||
sort: popoverSelect(),
|
||||
sort: popoverSelect('[data-test-sort-control]'),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ export default function(visitable, attribute, collection, text, intentions, filt
|
|||
intentions: intentions(),
|
||||
};
|
||||
page.tabs.upstreamsTab = {
|
||||
services: collection('.consul-upstream-list > ul > li:not(:first-child)', {
|
||||
services: collection('.consul-service-list > ul > li:not(:first-child)', {
|
||||
name: text('[data-test-service-name]'),
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import factory from 'consul-ui/filter/predicates/intention';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Filter | Predicates | intention', function() {
|
||||
const predicate = factory();
|
||||
|
||||
test('it returns items depending on Action', function(assert) {
|
||||
const items = [
|
||||
{
|
||||
Action: 'allow',
|
||||
},
|
||||
{
|
||||
Action: 'deny',
|
||||
},
|
||||
];
|
||||
|
||||
let expected, actual;
|
||||
|
||||
expected = [items[0]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
accesses: ['allow'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = [items[1]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
accesses: ['deny'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = items;
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
accesses: ['allow', 'deny'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,171 @@
|
|||
import factory from 'consul-ui/filter/predicates/service';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Filter | Predicates | service', function() {
|
||||
const predicate = factory();
|
||||
|
||||
test('it returns registered/unregistered items depending on instance count', function(assert) {
|
||||
const items = [
|
||||
{
|
||||
InstanceCount: 1,
|
||||
},
|
||||
{
|
||||
InstanceCount: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let expected, actual;
|
||||
|
||||
expected = [items[0]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
instances: ['registered'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = [items[1]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
instances: ['not-registered'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = items;
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
instances: ['registered', 'not-registered'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('it returns items depending on status', function(assert) {
|
||||
const items = [
|
||||
{
|
||||
MeshStatus: 'passing',
|
||||
},
|
||||
{
|
||||
MeshStatus: 'warning',
|
||||
},
|
||||
{
|
||||
MeshStatus: 'critical',
|
||||
},
|
||||
];
|
||||
|
||||
let expected, actual;
|
||||
|
||||
expected = [items[0]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
statuses: ['passing'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = [items[1]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
statuses: ['warning'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = items;
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
statuses: ['passing', 'warning', 'critical'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
|
||||
test('it returns items depending on service type', function(assert) {
|
||||
const items = [
|
||||
{
|
||||
Kind: 'ingress-gateway',
|
||||
},
|
||||
{
|
||||
Kind: 'mesh-gateway',
|
||||
},
|
||||
{},
|
||||
];
|
||||
|
||||
let expected, actual;
|
||||
|
||||
expected = [items[0]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
types: ['ingress-gateway'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = [items[1]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
types: ['mesh-gateway'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = items;
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
types: ['ingress-gateway', 'mesh-gateway', 'service'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
test('it returns items depending on a mixture of properties', function(assert) {
|
||||
const items = [
|
||||
{
|
||||
Kind: 'ingress-gateway',
|
||||
MeshStatus: 'passing',
|
||||
InstanceCount: 1,
|
||||
},
|
||||
{
|
||||
Kind: 'mesh-gateway',
|
||||
MeshStatus: 'warning',
|
||||
InstanceCount: 1,
|
||||
},
|
||||
{
|
||||
MeshStatus: 'critical',
|
||||
InstanceCount: 0,
|
||||
},
|
||||
];
|
||||
|
||||
let expected, actual;
|
||||
|
||||
expected = [items[0]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
types: ['ingress-gateway'],
|
||||
statuses: ['passing'],
|
||||
instances: ['registered'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = [items[1]];
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
types: ['mesh-gateway'],
|
||||
statuses: ['warning'],
|
||||
instances: ['registered'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
|
||||
expected = items;
|
||||
actual = items.filter(
|
||||
predicate({
|
||||
types: ['ingress-gateway', 'mesh-gateway', 'service'],
|
||||
statuses: ['passing', 'warning', 'critical'],
|
||||
instances: ['registered', 'not-registered'],
|
||||
})
|
||||
);
|
||||
assert.deepEqual(actual, expected);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue