diff options
Diffstat (limited to 'app/assets/javascripts/issues/show')
17 files changed, 207 insertions, 96 deletions
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue index 0daf77e03dc..e5428f87095 100644 --- a/app/assets/javascripts/issues/show/components/app.vue +++ b/app/assets/javascripts/issues/show/components/app.vue @@ -1,7 +1,7 @@ <script> import { GlIcon, GlBadge, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui'; import Visibility from 'visibilityjs'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import { IssuableStatus, IssuableStatusText, @@ -327,7 +327,7 @@ export default { this.store.updateState(data); }) .catch(() => { - createFlash({ + createAlert({ message: this.defaultErrorMessage, }); }); @@ -362,7 +362,7 @@ export default { this.updateAndShowForm(res.data); }) .catch(() => { - createFlash({ + createAlert({ message: this.defaultErrorMessage, }); this.updateAndShowForm(); @@ -429,7 +429,7 @@ export default { errMsg += `. ${message}`; } - this.flashContainer = createFlash({ + this.flashContainer = createAlert({ message: errMsg, }); }) diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue index 5c2a154362f..78e729b97da 100644 --- a/app/assets/javascripts/issues/show/components/description.vue +++ b/app/assets/javascripts/issues/show/components/description.vue @@ -1,11 +1,12 @@ <script> -import { GlSafeHtmlDirective as SafeHtml, GlToast, GlTooltip, GlModalDirective } from '@gitlab/ui'; +import { GlToast, GlTooltip, GlModalDirective } from '@gitlab/ui'; import $ from 'jquery'; import Sortable from 'sortablejs'; import Vue from 'vue'; +import SafeHtml from '~/vue_shared/directives/safe_html'; import { getIdFromGraphQLId, convertToGraphQLId } from '~/graphql_shared/utils'; import { TYPE_WORK_ITEM } from '~/graphql_shared/constants'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import { IssuableType } from '~/issues/constants'; import { isMetaKey } from '~/lib/utils/common_utils'; import { isPositiveInteger } from '~/lib/utils/number_utils'; @@ -27,6 +28,7 @@ import { TASK_TYPE_NAME, WIDGET_TYPE_DESCRIPTION, } from '~/work_items/constants'; +import { renderGFM } from '~/behaviors/markdown/render_gfm'; import animateMixin from '../mixins/animate'; import { convertDescriptionWithNewSort } from '../utils'; @@ -165,7 +167,7 @@ export default { this.renderGFM(); this.updateTaskStatusText(); - if (this.workItemId) { + if (this.workItemId && this.workItemsEnabled) { const taskLink = this.$el.querySelector( `.gfm-issue[data-issue="${getIdFromGraphQLId(this.workItemId)}"]`, ); @@ -177,7 +179,7 @@ export default { }, methods: { renderGFM() { - $(this.$refs['gfm-content']).renderGFM(); + renderGFM(this.$refs['gfm-content']); if (this.canUpdate) { // eslint-disable-next-line no-new @@ -283,7 +285,7 @@ export default { }, taskListUpdateError() { - createFlash({ + createAlert({ message: sprintf( __( 'Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again.', @@ -467,7 +469,7 @@ export default { this.workItemId = newWorkItem.id; this.openWorkItemDetailModal(el); } catch (error) { - createFlash({ + createAlert({ message: sprintfWorkItem(I18N_WORK_ITEM_ERROR_CREATING, workItemTypes.TASK), error, captureError: true, diff --git a/app/assets/javascripts/issues/show/components/fields/description.vue b/app/assets/javascripts/issues/show/components/fields/description.vue index 180dea77003..04c5007dbec 100644 --- a/app/assets/javascripts/issues/show/components/fields/description.vue +++ b/app/assets/javascripts/issues/show/components/fields/description.vue @@ -67,6 +67,7 @@ export default { :quick-actions-docs-path="quickActionsDocsPath" :enable-autocomplete="enableAutocomplete" supports-quick-actions + use-bottom-toolbar autofocus @input="$emit('input', $event)" @keydown.meta.enter="updateIssuable" diff --git a/app/assets/javascripts/issues/show/components/form.vue b/app/assets/javascripts/issues/show/components/form.vue index 0c6b61fb893..b56c91d7983 100644 --- a/app/assets/javascripts/issues/show/components/form.vue +++ b/app/assets/javascripts/issues/show/components/form.vue @@ -164,7 +164,7 @@ export default { <template> <form data-testid="issuable-form"> - <locked-warning v-if="showLockedWarning" /> + <locked-warning v-if="showLockedWarning" :issuable-type="issuableType" /> <gl-alert v-if="showOutdatedDescriptionWarning" class="gl-mb-5" diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue index c01de63ced9..983e2e6530e 100644 --- a/app/assets/javascripts/issues/show/components/header_actions.vue +++ b/app/assets/javascripts/issues/show/components/header_actions.vue @@ -10,7 +10,7 @@ import { GlTooltipDirective, } from '@gitlab/ui'; import { mapActions, mapGetters, mapState } from 'vuex'; -import createFlash, { FLASH_TYPES } from '~/flash'; +import { createAlert, VARIANT_SUCCESS } from '~/flash'; import { EVENT_ISSUABLE_VUE_APP_CHANGE } from '~/issuable/constants'; import { IssuableStatus, IssueType } from '~/issues/constants'; import { ISSUE_STATE_EVENT_CLOSE, ISSUE_STATE_EVENT_REOPEN } from '~/issues/show/constants'; @@ -40,6 +40,7 @@ export default { promoteSuccessMessage: __( 'The issue was successfully promoted to an epic. Redirecting to epic...', ), + reportAbuse: __('Report abuse to administrator'), }, components: { DeleteIssueModal, @@ -191,7 +192,7 @@ export default { // Dispatch event which updates open/close state, shared among the issue show page document.dispatchEvent(new CustomEvent(EVENT_ISSUABLE_VUE_APP_CHANGE, payload)); }) - .catch(() => createFlash({ message: __('Error occurred while updating the issue status') })) + .catch(() => createAlert({ message: __('Error occurred while updating the issue status') })) .finally(() => { this.toggleStateButtonLoading(false); }); @@ -214,14 +215,14 @@ export default { throw new Error(); } - createFlash({ + createAlert({ message: this.$options.i18n.promoteSuccessMessage, - type: FLASH_TYPES.SUCCESS, + variant: VARIANT_SUCCESS, }); visitUrl(data.promoteToEpic.epic.webPath); }) - .catch(() => createFlash({ message: this.$options.i18n.promoteErrorMessage })) + .catch(() => createAlert({ message: this.$options.i18n.promoteErrorMessage })) .finally(() => { this.toggleStateButtonLoading(false); }); @@ -255,7 +256,7 @@ export default { {{ __('Promote to epic') }} </gl-dropdown-item> <gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath"> - {{ __('Report abuse') }} + {{ $options.i18n.reportAbuse }} </gl-dropdown-item> <gl-dropdown-item v-if="canReportSpam" @@ -314,7 +315,7 @@ export default { {{ __('Promote to epic') }} </gl-dropdown-item> <gl-dropdown-item v-if="!isIssueAuthor" :href="reportAbusePath"> - {{ __('Report abuse') }} + {{ $options.i18n.reportAbuse }} </gl-dropdown-item> <gl-dropdown-item v-if="canReportSpam" diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js index db846009409..22db19610c1 100644 --- a/app/assets/javascripts/issues/show/components/incidents/constants.js +++ b/app/assets/javascripts/issues/show/components/incidents/constants.js @@ -14,6 +14,7 @@ export const timelineFormI18n = Object.freeze({ areaPlaceholder: s__('Incident|Timeline text...'), save: __('Save'), cancel: __('Cancel'), + delete: __('Delete'), description: __('Description'), hint: __('You can enter up to 280 characters'), textRemaining: (count) => n__('%d character remaining', '%d characters remaining', count), diff --git a/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue index 60fa8cb949b..8cdd62ca9ef 100644 --- a/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue +++ b/app/assets/javascripts/issues/show/components/incidents/edit_timeline_event.vue @@ -40,8 +40,10 @@ export default { :is-event-processed="editTimelineEventActive" :previous-occurred-at="event.occurredAt" :previous-note="event.note" + show-delete @save-event="saveEvent" @cancel="$emit('hide-edit')" + @delete="$emit('delete')" /> </div> </template> diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql index f1fc27dcb2a..4a8786b04b1 100644 --- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql +++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/create_timeline_event.mutation.graphql @@ -7,6 +7,12 @@ mutation CreateTimelineEvent($input: TimelineEventCreateInput!) { action occurredAt createdAt + timelineEventTags { + nodes { + id + name + } + } } errors } diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql index d88633f2ae9..e057267b006 100644 --- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql +++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_alert.graphql @@ -4,6 +4,7 @@ query getAlert($iid: String!, $fullPath: ID!) { issue(iid: $iid) { id alertManagementAlert { + id iid title detailsUrl diff --git a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql index bc4e8414bfc..baeb81745ab 100644 --- a/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql +++ b/app/assets/javascripts/issues/show/components/incidents/graphql/queries/get_timeline_events.query.graphql @@ -9,6 +9,12 @@ query GetTimelineEvents($fullPath: ID!, $incidentId: IssueID!) { action occurredAt createdAt + timelineEventTags { + nodes { + id + name + } + } } } } diff --git a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue index 5725d0f8d6a..53956fcb4b2 100644 --- a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue +++ b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue @@ -1,16 +1,29 @@ <script> import { GlTab, GlTabs } from '@gitlab/ui'; -import createFlash from '~/flash'; +import { createAlert } from '~/flash'; import { trackIncidentDetailsViewsOptions } from '~/incidents/constants'; import { s__ } from '~/locale'; import Tracking from '~/tracking'; import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import DescriptionComponent from '../description.vue'; import getAlert from './graphql/queries/get_alert.graphql'; import HighlightBar from './highlight_bar.vue'; import TimelineTab from './timeline_events_tab.vue'; +export const incidentTabsI18n = Object.freeze({ + summaryTitle: s__('Incident|Summary'), + metricsTitle: s__('Incident|Metrics'), + alertsTitle: s__('Incident|Alert details'), + timelineTitle: s__('Incident|Timeline'), +}); + +export const TAB_NAMES = Object.freeze({ + SUMMARY: '', + ALERTS: 'alerts', + METRICS: 'metrics', + TIMELINE: 'timeline', +}); + export default { components: { AlertDetailsTable, @@ -22,8 +35,8 @@ export default { IncidentMetricTab: () => import('ee_component/issues/show/components/incidents/incident_metric_tab.vue'), }, - mixins: [glFeatureFlagsMixin()], - inject: ['fullPath', 'iid'], + inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'], + i18n: incidentTabsI18n, apollo: { alert: { query: getAlert, @@ -37,7 +50,7 @@ export default { return data?.project?.issue?.alertManagementAlert; }, error() { - createFlash({ + createAlert({ message: s__('Incident|There was an issue loading alert data. Please try again.'), }); }, @@ -46,12 +59,44 @@ export default { data() { return { alert: null, + activeTabIndex: 0, }; }, computed: { loading() { return this.$apollo.queries.alert.loading; }, + tabMapping() { + const availableTabs = [TAB_NAMES.SUMMARY]; + + if (this.uploadMetricsFeatureAvailable) { + availableTabs.push(TAB_NAMES.METRICS); + } + if (this.alert) { + availableTabs.push(TAB_NAMES.ALERTS); + } + + availableTabs.push(TAB_NAMES.TIMELINE); + + const tabNamesToIndex = {}; + const tabIndexToName = {}; + + availableTabs.forEach((item, index) => { + tabNamesToIndex[item] = index; + tabIndexToName[index] = item; + }); + + return { tabNamesToIndex, tabIndexToName }; + }, + currentTabIndex: { + get() { + return this.activeTabIndex; + }, + set(index) { + this.handleTabChange(index); + this.activeTabIndex = index; + }, + }, }, mounted() { this.trackPageViews(); @@ -91,25 +136,33 @@ export default { <template> <div> <gl-tabs + v-model="currentTabIndex" content-class="gl-reset-line-height" class="gl-mt-n3" data-testid="incident-tabs" - @input="handleTabChange" > - <gl-tab :title="s__('Incident|Summary')"> + <gl-tab :title="$options.i18n.summaryTitle" data-testid="summary-tab"> <highlight-bar :alert="alert" /> <description-component v-bind="$attrs" v-on="$listeners" /> </gl-tab> - <incident-metric-tab /> + <gl-tab + v-if="uploadMetricsFeatureAvailable" + :title="$options.i18n.metricsTitle" + data-testid="metrics-tab" + > + <incident-metric-tab /> + </gl-tab> <gl-tab v-if="alert" class="alert-management-details" - :title="s__('Incident|Alert details')" + :title="$options.i18n.alertsTitle" data-testid="alert-details-tab" > <alert-details-table :alert="alert" :loading="loading" /> </gl-tab> - <timeline-tab /> + <gl-tab :title="$options.i18n.timelineTitle" data-testid="timeline-tab"> + <timeline-tab /> + </gl-tab> </gl-tabs> </div> </template> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue index 72dfccca467..f1a3aebc990 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue @@ -1,7 +1,6 @@ <script> import { GlDatepicker, GlFormInput, GlFormGroup, GlButton } from '@gitlab/ui'; import MarkdownField from '~/vue_shared/components/markdown/field.vue'; -import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; import { MAX_TEXT_LENGTH, timelineFormI18n } from './constants'; import { getUtcShiftedDate } from './utils'; @@ -27,15 +26,17 @@ export default { }, i18n: timelineFormI18n, MAX_TEXT_LENGTH, - directives: { - autofocusonshow, - }, props: { showSaveAndAdd: { type: Boolean, required: false, default: false, }, + showDelete: { + type: Boolean, + required: false, + default: false, + }, isEventProcessed: { type: Boolean, required: true, @@ -97,7 +98,7 @@ export default { this.timelineText = ''; }, focusDate() { - this.$refs.datepicker.$el.querySelector('input').focus(); + this.$refs.datepicker.$el.querySelector('input')?.focus(); }, handleSave(addAnotherEvent) { const event = { @@ -185,32 +186,42 @@ export default { </gl-form-group> </div> <gl-form-group class="gl-mb-0"> - <gl-button - variant="confirm" - category="primary" - class="gl-mr-3" - data-testid="save-button" - :disabled="!isTimelineTextValid" - :loading="isEventProcessed" - @click="handleSave(false)" - > - {{ $options.i18n.save }} - </gl-button> - <gl-button - v-if="showSaveAndAdd" - variant="confirm" - category="secondary" - class="gl-mr-3 gl-ml-n2" - data-testid="save-and-add-button" - :disabled="!isTimelineTextValid" - :loading="isEventProcessed" - @click="handleSave(true)" - > - {{ $options.i18n.saveAndAdd }} - </gl-button> - <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')"> - {{ $options.i18n.cancel }} - </gl-button> + <div class="gl-display-flex"> + <gl-button + variant="confirm" + category="primary" + class="gl-mr-3" + data-testid="save-button" + :disabled="!isTimelineTextValid" + :loading="isEventProcessed" + @click="handleSave(false)" + > + {{ $options.i18n.save }} + </gl-button> + <gl-button + v-if="showSaveAndAdd" + variant="confirm" + category="secondary" + class="gl-mr-3 gl-ml-n2" + data-testid="save-and-add-button" + :disabled="!isTimelineTextValid" + :loading="isEventProcessed" + @click="handleSave(true)" + > + {{ $options.i18n.saveAndAdd }} + </gl-button> + <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')"> + {{ $options.i18n.cancel }} + </gl-button> + <gl-button + v-if="showDelete" + class="gl-ml-auto btn-danger" + :disabled="isEventProcessed" + @click="$emit('delete')" + > + {{ $options.i18n.delete }} + </gl-button> + </div> <div class="timeline-event-bottom-border"></div> </gl-form-group> </form> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue index cbf3c387fa3..90ee4351e39 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue @@ -1,5 +1,6 @@ <script> -import { GlDropdown, GlDropdownItem, GlIcon, GlSafeHtmlDirective, GlSprintf } from '@gitlab/ui'; +import { GlDropdown, GlDropdownItem, GlIcon, GlSprintf, GlBadge } from '@gitlab/ui'; +import SafeHtml from '~/vue_shared/directives/safe_html'; import { formatDate } from '~/lib/utils/datetime_utility'; import { timelineItemI18n } from './constants'; import { getEventIcon } from './utils'; @@ -12,9 +13,10 @@ export default { GlDropdownItem, GlIcon, GlSprintf, + GlBadge, }, directives: { - SafeHtml: GlSafeHtmlDirective, + SafeHtml, }, inject: ['canUpdateTimelineEvent'], props: { @@ -30,6 +32,11 @@ export default { type: String, required: true, }, + eventTag: { + type: String, + required: false, + default: null, + }, }, computed: { time() { @@ -42,41 +49,41 @@ export default { }; </script> <template> - <div class="gl-display-flex gl-align-items-start"> + <div class="timeline-event gl-display-grid"> <div class="gl-display-flex gl-align-items-center gl-justify-content-center gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-mt-2 gl-mr-3 gl-w-8 gl-h-8 gl-p-3 gl-z-index-1" > <gl-icon :name="getEventIcon(action)" class="note-icon" /> </div> - <div - class="timeline-event-note timeline-event-border gl-w-full gl-display-flex gl-flex-direction-row" - data-testid="event-text-container" - > - <div> + <div class="timeline-event-note timeline-event-border" data-testid="event-text-container"> + <div class="gl-display-flex gl-align-items-center gl-mb-3"> <strong class="gl-font-lg" data-testid="event-time"> <gl-sprintf :message="$options.i18n.timeUTC"> <template #time>{{ time }}</template> </gl-sprintf> </strong> - <div v-safe-html="noteHtml"></div> + <gl-badge v-if="eventTag" variant="muted" icon="tag" class="gl-ml-3"> + {{ eventTag }} + </gl-badge> </div> - <gl-dropdown - v-if="canUpdateTimelineEvent" - right - class="event-note-actions gl-ml-auto gl-align-self-start" - icon="ellipsis_v" - text-sr-only - :text="$options.i18n.moreActions" - category="tertiary" - no-caret - > - <gl-dropdown-item @click="$emit('edit')"> - {{ $options.i18n.edit }} - </gl-dropdown-item> - <gl-dropdown-item @click="$emit('delete')"> - {{ $options.i18n.delete }} - </gl-dropdown-item> - </gl-dropdown> + <div v-safe-html="noteHtml" class="md"></div> </div> + <gl-dropdown + v-if="canUpdateTimelineEvent" + right + class="event-note-actions gl-ml-auto gl-align-self-start" + icon="ellipsis_v" + text-sr-only + :text="$options.i18n.moreActions" + category="tertiary" + no-caret + > + <gl-dropdown-item @click="$emit('edit')"> + {{ $options.i18n.edit }} + </gl-dropdown-item> + <gl-dropdown-item @click="$emit('delete')"> + {{ $options.i18n.delete }} + </gl-dropdown-item> + </gl-dropdown> </div> </template> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue index 321b7ccc14a..c6b93201c97 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue @@ -50,6 +50,9 @@ export default { }, }, methods: { + getFirstTag(eventTag) { + return eventTag.nodes?.[0]?.name; + }, handleEditSelection(event) { this.eventToEdit = event.id; this.$emit('hide-new-incident-timeline-event-form'); @@ -153,6 +156,7 @@ export default { :edit-timeline-event-active="editTimelineEventActive" @handle-save-edit="handleSaveEdit" @hide-edit="hideEdit()" + @delete="handleDelete(event)" /> <incident-timeline-event-item v-else @@ -160,6 +164,7 @@ export default { :action="event.action" :occurred-at="event.occurredAt" :note-html="event.noteHtml" + :event-tag="getFirstTag(event.timelineEventTags)" @delete="handleDelete(event)" @edit="handleEditSelection(event)" /> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue index 5f70d9acac9..c8237766505 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue @@ -1,5 +1,5 @@ <script> -import { GlButton, GlEmptyState, GlLoadingIcon, GlTab } from '@gitlab/ui'; +import { GlButton, GlEmptyState, GlLoadingIcon } from '@gitlab/ui'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import { TYPE_ISSUE } from '~/graphql_shared/constants'; import { fetchPolicies } from '~/lib/graphql'; @@ -15,7 +15,6 @@ export default { GlButton, GlEmptyState, GlLoadingIcon, - GlTab, CreateTimelineEvent, IncidentTimelineEventsList, }, @@ -77,7 +76,7 @@ export default { </script> <template> - <gl-tab :title="$options.i18n.title"> + <div> <gl-loading-icon v-if="timelineEventLoading" size="lg" color="dark" class="gl-mt-5" /> <gl-empty-state v-else-if="showEmptyState" @@ -106,5 +105,5 @@ export default { > {{ $options.i18n.addEventButton }} </gl-button> - </gl-tab> + </div> </template> diff --git a/app/assets/javascripts/issues/show/components/locked_warning.vue b/app/assets/javascripts/issues/show/components/locked_warning.vue index 12feacb027b..4414e693ed0 100644 --- a/app/assets/javascripts/issues/show/components/locked_warning.vue +++ b/app/assets/javascripts/issues/show/components/locked_warning.vue @@ -1,29 +1,44 @@ <script> import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; +import { IssuableType } from '~/issues/constants'; -const alertMessage = __( - 'Someone edited the issue at the same time you did. Please check out %{linkStart}the issue%{linkEnd} and make sure your changes will not unintentionally remove theirs.', -); +export const i18n = Object.freeze({ + alertMessage: __( + "Someone edited the %{issuableType} at the same time you did. Review %{linkStart}the %{issuableType}%{linkEnd} and make sure you don't unintentionally overwrite their changes.", + ), +}); export default { - alertMessage, components: { GlSprintf, GlLink, GlAlert, }, + props: { + issuableType: { + type: String, + required: true, + validator(value) { + return Object.values(IssuableType).includes(value); + }, + }, + }, computed: { currentPath() { return window.location.pathname; }, + alertMessage() { + return sprintf(this.$options.i18n.alertMessage, { issuableType: this.issuableType }); + }, }, + i18n, }; </script> <template> <gl-alert variant="danger" class="gl-mb-5" :dismissible="false"> - <gl-sprintf :message="$options.alertMessage"> + <gl-sprintf :message="alertMessage"> <template #link="{ content }"> <gl-link :href="currentPath" target="_blank" rel="nofollow"> {{ content }} diff --git a/app/assets/javascripts/issues/show/components/title.vue b/app/assets/javascripts/issues/show/components/title.vue index 307d9f9f69a..6978f730e1d 100644 --- a/app/assets/javascripts/issues/show/components/title.vue +++ b/app/assets/javascripts/issues/show/components/title.vue @@ -1,5 +1,6 @@ <script> -import { GlButton, GlTooltipDirective, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui'; +import { GlButton, GlTooltipDirective } from '@gitlab/ui'; +import SafeHtml from '~/vue_shared/directives/safe_html'; import { __ } from '~/locale'; import eventHub from '../event_hub'; import animateMixin from '../mixins/animate'; |