-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UI: [VAULT-21530] Custom Messages List View w/ Pagination and LazyPaginatedQuery #24133
Changes from all commits
43455ac
2b84b22
73310c2
1aea0d4
dc6a7c8
a44013c
d162ff0
511bbcc
1eb3568
82b49d2
f3dd0d8
447b815
6899246
6063379
d85e3fc
7a49bc1
3b8b9fd
4e08f5d
324839a
8200269
82aca4e
9bc1613
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
{{! | ||
Copyright (c) HashiCorp, Inc. | ||
SPDX-License-Identifier: BUSL-1.1 | ||
~}} | ||
|
||
<Messages::TabPageHeader @authenticated={{@authenticated}}> | ||
<:toolbarActions> | ||
<ToolbarLink data-test-toolbar-create-message @route="messages.create" @type="add"> | ||
Create message | ||
</ToolbarLink> | ||
</:toolbarActions> | ||
</Messages::TabPageHeader> | ||
|
||
{{#if @messages.length}} | ||
{{#each this.getMessages as |message|}} | ||
<LinkedBlock | ||
data-test-list-item={{message.id}} | ||
class="list-item-row" | ||
@params={{array "messages.message.details" message.id}} | ||
@linkPrefix="vault.cluster.config-ui" | ||
> | ||
<div class="level is-mobile"> | ||
<div class="level-left"> | ||
<div> | ||
<Hds::Text::Display @tag="h2" data-linked-block-title={{message.id}}> | ||
<Icon @name="message-circle" class="auto-width" aria-label="message" /> | ||
{{message.title}} | ||
</Hds::Text::Display> | ||
<div class="has-top-margin-xs"> | ||
<Hds::Badge | ||
@text={{message.badgeDisplayText}} | ||
@color={{if message.active "highlight" "neutral"}} | ||
data-test-badge={{concat "active:" message.active}} | ||
/> | ||
<Hds::Badge | ||
@text={{(capitalize message.type)}} | ||
@color={{if message.active "highlight" "neutral"}} | ||
data-test-badge={{message.type}} | ||
/> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="level-right is-flex is-paddingless is-marginless"> | ||
<div class="level-item"> | ||
<PopupMenu @name="engine-menu"> | ||
<Confirm as |c|> | ||
<nav class="menu"> | ||
<ul class="menu-list"> | ||
{{#if message.canEditCustomMessages}} | ||
kiannaquach marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<li class="action"> | ||
<LinkTo @route="messages.message.edit" @model={{message.id}}> | ||
Edit | ||
</LinkTo> | ||
</li> | ||
{{/if}} | ||
{{#if message.canDeleteCustomMessages}} | ||
<li class="action"> | ||
<c.Message | ||
@id={{message.id}} | ||
@triggerText="Delete" | ||
@message="This will delete this message permanently. You cannot undo this action." | ||
@title="Are you sure?" | ||
@confirmButtonText="Delete" | ||
@onConfirm={{perform this.deleteMessage message}} | ||
data-test-delete-custom-message | ||
/> | ||
</li> | ||
{{/if}} | ||
</ul> | ||
</nav> | ||
</Confirm> | ||
</PopupMenu> | ||
</div> | ||
</div> | ||
</div> | ||
</LinkedBlock> | ||
{{/each}} | ||
<Hds::Pagination::Numbered | ||
class="has-top-margin-m has-bottom-margin-m" | ||
@currentPage={{@messages.meta.currentPage}} | ||
@currentPageSize={{@messages.meta.pageSize}} | ||
@route="messages.index" | ||
@showSizeSelector={{false}} | ||
@totalItems={{@messages.meta.total}} | ||
@queryFunction={{this.paginationQueryParams}} | ||
/> | ||
{{else}} | ||
<EmptyState | ||
@title="No messages yet" | ||
@message="Add a custom message for all users after they log into Vault. Create message to get started." | ||
> | ||
<Hds::Link::Standalone | ||
@icon="plus" | ||
@iconPosition="leading" | ||
@text="Create message" | ||
@route="messages.create" | ||
class="is-no-underline" | ||
data-test-action-text="Create message" | ||
/> | ||
</EmptyState> | ||
{{/if}} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
|
||
import Component from '@glimmer/component'; | ||
import { inject as service } from '@ember/service'; | ||
import { task } from 'ember-concurrency'; | ||
import { dateFormat } from 'core/helpers/date-format'; | ||
|
||
/** | ||
* @module Page::MessagesList | ||
* Page::MessagesList components are used to display list of messages. | ||
* @example | ||
* ```js | ||
* <Page::MessagesList @messages={{this.messages}} /> | ||
* ``` | ||
* @param {array} messages - array message objects | ||
*/ | ||
|
||
export default class MessagesList extends Component { | ||
@service store; | ||
|
||
get getMessages() { | ||
return this.args.messages.map((message) => { | ||
let badgeDisplayText = ''; | ||
|
||
if (message.active) { | ||
if (message.endTime) { | ||
badgeDisplayText = `Active until ${dateFormat([message.endTime, 'MMM d, yyyy hh:mm aaa'], { | ||
withTimeZone: true, | ||
})}`; | ||
} else { | ||
badgeDisplayText = 'Active'; | ||
} | ||
} else { | ||
if (message.isStartTimeAfterToday) { | ||
badgeDisplayText = `Scheduled: ${dateFormat([message.startTime, 'MMM d, yyyy hh:mm aaa'], { | ||
withTimeZone: true, | ||
})}`; | ||
} else { | ||
badgeDisplayText = `Inactive: ${dateFormat([message.startTime, 'MMM d, yyyy hh:mm aaa'], { | ||
withTimeZone: true, | ||
})}`; | ||
} | ||
} | ||
|
||
message.badgeDisplayText = badgeDisplayText; | ||
return message; | ||
}); | ||
} | ||
|
||
// callback from HDS pagination to set the queryParams page | ||
get paginationQueryParams() { | ||
return (page) => { | ||
return { | ||
page, | ||
}; | ||
}; | ||
} | ||
|
||
@task | ||
*deleteMessage(message) { | ||
kiannaquach marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this.store.clearDataset('config-ui/message'); | ||
yield message.destroyRecord(message.id); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
{{! | ||
Copyright (c) HashiCorp, Inc. | ||
SPDX-License-Identifier: BUSL-1.1 | ||
~}} | ||
|
||
<PageHeader as |p|> | ||
{{#if @breadcrumbs}} | ||
<p.top> | ||
<Page::Breadcrumbs @breadcrumbs={{@breadcrumbs}} /> | ||
</p.top> | ||
{{/if}} | ||
<p.levelLeft> | ||
<Hds::Text::Display @tag="h2" @size="500" class="has-top-margin-xl"> | ||
Custom messages | ||
</Hds::Text::Display> | ||
</p.levelLeft> | ||
</PageHeader> | ||
|
||
<div class="tabs-container box is-bottomless is-marginless is-paddingless"> | ||
<nav class="tabs" aria-label="custom-messages"> | ||
<ul> | ||
{{! Explicitly setting page to 1 here since we want to reset the page param on transition}} | ||
<LinkTo | ||
class={{if @authenticated "active"}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. An issue I ran into: when transitioning to different tabs, the page query params are retained from the previous tab. This is the default behavior since controllers are singletons. I tried adding a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should be able to reset to null. Let's zoom through this as it gets a little confusing. Ex:
|
||
@route="messages" | ||
@query={{hash authenticated=true page=1}} | ||
data-test-custom-messages-tab="After user logs in" | ||
> | ||
After user logs in | ||
</LinkTo> | ||
<LinkTo | ||
class={{unless @authenticated "active"}} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you might also try using the ember truth-helper not. https://github.com/jmurphyau/ember-truth-helpers. It reads easier than unless. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The linter recommends There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TIL. |
||
@route="messages" | ||
@query={{hash authenticated=false page=1}} | ||
kiannaquach marked this conversation as resolved.
Show resolved
Hide resolved
|
||
data-test-custom-messages-tab="On login page" | ||
> | ||
On login page | ||
</LinkTo> | ||
</ul> | ||
</nav> | ||
</div> | ||
|
||
{{#if (or (has-block "toolbarFilters") (has-block "toolbarActions"))}} | ||
<Toolbar> | ||
<ToolbarFilters> | ||
{{yield to="toolbarFilters"}} | ||
</ToolbarFilters> | ||
<ToolbarActions> | ||
{{yield to="toolbarActions"}} | ||
</ToolbarActions> | ||
</Toolbar> | ||
{{/if}} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* Copyright (c) HashiCorp, Inc. | ||
* SPDX-License-Identifier: BUSL-1.1 | ||
*/ | ||
|
||
import Controller from '@ember/controller'; | ||
export default class MessagesController extends Controller { | ||
queryParams = ['authenticated', 'page']; | ||
|
||
authenticated = true; | ||
kiannaquach marked this conversation as resolved.
Show resolved
Hide resolved
|
||
page = 1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if query is false? Does the API error? You might want to consider a catch statement here and doing something with the error if it's a 404 (no response, e.g. no items in the list) vs anything else like a 403. There is a good example of this on the ldap library adapter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had a try/catch here but the 404/403 error didn't bubble to the query. Since I'm using lazyPaginatedQuery, I did the try/catch in the route instead and the errors get surfaced in there. https://github.com/hashicorp/vault/pull/24133/files#diff-5a46be6bfd29de6b487be8b6242b62bdd861524c47cc1fb10eb4a876e00c129f