summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-03 00:10:55 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-03 00:10:55 +0000
commit198460d5879a516844f81f667c6bc4fe84ed1719 (patch)
treecd6e50aec1232500dc4486adf2cb8dd28c8ac378 /app
parent9579eee8954e0405c2dadb19c2a73c9597ce37ea (diff)
downloadgitlab-ce-198460d5879a516844f81f667c6bc4fe84ed1719.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/frequent_items/components/app.vue14
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue11
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue15
-rw-r--r--app/assets/javascripts/header.js7
-rw-r--r--app/assets/javascripts/header_search/components/app.vue4
-rw-r--r--app/assets/javascripts/nav/components/new_nav_toggle.vue2
-rw-r--r--app/assets/javascripts/nav/components/top_nav_app.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue10
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue100
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/approvals/queries/approved_by.query.graphql16
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mappers.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js1
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue2
-rw-r--r--app/assets/javascripts/whats_new/components/app.vue6
-rw-r--r--app/helpers/nav/new_dropdown_helper.rb19
-rw-r--r--app/helpers/nav/top_nav_helper.rb6
-rw-r--r--app/models/concerns/vulnerability_finding_helpers.rb2
-rw-r--r--app/models/packages/package.rb1
-rw-r--r--app/views/layouts/header/_current_user_dropdown.html.haml16
-rw-r--r--app/views/layouts/header/_default.html.haml34
-rw-r--r--app/views/layouts/header/_gitlab_version.html.haml2
-rw-r--r--app/views/layouts/header/_help_dropdown.html.haml10
-rw-r--r--app/views/layouts/header/_new_dropdown.html.haml2
-rw-r--r--app/views/shared/_help_dropdown_forum_link.html.haml2
-rw-r--r--app/views/shared/_user_dropdown_contributing_link.html.haml2
-rw-r--r--app/views/shared/_user_dropdown_instance_review.html.haml2
26 files changed, 212 insertions, 79 deletions
diff --git a/app/assets/javascripts/frequent_items/components/app.vue b/app/assets/javascripts/frequent_items/components/app.vue
index a4e883c96b5..947d3053094 100644
--- a/app/assets/javascripts/frequent_items/components/app.vue
+++ b/app/assets/javascripts/frequent_items/components/app.vue
@@ -6,6 +6,7 @@ import {
mapVuexModuleActions,
mapVuexModuleGetters,
} from '~/lib/utils/vuex_module_mappers';
+import Tracking from '~/tracking';
import { FREQUENT_ITEMS, STORAGE_KEY } from '../constants';
import eventHub from '../event_hub';
import { isMobile, updateExistingFrequentItem, sanitizeItem } from '../utils';
@@ -13,6 +14,8 @@ import FrequentItemsList from './frequent_items_list.vue';
import frequentItemsMixin from './frequent_items_mixin';
import FrequentItemsSearchInput from './frequent_items_search_input.vue';
+const trackingMixin = Tracking.mixin();
+
export default {
components: {
FrequentItemsSearchInput,
@@ -24,7 +27,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [frequentItemsMixin],
+ mixins: [frequentItemsMixin, trackingMixin],
inject: ['vuexModule'],
props: {
currentUserName: {
@@ -84,6 +87,13 @@ export default {
'toggleItemsListEditablity',
'fetchFrequentItems',
]),
+ toggleItemsListEditablityTracked() {
+ this.track('click_button', {
+ label: 'toggle_edit_frequent_items',
+ property: 'navigation_top',
+ });
+ this.toggleItemsListEditablity();
+ },
dropdownOpenHandler() {
if (this.searchQuery === '' || isMobile()) {
this.fetchFrequentItems();
@@ -155,7 +165,7 @@ export default {
:title="translations.headerEditToggle"
:class="{ 'gl-bg-gray-100!': isItemsListEditable }"
class="gl-p-2!"
- @click="toggleItemsListEditablity"
+ @click="toggleItemsListEditablityTracked"
>
<gl-icon name="pencil" :class="{ 'gl-text-gray-900!': isItemsListEditable }" />
</gl-button>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 430498e7194..056dedf8757 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -64,6 +64,13 @@ export default {
},
},
methods: {
+ removeFrequentItemTracked(item) {
+ this.track('click_button', {
+ label: `${this.dropdownType}_dropdown_remove_frequent_item`,
+ property: 'navigation_top',
+ });
+ this.removeFrequentItem(item);
+ },
...mapVuexModuleActions((vm) => vm.vuexModule, ['removeFrequentItem']),
},
};
@@ -77,7 +84,7 @@ export default {
class="gl-text-left gl-w-full"
button-text-classes="gl-display-flex gl-w-full"
data-testid="frequent-item-link"
- @click="track('click_link', { label: itemTrackingLabel })"
+ @click="track('click_link', { label: itemTrackingLabel, property: 'navigation_top' })"
>
<div class="gl-flex-grow-1">
<project-avatar
@@ -117,7 +124,7 @@ export default {
:title="__('Remove')"
class="gl-align-self-center gl-p-1! gl-absolute! gl-w-auto! gl-right-4 gl-top-half gl-translate-y-n50"
data-testid="item-remove"
- @click.stop.prevent="removeFrequentItem(itemId)"
+ @click.stop.prevent="removeFrequentItemTracked(itemId)"
>
<gl-icon name="close" />
</gl-button>
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
index 4a1b7e57749..023245f050b 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_search_input.vue
@@ -28,12 +28,25 @@ export default {
searchQuery: debounce(function debounceSearchQuery() {
this.track('type_search_query', {
label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
+ property: 'navigation_top',
});
this.setSearchQuery(this.searchQuery);
}, 500),
},
methods: {
...mapVuexModuleActions((vm) => vm.vuexModule, ['setSearchQuery']),
+ trackFocus() {
+ this.track('focus_input', {
+ label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
+ property: 'navigation_top',
+ });
+ },
+ trackBlur() {
+ this.track('blur_input', {
+ label: `${this.dropdownType}_dropdown_frequent_items_search_input`,
+ property: 'navigation_top',
+ });
+ },
},
};
</script>
@@ -43,6 +56,8 @@ export default {
<gl-search-box-by-type
v-model="searchQuery"
:placeholder="translations.searchInputPlaceholder"
+ @focus="trackFocus"
+ @blur="trackBlur"
/>
</div>
</template>
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index f58781fa9ec..6c9354b663f 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -33,6 +33,13 @@ function initStatusTriggers() {
if (setStatusModalTriggerEl) {
setStatusModalTriggerEl.addEventListener('click', () => {
+ const topNavbar = document.querySelector('.navbar-gitlab');
+ const buttonWithinTopNav = topNavbar && topNavbar.contains(setStatusModalTriggerEl);
+ Tracking.event(undefined, 'click_button', {
+ label: 'user_edit_status',
+ property: buttonWithinTopNav ? 'navigation_top' : undefined,
+ });
+
import(
/* webpackChunkName: 'statusModalBundle' */ './set_status_modal/set_status_modal_wrapper.vue'
)
diff --git a/app/assets/javascripts/header_search/components/app.vue b/app/assets/javascripts/header_search/components/app.vue
index bf5daf29b21..ace0d77c431 100644
--- a/app/assets/javascripts/header_search/components/app.vue
+++ b/app/assets/javascripts/header_search/components/app.vue
@@ -171,7 +171,7 @@ export default {
Tracking.event(undefined, 'focus_input', {
label: 'global_search',
- property: 'top_navigation',
+ property: 'navigation_top',
});
}
},
@@ -190,7 +190,7 @@ export default {
Tracking.event(undefined, 'blur_input', {
label: 'global_search',
- property: 'top_navigation',
+ property: 'navigation_top',
});
}, 200);
},
diff --git a/app/assets/javascripts/nav/components/new_nav_toggle.vue b/app/assets/javascripts/nav/components/new_nav_toggle.vue
index 7b0076cc5d4..da22a8d2fb7 100644
--- a/app/assets/javascripts/nav/components/new_nav_toggle.vue
+++ b/app/assets/javascripts/nav/components/new_nav_toggle.vue
@@ -45,7 +45,7 @@ export default {
Tracking.event(undefined, 'click_toggle', {
label: this.enabled ? 'disable_new_nav_beta' : 'enable_new_nav_beta',
- property: 'navigation',
+ property: this.enabled ? 'navigation' : 'navigation_top',
});
window.location.reload();
diff --git a/app/assets/javascripts/nav/components/top_nav_app.vue b/app/assets/javascripts/nav/components/top_nav_app.vue
index e55bf25a60c..ab9313f7041 100644
--- a/app/assets/javascripts/nav/components/top_nav_app.vue
+++ b/app/assets/javascripts/nav/components/top_nav_app.vue
@@ -24,7 +24,7 @@ export default {
trackToggleEvent() {
Tracking.event(undefined, 'click_nav', {
label: 'hamburger_menu',
- property: 'top_navigation',
+ property: 'navigation_top',
});
},
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
index a04b082fa87..4b65d6fd9ac 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue
@@ -62,6 +62,7 @@ export default {
fetchingApprovals: true,
hasApprovalAuthError: false,
isApproving: false,
+ updatedCount: 0,
};
},
computed: {
@@ -197,6 +198,7 @@ export default {
return serviceFn()
.then((data) => {
this.mr.setApprovals(data);
+ this.updatedCount += 1;
if (!window.gon?.features?.realtimeMrStatusChange) {
eventHub.$emit('MRWidgetUpdateRequested');
@@ -250,10 +252,10 @@ export default {
/>
<approvals-summary
v-else
- :approved="isApproved"
- :approvals-left="approvals.approvals_left || 0"
- :rules-left="approvals.approvalRuleNamesLeft"
- :approvers="approvedBy"
+ :project-path="mr.targetProjectFullPath"
+ :iid="`${mr.iid}`"
+ :updated-count="updatedCount"
+ :multiple-approval-rules-available="mr.multipleApprovalRulesAvailable"
/>
</div>
<div v-if="hasInvalidRules" class="gl-text-gray-400 gl-mt-2" data-testid="invalid-rules">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
index d7255eb6ad2..7c283c42a03 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals_summary.vue
@@ -1,4 +1,5 @@
<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
import { toNounSeriesText } from '~/lib/utils/grammar';
import { n__, sprintf } from '~/locale';
import {
@@ -7,32 +8,68 @@ import {
APPROVED_BY_OTHERS,
} from '~/vue_merge_request_widget/components/approvals/messages';
import UserAvatarList from '~/vue_shared/components/user_avatar/user_avatar_list.vue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { getApprovalRuleNamesLeft } from 'ee_else_ce/vue_merge_request_widget/mappers';
+import approvedByQuery from 'ee_else_ce/vue_merge_request_widget/components/approvals/queries/approved_by.query.graphql';
export default {
+ apollo: {
+ approvalState: {
+ query: approvedByQuery,
+ variables() {
+ return {
+ projectPath: this.projectPath,
+ iid: this.iid,
+ };
+ },
+ update: (data) => data.project.mergeRequest,
+ },
+ },
components: {
+ GlSkeletonLoader,
UserAvatarList,
},
props: {
- approved: {
- type: Boolean,
+ projectPath: {
+ type: String,
required: true,
},
- approvalsLeft: {
- type: Number,
+ iid: {
+ type: String,
required: true,
},
- rulesLeft: {
- type: Array,
+ updatedCount: {
+ type: Number,
required: false,
- default: () => [],
+ default: 0,
},
- approvers: {
- type: Array,
+ multipleApprovalRulesAvailable: {
+ type: Boolean,
required: false,
- default: () => [],
+ default: false,
},
},
+ data() {
+ return {
+ approvalState: {},
+ };
+ },
computed: {
+ approvers() {
+ return this.approvalState.approvedBy?.nodes || [];
+ },
+ approved() {
+ return this.approvalState.approved || this.approvalState.approvedBy?.nodes.length > 0;
+ },
+ approvalsLeft() {
+ return this.approvalState.approvalsLeft || 0;
+ },
+ rulesLeft() {
+ return getApprovalRuleNamesLeft(
+ this.multipleApprovalRulesAvailable,
+ (this.approvalState.approvalState?.rules || []).filter((r) => r.approvalsRequired !== 0),
+ );
+ },
approvalLeftMessage() {
if (this.rulesLeft.length) {
return sprintf(
@@ -81,32 +118,53 @@ export default {
if (!this.currentUserId) {
return false;
}
- return this.approvers.some((approver) => approver.id === this.currentUserId);
+ return this.approvers.some(
+ (approver) => getIdFromGraphQLId(approver.id) === this.currentUserId,
+ );
},
approvedByOthers() {
if (!this.currentUserId) {
return false;
}
- return this.approvers.some((approver) => approver.id !== this.currentUserId);
+ return this.approvers.some(
+ (approver) => getIdFromGraphQLId(approver.id) !== this.currentUserId,
+ );
},
currentUserId() {
return gon.current_user_id;
},
},
+ watch: {
+ updatedCount() {
+ this.$apollo.queries.approvalState.refetch();
+ },
+ },
};
</script>
<template>
<div data-qa-selector="approvals_summary_content">
- <span class="gl-font-weight-bold">{{ approvalLeftMessage }}</span>
- <template v-if="hasApprovers">
- <span v-if="approvalLeftMessage">{{ message }}</span>
- <span v-else class="gl-font-weight-bold">{{ message }}</span>
- <user-avatar-list
- class="gl-display-inline-block gl-vertical-align-middle gl-pt-1"
- :img-size="24"
- :items="approvers"
- />
+ <div
+ v-if="$apollo.queries.approvalState.loading"
+ class="gl-display-inline-block gl-vertical-align-middle"
+ style="width: 132px; height: 24px"
+ >
+ <gl-skeleton-loader :width="132" :height="24">
+ <rect width="100" height="24" x="0" y="0" rx="4" />
+ <circle cx="120" cy="12" r="12" />
+ </gl-skeleton-loader>
+ </div>
+ <template v-else>
+ <span class="gl-font-weight-bold">{{ approvalLeftMessage }}</span>
+ <template v-if="hasApprovers">
+ <span v-if="approvalLeftMessage">{{ message }}</span>
+ <span v-else class="gl-font-weight-bold">{{ message }}</span>
+ <user-avatar-list
+ class="gl-display-inline-block gl-vertical-align-middle gl-pt-1"
+ :img-size="24"
+ :items="approvers"
+ />
+ </template>
</template>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/queries/approved_by.query.graphql b/app/assets/javascripts/vue_merge_request_widget/components/approvals/queries/approved_by.query.graphql
new file mode 100644
index 00000000000..c8cae6a8885
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/queries/approved_by.query.graphql
@@ -0,0 +1,16 @@
+query approvedBy($projectPath: ID!, $iid: String!) {
+ project(fullPath: $projectPath) {
+ id
+ mergeRequest(iid: $iid) {
+ id
+ approvedBy {
+ nodes {
+ id
+ name
+ avatarUrl
+ webUrl
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/mappers.js b/app/assets/javascripts/vue_merge_request_widget/mappers.js
new file mode 100644
index 00000000000..63c4c3dc871
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/mappers.js
@@ -0,0 +1,3 @@
+export function getApprovalRuleNamesLeft(_, rules) {
+ return rules;
+}
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index 85df2ea63c8..f6a7ef58c10 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -30,6 +30,7 @@ export default class MergeRequestStore {
this.machineValue = this.stateMachine.value;
this.mergeDetailsCollapsed = window.innerWidth < 768;
this.mergeError = data.mergeError;
+ this.multipleApprovalRulesAvailable = data.multiple_approval_rules_available || false;
this.id = data.id;
this.setPaths(data);
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue
index 231f5ff3d1f..4f3b482d7f8 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_list.vue
@@ -75,7 +75,7 @@ export default {
v-for="item in visibleItems"
:key="item.id"
:link-href="item.web_url"
- :img-src="item.avatar_url"
+ :img-src="item.avatar_url || item.avatarUrl"
:img-alt="item.name"
:tooltip-text="item.name"
:img-size="imgSize"
diff --git a/app/assets/javascripts/whats_new/components/app.vue b/app/assets/javascripts/whats_new/components/app.vue
index 9e5361e8302..472bc1dfacc 100644
--- a/app/assets/javascripts/whats_new/components/app.vue
+++ b/app/assets/javascripts/whats_new/components/app.vue
@@ -35,7 +35,11 @@ export default {
const body = document.querySelector('body');
const { namespaceId } = body.dataset;
- this.track('click_whats_new_drawer', { label: 'namespace_id', value: namespaceId });
+ this.track('click_whats_new_drawer', {
+ label: 'namespace_id',
+ value: namespaceId,
+ property: 'navigation_top',
+ });
},
methods: {
...mapActions(['openDrawer', 'closeDrawer', 'fetchItems', 'setDrawerBodyHeight']),
diff --git a/app/helpers/nav/new_dropdown_helper.rb b/app/helpers/nav/new_dropdown_helper.rb
index 4972d359bd6..ddd6469a9e4 100644
--- a/app/helpers/nav/new_dropdown_helper.rb
+++ b/app/helpers/nav/new_dropdown_helper.rb
@@ -32,7 +32,7 @@ module Nav
id: 'new_project',
title: _('New project/repository'),
href: new_project_path(namespace_id: group.id),
- data: { track_action: 'click_link_new_project_group', track_label: 'plus_menu_dropdown' }
+ data: { track_action: 'click_link_new_project_group', track_label: 'plus_menu_dropdown', track_property: 'navigation_top' }
)
)
end
@@ -43,7 +43,7 @@ module Nav
id: 'new_subgroup',
title: _('New subgroup'),
href: new_group_path(parent_id: group.id, anchor: 'create-group-pane'),
- data: { track_action: 'click_link_new_subgroup', track_label: 'plus_menu_dropdown' }
+ data: { track_action: 'click_link_new_subgroup', track_label: 'plus_menu_dropdown', track_property: 'navigation_top' }
)
)
end
@@ -74,7 +74,7 @@ module Nav
id: 'new_issue',
title: _('New issue'),
href: new_project_issue_path(project),
- data: { track_action: 'click_link_new_issue', track_label: 'plus_menu_dropdown', qa_selector: 'new_issue_link' }
+ data: { track_action: 'click_link_new_issue', track_label: 'plus_menu_dropdown', track_property: 'navigation_top', qa_selector: 'new_issue_link' }
)
)
end
@@ -85,7 +85,7 @@ module Nav
id: 'new_mr',
title: _('New merge request'),
href: project_new_merge_request_path(merge_project),
- data: { track_action: 'click_link_new_mr', track_label: 'plus_menu_dropdown' }
+ data: { track_action: 'click_link_new_mr', track_label: 'plus_menu_dropdown', track_property: 'navigation_top' }
)
)
end
@@ -96,7 +96,7 @@ module Nav
id: 'new_snippet',
title: _('New snippet'),
href: new_project_snippet_path(project),
- data: { track_action: 'click_link_new_snippet_project', track_label: 'plus_menu_dropdown' }
+ data: { track_action: 'click_link_new_snippet_project', track_label: 'plus_menu_dropdown', track_property: 'navigation_top' }
)
)
end
@@ -124,7 +124,7 @@ module Nav
id: 'general_new_project',
title: _('New project/repository'),
href: new_project_path,
- data: { track_action: 'click_link_new_project', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_project_link' }
+ data: { track_action: 'click_link_new_project', track_label: 'plus_menu_dropdown', track_property: 'navigation_top', qa_selector: 'global_new_project_link' }
)
)
end
@@ -135,7 +135,7 @@ module Nav
id: 'general_new_group',
title: _('New group'),
href: new_group_path,
- data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_group_link' }
+ data: { track_action: 'click_link_new_group', track_label: 'plus_menu_dropdown', track_property: 'navigation_top', qa_selector: 'global_new_group_link' }
)
)
end
@@ -146,7 +146,7 @@ module Nav
id: 'general_new_snippet',
title: _('New snippet'),
href: new_snippet_path,
- data: { track_action: 'click_link_new_snippet_parent', track_label: 'plus_menu_dropdown', qa_selector: 'global_new_snippet_link' }
+ data: { track_action: 'click_link_new_snippet_parent', track_label: 'plus_menu_dropdown', track_property: 'navigation_top', qa_selector: 'global_new_snippet_link' }
)
)
end
@@ -165,7 +165,8 @@ module Nav
href: href,
data: {
track_action: 'click_link_invite_members',
- track_label: 'plus_menu_dropdown'
+ track_label: 'plus_menu_dropdown',
+ track_property: 'navigation_top'
}
)
end
diff --git a/app/helpers/nav/top_nav_helper.rb b/app/helpers/nav/top_nav_helper.rb
index bd4d661ab49..fb11c183aeb 100644
--- a/app/helpers/nav/top_nav_helper.rb
+++ b/app/helpers/nav/top_nav_helper.rb
@@ -101,7 +101,7 @@ module Nav
builder.add_primary_menu_item_with_shortcut(
header: top_nav_localized_headers[:switch_to],
active: nav == 'project' || active_nav_link?(path: %w[root#index projects#trending projects#starred dashboard/projects#index]),
- data: { track_label: "projects_dropdown", track_action: "click_dropdown", qa_selector: "projects_dropdown" },
+ data: { track_label: "projects_dropdown", track_action: "click_dropdown", track_property: "navigation_top", qa_selector: "projects_dropdown" },
view: PROJECTS_VIEW,
shortcut_href: dashboard_projects_path,
**projects_menu_item_attrs
@@ -115,7 +115,7 @@ module Nav
builder.add_primary_menu_item_with_shortcut(
header: top_nav_localized_headers[:switch_to],
active: nav == 'group' || active_nav_link?(path: %w[dashboard/groups explore/groups]),
- data: { track_label: "groups_dropdown", track_action: "click_dropdown", qa_selector: "groups_dropdown" },
+ data: { track_label: "groups_dropdown", track_action: "click_dropdown", track_property: "navigation_top", qa_selector: "groups_dropdown" },
view: GROUPS_VIEW,
shortcut_href: dashboard_groups_path,
**groups_menu_item_attrs
@@ -233,7 +233,7 @@ module Nav
tracking_attrs(
"menu_#{label.underscore.parameterize(separator: '_')}",
'click_dropdown',
- 'navigation'
+ 'navigation_top'
)[:data] || {}
end
diff --git a/app/models/concerns/vulnerability_finding_helpers.rb b/app/models/concerns/vulnerability_finding_helpers.rb
index b5d48260072..1e8a290c050 100644
--- a/app/models/concerns/vulnerability_finding_helpers.rb
+++ b/app/models/concerns/vulnerability_finding_helpers.rb
@@ -49,7 +49,7 @@ module VulnerabilityFindingHelpers
finding_data = report_finding.to_hash.except(:compare_key, :identifiers, :location, :scanner, :links, :signatures,
:flags, :evidence)
- identifiers = report_finding.identifiers.map do |identifier|
+ identifiers = report_finding.identifiers.uniq(&:fingerprint).map do |identifier|
Vulnerabilities::Identifier.new(identifier.to_hash.merge({ project: project }))
end
signatures = report_finding.signatures.map do |signature|
diff --git a/app/models/packages/package.rb b/app/models/packages/package.rb
index c6e8ff6c3fa..c5da647eea5 100644
--- a/app/models/packages/package.rb
+++ b/app/models/packages/package.rb
@@ -279,6 +279,7 @@ class Packages::Package < ApplicationRecord
project.packages
.preload_pipelines
.including_tags
+ .displayable
.with_name(name)
.where.not(version: version)
.with_package_type(package_type)
diff --git a/app/views/layouts/header/_current_user_dropdown.html.haml b/app/views/layouts/header/_current_user_dropdown.html.haml
index 8363d424c1b..af27026845e 100644
--- a/app/views/layouts/header/_current_user_dropdown.html.haml
+++ b/app/views/layouts/header/_current_user_dropdown.html.haml
@@ -3,7 +3,7 @@
%ul
%li.current-user
- if current_user_menu?(:profile)
- = link_to current_user, class: 'gl-line-height-20!', data: { user: current_user.username, testid: 'user-profile-link', qa_selector: 'user_profile_link' } do
+ = link_to current_user, class: 'gl-line-height-20!', data: { user: current_user.username, testid: 'user-profile-link', track_action: "click_link", track_label: "user_profile", track_property: "navigation_top", qa_selector: 'user_profile_link' } do
= render 'layouts/header/current_user_dropdown_item'
- else
.gl-py-3.gl-px-4
@@ -23,28 +23,28 @@
= emoji_icon('rocket')
- if current_user_menu?(:settings)
%li
- = link_to s_("CurrentUser|Edit profile"), profile_path, data: { qa_selector: 'edit_profile_link' }
+ = link_to s_("CurrentUser|Edit profile"), profile_path, data: { qa_selector: 'edit_profile_link', track_action: "click_link", track_label: "user_edit_profile", track_property: "navigation_top" }
%li
- = link_to s_("CurrentUser|Preferences"), profile_preferences_path
+ = link_to s_("CurrentUser|Preferences"), profile_preferences_path, data: { track_action: "click_link", track_label: "user_preferences", track_property: "navigation_top" }
= render_if_exists 'layouts/header/buy_pipeline_minutes', project: @project, namespace: @group
- if current_user_menu?(:help)
%li.divider.d-md-none
%li.d-md-none
- = link_to _("Help"), help_path
+ = link_to _("Help"), help_path, data: {track_action: 'click_link', track_label: 'help', track_property: 'navigation_top'}
%li.d-md-none
- = link_to _("Support"), support_url
+ = link_to _("Support"), support_url, data: {track_action: 'click_link', track_label: 'support', track_property: 'navigation_top'}
%li.d-md-none
= render 'shared/help_dropdown_forum_link'
%li.d-md-none
- = link_to _("Submit feedback"), Gitlab::Utils.append_path(promo_url, "submit-feedback")
+ = link_to _("Submit feedback"), Gitlab::Utils.append_path(promo_url, "submit-feedback"), data: {track_action: 'click_link', track_label: 'submit_feedback', track_property: 'navigation_top'}
- if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
%li.d-md-none
= render 'shared/user_dropdown_contributing_link'
= render 'shared/user_dropdown_instance_review'
- if Gitlab.com_but_not_canary?
%li.d-md-none
- = link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url
+ = link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url, data: { track_action: "click_link", track_label: "switch_to_canary", track_property: "navigation_top" }
- if Feature.enabled?(:super_sidebar_nav, current_user)
%li.divider
@@ -53,4 +53,4 @@
- if current_user_menu?(:sign_out)
%li.divider
%li
- = link_to _("Sign out"), destroy_user_session_path, method: :post, class: "sign-out-link", data: { qa_selector: 'sign_out_link' }
+ = link_to _("Sign out"), destroy_user_session_path, method: :post, class: "sign-out-link", data: { qa_selector: 'sign_out_link', track_action: "click_link", track_label: "user_sign_out", track_property: "navigation_top" }
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 4cd876e4fc0..3b2cd4c30dd 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -8,7 +8,7 @@
.title-container.hide-when-top-nav-responsive-open.gl-transition-medium.gl-display-flex.gl-align-items-stretch.gl-pt-0.gl-mr-3
.title
%span.gl-sr-only GitLab
- = link_to root_path, title: _('Dashboard'), id: 'logo', class: 'has-tooltip', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do
+ = link_to root_path, title: _('Dashboard'), id: 'logo', class: 'has-tooltip', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation_top') do
= brand_header_logo
.gl-display-flex.gl-align-items-center
- if Gitlab.com_and_canary?
@@ -36,7 +36,11 @@
- else
= render 'layouts/search'
%li.nav-item{ class: 'd-none d-sm-inline-block d-lg-none' }
- = link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to search_menu_item.fetch(:href), title: search_menu_item.fetch(:title), aria: { label: search_menu_item.fetch(:title) },
+ data: { toggle: 'tooltip', placement: 'bottom', container: 'body',
+ track_action: 'click_link',
+ track_label: 'global_search',
+ track_property: 'navigation_top' } do
= sprite_icon(search_menu_item.fetch(:icon))
.navbar-collapse.gl-transition-medium.collapse
@@ -49,7 +53,7 @@
data: { qa_selector: 'issues_shortcut_button', toggle: 'tooltip', placement: 'bottom',
track_label: 'main_navigation',
track_action: 'click_issues_link',
- track_property: 'navigation',
+ track_property: 'navigation_top',
container: 'body' } do
= sprite_icon('issues')
- issues_count = assigned_issuables_count(:issues)
@@ -62,9 +66,9 @@
data: { qa_selector: 'merge_requests_shortcut_button',
toggle: "dropdown",
placement: 'bottom',
- track_label: 'main_navigation',
- track_action: 'click_merge_link',
- track_property: 'navigation',
+ track_label: 'merge_requests_menu',
+ track_action: 'click_dropdown',
+ track_property: 'navigation_top',
container: 'body' } do
= sprite_icon('git-merge')
= gl_badge_tag({ size: :sm, variant: :warning }, { class: "js-merge-requests-count gl-ml-n2 #{'gl-display-none' if user_merge_requests_counts[:total] == 0}", "aria-label": n_("%d merge request", "%d merge requests", user_merge_requests_counts[:total]) % user_merge_requests_counts[:total] }) do
@@ -75,12 +79,16 @@
%li.dropdown-header
= _('Merge requests')
%li
- = link_to assigned_mrs_dashboard_path, class: 'gl-display-flex! gl-align-items-center js-prefetch-document' do
+ = link_to assigned_mrs_dashboard_path,
+ class: 'gl-display-flex! gl-align-items-center js-prefetch-document',
+ data: {track_action: 'click_link', track_label: 'merge_requests_assigned', track_property: 'navigation_top'} do
= _('Assigned')
= gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-assigned-mr-count gl-ml-auto" }) do
= user_merge_requests_counts[:assigned]
%li
- = link_to reviewer_mrs_dashboard_path, class: 'dashboard-shortcuts-review_requests gl-display-flex! gl-align-items-center js-prefetch-document' do
+ = link_to reviewer_mrs_dashboard_path,
+ class: 'dashboard-shortcuts-review_requests gl-display-flex! gl-align-items-center js-prefetch-document',
+ data: {track_action: 'click_link', track_label: 'merge_requests_to_review', track_property: 'navigation_top'} do
= _('Review requests')
= gl_badge_tag({ variant: :neutral, size: :sm }, { class: "js-reviewer-mr-count gl-ml-auto" }) do
= user_merge_requests_counts[:review_requested]
@@ -90,15 +98,15 @@
data: { qa_selector: 'todos_shortcut_button', toggle: 'tooltip', placement: 'bottom',
track_label: 'main_navigation',
track_action: 'click_to_do_link',
- track_property: 'navigation',
+ track_property: 'navigation_top',
container: 'body' } do
= sprite_icon('todo-done')
-# The todos' counter badge's visibility is being toggled by adding or removing the .hidden class in Js.
-# We'll eventually migrate to .gl-display-none: https://gitlab.com/gitlab-org/gitlab/-/issues/351792.
= gl_badge_tag({ size: :sm, variant: :info }, { class: "js-todos-count gl-ml-n2 #{'hidden' if todos_pending_count == 0}", "aria-label": _("Todos count") }) do
= todos_count_format(todos_pending_count)
- %li.nav-item.header-help.dropdown.d-none.d-md-block{ data: { track_action: 'click_question_mark_link', track_label: 'main_navigation', track_property: 'navigation', track_experiment: 'cross_stage_fdm' } }
- = link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown" } do
+ %li.nav-item.header-help.dropdown.d-none.d-md-block
+ = link_to help_path, class: 'header-help-dropdown-toggle gl-relative', data: { toggle: "dropdown", track_action: 'click_question_mark_link', track_label: 'main_navigation', track_property: 'navigation_top', track_experiment: 'cross_stage_fdm' } do
%span.gl-sr-only
= s_('Nav|Help')
= sprite_icon('question-o')
@@ -110,8 +118,8 @@
%li.nav-item.gl-display-none.gl-sm-display-block
= render "layouts/nav/top_nav"
- if header_link?(:user_dropdown)
- %li.nav-item.header-user.js-nav-user-dropdown.dropdown{ data: { track_label: "profile_dropdown", track_action: "click_dropdown", track_value: "", qa_selector: 'user_menu', testid: 'user-menu' }, class: ('mr-0' if has_impersonation_link) }
- = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do
+ %li.nav-item.header-user.js-nav-user-dropdown.dropdown{ data: { qa_selector: 'user_menu', testid: 'user-menu' }, class: ('mr-0' if has_impersonation_link) }
+ = link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown", track_label: "profile_dropdown", track_action: "click_dropdown", track_property: "navigation_top" } do
= render Pajamas::AvatarComponent.new(current_user, size: 24, class: 'header-user-avatar', avatar_options: { data: { qa_selector: 'user_avatar_content' } })
= render_if_exists 'layouts/header/user_notification_dot', project: project, namespace: group
= sprite_icon('chevron-down', css_class: 'caret-down')
diff --git a/app/views/layouts/header/_gitlab_version.html.haml b/app/views/layouts/header/_gitlab_version.html.haml
index 581d4d498e1..22771ac09c9 100644
--- a/app/views/layouts/header/_gitlab_version.html.haml
+++ b/app/views/layouts/header/_gitlab_version.html.haml
@@ -7,7 +7,7 @@
testid: 'gitlab-version-container',
track_action: 'click_link',
track_label: 'version_help_dropdown',
- track_property: "#{Gitlab.version_info.major}.#{Gitlab.version_info.minor}"
+ track_property: 'navigation_top'
}
}
%span
diff --git a/app/views/layouts/header/_help_dropdown.html.haml b/app/views/layouts/header/_help_dropdown.html.haml
index bdd1ae291fd..f50df72afbc 100644
--- a/app/views/layouts/header/_help_dropdown.html.haml
+++ b/app/views/layouts/header/_help_dropdown.html.haml
@@ -5,22 +5,22 @@
= render_if_exists 'layouts/header/help_dropdown/cross_stage_fdm'
= render 'layouts/header/whats_new_dropdown_item'
%li
- = link_to _("Help"), help_path
+ = link_to _("Help"), help_path, data: {track_action: 'click_link', track_label: 'help', track_property: 'navigation_top'}
%li
- = link_to _("Support"), support_url
+ = link_to _("Support"), support_url, data: {track_action: 'click_link', track_label: 'support', track_property: 'navigation_top'}
%li
= render 'shared/help_dropdown_forum_link'
%li
- %button.js-shortcuts-modal-trigger{ type: "button" }
+ %button.js-shortcuts-modal-trigger{ type: "button", data: {track_action: 'click_button', track_label: 'keyboard_shortcuts_help', track_property: 'navigation_top'} }
= _("Keyboard shortcuts")
%kbd.flat.float-right{ "aria-hidden": "true" }= '?'.html_safe
%li.divider
%li
- = link_to _("Submit feedback"), "https://about.gitlab.com/submit-feedback"
+ = link_to _("Submit feedback"), "https://about.gitlab.com/submit-feedback", data: {track_action: 'click_link', track_label: 'submit_feedback', track_property: 'navigation_top'}
- if current_user_menu?(:help) || current_user_menu?(:settings) || current_user_menu?(:profile)
%li
= render 'shared/user_dropdown_contributing_link'
= render 'shared/user_dropdown_instance_review'
- if Gitlab.com_but_not_canary?
%li
- = link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url
+ = link_to _("Switch to GitLab Next"), Gitlab::Saas.canary_toggle_com_url, data: {track_action: 'click_link', track_label: 'gitlab_next', track_property: 'navigation_top'}
diff --git a/app/views/layouts/header/_new_dropdown.html.haml b/app/views/layouts/header/_new_dropdown.html.haml
index f39fb53032d..a135ee8fed8 100644
--- a/app/views/layouts/header/_new_dropdown.html.haml
+++ b/app/views/layouts/header/_new_dropdown.html.haml
@@ -6,7 +6,7 @@
- return if menu_sections.empty?
-%li.header-new.gl-flex-grow-1.gl-flex-shrink-1.dropdown{ class: top_class, data: { track_label: "new_dropdown", track_action: "click_dropdown" } }
+%li.header-new.gl-flex-grow-1.gl-flex-shrink-1.dropdown{ class: top_class, data: { track_label: "new_dropdown", track_action: "click_dropdown", track_property: "navigation_top" } }
= link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip gl-display-flex", id: "js-onboarding-new-project-link", title: title, ref: 'tooltip', aria: { label: title }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static', qa_selector: 'new_menu_toggle' } do
= sprite_icon('plus-square')
= sprite_icon('chevron-down', css_class: 'caret-down')
diff --git a/app/views/shared/_help_dropdown_forum_link.html.haml b/app/views/shared/_help_dropdown_forum_link.html.haml
index 06889428e82..6d65f2e61bd 100644
--- a/app/views/shared/_help_dropdown_forum_link.html.haml
+++ b/app/views/shared/_help_dropdown_forum_link.html.haml
@@ -1,2 +1,2 @@
= link_to _("Community forum"), ApplicationHelper.community_forum, target: '_blank', class: 'text-nowrap',
- rel: 'noopener noreferrer', data: { 'track_action': 'click_forum', 'track_property': 'question_menu' }
+ rel: 'noopener noreferrer', data: { 'track_action': 'click_link', 'track_label': 'community_forum', 'track_property': 'navigation_top' }
diff --git a/app/views/shared/_user_dropdown_contributing_link.html.haml b/app/views/shared/_user_dropdown_contributing_link.html.haml
index d4c3e11d051..70d9db998fc 100644
--- a/app/views/shared/_user_dropdown_contributing_link.html.haml
+++ b/app/views/shared/_user_dropdown_contributing_link.html.haml
@@ -1,2 +1,2 @@
-= link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap' do
+= link_to "https://about.gitlab.com/contributing", target: '_blank', class: 'text-nowrap', data: {track_action: 'click_link', track_label: 'contribute_to_gitlab', track_property: 'navigation_top'} do
= _("Contribute to GitLab")
diff --git a/app/views/shared/_user_dropdown_instance_review.html.haml b/app/views/shared/_user_dropdown_instance_review.html.haml
index 18bfb5d7e3e..1a02f9958b0 100644
--- a/app/views/shared/_user_dropdown_instance_review.html.haml
+++ b/app/views/shared/_user_dropdown_instance_review.html.haml
@@ -2,5 +2,5 @@
%li.divider
%li
- = link_to admin_instance_review_path, target: '_blank', class: 'text-nowrap' do
+ = link_to admin_instance_review_path, target: '_blank', class: 'text-nowrap', data: {track_action: 'click_link', track_label: 'instance_review', track_property: 'navigation_top'} do
= _("Get a free instance review")