1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
<script>
import { GlLoadingIcon, GlTableLite } from '@gitlab/ui';
import createFlash from '~/flash';
import { TYPE_ISSUE, TYPE_MERGE_REQUEST } from '~/graphql_shared/constants';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { formatDate, parseSeconds, stringifyTime } from '~/lib/utils/datetime_utility';
import { __ } from '~/locale';
import { timelogQueries } from '~/sidebar/constants';
const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM ("UTC:" o)';
export default {
components: {
GlLoadingIcon,
GlTableLite,
},
inject: ['issuableType'],
props: {
limitToHours: {
type: Boolean,
default: false,
required: false,
},
issuableId: {
type: String,
required: true,
},
},
data() {
return { report: [], isLoading: true };
},
apollo: {
report: {
query() {
return timelogQueries[this.issuableType].query;
},
variables() {
return {
id: convertToGraphQLId(this.getGraphQLEntityType(), this.issuableId),
};
},
update(data) {
this.isLoading = false;
return this.extractTimelogs(data);
},
error() {
createFlash({ message: __('Something went wrong. Please try again.') });
},
},
},
methods: {
isIssue() {
return this.issuableType === 'issue';
},
getGraphQLEntityType() {
return this.isIssue() ? TYPE_ISSUE : TYPE_MERGE_REQUEST;
},
extractTimelogs(data) {
const timelogs = data?.issuable?.timelogs?.nodes || [];
return timelogs.slice().sort((a, b) => new Date(a.spentAt) - new Date(b.spentAt));
},
formatDate(date) {
return formatDate(date, TIME_DATE_FORMAT);
},
getSummary(summary, note) {
return summary ?? note?.body;
},
getTotalTimeSpent() {
const seconds = this.report.reduce((acc, item) => acc + item.timeSpent, 0);
return this.formatTimeSpent(seconds);
},
formatTimeSpent(seconds) {
const negative = seconds < 0;
return (
(negative ? '- ' : '') +
stringifyTime(parseSeconds(seconds, { limitToHours: this.limitToHours }))
);
},
},
fields: [
{ key: 'spentAt', label: __('Spent At'), sortable: true, tdClass: 'gl-w-quarter' },
{ key: 'user', label: __('User'), sortable: true },
{ key: 'timeSpent', label: __('Time Spent'), sortable: true, tdClass: 'gl-w-15' },
{ key: 'summary', label: __('Summary / Note'), sortable: true },
],
};
</script>
<template>
<div>
<div v-if="isLoading"><gl-loading-icon size="md" /></div>
<gl-table-lite v-else :items="report" :fields="$options.fields" foot-clone>
<template #cell(spentAt)="{ item: { spentAt } }">
<div>{{ formatDate(spentAt) }}</div>
</template>
<template #foot(spentAt)> </template>
<template #cell(user)="{ item: { user } }">
<div>{{ user.name }}</div>
</template>
<template #foot(user)> </template>
<template #cell(timeSpent)="{ item: { timeSpent } }">
<div>{{ formatTimeSpent(timeSpent) }}</div>
</template>
<template #foot(timeSpent)>
<div>{{ getTotalTimeSpent() }}</div>
</template>
<template #cell(summary)="{ item: { summary, note } }">
<div>{{ getSummary(summary, note) }}</div>
</template>
<template #foot(note)> </template>
</gl-table-lite>
</div>
</template>
|