Skip to content
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-21534 VAULT-21533 VAULT-21536] edit, preview, and delete custom message #24603

10 changes: 10 additions & 0 deletions ui/app/adapters/config-ui/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,14 @@ export default class MessageAdapter extends ApplicationAdapter {
const { authenticated } = query;
return super.query(store, type, { authenticated, list: true });
}

queryRecord(store, type, id) {
return this.ajax(`${this.buildURL(type)}/${id}`, 'GET');
}

updateRecord(store, type, snapshot) {
return this.ajax(`${this.buildURL(type)}/${snapshot.record.id}`, 'POST', {
data: this.serialize(snapshot.record),
});
}
}
7 changes: 3 additions & 4 deletions ui/app/models/config-ui/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
*/
import Model, { attr } from '@ember-data/model';
import lazyCapabilities, { apiPath } from 'vault/macros/lazy-capabilities';
import { isAfter, format, addDays, startOfDay } from 'date-fns';
import { datetimeLocalStringFormat, parseAPITimestamp } from 'core/utils/date-formatters';
import { isAfter, addDays, startOfDay } from 'date-fns';
import { withModelValidations } from 'vault/decorators/model-validations';
import { withFormFields } from 'vault/decorators/model-form-fields';

Expand Down Expand Up @@ -77,7 +76,7 @@ export default class MessageModel extends Model {
editType: 'dateTimeLocal',
label: 'Message starts',
subText: 'Defaults to 12:00 a.m. the following day (local timezone).',
defaultValue: format(addDays(startOfDay(new Date() || this.startTime), 1), datetimeLocalStringFormat),
defaultValue: addDays(startOfDay(new Date() || this.startTime), 1).toISOString(),
})
startTime;
@attr('date', { editType: 'yield', label: 'Message expires' }) endTime;
Expand All @@ -90,7 +89,7 @@ export default class MessageModel extends Model {

// date helpers
get isStartTimeAfterToday() {
return isAfter(parseAPITimestamp(this.startTime), new Date());
return isAfter(this.startTime, new Date());
}

// capabilities
Expand Down
37 changes: 28 additions & 9 deletions ui/app/serializers/config-ui/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,42 @@
* SPDX-License-Identifier: BUSL-1.1
*/

import { encodeString } from 'core/utils/b64';
import { decodeString, encodeString } from 'core/utils/b64';
import ApplicationSerializer from '../application';

export default class MessageSerializer extends ApplicationSerializer {
primaryKey = 'id';
attrs = {
link: { serialize: false },
active: { serialize: false },
};

serialize() {
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
if (requestType === 'queryRecord') {
const transformed = {
...payload.data,
message: decodeString(payload.data.message),
link_title: payload.data.link.title,
link_href: payload.data.link.href,
};
delete transformed.link;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of deleting this here, does it work if you do something above about attributes you do not want to serialize? I ask because it makes it easier to debug later (I'm noticing this in ember data upgrade things) if all attributes removed are listed at the top of the serializer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 attrs = {
    link: { serialize: false },
  };

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll address the link attribute feedback in a different PR :) Ticket to address this feedback is VAULT-22908

return super.normalizeResponse(store, primaryModelClass, transformed, id, requestType);
}
return super.normalizeResponse(store, primaryModelClass, payload, id, requestType);
}

serialize(snapshot) {
const json = super.serialize(...arguments);
json.message = encodeString(json.message);
json.link = {
title: json.link_title,
href: json.link_href,
title: json?.link_title || '',
href: json?.link_href || '',
};

delete json.link_title;
delete json.link_href;

// using the snapshot startTime and endTime since the json start and end times are null when
// it gets to the serialize function.
json.start_time = snapshot.record.startTime;
json.end_time = snapshot.record.endTime;
delete json?.link_title;
delete json?.link_href;
return json;
}

Expand Down
13 changes: 13 additions & 0 deletions ui/app/styles/helper-classes/layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@
visibility: hidden;
}

// overflow
.is-overflow-hidden {
overflow: hidden;
}

// width and height
.is-fullwidth {
width: 100%;
Expand All @@ -59,6 +64,10 @@
width: 75%;
}

.is-two-thirds-width {
width: 66%;
}

.is-auto-width {
width: auto;
}
Expand All @@ -75,6 +84,10 @@
height: 125px;
}

.is-calc-large-height {
height: calc($desktop * 0.66);
}

// float
.is-pulled-left {
float: left !important;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Messages::TabPageHeader
@authenticated={{@message.authenticated}}
@pageTitle="{{if @message.isNew 'Create' 'Edit'}} message"
@breadcrumbs={{this.breadcrumbs}}
@breadcrumbs={{@breadcrumbs}}
/>

<form id="message-create-edit-form" {{on "submit" (perform this.save)}} data-test-form="create-and-edit">
Expand Down Expand Up @@ -58,8 +58,14 @@
{{/each}}

<Hds::ButtonSet class="has-top-margin-s has-bottom-margin-m has-top-margin-xl">
{{! TODO: VAULT-21533 preview modal }}
<Hds::Button @text="Preview" @color="tertiary" @icon="eye" />
<Hds::Button
@text="Preview"
@color="tertiary"
@icon="eye"
disabled={{and (not @message.title @message.message)}}
{{on "click" (fn (mut this.showMessagePreviewModal) true)}}
data-test-button="preview"
/>

<Hds::Button
@text="{{if @message.isNew 'Create' 'Edit'}} message"
Expand All @@ -76,4 +82,34 @@
/>
</Hds::ButtonSet>
</div>
</form>
</form>

{{#if this.showMessagePreviewModal}}
{{#if (eq @message.type "modal")}}
<Hds::Modal
id="message-modal-preview"
@size="large"
@color="warning"
@onClose={{fn (mut this.showMessagePreviewModal) false}}
data-test-modal="preview modal"
as |M|
>
<M.Header data-test-modal-title={{@message.title}}>
{{@message.title}}
</M.Header>
<M.Body data-test-modal-body>
{{@message.message}}
{{#if @message.linkHref}}
<Hds::Link::Inline @icon="external-link" @href={{@message.linkHref}}>
{{@message.linkTitle}}
</Hds::Link::Inline>
{{/if}}
</M.Body>
<M.Footer as |F|>
<Hds::Button @text="Confirm" {{on "click" F.close}} data-test-modal-button="Close" />
</M.Footer>
</Hds::Modal>
{{else}}
kiannaquach marked this conversation as resolved.
Show resolved Hide resolved
kiannaquach marked this conversation as resolved.
Show resolved Hide resolved
<Messages::PreviewImage @message={{@message}} @showMessagePreviewModal={{this.showMessagePreviewModal}} />
{{/if}}
{{/if}}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import { inject as service } from '@ember/service';

export default class MessagesList extends Component {
@service router;
@service store;
@service flashMessages;

@tracked errorBanner = '';
@tracked modelValidations;
@tracked invalidFormMessage;
@tracked showMessagePreviewModal = false;

willDestroy() {
super.willDestroy();
Expand All @@ -36,15 +38,6 @@ export default class MessagesList extends Component {
}
}

get breadcrumbs() {
const authenticated =
this.args.message.authenticated === undefined ? true : this.args.message.authenticated;
return [
{ label: 'Messages', route: 'messages.index', query: { authenticated } },
{ label: 'Create Message' },
];
}

@task
*save(event) {
event.preventDefault();
Expand All @@ -55,17 +48,9 @@ export default class MessagesList extends Component {

if (isValid) {
const { isNew } = this.args.message;

// We do these checks here since there could be a scenario where startTime and endTime are strings.
// The model expects these attrs to be a date object, so we will need to update these attrs to be in
// date object format.
if (typeof this.args.message.startTime === 'string')
this.args.message.startTime = new Date(this.args.message.startTime);
if (typeof this.args.message.endTime === 'string')
this.args.message.endTime = new Date(this.args.message.endTime);

const { id } = yield this.args.message.save();
this.flashMessages.success(`Successfully ${isNew ? 'created' : 'updated'} the message.`);
const { id, title } = yield this.args.message.save();
this.flashMessages.success(`Successfully ${isNew ? 'created' : 'updated'} ${title} message.`);
this.store.clearDataset('config-ui/message');
this.router.transitionTo('vault.cluster.config-ui.messages.message.details', id);
}
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion ui/lib/config-ui/addon/components/messages/page/list.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</Messages::TabPageHeader>

{{#if @messages.length}}
{{#each this.getMessages as |message|}}
{{#each this.formattedMessages as |message|}}
<LinkedBlock
data-test-list-item={{message.id}}
class="list-item-row"
Expand Down
7 changes: 5 additions & 2 deletions ui/lib/config-ui/addon/components/messages/page/list.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ import { dateFormat } from 'core/helpers/date-format';

export default class MessagesList extends Component {
@service store;
@service router;
@service flashMessages;

get getMessages() {
get formattedMessages() {
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'], {
Expand Down Expand Up @@ -68,5 +69,7 @@ export default class MessagesList extends Component {
*deleteMessage(message) {
this.store.clearDataset('config-ui/message');
yield message.destroyRecord(message.id);
this.router.transitionTo('vault.cluster.config-ui.messages');
this.flashMessages.success(`Successfully deleted ${message.title}.`);
}
}
40 changes: 40 additions & 0 deletions ui/lib/config-ui/addon/components/messages/preview-image.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}

<Hds::Modal
@onClose={{fn (mut @showMessagePreviewModal) false}}
id="message-alert-preview"
class="is-calc-large-height is-two-thirds-width"
data-test-modal="preview image"
as |M|
>
<M.Body class="is-paddingless is-overflow-hidden">
<Hds::Alert
@type="page"
@color="warning"
@onDismiss={{fn (mut @showMessagePreviewModal) false}}
class="has-bottom-margin-s"
data-test-alert={{@message.title}}
as |A|
>
<A.Title data-test-alert-title={{@message.title}}>{{@message.title}}</A.Title>
<A.Description data-test-alert-description={{@message.title}}>
{{@message.message}}
{{#if @message.linkHref}}
<Hds::Link::Inline @icon="external-link" @href={{@message.linkHref}}>
{{@message.linkTitle}}
</Hds::Link::Inline>
{{/if}}
</A.Description>
</Hds::Alert>
<img
src={{img-path (if @message.authenticated "~/custom-messages-dashboard.png" "~/custom-messages-login.png")}}
alt={{if @message.authenticated "dashboard page preview" "login page preview"}}
/>
</M.Body>
<M.Footer as |F|>
<Hds::Button @text="Close preview" {{on "click" F.close}} data-test-modal-button="Close" />
</M.Footer>
</Hds::Modal>
9 changes: 9 additions & 0 deletions ui/lib/config-ui/addon/routes/messages/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ export default class MessagesCreateRoute extends Route {
authenticated,
});
}

setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);

controller.breadcrumbs = [
{ label: 'Messages', route: 'messages', query: { authenticated: !!resolvedModel.authenticated } },
{ label: 'Create Message' },
];
}
}
20 changes: 19 additions & 1 deletion ui/lib/config-ui/addon/routes/messages/message/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,23 @@
*/

import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class MessagesMessageEditRoute extends Route {}
export default class MessagesMessageEditRoute extends Route {
@service store;

model() {
const { id } = this.paramsFor('messages.message');

return this.store.queryRecord('config-ui/message', id);
}

setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);

controller.breadcrumbs = [
{ label: 'Messages', route: 'messages', query: { authenticated: resolvedModel.authenticated } },
{ label: 'Edit Message' },
];
}
}
2 changes: 1 addition & 1 deletion ui/lib/config-ui/addon/templates/messages/create.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
SPDX-License-Identifier: BUSL-1.1
~}}

<Messages::Page::CreateAndEditMessageForm @message={{this.model}} />
<Messages::Page::CreateAndEditMessageForm @message={{this.model}} @breadcrumbs={{this.breadcrumbs}} />
3 changes: 1 addition & 2 deletions ui/lib/config-ui/addon/templates/messages/message/edit.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
SPDX-License-Identifier: BUSL-1.1
~}}

Message Edit
{{outlet}}
<Messages::Page::CreateAndEditMessageForm @message={{this.model}} @breadcrumbs={{this.breadcrumbs}} />
Binary file added ui/public/images/custom-messages-dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ui/public/images/custom-messages-login.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading