Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/issues')
-rw-r--r--app/assets/javascripts/issues/index.js2
-rw-r--r--app/assets/javascripts/issues/list/components/issues_list_app.vue42
-rw-r--r--app/assets/javascripts/issues/list/constants.js19
-rw-r--r--app/assets/javascripts/issues/list/queries/issue.fragment.graphql4
-rw-r--r--app/assets/javascripts/issues/related_merge_requests/components/related_merge_requests.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue9
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/constants.js5
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/create_timeline_event.vue117
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_form.vue157
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_item.vue (renamed from app/assets/javascripts/issues/show/components/incidents/timeline_events_list_item.vue)12
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_list.vue27
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/timeline_events_tab.vue15
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/utils.js1
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',
};