diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-18 21:09:22 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-10-18 21:09:22 +0300 |
commit | e4220eeccaf1d53444fdd9102a4061336f91784e (patch) | |
tree | 541d4f0b9fb1273c722973633051795308c46dee /app/assets/javascripts/admin | |
parent | b556d0fab74a7ef460d868e508ea5ca72d0e5eed (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/admin')
4 files changed, 193 insertions, 24 deletions
diff --git a/app/assets/javascripts/admin/broadcast_messages/components/base.vue b/app/assets/javascripts/admin/broadcast_messages/components/base.vue index bc395a83625..b7bafe46327 100644 --- a/app/assets/javascripts/admin/broadcast_messages/components/base.vue +++ b/app/assets/javascripts/admin/broadcast_messages/components/base.vue @@ -1,21 +1,112 @@ <script> +import { GlPagination } from '@gitlab/ui'; +import { redirectTo } from '~/lib/utils/url_utility'; +import { buildUrlWithCurrentLocation } from '~/lib/utils/common_utils'; +import { createAlert, VARIANT_DANGER } from '~/flash'; +import { s__ } from '~/locale'; +import axios from '~/lib/utils/axios_utils'; import MessagesTable from './messages_table.vue'; +const PER_PAGE = 20; + export default { name: 'BroadcastMessagesBase', components: { + GlPagination, MessagesTable, }, + props: { + page: { + type: Number, + required: true, + }, + messagesCount: { + type: Number, + required: true, + }, messages: { type: Array, required: true, }, }, + + i18n: { + deleteError: s__( + 'BroadcastMessages|There was an issue deleting this message, please try again later.', + ), + }, + + data() { + return { + currentPage: this.page, + totalMessages: this.messagesCount, + visibleMessages: this.messages.map((message) => ({ + ...message, + disable_delete: false, + })), + }; + }, + + computed: { + hasVisibleMessages() { + return this.visibleMessages.length > 0; + }, + }, + + watch: { + totalMessages(newVal, oldVal) { + // Pagination controls disappear when there is only + // one page worth of messages. Since we're relying on static data, + // this could hide messages on the next page, or leave the user + // stranded on page 2 when deleting the last message. + // Force a page reload to avoid this edge case. + if (newVal === PER_PAGE && oldVal === PER_PAGE + 1) { + redirectTo(this.buildPageUrl(1)); + } + }, + }, + + methods: { + buildPageUrl(newPage) { + return buildUrlWithCurrentLocation(`?page=${newPage}`); + }, + + async deleteMessage(messageId) { + const index = this.visibleMessages.findIndex((m) => m.id === messageId); + if (!index === -1) return; + + const message = this.visibleMessages[index]; + this.$set(this.visibleMessages, index, { ...message, disable_delete: true }); + + try { + await axios.delete(message.delete_path); + } catch (e) { + this.$set(this.visibleMessages, index, { ...message, disable_delete: false }); + createAlert({ message: this.$options.i18n.deleteError, variant: VARIANT_DANGER }); + return; + } + + // Remove the message from the table + this.visibleMessages = this.visibleMessages.filter((m) => m.id !== messageId); + this.totalMessages -= 1; + }, + }, }; </script> + <template> <div> - <messages-table v-if="messages.length > 0" :messages="messages" /> + <messages-table + v-if="hasVisibleMessages" + :messages="visibleMessages" + @delete-message="deleteMessage" + /> + <gl-pagination + v-model="currentPage" + :total-items="totalMessages" + :link-gen="buildPageUrl" + align="center" + /> </div> </template> diff --git a/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue b/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue index 7b531b850c6..1408312d3e4 100644 --- a/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue +++ b/app/assets/javascripts/admin/broadcast_messages/components/messages_table.vue @@ -1,10 +1,23 @@ <script> -import MessagesTableRow from './messages_table_row.vue'; +import { GlButton, GlTableLite, GlSafeHtmlDirective } from '@gitlab/ui'; +import { __ } from '~/locale'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; + +const DEFAULT_TD_CLASSES = 'gl-vertical-align-middle!'; export default { name: 'MessagesTable', components: { - MessagesTableRow, + GlButton, + GlTableLite, + }, + directives: { + SafeHtml: GlSafeHtmlDirective, + }, + mixins: [glFeatureFlagsMixin()], + i18n: { + edit: __('Edit'), + delete: __('Delete'), }, props: { messages: { @@ -12,10 +25,89 @@ export default { required: true, }, }, + computed: { + fields() { + if (this.glFeatures.roleTargetedBroadcastMessages) return this.$options.allFields; + return this.$options.allFields.filter((f) => f.key !== 'target_roles'); + }, + }, + allFields: [ + { + key: 'status', + label: __('Status'), + tdClass: DEFAULT_TD_CLASSES, + }, + { + key: 'preview', + label: __('Preview'), + tdClass: DEFAULT_TD_CLASSES, + }, + { + key: 'starts_at', + label: __('Starts'), + tdClass: DEFAULT_TD_CLASSES, + }, + { + key: 'ends_at', + label: __('Ends'), + tdClass: DEFAULT_TD_CLASSES, + }, + { + key: 'target_roles', + label: __('Target roles'), + tdClass: DEFAULT_TD_CLASSES, + thAttr: { 'data-testid': 'target-roles-th' }, + }, + { + key: 'target_path', + label: __('Target Path'), + tdClass: DEFAULT_TD_CLASSES, + }, + { + key: 'type', + label: __('Type'), + tdClass: DEFAULT_TD_CLASSES, + }, + { + key: 'buttons', + label: '', + tdClass: `${DEFAULT_TD_CLASSES} gl-white-space-nowrap`, + }, + ], + safeHtmlConfig: { + ADD_TAGS: ['use'], + }, }; </script> <template> - <div> - <messages-table-row v-for="message in messages" :key="message.id" :message="message" /> - </div> + <gl-table-lite + :items="messages" + :fields="fields" + :tbody-tr-attr="{ 'data-testid': 'message-row' }" + stacked="md" + > + <template #cell(preview)="{ item: { preview } }"> + <div v-safe-html:[$options.safeHtmlConfig]="preview"></div> + </template> + + <template #cell(buttons)="{ item: { id, edit_path, disable_delete } }"> + <gl-button + icon="pencil" + :aria-label="$options.i18n.edit" + :href="edit_path" + data-testid="edit-message" + /> + + <gl-button + class="gl-ml-3" + icon="remove" + variant="danger" + :aria-label="$options.i18n.delete" + rel="nofollow" + :disabled="disable_delete" + :data-testid="`delete-message-${id}`" + @click="$emit('delete-message', id)" + /> + </template> + </gl-table-lite> </template> diff --git a/app/assets/javascripts/admin/broadcast_messages/components/messages_table_row.vue b/app/assets/javascripts/admin/broadcast_messages/components/messages_table_row.vue deleted file mode 100644 index bd45bcc4fc4..00000000000 --- a/app/assets/javascripts/admin/broadcast_messages/components/messages_table_row.vue +++ /dev/null @@ -1,16 +0,0 @@ -<script> -export default { - name: 'MessagesTableRow', - props: { - message: { - type: Object, - required: true, - }, - }, -}; -</script> -<template> - <div> - {{ message.id }} - </div> -</template> diff --git a/app/assets/javascripts/admin/broadcast_messages/index.js b/app/assets/javascripts/admin/broadcast_messages/index.js index e71495804ee..81952d2033e 100644 --- a/app/assets/javascripts/admin/broadcast_messages/index.js +++ b/app/assets/javascripts/admin/broadcast_messages/index.js @@ -3,14 +3,16 @@ import BroadcastMessagesBase from './components/base.vue'; export default () => { const el = document.querySelector('#js-broadcast-messages'); - const { messages } = el.dataset; + const { page, messagesCount, messages } = el.dataset; return new Vue({ el, - name: 'BroadcastMessagesBase', + name: 'BroadcastMessages', render(createElement) { return createElement(BroadcastMessagesBase, { props: { + page: Number(page), + messagesCount: Number(messagesCount), messages: JSON.parse(messages), }, }); |