Skip to content

Commit 8716e15

Browse files
author
GitLab Bot
committed
Add latest changes from gitlab-org/gitlab@master
1 parent e6f6144 commit 8716e15

File tree

85 files changed

+1027
-209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+1027
-209
lines changed

.gitlab/CODEOWNERS

+16
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,29 @@ config/bounded_contexts.yml @fabiopitino @grzesiek @stanhu @cwoolley-gitlab @tku
7272
/ee/db/schema_migrations/
7373
# The following two lines only match db/ root files
7474
/db/*
75+
/spec/db/*
7576
/ee/db/*
77+
/ee/spec/db/*
7678
/lib/gitlab/background_migration/
79+
/spec/lib/gitlab/background_migration/
7780
/ee/lib/ee/gitlab/background_migration/
81+
/ee/spec/lib/ee/gitlab/background_migration/
82+
/lib/gitlab/database.rb
7883
/lib/gitlab/database/
84+
/spec/lib/gitlab/database_spec.rb
85+
/spec/lib/gitlab/database/
86+
/ee/lib/gitlab/database/
87+
/ee/spec/lib/gitlab/database/
88+
/ee/lib/ee/gitlab/database.rb
89+
/ee/lib/ee/gitlab/database/
90+
/ee/spec/lib/ee/gitlab/database/
91+
/ee/spec/lib/ee/gitlab/database_spec.rb
7992
/lib/gitlab/sql/
93+
/spec/lib/gitlab/sql/
8094
/app/finders/
95+
/spec/finders/
8196
/ee/app/finders/
97+
/ee/spec/finders/
8298
/rubocop/rubocop-migrations.yml
8399

84100
[Pipeline configuration] @gl-quality/eng-prod

.rubocop_todo/layout/parameter_alignment.yml

-10
This file was deleted.

.rubocop_todo/layout/space_before_block_braces.yml

-5
This file was deleted.

.rubocop_todo/layout/trailing_whitespace.yml

-8
This file was deleted.

app/assets/javascripts/sidebar/components/time_tracking/create_timelog_form.vue

+54-6
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { formatDate } from '~/lib/utils/datetime_utility';
1414
import { TYPENAME_ISSUE, TYPENAME_MERGE_REQUEST } from '~/graphql_shared/constants';
1515
import { joinPaths } from '~/lib/utils/url_utility';
1616
import { s__, sprintf } from '~/locale';
17+
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
1718
import createTimelogMutation from '../../queries/create_timelog.mutation.graphql';
1819
import { CREATE_TIMELOG_MODAL_ID } from './constants';
1920
@@ -35,7 +36,18 @@ export default {
3536
props: {
3637
issuableId: {
3738
type: String,
38-
required: true,
39+
required: false,
40+
default: '',
41+
},
42+
workItemId: {
43+
type: String,
44+
required: false,
45+
default: '',
46+
},
47+
workItemType: {
48+
type: String,
49+
required: false,
50+
default: '',
3951
},
4052
},
4153
data() {
@@ -49,13 +61,13 @@ export default {
4961
},
5062
computed: {
5163
modalText() {
52-
const issuableTypeName = issuableTypeText[this.issuableType];
64+
const issuableTypeName = issuableTypeText[this.issuableType || this.workItemType];
5365
return sprintf(s__('TimeTracking|Track time spent on this %{issuableTypeName}.'), {
5466
issuableTypeName,
5567
});
5668
},
5769
submitDisabled() {
58-
return this.isLoading || this.timeSpent.length === 0;
70+
return this.isLoading || this.timeSpent?.length === 0;
5971
},
6072
primaryProps() {
6173
return {
@@ -91,14 +103,50 @@ export default {
91103
registerTimeSpent(event) {
92104
event.preventDefault();
93105
94-
if (this.timeSpent.length === 0) {
95-
return;
106+
if (this.timeSpent?.length === 0) {
107+
return null;
96108
}
97109
98110
this.isLoading = true;
99111
this.saveError = '';
100112
101-
this.$apollo
113+
if (this.workItemId) {
114+
return this.$apollo
115+
.mutate({
116+
mutation: updateWorkItemMutation,
117+
variables: {
118+
input: {
119+
id: this.workItemId,
120+
timeTrackingWidget: {
121+
timelog: {
122+
spentAt: this.spentAt
123+
? formatDate(this.spentAt, 'isoDateTime')
124+
: formatDate(Date.now(), 'isoDateTime'),
125+
summary: this.summary,
126+
timeSpent: this.timeSpent,
127+
},
128+
},
129+
},
130+
},
131+
})
132+
.then(({ data }) => {
133+
if (data.workItemUpdate.errors.length) {
134+
throw new Error(data.workItemUpdate.errors);
135+
}
136+
137+
this.close();
138+
})
139+
.catch((error) => {
140+
this.saveError =
141+
error?.message ||
142+
s__('TimeTracking|An error occurred while saving the time estimate.');
143+
})
144+
.finally(() => {
145+
this.isLoading = false;
146+
});
147+
}
148+
149+
return this.$apollo
102150
.mutate({
103151
mutation: createTimelogMutation,
104152
variables: {

app/assets/javascripts/sidebar/components/time_tracking/report.vue

+78-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
<!-- eslint-disable vue/multi-word-component-names -->
22
<script>
33
import { GlLoadingIcon, GlTableLite, GlButton, GlTooltipDirective } from '@gitlab/ui';
4+
import produce from 'immer';
45
import { createAlert } from '~/alert';
56
import { TYPENAME_ISSUE, TYPENAME_MERGE_REQUEST } from '~/graphql_shared/constants';
67
import { convertToGraphQLId } from '~/graphql_shared/utils';
78
import { TYPE_ISSUE } from '~/issues/constants';
89
import { formatDate, parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
910
import { __, s__ } from '~/locale';
11+
import { WIDGET_TYPE_TIME_TRACKING } from '~/work_items/constants';
12+
import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql';
13+
import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql';
1014
import { timelogQueries } from '../../queries/constants';
1115
import deleteTimelogMutation from '../../queries/delete_timelog.mutation.graphql';
1216

@@ -22,6 +26,12 @@ export default {
2226
GlTooltip: GlTooltipDirective,
2327
},
2428
inject: {
29+
fullPath: {
30+
default: null,
31+
},
32+
isGroup: {
33+
default: null,
34+
},
2535
issuableType: {
2636
default: null,
2737
},
@@ -34,11 +44,25 @@ export default {
3444
},
3545
issuableId: {
3646
type: String,
37-
required: true,
47+
required: false,
48+
default: '',
49+
},
50+
timelogs: {
51+
type: Array,
52+
required: false,
53+
default: undefined,
54+
},
55+
workItemIid: {
56+
type: String,
57+
required: false,
58+
default: '',
3859
},
3960
},
4061
data() {
41-
return { report: [], isLoading: true, removingIds: [] };
62+
return {
63+
report: this.timelogs ?? [],
64+
removingIds: [],
65+
};
4266
},
4367
apollo: {
4468
report: {
@@ -49,18 +73,46 @@ export default {
4973
return this.getQueryVariables();
5074
},
5175
update(data) {
52-
this.isLoading = false;
53-
return this.extractTimelogs(data);
76+
const timelogs = data?.issuable?.timelogs?.nodes || [];
77+
return timelogs.slice().sort((a, b) => new Date(a.spentAt) - new Date(b.spentAt));
5478
},
5579
error() {
5680
createAlert({ message: __('Something went wrong. Please try again.') });
5781
},
82+
skip() {
83+
return Boolean(this.timelogs);
84+
},
85+
},
86+
workItem: {
87+
query() {
88+
return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery;
89+
},
90+
variables() {
91+
return {
92+
fullPath: this.fullPath,
93+
iid: this.workItemIid,
94+
};
95+
},
96+
update(data) {
97+
return data.workspace.workItem ?? {};
98+
},
99+
skip() {
100+
return !this.workItemIid;
101+
},
58102
},
59103
},
60104
computed: {
61105
deleteButtonTooltip() {
62106
return s__('TimeTracking|Delete time spent');
63107
},
108+
isLoading() {
109+
return this.$apollo.queries.report.loading || this.$apollo.queries.workItem.loading;
110+
},
111+
},
112+
watch: {
113+
timelogs(timelogs) {
114+
this.report = timelogs;
115+
},
64116
},
65117
methods: {
66118
isDeletingTimelog(timelogId) {
@@ -77,10 +129,6 @@ export default {
77129
getGraphQLEntityType() {
78130
return this.isIssue() ? TYPENAME_ISSUE : TYPENAME_MERGE_REQUEST;
79131
},
80-
extractTimelogs(data) {
81-
const timelogs = data?.issuable?.timelogs?.nodes || [];
82-
return timelogs.slice().sort((a, b) => new Date(a.spentAt) - new Date(b.spentAt));
83-
},
84132
formatDate(date) {
85133
return formatDate(date, TIME_DATE_FORMAT);
86134
},
@@ -104,6 +152,28 @@ export default {
104152
.mutate({
105153
mutation: deleteTimelogMutation,
106154
variables: { input: { id: timelogId } },
155+
update: (store) => {
156+
if (!this.workItemIid) {
157+
return;
158+
}
159+
store.updateQuery(
160+
{
161+
query: this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery,
162+
variables: { fullPath: this.fullPath, iid: this.workItemIid },
163+
},
164+
(sourceData) =>
165+
produce(sourceData, (draftState) => {
166+
const timeTrackingWidget = draftState.workspace.workItem.widgets.find(
167+
(widget) => widget.type === WIDGET_TYPE_TIME_TRACKING,
168+
);
169+
const timelogs = timeTrackingWidget.timelogs.nodes;
170+
const index = timelogs.findIndex((timelog) => timelog.id === timelogId);
171+
172+
timeTrackingWidget.totalTimeSpent -= timelogs[index].timeSpent;
173+
timelogs.splice(index, 1);
174+
}),
175+
);
176+
},
107177
})
108178
.then(({ data }) => {
109179
if (data.timelogDelete?.errors?.length) {

app/assets/javascripts/sidebar/components/time_tracking/set_time_estimate_form.vue

+51-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
import { GlFormGroup, GlFormInput, GlModal, GlAlert, GlLink } from '@gitlab/ui';
33
import { helpPagePath } from '~/helpers/help_page_helper';
44
import { issuableTypeText, TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/issues/constants';
5+
import { isPositiveInteger } from '~/lib/utils/number_utils';
56
import { s__, __, sprintf } from '~/locale';
7+
import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql';
68
import issueSetTimeEstimateMutation from '../../queries/issue_set_time_estimate.mutation.graphql';
79
import mergeRequestSetTimeEstimateMutation from '../../queries/merge_request_set_time_estimate.mutation.graphql';
810
import { SET_TIME_ESTIMATE_MODAL_ID } from './constants';
@@ -28,11 +30,13 @@ export default {
2830
props: {
2931
fullPath: {
3032
type: String,
31-
required: true,
33+
required: false,
34+
default: '',
3235
},
3336
issuableIid: {
3437
type: String,
35-
required: true,
38+
required: false,
39+
default: '',
3640
},
3741
/**
3842
* This object must contain the following keys, used to show
@@ -44,6 +48,16 @@ export default {
4448
type: Object,
4549
required: true,
4650
},
51+
workItemId: {
52+
type: String,
53+
required: false,
54+
default: '',
55+
},
56+
workItemType: {
57+
type: String,
58+
required: false,
59+
default: '',
60+
},
4761
},
4862
data() {
4963
return {
@@ -134,7 +148,41 @@ export default {
134148
updateEstimatedTime(timeEstimate) {
135149
this.saveError = '';
136150
137-
this.$apollo
151+
if (this.workItemId) {
152+
return this.$apollo
153+
.mutate({
154+
mutation: updateWorkItemMutation,
155+
variables: {
156+
input: {
157+
id: this.workItemId,
158+
timeTrackingWidget: {
159+
timeEstimate:
160+
isPositiveInteger(timeEstimate) && timeEstimate > 0
161+
? `${timeEstimate}h`
162+
: timeEstimate,
163+
},
164+
},
165+
},
166+
})
167+
.then(({ data }) => {
168+
if (data.workItemUpdate.errors.length) {
169+
throw new Error(data.workItemUpdate.errors);
170+
}
171+
172+
this.close();
173+
})
174+
.catch((error) => {
175+
this.saveError =
176+
error?.message ||
177+
s__('TimeTracking|An error occurred while saving the time estimate.');
178+
})
179+
.finally(() => {
180+
this.isSaving = false;
181+
this.isResetting = false;
182+
});
183+
}
184+
185+
return this.$apollo
138186
.mutate({
139187
mutation: MUTATIONS[this.issuableType],
140188
variables: {

0 commit comments

Comments
 (0)