summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/sidebar/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/sidebar/components')
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/attention_requested_toggle.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue13
-rw-r--r--app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js20
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql5
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/report.vue92
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue1
18 files changed, 144 insertions, 28 deletions
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
index ef40de82d01..c20dd3b677d 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_avatar_link.vue
@@ -1,6 +1,7 @@
<script>
import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { IssuableType } from '~/issues/constants';
+import { isGid, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { __ } from '~/locale';
import { isUserBusy } from '~/set_status_modal/utils';
import AssigneeAvatar from './assignee_avatar.vue';
@@ -94,6 +95,9 @@ export default {
assigneeUrl() {
return this.user.web_url || this.user.webUrl;
},
+ assigneeId() {
+ return isGid(this.user.id) ? getIdFromGraphQLId(this.user.id) : this.user.id;
+ },
},
};
</script>
@@ -103,7 +107,7 @@ export default {
<gl-link
:href="assigneeUrl"
:title="tooltipTitle"
- :data-user-id="user.id"
+ :data-user-id="assigneeId"
data-placement="left"
class="gl-display-inline-block js-user-link"
>
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index bdd014163a0..3602b5ec4f6 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -55,7 +55,12 @@ export default {
{{ __('None') }}
<template v-if="editable">
-
- <button type="button" class="btn-link" data-testid="assign-yourself" @click="assignSelf">
+ <button
+ type="button"
+ class="gl-button btn-link gl-reset-color!"
+ data-testid="assign-yourself"
+ @click="assignSelf"
+ >
{{ __('assign yourself') }}
</button>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue
index af4227fa48d..46bda26c327 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee.vue
@@ -26,7 +26,7 @@ export default {
};
</script>
<template>
- <button type="button" class="btn-link">
+ <button type="button" class="gl-button btn-link">
<assignee-avatar :user="user" :img-size="24" :issuable-type="issuableType" />
<user-name-with-status
:name="user.name"
diff --git a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
index 50b1955abcc..f894ef0c42d 100644
--- a/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/collapsed_assignee_list.vue
@@ -123,7 +123,7 @@ export default {
:user="user"
:issuable-type="issuableType"
/>
- <button v-if="hasMoreThanTwoAssignees" class="btn-link" type="button">
+ <button v-if="hasMoreThanTwoAssignees" class="btn-link gl-button" type="button">
<span
class="avatar-counter sidebar-avatar-counter gl-display-flex gl-align-items-center gl-pl-3"
>
diff --git a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
index 01d29da5486..b6260418837 100644
--- a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
@@ -120,7 +120,7 @@ export default {
<div v-if="renderShowMoreSection" class="user-list-more gl-hover-text-blue-800">
<button
type="button"
- class="btn-link"
+ class="btn-link gl-button gl-reset-color!"
data-qa-selector="more_assignees_link"
@click="toggleShowLess"
>
diff --git a/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue b/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue
index 031de669489..974ad189f32 100644
--- a/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue
+++ b/app/assets/javascripts/sidebar/components/attention_requested_toggle.vue
@@ -49,14 +49,14 @@ export default {
},
request() {
const state = {
- variant: 'default',
+ selected: false,
icon: 'attention',
direction: 'add',
};
if (this.user.attention_requested) {
Object.assign(state, {
- variant: 'warning',
+ selected: true,
icon: 'attention-solid',
direction: 'remove',
});
@@ -92,7 +92,7 @@ export default {
>
<gl-button
:loading="loading"
- :variant="request.variant"
+ :selected="request.selected"
:icon="request.icon"
:aria-label="tooltipTitle"
:class="{ 'gl-pointer-events-none': !user.can_update_merge_request }"
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
index 71e40fde77d..c44ce8b0057 100644
--- a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
@@ -8,7 +8,7 @@ import { confidentialityQueries } from '~/sidebar/constants';
export default {
i18n: {
confidentialityOnWarning: __(
- 'You are going to turn on confidentiality. Only %{context} members with %{strongStart}at least Reporter role%{strongEnd} can view or be notified about this %{issuableType}.',
+ 'You are going to turn on confidentiality. Only %{context} members with %{strongStart}%{permissions}%{strongEnd} can view or be notified about this %{issuableType}.',
),
confidentialityOffWarning: __(
'You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}.',
@@ -65,6 +65,11 @@ export default {
groupPath: this.fullPath,
};
},
+ permissions() {
+ return this.issuableType === IssuableType.Issue
+ ? __('at least the Reporter role, the author, and assignees')
+ : __('at least the Reporter role');
+ },
},
methods: {
submitForm() {
@@ -120,7 +125,11 @@ export default {
<p data-testid="warning-message">
<gl-sprintf :message="warningMessage">
<template #strong="{ content }">
- <strong>{{ content }}</strong>
+ <strong>
+ <gl-sprintf :message="content">
+ <template #permissions>{{ permissions }}</template>
+ </gl-sprintf>
+ </strong>
</template>
<template #context>{{ context }}</template>
<template #issuableType>{{ issuableType }}</template>
diff --git a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
index be7a89c2869..ef99d540c86 100644
--- a/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
+++ b/app/assets/javascripts/sidebar/components/date/sidebar_date_widget.vue
@@ -274,7 +274,7 @@ export default {
<template #collapsed>
<div v-gl-tooltip.viewport.left :title="dateLabel" class="sidebar-collapsed-icon">
<gl-icon :size="16" name="calendar" />
- <span class="collapse-truncated-title">{{ formattedDate }}</span>
+ <span class="gl-pt-2 gl-px-3 gl-font-sm">{{ formattedDate }}</span>
</div>
<sidebar-inherit-date
v-if="canInherit && !initialLoading"
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
index 2ab46a7a655..8145506f32c 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form_buttons.vue
@@ -74,7 +74,7 @@ export default {
<gl-button
data-testid="lock-toggle"
category="secondary"
- variant="warning"
+ variant="confirm"
:disabled="isLoading"
:loading="isLoading"
@click.prevent="submitForm"
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 77e41648e9b..b8804de653f 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -99,7 +99,9 @@ export default {
>
<gl-icon name="users" />
<gl-loading-icon v-if="loading" size="sm" />
- <span v-else data-testid="collapsed-count"> {{ participantCount }} </span>
+ <span v-else data-testid="collapsed-count" class="gl-pt-2 gl-px-3 gl-font-sm">
+ {{ participantCount }}
+ </span>
</div>
<div
v-if="showParticipantLabel"
diff --git a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer.vue b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer.vue
index 6de926e0ff9..2ea7c125a85 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer.vue
@@ -17,7 +17,7 @@ export default {
</script>
<template>
- <button type="button" class="btn-link">
+ <button type="button" class="btn-link gl-button">
<reviewer-avatar :user="user" :img-size="24" />
<span class="author"> {{ user.name }} </span>
</button>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
index e09b5d913f7..9502b2e78b3 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/collapsed_reviewer_list.vue
@@ -95,7 +95,7 @@ export default {
>
<gl-icon v-if="hasNoUsers" name="user" :aria-label="__('None')" />
<collapsed-reviewer v-for="user in collapsedUsers" :key="user.id" :user="user" />
- <button v-if="hasMoreThanTwoReviewers" class="btn-link" type="button">
+ <button v-if="hasMoreThanTwoReviewers" class="btn-link gl-button" type="button">
<span
class="avatar-counter sidebar-avatar-counter gl-display-flex gl-align-items-center gl-pl-3"
>
diff --git a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
index 897cab45fe4..3d8a2cd847c 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_dropdown_widget.vue
@@ -322,7 +322,7 @@ export default {
class="sidebar-collapsed-icon"
>
<gl-icon :aria-label="attributeTypeTitle" :name="attributeTypeIcon" />
- <span class="collapse-truncated-title">
+ <span class="collapse-truncated-title gl-pt-2 gl-px-3 gl-font-sm">
{{ attributeTitle }}
</span>
</div>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
index 7b67c34ded6..465f971717f 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
@@ -58,7 +58,7 @@ export default {
} else if (this.showEstimateOnlyState || this.showSpentOnlyState) {
return 'bold';
} else if (this.showNoTimeTrackingState) {
- return 'no-value';
+ return 'no-value collapse-truncated-title gl-pt-2 gl-px-3 gl-font-sm';
}
return '';
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js b/app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js
new file mode 100644
index 00000000000..70177d84b1b
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js
@@ -0,0 +1,20 @@
+import produce from 'immer';
+
+export function removeTimelogFromStore(store, deletedTimelogId, query, variables) {
+ const sourceData = store.readQuery({
+ query,
+ variables,
+ });
+
+ const data = produce(sourceData, (draftData) => {
+ draftData.issuable.timelogs.nodes = draftData.issuable.timelogs.nodes.filter(
+ ({ id }) => id !== deletedTimelogId,
+ );
+ });
+
+ store.writeQuery({
+ query,
+ variables,
+ data,
+ });
+}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql b/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
new file mode 100644
index 00000000000..17bbad1acb1
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql
@@ -0,0 +1,5 @@
+mutation deleteTimelog($input: TimelogDeleteInput!) {
+ timelogDelete(input: $input) {
+ errors
+ }
+}
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/report.vue b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
index d9797961d40..79ef5a32474 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/report.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/report.vue
@@ -1,11 +1,13 @@
<script>
-import { GlLoadingIcon, GlTableLite } from '@gitlab/ui';
+import { GlLoadingIcon, GlTableLite, GlButton, GlTooltipDirective } 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 { __, s__ } from '~/locale';
import { timelogQueries } from '~/sidebar/constants';
+import deleteTimelogMutation from './graphql/mutations/delete_timelog.mutation.graphql';
+import { removeTimelogFromStore } from './graphql/cache_update';
const TIME_DATE_FORMAT = 'mmmm d, yyyy, HH:MM ("UTC:" o)';
@@ -13,6 +15,10 @@ export default {
components: {
GlLoadingIcon,
GlTableLite,
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
},
inject: ['issuableType'],
props: {
@@ -27,7 +33,7 @@ export default {
},
},
data() {
- return { report: [], isLoading: true };
+ return { report: [], isLoading: true, removingIds: [] };
},
apollo: {
report: {
@@ -35,9 +41,7 @@ export default {
return timelogQueries[this.issuableType].query;
},
variables() {
- return {
- id: convertToGraphQLId(this.getGraphQLEntityType(), this.issuableId),
- };
+ return this.getQueryVariables();
},
update(data) {
this.isLoading = false;
@@ -48,10 +52,23 @@ export default {
},
},
},
+ computed: {
+ deleteButtonTooltip() {
+ return s__('TimeTracking|Delete time spent');
+ },
+ },
methods: {
+ isDeletingTimelog(timelogId) {
+ return this.removingIds.includes(timelogId);
+ },
isIssue() {
return this.issuableType === 'issue';
},
+ getQueryVariables() {
+ return {
+ id: convertToGraphQLId(this.getGraphQLEntityType(), this.issuableId),
+ };
+ },
getGraphQLEntityType() {
return this.isIssue() ? TYPE_ISSUE : TYPE_MERGE_REQUEST;
},
@@ -76,19 +93,51 @@ export default {
stringifyTime(parseSeconds(seconds, { limitToHours: this.limitToHours }))
);
},
+ deleteTimelog(timelogId) {
+ this.removingIds.push(timelogId);
+ this.$apollo
+ .mutate({
+ mutation: deleteTimelogMutation,
+ variables: { input: { id: timelogId } },
+ update: (store) => {
+ removeTimelogFromStore(
+ store,
+ timelogId,
+ timelogQueries[this.issuableType].query,
+ this.getQueryVariables(),
+ );
+ },
+ })
+ .then(({ data }) => {
+ if (data.timelogDelete?.errors?.length) {
+ throw new Error(data.timelogDelete.errors[0]);
+ }
+ })
+ .catch((error) => {
+ createFlash({
+ message: s__('TimeTracking|An error occurred while removing the timelog.'),
+ captureError: true,
+ error,
+ });
+ })
+ .finally(() => {
+ this.removingIds.splice(this.removingIds.indexOf(timelogId), 1);
+ });
+ },
},
fields: [
- { key: 'spentAt', label: __('Spent At'), sortable: true, tdClass: 'gl-w-quarter' },
+ { 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 },
+ { key: 'timeSpent', label: __('Time spent'), sortable: true, tdClass: 'gl-w-15' },
+ { key: 'summary', label: __('Summary / note'), sortable: true },
+ { key: 'actions', label: '', tdClass: 'gl-w-10' },
],
};
</script>
<template>
<div>
- <div v-if="isLoading"><gl-loading-icon size="md" /></div>
+ <div v-if="isLoading"><gl-loading-icon size="lg" /></div>
<gl-table-lite v-else :items="report" :fields="$options.fields" foot-clone>
<template #cell(spentAt)="{ item: { spentAt } }">
<div>{{ formatDate(spentAt) }}</div>
@@ -110,7 +159,28 @@ export default {
<template #cell(summary)="{ item: { summary, note } }">
<div>{{ getSummary(summary, note) }}</div>
</template>
- <template #foot(note)>&nbsp;</template>
+ <template #foot(summary)>&nbsp;</template>
+
+ <template
+ #cell(actions)="{
+ item: {
+ id,
+ userPermissions: { adminTimelog },
+ },
+ }"
+ >
+ <div v-if="adminTimelog">
+ <gl-button
+ v-gl-tooltip="{ title: deleteButtonTooltip }"
+ category="secondary"
+ icon="remove"
+ data-testid="deleteButton"
+ :loading="isDeletingTimelog(id)"
+ @click="deleteTimelog(id)"
+ />
+ </div>
+ </template>
+ <template #foot(actions)>&nbsp;</template>
</gl-table-lite>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index 057bb9f0100..e39d9f9fb49 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -252,6 +252,7 @@ export default {
size="lg"
:title="__('Time tracking report')"
:hide-footer="true"
+ @hide="refresh"
>
<time-tracking-report :limit-to-hours="limitToHours" :issuable-id="issuableId" />
</gl-modal>