diff options
Diffstat (limited to 'app/assets/javascripts/issues')
13 files changed, 253 insertions, 159 deletions
diff --git a/app/assets/javascripts/issues/index.js b/app/assets/javascripts/issues/index.js index 380bb5f5346..22ac37656ea 100644 --- a/app/assets/javascripts/issues/index.js +++ b/app/assets/javascripts/issues/index.js @@ -9,7 +9,7 @@ import { IssueType } from '~/issues/constants'; import Issue from '~/issues/issue'; import { initTitleSuggestions, initTypePopover } from '~/issues/new'; import { initRelatedMergeRequests } from '~/issues/related_merge_requests'; -import initRelatedIssues from '~/related_issues'; +import { initRelatedIssues } from '~/related_issues'; import { initHeaderActions, initIncidentApp, diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index f567b0f1d68..11911adb401 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -39,13 +39,13 @@ import { TOKEN_TITLE_TYPE, } from '~/vue_shared/components/filtered_search_bar/constants'; import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue'; -import { - IssuableListTabs, - IssuableStates, - IssuableTypes, -} from '~/vue_shared/issuable/list/constants'; +import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants'; +import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; +import { WORK_ITEM_TYPE_ENUM_TASK } from '~/work_items/constants'; import { CREATED_DESC, + defaultTypeTokenOptions, + defaultWorkItemTypes, i18n, ISSUE_REFERENCE, MAX_LIST_SIZE, @@ -67,6 +67,7 @@ import { TOKEN_TYPE_ORGANIZATION, TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, + TYPE_TOKEN_TASK_OPTION, UPDATED_DESC, urlSortParams, } from '../constants'; @@ -107,7 +108,6 @@ const CrmOrganizationToken = () => export default { i18n, IssuableListTabs, - IssuableTypes: [IssuableTypes.Issue, IssuableTypes.Incident, IssuableTypes.TestCase], components: { CsvImportExportButtons, GlButton, @@ -123,6 +123,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, + mixins: [glFeatureFlagMixin()], inject: [ 'autocompleteAwardEmojisPath', 'calendarPath', @@ -180,9 +181,7 @@ export default { issues: { query: getIssuesQuery, variables() { - const { types } = this.queryVariables; - - return { ...this.queryVariables, types: types ? [types] : this.$options.IssuableTypes }; + return this.queryVariables; }, update(data) { return data[this.namespace]?.issues.nodes ?? []; @@ -206,9 +205,7 @@ export default { issuesCounts: { query: getIssuesCountsQuery, variables() { - const { types } = this.queryVariables; - - return { ...this.queryVariables, types: types ? [types] : this.$options.IssuableTypes }; + return this.queryVariables; }, update(data) { return data[this.namespace] ?? {}; @@ -240,11 +237,22 @@ export default { state: this.state, ...this.pageParams, ...this.apiFilterParams, + types: this.apiFilterParams.types || this.defaultWorkItemTypes, }; }, namespace() { return this.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP; }, + defaultWorkItemTypes() { + return this.isWorkItemsEnabled + ? defaultWorkItemTypes.concat(WORK_ITEM_TYPE_ENUM_TASK) + : defaultWorkItemTypes; + }, + typeTokenOptions() { + return this.isWorkItemsEnabled + ? defaultTypeTokenOptions.concat(TYPE_TOKEN_TASK_OPTION) + : defaultTypeTokenOptions; + }, hasSearch() { return ( this.searchQuery || @@ -262,6 +270,9 @@ export default { isOpenTab() { return this.state === IssuableStates.Opened; }, + isWorkItemsEnabled() { + return this.glFeatures.workItems; + }, showCsvButtons() { return this.isProject && this.isSignedIn; }, @@ -340,11 +351,7 @@ export default { title: TOKEN_TITLE_TYPE, icon: 'issues', token: GlFilteredSearchToken, - options: [ - { icon: 'issue-type-issue', title: 'issue', value: 'issue' }, - { icon: 'issue-type-incident', title: 'incident', value: 'incident' }, - { icon: 'issue-type-test-case', title: 'test_case', value: 'test_case' }, - ], + options: this.typeTokenOptions, }, ]; @@ -767,6 +774,7 @@ export default { :show-page-size-change-controls="showPageSizeControls" :has-next-page="pageInfo.hasNextPage" :has-previous-page="pageInfo.hasPreviousPage" + show-work-item-type-icon @click-tab="handleClickTab" @dismiss-alert="handleDismissAlert" @filter="handleFilter" diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index a921eb62e26..38fe4c33792 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -8,6 +8,11 @@ import { OPERATOR_IS, OPERATOR_IS_NOT, } from '~/vue_shared/components/filtered_search_bar/constants'; +import { + WORK_ITEM_TYPE_ENUM_INCIDENT, + WORK_ITEM_TYPE_ENUM_ISSUE, + WORK_ITEM_TYPE_ENUM_TEST_CASE, +} from '~/work_items/constants'; export const i18n = { anonymousSearchingMessage: __('You must sign in to search for specific terms.'), @@ -147,6 +152,20 @@ export const TOKEN_TYPE_WEIGHT = 'weight'; export const TOKEN_TYPE_CONTACT = 'crm_contact'; export const TOKEN_TYPE_ORGANIZATION = 'crm_organization'; +export const TYPE_TOKEN_TASK_OPTION = { icon: 'task-done', title: 'task', value: 'task' }; + +export const defaultWorkItemTypes = [ + WORK_ITEM_TYPE_ENUM_ISSUE, + WORK_ITEM_TYPE_ENUM_INCIDENT, + WORK_ITEM_TYPE_ENUM_TEST_CASE, +]; + +export const defaultTypeTokenOptions = [ + { icon: 'issue-type-issue', title: 'issue', value: 'issue' }, + { icon: 'issue-type-incident', title: 'incident', value: 'incident' }, + { icon: 'issue-type-test-case', title: 'test_case', value: 'test_case' }, +]; + export const filters = { [TOKEN_TYPE_AUTHOR]: { [API_PARAM]: { diff --git a/app/assets/javascripts/issues/list/queries/issue.fragment.graphql b/app/assets/javascripts/issues/list/queries/issue.fragment.graphql index 35762120f71..040763f2ba4 100644 --- a/app/assets/javascripts/issues/list/queries/issue.fragment.graphql +++ b/app/assets/javascripts/issues/list/queries/issue.fragment.graphql @@ -1,5 +1,4 @@ fragment IssueFragment on Issue { - __typename id iid confidential @@ -18,9 +17,9 @@ fragment IssueFragment on Issue { userDiscussionsCount @include(if: $isSignedIn) webPath webUrl + type assignees @skip(if: $hideUsers) { nodes { - __typename id avatarUrl name @@ -29,7 +28,6 @@ fragment IssueFragment on Issue { } } author @skip(if: $hideUsers) { - __typename id avatarUrl name diff --git a/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue b/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue index 1d48446b083..a5cba3daafa 100644 --- a/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue +++ b/app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue @@ -66,7 +66,7 @@ export default { <template> <div v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)"> <div class="card card-slim gl-mt-5"> - <div class="card-header"> + <div class="card-header gl-bg-gray-10"> <div class="card-title gl-relative gl-display-flex gl-align-items-center gl-line-height-20 gl-font-weight-bold gl-m-0" > diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue index 449da394841..a6747d67611 100644 --- a/app/assets/javascripts/issues/show/components/description.vue +++ b/app/assets/javascripts/issues/show/components/description.vue @@ -133,7 +133,7 @@ export default { }, computed: { workItemsEnabled() { - return this.glFeatures.workItems; + return this.glFeatures.workItemsCreateFromMarkdown; }, taskWorkItemType() { return this.workItemTypes.find((type) => type.name === TASK_TYPE_NAME)?.id; @@ -302,7 +302,9 @@ export default { if (taskRegexMatches) { $tasks.text(this.taskStatus); $tasksShort.text( - `${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ? 's' : ''}`, + `${taskRegexMatches[1]}/${taskRegexMatches[2]} checklist item${ + taskRegexMatches[2] > 1 ? 's' : '' + }`, ); } else { $tasks.text(''); @@ -315,7 +317,7 @@ export default { } this.taskButtons = []; - const taskListFields = this.$el.querySelectorAll('.task-list-item'); + const taskListFields = this.$el.querySelectorAll('.task-list-item:not(.inapplicable)'); taskListFields.forEach((item, index) => { const taskLink = item.querySelector('.gfm-issue'); @@ -326,6 +328,7 @@ export default { } const workItemId = convertToGraphQLId(TYPE_WORK_ITEM, issue); this.addHoverListeners(taskLink, workItemId); + taskLink.classList.add('gl-link'); taskLink.addEventListener('click', (e) => { e.preventDefault(); this.openWorkItemDetailModal(taskLink); diff --git a/app/assets/javascripts/issues/show/components/incidents/constants.js b/app/assets/javascripts/issues/show/components/incidents/constants.js index 9fc5027d457..77d13fe085a 100644 --- a/app/assets/javascripts/issues/show/components/incidents/constants.js +++ b/app/assets/javascripts/issues/show/components/incidents/constants.js @@ -1,4 +1,4 @@ -import { s__ } from '~/locale'; +import { __, s__ } from '~/locale'; export const timelineTabI18n = Object.freeze({ title: s__('Incident|Timeline'), @@ -12,6 +12,9 @@ export const timelineFormI18n = Object.freeze({ 'Incident|Something went wrong while creating the incident timeline event.', ), areaPlaceholder: s__('Incident|Timeline text...'), + save: __('Save'), + cancel: __('Cancel'), + description: __('Description'), saveAndAdd: s__('Incident|Save and add another event'), areaLabel: s__('Incident|Timeline text'), }); diff --git a/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue b/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue new file mode 100644 index 00000000000..c902895702e --- /dev/null +++ b/app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue @@ -0,0 +1,117 @@ +<script> +import { produce } from 'immer'; +import { sortBy } from 'lodash'; +import { sprintf } from '~/locale'; +import { createAlert } from '~/flash'; +import { convertToGraphQLId } from '~/graphql_shared/utils'; +import { TYPE_ISSUE } from '~/graphql_shared/constants'; +import { timelineFormI18n } from './constants'; +import TimelineEventsForm from './timeline_events_form.vue'; + +import CreateTimelineEvent from './graphql/queries/create_timeline_event.mutation.graphql'; +import getTimelineEvents from './graphql/queries/get_timeline_events.query.graphql'; + +export default { + name: 'CreateTimelineEvent', + i18n: timelineFormI18n, + components: { + TimelineEventsForm, + }, + inject: ['fullPath', 'issuableId'], + props: { + hasTimelineEvents: { + type: Boolean, + required: true, + }, + }, + data() { + return { createTimelineEventActive: false }; + }, + methods: { + clearForm() { + this.$refs.eventForm.clear(); + }, + focusDate() { + this.$refs.eventForm.focusDate(); + }, + updateCache(store, { data }) { + const { timelineEvent: event, errors } = data?.timelineEventCreate || {}; + + if (errors.length) { + return; + } + + const variables = { + incidentId: convertToGraphQLId(TYPE_ISSUE, this.issuableId), + fullPath: this.fullPath, + }; + + const sourceData = store.readQuery({ + query: getTimelineEvents, + variables, + }); + + const newData = produce(sourceData, (draftData) => { + const { nodes: draftEventList } = draftData.project.incidentManagementTimelineEvents; + draftEventList.push(event); + // ISOStrings sort correctly in lexical order + const sortedEvents = sortBy(draftEventList, 'occurredAt'); + draftData.project.incidentManagementTimelineEvents.nodes = sortedEvents; + }); + + store.writeQuery({ + query: getTimelineEvents, + variables, + data: newData, + }); + }, + createIncidentTimelineEvent(eventDetails, addAnotherEvent = false) { + this.createTimelineEventActive = true; + return this.$apollo + .mutate({ + mutation: CreateTimelineEvent, + variables: { + input: { + incidentId: convertToGraphQLId(TYPE_ISSUE, this.issuableId), + note: eventDetails.note, + occurredAt: eventDetails.occurredAt, + }, + }, + update: this.updateCache, + }) + .then(({ data = {} }) => { + this.createTimelineEventActive = false; + const errors = data.timelineEventCreate?.errors; + if (errors.length) { + createAlert({ + message: sprintf(this.$options.i18n.createError, { error: errors.join('. ') }, false), + }); + return; + } + if (addAnotherEvent) { + this.$refs.eventForm.clear(); + } else { + this.$emit('hide-new-timeline-events-form'); + } + }) + .catch((error) => { + createAlert({ + message: this.$options.i18n.createErrorGeneric, + captureError: true, + error, + }); + }); + }, + }, +}; +</script> + +<template> + <timeline-events-form + ref="eventForm" + :is-event-processed="createTimelineEventActive" + :has-timeline-events="hasTimelineEvents" + @save-event="createIncidentTimelineEvent" + @cancel="$emit('hide-new-timeline-events-form')" + /> +</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 36ec6362a22..0d84fabb1be 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,21 +1,12 @@ <script> import { GlDatepicker, GlFormInput, GlFormGroup, GlButton, GlIcon } from '@gitlab/ui'; -import { produce } from 'immer'; -import { sortBy } from 'lodash'; -import { convertToGraphQLId } from '~/graphql_shared/utils'; -import { TYPE_ISSUE } from '~/graphql_shared/constants'; import MarkdownField from '~/vue_shared/components/markdown/field.vue'; -import { createAlert } from '~/flash'; import autofocusonshow from '~/vue_shared/directives/autofocusonshow'; -import { sprintf } from '~/locale'; -import { getUtcShiftedDateNow } from './utils'; import { timelineFormI18n } from './constants'; - -import CreateTimelineEvent from './graphql/queries/create_timeline_event.mutation.graphql'; -import getTimelineEvents from './graphql/queries/get_timeline_events.query.graphql'; +import { getUtcShiftedDateNow } from './utils'; export default { - name: 'IncidentTimelineEventForm', + name: 'TimelineEventsForm', restrictedToolBarItems: [ 'quote', 'strikethrough', @@ -38,112 +29,55 @@ export default { directives: { autofocusonshow, }, - inject: ['fullPath', 'issuableId'], props: { hasTimelineEvents: { type: Boolean, required: true, }, + isEventProcessed: { + type: Boolean, + required: true, + }, }, data() { - // Create shifted date to force the datepicker to format in UTC - const utcShiftedDate = getUtcShiftedDateNow(); + // if occurredAt is undefined, returns "now" in UTC + const placeholderDate = getUtcShiftedDateNow(); + return { - currentDate: utcShiftedDate, - currentHour: utcShiftedDate.getHours(), - currentMinute: utcShiftedDate.getMinutes(), timelineText: '', - createTimelineEventActive: false, + placeholderDate, + hourPickerInput: placeholderDate.getHours(), + minutePickerInput: placeholderDate.getMinutes(), datepickerTextInput: null, }; }, + computed: { + occurredAt() { + const [years, months, days] = this.datepickerTextInput.split('-'); + const utcDate = new Date( + Date.UTC(years, months - 1, days, this.hourPickerInput, this.minutePickerInput), + ); + + return utcDate.toISOString(); + }, + }, methods: { clear() { - const utcShiftedDate = getUtcShiftedDateNow(); - this.currentDate = utcShiftedDate; - this.currentHour = utcShiftedDate.getHours(); - this.currentMinute = utcShiftedDate.getMinutes(); - }, - hideIncidentTimelineEventForm() { - this.$emit('hide-incident-timeline-event-form'); + const utcShiftedDateNow = getUtcShiftedDateNow(); + this.placeholderDate = utcShiftedDateNow; + this.hourPickerInput = utcShiftedDateNow.getHours(); + this.minutePickerInput = utcShiftedDateNow.getMinutes(); + this.timelineText = ''; }, focusDate() { this.$refs.datepicker.$el.focus(); }, - updateCache(store, { data }) { - const { timelineEvent: event, errors } = data?.timelineEventCreate || {}; - - if (errors.length) { - return; - } - - const variables = { - incidentId: convertToGraphQLId(TYPE_ISSUE, this.issuableId), - fullPath: this.fullPath, + handleSave(addAnotherEvent) { + const eventDetails = { + note: this.timelineText, + occurredAt: this.occurredAt, }; - - const sourceData = store.readQuery({ - query: getTimelineEvents, - variables, - }); - - const newData = produce(sourceData, (draftData) => { - const { nodes: draftEventList } = draftData.project.incidentManagementTimelineEvents; - draftEventList.push(event); - // ISOStrings sort correctly in lexical order - const sortedEvents = sortBy(draftEventList, 'occurredAt'); - draftData.project.incidentManagementTimelineEvents.nodes = sortedEvents; - }); - - store.writeQuery({ - query: getTimelineEvents, - variables, - data: newData, - }); - }, - createIncidentTimelineEvent(addOneEvent) { - this.createTimelineEventActive = true; - return this.$apollo - .mutate({ - mutation: CreateTimelineEvent, - variables: { - input: { - incidentId: convertToGraphQLId(TYPE_ISSUE, this.issuableId), - note: this.timelineText, - occurredAt: this.createDateString(), - }, - }, - update: this.updateCache, - }) - .then(({ data = {} }) => { - const errors = data.timelineEventCreate?.errors; - if (errors.length) { - createAlert({ - message: sprintf(this.$options.i18n.createError, { error: errors.join('. ') }, false), - }); - } - }) - .catch((error) => { - createAlert({ - message: this.$options.i18n.createErrorGeneric, - captureError: true, - error, - }); - }) - .finally(() => { - this.createTimelineEventActive = false; - this.timelineText = ''; - if (addOneEvent) { - this.hideIncidentTimelineEventForm(); - } - }); - }, - createDateString() { - const [years, months, days] = this.datepickerTextInput.split('-'); - const utcDate = new Date( - Date.UTC(years, months - 1, days, this.currentHour, this.currentMinute), - ); - return utcDate.toISOString(); + this.$emit('save-event', eventDetails, addAnotherEvent); }, }, }; @@ -165,7 +99,7 @@ export default { class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row datetime-picker" > <gl-form-group :label="__('Date')" class="gl-mt-5 gl-mr-5"> - <gl-datepicker id="incident-date" #default="{ formattedDate }" v-model="currentDate"> + <gl-datepicker id="incident-date" #default="{ formattedDate }" v-model="placeholderDate"> <gl-form-input id="incident-date" ref="datepicker" @@ -184,7 +118,7 @@ export default { <label label-for="timeline-input-hours" class="sr-only"></label> <gl-form-input id="timeline-input-hours" - v-model="currentHour" + v-model="hourPickerInput" data-testid="input-hours" size="xs" type="number" @@ -194,7 +128,7 @@ export default { <label label-for="timeline-input-minutes" class="sr-only"></label> <gl-form-input id="timeline-input-minutes" - v-model="currentMinute" + v-model="minutePickerInput" class="gl-ml-3" data-testid="input-minutes" size="xs" @@ -223,9 +157,10 @@ export default { <textarea v-model="timelineText" class="note-textarea js-gfm-input js-autosize markdown-area" + data-testid="input-note" dir="auto" data-supports-quick-actions="false" - :aria-label="__('Description')" + :aria-label="$options.i18n.description" :placeholder="$options.i18n.areaPlaceholder" > </textarea> @@ -238,26 +173,22 @@ export default { variant="confirm" category="primary" class="gl-mr-3" - :loading="createTimelineEventActive" - @click="createIncidentTimelineEvent(true)" + :loading="isEventProcessed" + @click="handleSave(false)" > - {{ __('Save') }} + {{ $options.i18n.save }} </gl-button> <gl-button variant="confirm" category="secondary" class="gl-mr-3 gl-ml-n2" - :loading="createTimelineEventActive" - @click="createIncidentTimelineEvent(false)" + :loading="isEventProcessed" + @click="handleSave(true)" > {{ $options.i18n.saveAndAdd }} </gl-button> - <gl-button - class="gl-ml-n2" - :disabled="createTimelineEventActive" - @click="hideIncidentTimelineEventForm" - > - {{ __('Cancel') }} + <gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')"> + {{ $options.i18n.cancel }} </gl-button> <div class="gl-border-b gl-pt-5"></div> </gl-form-group> diff --git a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list_item.vue b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue index 62ccd696ef6..6175c9969ec 100644 --- a/app/assets/javascripts/issues/show/components/incidents/timeline_events_list_item.vue +++ b/app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue @@ -1,5 +1,12 @@ <script> -import { GlDropdown, GlDropdownItem, GlIcon, GlSafeHtmlDirective, GlSprintf } from '@gitlab/ui'; +import { + GlButton, + GlDropdown, + GlDropdownItem, + GlIcon, + GlSafeHtmlDirective, + GlSprintf, +} from '@gitlab/ui'; import { formatDate } from '~/lib/utils/datetime_utility'; import { __ } from '~/locale'; import { getEventIcon } from './utils'; @@ -12,6 +19,7 @@ export default { timeUTC: __('%{time} UTC'), }, components: { + GlButton, GlDropdown, GlDropdownItem, GlIcon, @@ -83,7 +91,7 @@ export default { no-caret > <gl-dropdown-item @click="$emit('delete')"> - {{ $options.i18n.delete }} + <gl-button>{{ $options.i18n.delete }}</gl-button> </gl-dropdown-item> </gl-dropdown> </div> 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 519c0d402a0..80ac1c372cd 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 @@ -4,7 +4,7 @@ import { createAlert } from '~/flash'; import { sprintf } from '~/locale'; import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal'; import { ignoreWhilePending } from '~/lib/utils/ignore_while_pending'; -import IncidentTimelineEventListItem from './timeline_events_list_item.vue'; +import IncidentTimelineEventItem from './timeline_events_item.vue'; import deleteTimelineEvent from './graphql/queries/delete_timeline_event.mutation.graphql'; import { timelineListI18n } from './constants'; @@ -12,7 +12,7 @@ export default { name: 'IncidentTimelineEventList', i18n: timelineListI18n, components: { - IncidentTimelineEventListItem, + IncidentTimelineEventItem, }, props: { timelineEventLoading: { @@ -99,16 +99,21 @@ export default { <div class="gl-pb-3 gl-border-gray-50 gl-border-1 gl-border-b-solid"> <strong class="gl-font-size-h2" data-testid="event-date">{{ eventDate }}</strong> </div> - <ul class="notes main-notes-list gl-pl-n3"> - <incident-timeline-event-list-item + <ul class="notes main-notes-list"> + <li v-for="(event, eventIndex) in events" - :key="event.id" - :action="event.action" - :occurred-at="event.occurredAt" - :note-html="event.noteHtml" - :is-last-item="isLastItem(dateGroupedEvents, groupIndex, events, eventIndex)" - @delete="handleDelete(event)" - /> + :key="eventIndex" + class="timeline-entry-vertical-line note system-note note-wrapper gl-my-2! gl-pr-0!" + > + <incident-timeline-event-item + :key="event.id" + :action="event.action" + :occurred-at="event.occurredAt" + :note-html="event.noteHtml" + :is-last-item="isLastItem(dateGroupedEvents, groupIndex, events, eventIndex)" + @delete="handleDelete(event)" + /> + </li> </ul> </div> </div> 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 e1946ef4d07..7c2a7878c58 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 @@ -7,7 +7,7 @@ import getTimelineEvents from './graphql/queries/get_timeline_events.query.graph import { displayAndLogError } from './utils'; import { timelineTabI18n } from './constants'; -import IncidentTimelineEventForm from './timeline_events_form.vue'; +import CreateTimelineEvent from './create_timeline_event.vue'; import IncidentTimelineEventsList from './timeline_events_list.vue'; export default { @@ -16,7 +16,7 @@ export default { GlEmptyState, GlLoadingIcon, GlTab, - IncidentTimelineEventForm, + CreateTimelineEvent, IncidentTimelineEventsList, }, i18n: timelineTabI18n, @@ -61,10 +61,10 @@ export default { this.isEventFormVisible = false; }, async showEventForm() { - this.$refs.eventForm.clear(); + this.$refs.createEventForm.clearForm(); this.isEventFormVisible = true; await this.$nextTick(); - this.$refs.eventForm.focusDate(); + this.$refs.createEventForm.focusDate(); }, }, }; @@ -82,14 +82,15 @@ export default { v-if="hasTimelineEvents" :timeline-event-loading="timelineEventLoading" :timeline-events="timelineEvents" + @hide-new-timeline-events-form="hideEventForm" /> - <incident-timeline-event-form + <create-timeline-event v-show="isEventFormVisible" - ref="eventForm" + ref="createEventForm" :has-timeline-events="hasTimelineEvents" class="timeline-event-note timeline-event-note-form" :class="{ 'gl-pl-0': !hasTimelineEvents }" - @hide-incident-timeline-event-form="hideEventForm" + @hide-new-timeline-events-form="hideEventForm" /> <gl-button v-if="canUpdate" variant="default" class="gl-mb-3 gl-mt-7" @click="showEventForm"> {{ $options.i18n.addEventButton }} diff --git a/app/assets/javascripts/issues/show/components/incidents/utils.js b/app/assets/javascripts/issues/show/components/incidents/utils.js index 256e3025f19..cf790a11b67 100644 --- a/app/assets/javascripts/issues/show/components/incidents/utils.js +++ b/app/assets/javascripts/issues/show/components/incidents/utils.js @@ -11,6 +11,7 @@ export const displayAndLogError = (error) => const EVENT_ICONS = { comment: 'comment', issues: 'issues', + label: 'label', status: 'status', default: 'comment', }; |