ui: Notifications re-organization/re-style (#11577)
- Moves where they appear up to the <App /> component. - Instead of a <Notification /> wrapping component to move whatever you use for a notification up to where they need to appear (via ember-cli-flash), we now use a {{notification}} modifier now we have modifiers. - Global notifications/flashes are no longer special styles of their own. You just use the {{notification}} modifier to hoist whatever component/element you want up to the top of the page. This means we can re-use our existing <Notice /> component for all our global UI notifications (this is the user visible change here)
This commit is contained in:
parent
f605689154
commit
124fa8f168
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:improvement
|
||||||
|
ui: Update global notification styling
|
||||||
|
```
|
|
@ -8,9 +8,7 @@ state: needs-love
|
||||||
the app chrome), every 'top level main section/template' should have one of
|
the app chrome), every 'top level main section/template' should have one of
|
||||||
these.
|
these.
|
||||||
|
|
||||||
It contains legacy authorization code (that can probably be removed now), and
|
This component will potentially be renamed to `Page` or `View` or similar now
|
||||||
our flash messages (that should be moved to the `<App />` or `<HashicorpConsul
|
|
||||||
/>` component and potentially be renamed to `Page` or `View` or similar now
|
|
||||||
that we don't need two words.
|
that we don't need two words.
|
||||||
|
|
||||||
Other than that it provides the basic layout/slots for our main title, search
|
Other than that it provides the basic layout/slots for our main title, search
|
||||||
|
@ -86,20 +84,12 @@ breadcrumbs and back again.
|
||||||
</figure>
|
</figure>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Arguments
|
|
||||||
|
|
||||||
| Argument | Type | Default | Description |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `authorized` | `Boolean` | `true` | Whether the View is authorized or not |
|
|
||||||
| `enabled` | `Boolean` | `true` | Whether ACLs are enabled or not |
|
|
||||||
|
|
||||||
## Slots
|
## Slots
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `header` | The main title of the page, you probably want to put a `<h1>` in here |
|
| `header` | The main title of the page, you probably want to put a `<h1>` in here |
|
||||||
| `content` | The main content of the page, and potentially an `<Outlet />` somewhere |
|
| `content` | The main content of the page, and potentially an `<Outlet />` somewhere |
|
||||||
| `notification` | Old style notifications, also see `<Notification />` |
|
|
||||||
| `breadcrumbs` | Any breadcrumbs, you probably want an `ol/li/a` in here |
|
| `breadcrumbs` | Any breadcrumbs, you probably want an `ol/li/a` in here |
|
||||||
| `actions` | Any actions relevant for the entire page, probably using `<Action />` |
|
| `actions` | Any actions relevant for the entire page, probably using `<Action />` |
|
||||||
| `nav` | Secondary navigation goes in here, also see `<TabNav />` |
|
| `nav` | Secondary navigation goes in here, also see `<TabNav />` |
|
||||||
|
|
|
@ -4,74 +4,23 @@
|
||||||
>
|
>
|
||||||
{{yield}}
|
{{yield}}
|
||||||
<header>
|
<header>
|
||||||
{{#each flashMessages.queue as |flash|}}
|
|
||||||
<FlashMessage @flash={{flash}} as |component flash|>
|
|
||||||
{{#if flash.dom}}
|
|
||||||
{{{flash.dom}}}
|
|
||||||
{{else}}
|
|
||||||
{{#let (lowercase component.flashType) (lowercase flash.action) as |status type|}}
|
|
||||||
{{! flashes automatically ucfirst the type }}
|
|
||||||
|
|
||||||
<p data-notification role="alert" class={{concat status ' notification-' type}}>
|
|
||||||
<strong>
|
|
||||||
{{capitalize status}}!
|
|
||||||
</strong>
|
|
||||||
{{#yield-slot name="notification" params=(block-params status type flash.item flash.error)}}
|
|
||||||
{{yield}}
|
|
||||||
{{#if (eq type 'logout')}}
|
|
||||||
{{#if (eq status 'success') }}
|
|
||||||
You are now logged out.
|
|
||||||
{{else}}
|
|
||||||
There was an error logging out.
|
|
||||||
{{/if}}
|
|
||||||
{{else if (eq type 'authorize')}}
|
|
||||||
{{#if (eq status 'success') }}
|
|
||||||
You are now logged in.
|
|
||||||
{{else}}
|
|
||||||
There was an error, please check your SecretID/Token
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
{{#if (eq type 'logout')}}
|
|
||||||
{{#if (eq status 'success') }}
|
|
||||||
You are now logged out.
|
|
||||||
{{else}}
|
|
||||||
There was an error logging out.
|
|
||||||
{{/if}}
|
|
||||||
{{else if (eq type 'authorize')}}
|
|
||||||
{{#if (eq status 'success') }}
|
|
||||||
You are now logged in.
|
|
||||||
{{else}}
|
|
||||||
There was an error, please check your SecretID/Token
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
{{/yield-slot}}
|
|
||||||
</p>
|
|
||||||
{{/let}}
|
|
||||||
{{/if}}
|
|
||||||
</FlashMessage>
|
|
||||||
{{/each}}
|
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
{{#if authorized}}
|
|
||||||
<nav aria-label="Breadcrumb" data-test-breadcrumbs>
|
<nav aria-label="Breadcrumb" data-test-breadcrumbs>
|
||||||
<YieldSlot @name="breadcrumbs">
|
<YieldSlot @name="breadcrumbs">
|
||||||
{{document-attrs class="with-breadcrumbs"}}
|
{{document-attrs class="with-breadcrumbs"}}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
</nav>
|
</nav>
|
||||||
{{/if}}
|
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<YieldSlot @name="header">
|
<YieldSlot @name="header">
|
||||||
{{yield}}
|
{{yield}}
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
{{#if authorized}}
|
|
||||||
<YieldSlot @name="actions">
|
<YieldSlot @name="actions">
|
||||||
<PortalTarget @name="app-view-actions" />
|
<PortalTarget @name="app-view-actions" />
|
||||||
{{yield}}
|
{{yield}}
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<YieldSlot @name="nav">
|
<YieldSlot @name="nav">
|
||||||
|
@ -79,42 +28,12 @@
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{#if authorized}}
|
|
||||||
<YieldSlot @name="toolbar">
|
<YieldSlot @name="toolbar">
|
||||||
<input type="checkbox" id="toolbar-toggle" />
|
<input type="checkbox" id="toolbar-toggle" />
|
||||||
{{yield}}
|
{{yield}}
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
{{/if}}
|
|
||||||
</header>
|
</header>
|
||||||
<div>
|
<div>
|
||||||
{{#if (not enabled) }}
|
|
||||||
<EmptyState data-test-acls-disabled>
|
|
||||||
<BlockSlot @name="header">
|
|
||||||
<h2>Welcome to ACLs</h2>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="body">
|
|
||||||
<p>
|
|
||||||
ACLs are not enabled in this Consul cluster. We strongly encourage the use of ACLs in production environments for the best security practices.
|
|
||||||
</p>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="actions">
|
|
||||||
<li class="docs-link">
|
|
||||||
<a href="{{env 'CONSUL_DOCS_URL'}}/acl/index.html" rel="noopener noreferrer" target="_blank">Read the documentation</a>
|
|
||||||
</li>
|
|
||||||
<li class="learn-link">
|
|
||||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/security-networking/production-acls" rel="noopener noreferrer" target="_blank">Follow the guide</a>
|
|
||||||
</li>
|
|
||||||
</BlockSlot>
|
|
||||||
</EmptyState>
|
|
||||||
{{else if (not authorized)}}
|
|
||||||
<ErrorState
|
|
||||||
@error={{hash
|
|
||||||
status='403'
|
|
||||||
}}
|
|
||||||
@login={{login}}
|
|
||||||
/>
|
|
||||||
{{else}}
|
|
||||||
<YieldSlot @name="content">{{yield}}</YieldSlot>
|
<YieldSlot @name="content">{{yield}}</YieldSlot>
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,6 +2,4 @@ import Component from '@ember/component';
|
||||||
import SlotsMixin from 'block-slots';
|
import SlotsMixin from 'block-slots';
|
||||||
export default Component.extend(SlotsMixin, {
|
export default Component.extend(SlotsMixin, {
|
||||||
tagName: '',
|
tagName: '',
|
||||||
authorized: true,
|
|
||||||
enabled: true,
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{{#let (hash
|
{{#let (hash
|
||||||
main=(concat guid '-main')
|
main=(concat guid '-main')
|
||||||
|
Notification=(component 'app/notification')
|
||||||
) as |exported|}}
|
) as |exported|}}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
@ -13,7 +14,7 @@
|
||||||
>
|
>
|
||||||
<PortalTarget
|
<PortalTarget
|
||||||
@name="app-before-skip-links"
|
@name="app-before-skip-links"
|
||||||
@mutiple={{true}}
|
@multiple={{true}}
|
||||||
></PortalTarget>
|
></PortalTarget>
|
||||||
<a href={{concat '#' exported.main}}>{{t 'components.app.skip_to_content'}}</a>
|
<a href={{concat '#' exported.main}}>{{t 'components.app.skip_to_content'}}</a>
|
||||||
{{!--
|
{{!--
|
||||||
|
@ -25,7 +26,7 @@
|
||||||
--}}
|
--}}
|
||||||
<PortalTarget
|
<PortalTarget
|
||||||
@name="app-after-skip-links"
|
@name="app-after-skip-links"
|
||||||
@mutiple={{true}}
|
@multiple={{true}}
|
||||||
></PortalTarget>
|
></PortalTarget>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -78,6 +79,13 @@
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<main id={{concat guid '-main'}}>
|
<main id={{concat guid '-main'}}>
|
||||||
|
<div class="notifications">
|
||||||
|
{{yield exported to="notifications"}}
|
||||||
|
<PortalTarget
|
||||||
|
@name="app-notifications"
|
||||||
|
@multiple={{true}}
|
||||||
|
></PortalTarget>
|
||||||
|
</div>
|
||||||
{{yield exported to="main"}}
|
{{yield exported to="main"}}
|
||||||
</main>
|
</main>
|
||||||
<footer
|
<footer
|
||||||
|
|
|
@ -1,6 +1,33 @@
|
||||||
.app .skip-links {
|
.app .skip-links {
|
||||||
@extend %skip-links;
|
@extend %skip-links;
|
||||||
}
|
}
|
||||||
|
.app .notifications {
|
||||||
|
@extend %app-notifications;
|
||||||
|
}
|
||||||
|
%app-notifications {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
z-index: 50;
|
||||||
|
top: -45px;
|
||||||
|
left: 0;
|
||||||
|
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
%app-notifications .app-notification > * {
|
||||||
|
min-width: 400px;
|
||||||
|
}
|
||||||
|
%app-notifications .app-notification {
|
||||||
|
@extend %with-transition-500;
|
||||||
|
transition-property: opacity;
|
||||||
|
width: fit-content;
|
||||||
|
max-width: 80%;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
[role='banner'] {
|
[role='banner'] {
|
||||||
@extend %main-header-horizontal;
|
@extend %main-header-horizontal;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +59,7 @@
|
||||||
@extend %main-nav-horizontal-action-active;
|
@extend %main-nav-horizontal-action-active;
|
||||||
}
|
}
|
||||||
%main-nav-sidebar,
|
%main-nav-sidebar,
|
||||||
|
%main-notifications,
|
||||||
main {
|
main {
|
||||||
@extend %transition-pushover;
|
@extend %transition-pushover;
|
||||||
}
|
}
|
||||||
|
@ -39,34 +67,50 @@ main {
|
||||||
transition-property: left;
|
transition-property: left;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
%app-notifications,
|
||||||
main {
|
main {
|
||||||
margin-top: var(--chrome-height, 64px);
|
margin-top: var(--chrome-height, 64px);
|
||||||
transition-property: margin-left;
|
transition-property: margin-left;
|
||||||
}
|
}
|
||||||
|
%app-notifications {
|
||||||
|
transition-property: margin-left, width;
|
||||||
|
}
|
||||||
|
|
||||||
@media #{$--sidebar-open} {
|
@media #{$--sidebar-open} {
|
||||||
|
%main-nav-horizontal-toggle ~ main .notifications {
|
||||||
|
width: calc(100% - var(--chrome-width));
|
||||||
|
}
|
||||||
|
%main-nav-horizontal-toggle:checked ~ main .notifications {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
%main-nav-horizontal-toggle + header > div > nav:first-of-type {
|
%main-nav-horizontal-toggle + header > div > nav:first-of-type {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
%main-nav-horizontal-toggle:checked + header > div > nav:first-of-type {
|
%main-nav-horizontal-toggle:checked + header > div > nav:first-of-type {
|
||||||
left: calc(var(--chrome-width, 300px) * -1);
|
left: calc(var(--chrome-width, 300px) * -1);
|
||||||
}
|
}
|
||||||
|
%main-nav-horizontal-toggle ~ main .notifications,
|
||||||
%main-nav-horizontal-toggle ~ main,
|
%main-nav-horizontal-toggle ~ main,
|
||||||
%main-nav-horizontal-toggle ~ footer {
|
%main-nav-horizontal-toggle ~ footer {
|
||||||
margin-left: var(--chrome-width, 300px);
|
margin-left: var(--chrome-width, 300px);
|
||||||
}
|
}
|
||||||
|
%main-nav-horizontal-toggle:checked ~ main .notifications,
|
||||||
%main-nav-horizontal-toggle:checked ~ main,
|
%main-nav-horizontal-toggle:checked ~ main,
|
||||||
%main-nav-horizontal-toggle:checked ~ footer {
|
%main-nav-horizontal-toggle:checked ~ footer {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media #{$--lt-sidebar-open} {
|
@media #{$--lt-sidebar-open} {
|
||||||
|
%main-nav-horizontal-toggle ~ main .notifications {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
%main-nav-horizontal-toggle:checked + header > div > nav:first-of-type {
|
%main-nav-horizontal-toggle:checked + header > div > nav:first-of-type {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
%main-nav-horizontal-toggle + header > div > nav:first-of-type {
|
%main-nav-horizontal-toggle + header > div > nav:first-of-type {
|
||||||
left: calc(var(--chrome-width, 300px) * -1);
|
left: calc(var(--chrome-width, 300px) * -1);
|
||||||
}
|
}
|
||||||
|
%main-nav-horizontal-toggle ~ main .notifications,
|
||||||
%main-nav-horizontal-toggle ~ main,
|
%main-nav-horizontal-toggle ~ main,
|
||||||
%main-nav-horizontal-toggle ~ footer {
|
%main-nav-horizontal-toggle ~ footer {
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
<div
|
||||||
|
class="app-notification"
|
||||||
|
...attributes
|
||||||
|
{{style
|
||||||
|
(array
|
||||||
|
(array 'opacity' '1')
|
||||||
|
(array 'transition-delay' (concat @delay 'ms'))
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
{{style
|
||||||
|
(array
|
||||||
|
(array 'opacity' (if @sticky '1' '0'))
|
||||||
|
)
|
||||||
|
delay=0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{{yield}}
|
||||||
|
</div>
|
||||||
|
|
|
@ -14,21 +14,45 @@
|
||||||
@onsubmit={{action this.onsubmit}}
|
@onsubmit={{action this.onsubmit}}
|
||||||
as |api|>
|
as |api|>
|
||||||
|
|
||||||
<BlockSlot @name="error" as |Notification|>
|
<BlockSlot @name="error" as |after|>
|
||||||
<Notification>
|
|
||||||
<p data-notification role="alert" class="error notification-update">
|
|
||||||
{{#if (string-starts-with api.error.detail 'duplicate intention found:')}}
|
{{#if (string-starts-with api.error.detail 'duplicate intention found:')}}
|
||||||
<strong>Intention exists</strong>
|
<Notice
|
||||||
|
{{notification
|
||||||
|
after=(action after)
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
|
<strong>Intention exists!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
An intention already exists for this Source-Destination pair. Please enter a different combination of Services, or search the intentions to edit an existing intention.
|
An intention already exists for this Source-Destination pair. Please enter a different combination of Services, or search the intentions to edit an existing intention.
|
||||||
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
<Notice
|
||||||
|
{{notification
|
||||||
|
after=(action after)
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
There was an error saving your intention.
|
There was an error saving your intention.
|
||||||
{{#if (and api.error.status api.error.detail)}}
|
{{#if (and api.error.status api.error.detail)}}
|
||||||
<br />{{api.error.status}}: {{api.error.detail}}
|
<br />{{api.error.status}}: {{api.error.detail}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="form">
|
<BlockSlot @name="form">
|
||||||
|
|
|
@ -49,16 +49,26 @@
|
||||||
<State @matches={{array "idle" "disconnected"}}>
|
<State @matches={{array "idle" "disconnected"}}>
|
||||||
|
|
||||||
<State @matches="disconnected">
|
<State @matches="disconnected">
|
||||||
{{#yield-slot name="disconnected" params=(block-params (component 'notification' after=(action dispatch "RESET")))}}
|
{{#yield-slot name="disconnected" params=(block-params (action dispatch "RESET"))}}
|
||||||
{{yield api}}
|
{{yield api}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#if (not eq error.status '401')}}
|
{{#if (not eq error.status '401')}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/yield-slot}}
|
{{/yield-slot}}
|
||||||
</State>
|
</State>
|
||||||
|
|
|
@ -33,46 +33,89 @@
|
||||||
</State>
|
</State>
|
||||||
|
|
||||||
<State @matches="removed">
|
<State @matches="removed">
|
||||||
{{#yield-slot name="removed" params=(block-params (component 'notification' after=(queue (action dispatch "RESET") (action ondelete))))}}
|
{{#let
|
||||||
|
(queue (action dispatch "RESET") (action ondelete))
|
||||||
|
as |after|}}
|
||||||
|
{{#yield-slot name="removed" params=(block-params after)}}
|
||||||
{{yield api}}
|
{{yield api}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @after={{queue (action dispatch "RESET") (action ondelete)}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="success notification-delete">
|
{{notification
|
||||||
|
after=(action after)
|
||||||
|
}}
|
||||||
|
class="notification-delete"
|
||||||
|
@type="success"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Success!</strong>
|
<strong>Success!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
Your {{or label type}} has been deleted.
|
Your {{or label type}} has been deleted.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/yield-slot}}
|
{{/yield-slot}}
|
||||||
|
{{/let}}
|
||||||
</State>
|
</State>
|
||||||
|
|
||||||
<State @matches="persisted">
|
<State @matches="persisted">
|
||||||
<Notification @after={{action onchange}}>
|
{{#let
|
||||||
{{#yield-slot name="persisted"}}
|
(action onchange)
|
||||||
|
as |after|}}
|
||||||
|
{{#yield-slot name="persisted" params=(block-params after)}}
|
||||||
{{yield api}}
|
{{yield api}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<p data-notification role="alert" class="success notification-update">
|
<Notice
|
||||||
|
{{notification
|
||||||
|
after=(action after)
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="success"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Success!</strong>
|
<strong>Success!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
Your {{or label type}} has been saved.
|
Your {{or label type}} has been saved.
|
||||||
</p>
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/yield-slot}}
|
{{/yield-slot}}
|
||||||
</Notification>
|
{{/let}}
|
||||||
</State>
|
</State>
|
||||||
|
|
||||||
<State @matches="error">
|
<State @matches="error">
|
||||||
{{#yield-slot name="error" params=(block-params (component 'notification' after=(action dispatch "RESET")))}}
|
{{#let
|
||||||
|
(action dispatch "RESET")
|
||||||
|
as |after|}}
|
||||||
|
{{#yield-slot name="error" params=(block-params after)}}
|
||||||
{{yield api}}
|
{{yield api}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @after={{action dispatch "RESET"}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="error notification-update">
|
{{notification
|
||||||
|
after=(action after)
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
There was an error saving your {{or label type}}.
|
There was an error saving your {{or label type}}.
|
||||||
{{#if (and api.error.status api.error.detail)}}
|
{{#if (and api.error.status api.error.detail)}}
|
||||||
<br />{{api.error.status}}: {{api.error.detail}}
|
<br />{{api.error.status}}: {{api.error.detail}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/yield-slot}}
|
{{/yield-slot}}
|
||||||
|
{{/let}}
|
||||||
</State>
|
</State>
|
||||||
|
|
||||||
<YieldSlot @name="content">
|
<YieldSlot @name="content">
|
||||||
{{yield api}}
|
{{yield api}}
|
||||||
</YieldSlot>
|
</YieldSlot>
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
---
|
|
||||||
class: css
|
|
||||||
state: needs-love
|
|
||||||
---
|
|
||||||
# flash-message
|
|
||||||
|
|
||||||
CSS component for styling our flash messages
|
|
||||||
|
|
||||||
```hbs preview-template
|
|
||||||
<div class="flash-message">
|
|
||||||
<p
|
|
||||||
role="alert"
|
|
||||||
class={{or this.type 'success'}}
|
|
||||||
>
|
|
||||||
<strong>
|
|
||||||
{{capitalize (or this.type 'success')}}!
|
|
||||||
</strong>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<figcaption>Provide a widget to change the <code>class</code></figcaption>
|
|
||||||
|
|
||||||
<select
|
|
||||||
onchange={{action (mut this.type) value="target.value"}}
|
|
||||||
>
|
|
||||||
<option>success</option>
|
|
||||||
<option>warning</option>
|
|
||||||
<option>error</option>
|
|
||||||
<option>exists</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
</figure>
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
@import './skin';
|
|
||||||
@import './layout';
|
|
||||||
.flash-message {
|
|
||||||
@extend %flash-message;
|
|
||||||
}
|
|
||||||
%flash-message.exiting {
|
|
||||||
@extend %blink-in-fade-out;
|
|
||||||
}
|
|
||||||
/* This is for the flash message that appears */
|
|
||||||
/* when you save an intention that already exists */
|
|
||||||
/* once we have refactored app-view with data-source with nicer */
|
|
||||||
/* flash message usage we should be able to remove this */
|
|
||||||
%flash-message p.exists strong::before {
|
|
||||||
@extend %with-cancel-square-fill-mask;
|
|
||||||
color: rgb(var(--tone-red-500));
|
|
||||||
}
|
|
||||||
%flash-message p.exists {
|
|
||||||
@extend %frame-red-500;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
%flash-message {
|
|
||||||
display: flex;
|
|
||||||
position: relative;
|
|
||||||
z-index: 6;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 0 15%;
|
|
||||||
}
|
|
||||||
%flash-message p {
|
|
||||||
top: -46px;
|
|
||||||
position: absolute;
|
|
||||||
padding: 9px 15px;
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
%flash-message p {
|
|
||||||
border-width: 1px;
|
|
||||||
border-radius: var(--decor-radius-100);
|
|
||||||
}
|
|
||||||
%flash-message p strong::before {
|
|
||||||
@extend %as-pseudo;
|
|
||||||
}
|
|
||||||
%flash-message p.success strong::before {
|
|
||||||
@extend %with-check-circle-fill-mask;
|
|
||||||
color: rgb(var(--tone-green-500));
|
|
||||||
}
|
|
||||||
%flash-message p.warning strong::before {
|
|
||||||
@extend %with-alert-triangle-mask;
|
|
||||||
color: rgb(var(--tone-orange-500));
|
|
||||||
}
|
|
||||||
%flash-message p.error strong::before {
|
|
||||||
@extend %with-cancel-square-fill-mask;
|
|
||||||
color: rgb(var(--tone-red-500));
|
|
||||||
}
|
|
||||||
%flash-message p.success {
|
|
||||||
@extend %frame-green-500;
|
|
||||||
}
|
|
||||||
%flash-message p.warning {
|
|
||||||
@extend %frame-yellow-500;
|
|
||||||
}
|
|
||||||
%flash-message p.error {
|
|
||||||
@extend %frame-red-500;
|
|
||||||
}
|
|
|
@ -3,7 +3,81 @@
|
||||||
class="hashicorp-consul"
|
class="hashicorp-consul"
|
||||||
...attributes
|
...attributes
|
||||||
>
|
>
|
||||||
|
<:notifications as |app|>
|
||||||
|
{{#each flashMessages.queue as |flash|}}
|
||||||
|
<app.Notification
|
||||||
|
@delay={{sub flash.timeout flash.extendedTimeout}}
|
||||||
|
@sticky={{flash.sticky}}
|
||||||
|
>
|
||||||
|
{{#if flash.dom}}
|
||||||
|
{{{flash.dom}}}
|
||||||
|
{{else}}
|
||||||
|
{{#let (lowercase flash.type) (lowercase flash.action) as |status type|}}
|
||||||
|
<Notice
|
||||||
|
role="alert"
|
||||||
|
class={{concat status ' notification-' type}}
|
||||||
|
data-notification
|
||||||
|
@type={{status}}
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
|
<strong>
|
||||||
|
{{capitalize status}}!
|
||||||
|
</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
|
{{#if (eq type 'logout')}}
|
||||||
|
{{#if (eq status 'success') }}
|
||||||
|
You are now logged out.
|
||||||
|
{{else}}
|
||||||
|
There was an error logging out.
|
||||||
|
{{/if}}
|
||||||
|
{{else if (eq type 'authorize')}}
|
||||||
|
{{#if (eq status 'success') }}
|
||||||
|
You are now logged in.
|
||||||
|
{{else}}
|
||||||
|
There was an error, please check your SecretID/Token
|
||||||
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if (eq flash.model 'token')}}
|
||||||
|
<Consul::Token::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@item={{flash.item}}
|
||||||
|
@error={{flash.error}}
|
||||||
|
/>
|
||||||
|
{{else if (eq flash.model 'role')}}
|
||||||
|
<Consul::Role::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@item={{flash.item}}
|
||||||
|
@error={{flash.error}}
|
||||||
|
/>
|
||||||
|
{{else if (eq flash.model 'policy')}}
|
||||||
|
<Consul::Policy::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@item={{flash.item}}
|
||||||
|
@error={{flash.error}}
|
||||||
|
/>
|
||||||
|
{{else if (eq flash.model 'nspace')}}
|
||||||
|
<Consul::Nspace::Notifications
|
||||||
|
@type={{type}}
|
||||||
|
@status={{status}}
|
||||||
|
@item={{flash.item}}
|
||||||
|
@error={{flash.error}}
|
||||||
|
/>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
|
{{/let}}
|
||||||
|
{{/if}}
|
||||||
|
</app.Notification>
|
||||||
|
{{/each}}
|
||||||
|
|
||||||
|
</:notifications>
|
||||||
<:home-nav>
|
<:home-nav>
|
||||||
<a
|
<a
|
||||||
href={{href-to 'index'}}
|
href={{href-to 'index'}}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import Component from '@glimmer/component';
|
import Component from '@glimmer/component';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default class HashiCorpConsul extends Component {
|
export default class HashiCorpConsul extends Component {
|
||||||
|
@service('flashMessages') flashMessages;
|
||||||
|
|
||||||
@action
|
@action
|
||||||
open() {
|
open() {
|
||||||
this.authForm.focus();
|
this.authForm.focus();
|
||||||
|
|
|
@ -24,9 +24,12 @@
|
||||||
background-color: rgb(var(--tone-green-050));
|
background-color: rgb(var(--tone-green-050));
|
||||||
border-color: rgb(var(--tone-green-500));
|
border-color: rgb(var(--tone-green-500));
|
||||||
}
|
}
|
||||||
|
%notice-success header * {
|
||||||
|
color: rgb(var(--tone-green-800));
|
||||||
|
}
|
||||||
%notice-info {
|
%notice-info {
|
||||||
border-color: rgb(var(--tone-blue-100));
|
border-color: rgb(var(--tone-blue-100));
|
||||||
background-color: rgb(var(--tone-gray-010));
|
background-color: rgb(var(--tone-blue-010));
|
||||||
}
|
}
|
||||||
%notice-info header * {
|
%notice-info header * {
|
||||||
color: rgb(var(--tone-blue-700));
|
color: rgb(var(--tone-blue-700));
|
||||||
|
@ -36,7 +39,7 @@
|
||||||
border-color: rgb(var(--tone-gray-300));
|
border-color: rgb(var(--tone-gray-300));
|
||||||
}
|
}
|
||||||
%notice-info header * {
|
%notice-info header * {
|
||||||
color: rgb(var(--tone-gray-700));
|
color: rgb(var(--tone-blue-700));
|
||||||
}
|
}
|
||||||
%notice-warning {
|
%notice-warning {
|
||||||
border-color: rgb(var(--tone-yellow-100));
|
border-color: rgb(var(--tone-yellow-100));
|
||||||
|
@ -49,6 +52,9 @@
|
||||||
background-color: rgb(var(--tone-red-050));
|
background-color: rgb(var(--tone-red-050));
|
||||||
border-color: rgb(var(--tone-red-500));
|
border-color: rgb(var(--tone-red-500));
|
||||||
}
|
}
|
||||||
|
%notice-error header * {
|
||||||
|
color: rgb(var(--tone-red-500));
|
||||||
|
}
|
||||||
%notice-success::before {
|
%notice-success::before {
|
||||||
@extend %with-check-circle-fill-mask;
|
@extend %with-check-circle-fill-mask;
|
||||||
color: rgb(var(--tone-green-500));
|
color: rgb(var(--tone-green-500));
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
<div id={{guid}}>
|
|
||||||
{{yield}}
|
|
||||||
</div>
|
|
|
@ -1,40 +0,0 @@
|
||||||
import Component from '@ember/component';
|
|
||||||
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
|
|
||||||
export default Component.extend({
|
|
||||||
tagName: '',
|
|
||||||
notify: service('flashMessages'),
|
|
||||||
dom: service('dom'),
|
|
||||||
oncomplete: function() {},
|
|
||||||
init: function() {
|
|
||||||
this._super(...arguments);
|
|
||||||
this.guid = this.dom.guid(this);
|
|
||||||
},
|
|
||||||
didInsertElement: function() {
|
|
||||||
const $el = this.dom.element(`#${this.guid}`);
|
|
||||||
const options = {
|
|
||||||
timeout: 6000,
|
|
||||||
extendedTimeout: 300,
|
|
||||||
dom: $el.innerHTML,
|
|
||||||
};
|
|
||||||
if (this.sticky) {
|
|
||||||
options.sticky = true;
|
|
||||||
}
|
|
||||||
$el.remove();
|
|
||||||
this.notify.clearMessages();
|
|
||||||
if (typeof this.after === 'function') {
|
|
||||||
Promise.resolve(this.after())
|
|
||||||
.catch(e => {
|
|
||||||
if (e.name !== 'TransitionAborted') {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
this.notify.add(options);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.notify.add(options);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,6 +1,8 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
import Mixin from '@ember/object/mixin';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { set, get } from '@ember/object';
|
import { set, get } from '@ember/object';
|
||||||
|
import { singularize } from 'ember-inflector';
|
||||||
|
|
||||||
/** With Blocking Actions
|
/** With Blocking Actions
|
||||||
* This mixin contains common write actions (Create Update Delete) for routes.
|
* This mixin contains common write actions (Create Update Delete) for routes.
|
||||||
* It could also be an Route to extend but decoration seems to be more sense right now.
|
* It could also be an Route to extend but decoration seems to be more sense right now.
|
||||||
|
@ -25,7 +27,11 @@ export default Mixin.create({
|
||||||
const route = this;
|
const route = this;
|
||||||
set(this, 'feedback', {
|
set(this, 'feedback', {
|
||||||
execute: function(cb, type, error) {
|
execute: function(cb, type, error) {
|
||||||
return feedback.execute(cb, type, error, route.controller);
|
const temp = route.routeName.split('.');
|
||||||
|
temp.pop();
|
||||||
|
const routeName = singularize(temp.pop());
|
||||||
|
|
||||||
|
return feedback.execute(cb, type, error, routeName);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import Modifier from 'ember-modifier';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
|
export default class NotificationModifier extends Modifier {
|
||||||
|
@service('flashMessages') notify;
|
||||||
|
|
||||||
|
didInstall() {
|
||||||
|
this.element.setAttribute('role', 'alert');
|
||||||
|
this.element.dataset['notification'] = null;
|
||||||
|
const options = {
|
||||||
|
timeout: 6000,
|
||||||
|
extendedTimeout: 300,
|
||||||
|
...this.args.named,
|
||||||
|
};
|
||||||
|
options.dom = this.element.outerHTML;
|
||||||
|
this.element.remove();
|
||||||
|
this.notify.clearMessages();
|
||||||
|
if (typeof options.after === 'function') {
|
||||||
|
Promise.resolve().then(_ => options.after())
|
||||||
|
.catch(e => {
|
||||||
|
if (e.name !== 'TransitionAborted') {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
this.notify.add(options);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.notify.add(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
willDestroy() {
|
||||||
|
if(this.args.named.sticky) {
|
||||||
|
this.notify.clearMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
# notification
|
||||||
|
|
||||||
|
Consul UIs notification modifier is used to 'hoist' DOM elements into the
|
||||||
|
global notification area for the UI. The most common usage will be something
|
||||||
|
like the below:
|
||||||
|
|
||||||
|
```hbs preview-template
|
||||||
|
<figure>
|
||||||
|
<figcaption>Attach a Warning notice to the top of the app ^^^</figcaption>
|
||||||
|
|
||||||
|
<Notice
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
|
This service has been deregistered and no longer exists in the catalog.
|
||||||
|
</p>
|
||||||
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
|
|
||||||
|
</figure>
|
||||||
|
```
|
||||||
|
|
||||||
|
Currently this is backed by `ember-cli-flash` and the named options are
|
||||||
|
currently options that can be accepted by `ember-cli-flash`. Be aware that this
|
||||||
|
is likely to change in the future.
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
import Modifier from 'ember-modifier';
|
||||||
|
import { assert } from '@ember/debug';
|
||||||
|
|
||||||
|
export default class StyleModifier extends Modifier {
|
||||||
|
setStyles(newStyles = []) {
|
||||||
|
const rulesToRemove = this._oldStyles || new Set();
|
||||||
|
if(!Array.isArray(newStyles)) {
|
||||||
|
newStyles = Object.entries(newStyles)
|
||||||
|
}
|
||||||
|
newStyles.forEach(([property, value]) => {
|
||||||
|
assert(
|
||||||
|
`Your given value for property '${property}' is ${value} (${typeof value}). Accepted types are string and undefined. Please change accordingly.`,
|
||||||
|
typeof value === 'undefined' || typeof value === 'string'
|
||||||
|
);
|
||||||
|
|
||||||
|
// priority must be specified as separate argument
|
||||||
|
// value must not contain "!important"
|
||||||
|
let priority = '';
|
||||||
|
if (value.length > 0 && value.includes('!important')) {
|
||||||
|
priority = 'important';
|
||||||
|
value = value.replace('!important', '');
|
||||||
|
}
|
||||||
|
|
||||||
|
// update CSSOM
|
||||||
|
this.element.style.setProperty(property, value, priority);
|
||||||
|
|
||||||
|
// should not remove rules that have been updated in this cycle
|
||||||
|
rulesToRemove.delete(property);
|
||||||
|
});
|
||||||
|
|
||||||
|
// remove rules that were present in last cycle but aren't present in this one
|
||||||
|
rulesToRemove.forEach((rule) => this.element.style.removeProperty(rule));
|
||||||
|
|
||||||
|
// cache styles that in this rendering cycle for the next one
|
||||||
|
this._oldStyles = new Set(newStyles.map((e) => e[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
didReceiveArguments() {
|
||||||
|
if(typeof this.args.named.delay !== 'undefined') {
|
||||||
|
setTimeout(
|
||||||
|
_ => {
|
||||||
|
if(typeof this !== this.args.positional[0]) {
|
||||||
|
this.setStyles(this.args.positional[0]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
this.args.named.delay
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.setStyles(this.args.positional[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ const notificationDefaults = function() {
|
||||||
return {
|
return {
|
||||||
timeout: 6000,
|
timeout: 6000,
|
||||||
extendedTimeout: 300,
|
extendedTimeout: 300,
|
||||||
|
destroyOnClick: true
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export default class FeedbackService extends Service {
|
export default class FeedbackService extends Service {
|
||||||
|
@ -23,7 +24,7 @@ export default class FeedbackService extends Service {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
success(item, action, status = defaultStatus) {
|
success(item, action, status = defaultStatus, model) {
|
||||||
const getAction = callableType(action);
|
const getAction = callableType(action);
|
||||||
const getStatus = callableType(status);
|
const getStatus = callableType(status);
|
||||||
// returning exactly `false` for a feedback action means even though
|
// returning exactly `false` for a feedback action means even though
|
||||||
|
@ -38,11 +39,12 @@ export default class FeedbackService extends Service {
|
||||||
// here..
|
// here..
|
||||||
action: getAction(),
|
action: getAction(),
|
||||||
item: item,
|
item: item,
|
||||||
|
model: model
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
error(e, action, status = defaultStatus) {
|
error(e, action, status = defaultStatus, model) {
|
||||||
const getAction = callableType(action);
|
const getAction = callableType(action);
|
||||||
const getStatus = callableType(status);
|
const getStatus = callableType(status);
|
||||||
this.notify.clearMessages();
|
this.notify.clearMessages();
|
||||||
|
@ -53,6 +55,7 @@ export default class FeedbackService extends Service {
|
||||||
type: getStatus(TYPE_SUCCESS),
|
type: getStatus(TYPE_SUCCESS),
|
||||||
// and here
|
// and here
|
||||||
action: getAction(),
|
action: getAction(),
|
||||||
|
model: model
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.notify.add({
|
this.notify.add({
|
||||||
|
@ -60,17 +63,18 @@ export default class FeedbackService extends Service {
|
||||||
type: getStatus(TYPE_ERROR, e),
|
type: getStatus(TYPE_ERROR, e),
|
||||||
action: getAction(),
|
action: getAction(),
|
||||||
error: e,
|
error: e,
|
||||||
|
model: model
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(handle, action, status) {
|
async execute(handle, action, status, routeName) {
|
||||||
let result;
|
let result;
|
||||||
try {
|
try {
|
||||||
result = await handle();
|
result = await handle();
|
||||||
this.success(result, action, status);
|
this.success(result, action, status, routeName);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.error(e, action, status);
|
this.error(e, action, status, routeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
@import 'consul-ui/components/empty-state';
|
@import 'consul-ui/components/empty-state';
|
||||||
@import 'consul-ui/components/expanded-single-select';
|
@import 'consul-ui/components/expanded-single-select';
|
||||||
@import 'consul-ui/components/form-elements';
|
@import 'consul-ui/components/form-elements';
|
||||||
@import 'consul-ui/components/flash-message';
|
|
||||||
@import 'consul-ui/components/icon-definition';
|
@import 'consul-ui/components/icon-definition';
|
||||||
@import 'consul-ui/components/list-row';
|
@import 'consul-ui/components/list-row';
|
||||||
@import 'consul-ui/components/inline-alert';
|
@import 'consul-ui/components/inline-alert';
|
||||||
|
|
|
@ -38,14 +38,6 @@ as |dc partition nspace id item create|}}
|
||||||
<AppView
|
<AppView
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Policy::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="breadcrumbs">
|
<BlockSlot @name="breadcrumbs">
|
||||||
<ol>
|
<ol>
|
||||||
<li><a data-test-back href={{href-to 'dc.acls.policies'}}>All Policies</a></li>
|
<li><a data-test-back href={{href-to 'dc.acls.policies'}}>All Policies</a></li>
|
||||||
|
|
|
@ -55,14 +55,6 @@ as |route|>
|
||||||
<AppView
|
<AppView
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Policy::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
<route.Title @title="Policies" />
|
<route.Title @title="Policies" />
|
||||||
|
|
|
@ -36,14 +36,6 @@ as |dc partition nspace item create|}}
|
||||||
<AppView
|
<AppView
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Role::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="breadcrumbs">
|
<BlockSlot @name="breadcrumbs">
|
||||||
<ol>
|
<ol>
|
||||||
<li><a data-test-back href={{href-to 'dc.acls.roles'}}>All Roles</a></li>
|
<li><a data-test-back href={{href-to 'dc.acls.roles'}}>All Roles</a></li>
|
||||||
|
|
|
@ -49,14 +49,6 @@ as |route|>
|
||||||
<AppView
|
<AppView
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Role::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
<route.Title @title="Roles" />
|
<route.Title @title="Roles" />
|
||||||
|
|
|
@ -36,14 +36,6 @@ as |dc partition nspace item create|}}
|
||||||
<AppView
|
<AppView
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Token::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="breadcrumbs">
|
<BlockSlot @name="breadcrumbs">
|
||||||
<ol>
|
<ol>
|
||||||
<li><a data-test-back href={{href-to 'dc.acls.tokens'}}>All Tokens</a></li>
|
<li><a data-test-back href={{href-to 'dc.acls.tokens'}}>All Tokens</a></li>
|
||||||
|
|
|
@ -52,14 +52,6 @@ as |route|>
|
||||||
<AppView
|
<AppView
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
>
|
>
|
||||||
<BlockSlot @name="notification" as |status type item error|>
|
|
||||||
<Consul::Token::Notifications
|
|
||||||
@type={{type}}
|
|
||||||
@status={{status}}
|
|
||||||
@item={{item}}
|
|
||||||
@error={{error}}
|
|
||||||
/>
|
|
||||||
</BlockSlot>
|
|
||||||
<BlockSlot @name="header">
|
<BlockSlot @name="header">
|
||||||
<h1>
|
<h1>
|
||||||
<route.Title @title="Tokens" />
|
<route.Title @title="Tokens" />
|
||||||
|
|
|
@ -28,28 +28,58 @@ as |route|>
|
||||||
@login={{route.model.app.login.open}}
|
@login={{route.model.app.login.open}}
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |after|>
|
||||||
{{#if (eq loader.error.status "404")}}
|
{{#if (eq loader.error.status "404")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
This KV or parent of this KV was deleted.
|
This KV or parent of this KV was deleted.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else if (eq loader.error.status "403")}}
|
{{else if (eq loader.error.status "403")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="error notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
You no longer have access to this KV.
|
You no longer have access to this KV.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
|
|
|
@ -26,28 +26,58 @@ as |route|>
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |after|>
|
||||||
{{#if (eq loader.error.status "404")}}
|
{{#if (eq loader.error.status "404")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
This node no longer exists in the catalog.
|
This node no longer exists in the catalog.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else if (eq loader.error.status "403")}}
|
{{else if (eq loader.error.status "403")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="error notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
You no longer have access to this node
|
You no longer have access to this node
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
<BlockSlot @name="loaded">
|
<BlockSlot @name="loaded">
|
||||||
|
|
|
@ -21,28 +21,58 @@ as |route|>
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |after|>
|
||||||
{{#if (eq loader.error.status "404")}}
|
{{#if (eq loader.error.status "404")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
This service has been deregistered and no longer exists in the catalog.
|
This service has been deregistered and no longer exists in the catalog.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else if (eq loader.error.status "403")}}
|
{{else if (eq loader.error.status "403")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="error notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
You no longer have access to this service
|
You no longer have access to this service
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
|
|
|
@ -19,28 +19,58 @@ as |route|>
|
||||||
/>
|
/>
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
<BlockSlot @name="disconnected" as |Notification|>
|
<BlockSlot @name="disconnected" as |after|>
|
||||||
{{#if (eq loader.error.status "404")}}
|
{{#if (eq loader.error.status "404")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
This service has been deregistered and no longer exists in the catalog.
|
This service has been deregistered and no longer exists in the catalog.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else if (eq loader.error.status "403")}}
|
{{else if (eq loader.error.status "403")}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="error notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="error"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Error!</strong>
|
<strong>Error!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
You no longer have access to this service
|
You no longer have access to this service
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{else}}
|
{{else}}
|
||||||
<Notification @sticky={{true}}>
|
<Notice
|
||||||
<p data-notification role="alert" class="warning notification-update">
|
{{notification
|
||||||
|
sticky=true
|
||||||
|
}}
|
||||||
|
class="notification-update"
|
||||||
|
@type="warning"
|
||||||
|
as |notice|>
|
||||||
|
<notice.Header>
|
||||||
<strong>Warning!</strong>
|
<strong>Warning!</strong>
|
||||||
|
</notice.Header>
|
||||||
|
<notice.Body>
|
||||||
|
<p>
|
||||||
An error was returned whilst loading this data, refresh to try again.
|
An error was returned whilst loading this data, refresh to try again.
|
||||||
</p>
|
</p>
|
||||||
</Notification>
|
</notice.Body>
|
||||||
|
</Notice>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</BlockSlot>
|
</BlockSlot>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,20 @@
|
||||||
|
|
||||||
{{document-attrs class="is-debug"}}
|
{{document-attrs class="is-debug"}}
|
||||||
<App class="docs" id="wrapper">
|
<App class="docs" id="wrapper">
|
||||||
|
|
||||||
|
<:notifications as |app|>
|
||||||
|
{{#each flashMessages.queue as |flash|}}
|
||||||
|
{{#if flash.dom}}
|
||||||
|
<app.Notification
|
||||||
|
@delay={{sub flash.timeout flash.extendedTimeout}}
|
||||||
|
@sticky={{flash.sticky}}
|
||||||
|
>
|
||||||
|
{{{flash.dom}}}
|
||||||
|
</app.Notification>
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
</:notifications>
|
||||||
|
|
||||||
<:main-nav>
|
<:main-nav>
|
||||||
|
|
||||||
<DocfyOutput as |node|>
|
<DocfyOutput as |node|>
|
||||||
|
|
Loading…
Reference in New Issue