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:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-12-21 15:09:04 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-12-21 15:09:04 +0300
commitaac38cc2bd884cb979f1bbf7050c3f8692403cf3 (patch)
tree42c31b0dc05c63c11ac78171758d02fb9aa7cccf /app/assets/javascripts/issues
parent47a3dc65512c6eb3f88e6ba6842f58db3f03413c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/issues')
-rw-r--r--app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue116
-rw-r--r--app/assets/javascripts/issues/dashboard/index.js12
-rw-r--r--app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql12
-rw-r--r--app/assets/javascripts/issues/dashboard/utils.js23
4 files changed, 156 insertions, 7 deletions
diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
index b9d876ef72f..8edc9a08c9e 100644
--- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
+++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
@@ -30,20 +30,35 @@ import { __ } from '~/locale';
import {
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
+ TOKEN_TITLE_LABEL,
+ TOKEN_TITLE_MILESTONE,
+ TOKEN_TITLE_MY_REACTION,
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
+ TOKEN_TYPE_LABEL,
+ TOKEN_TYPE_MILESTONE,
+ TOKEN_TYPE_MY_REACTION,
} from '~/vue_shared/components/filtered_search_bar/constants';
import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
+import { AutocompleteCache } from '../utils';
const UserToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/user_token.vue');
+const EmojiToken = () =>
+ import('~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue');
+const LabelToken = () =>
+ import('~/vue_shared/components/filtered_search_bar/tokens/label_token.vue');
+const MilestoneToken = () =>
+ import('~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue');
export default {
i18n: {
calendarButtonText: __('Subscribe to calendar'),
closed: __('CLOSED'),
closedMoved: __('CLOSED (MOVED)'),
- emptyStateTitle: __('Please select at least one filter to see results'),
+ emptyStateWithFilterTitle: __('Sorry, your filter produced no results'),
+ emptyStateWithFilterDescription: __('To widen your search, change or remove filters above'),
+ emptyStateWithoutFilterTitle: __('Please select at least one filter to see results'),
errorFetchingIssues: __('An error occurred while loading issues'),
rssButtonText: __('Subscribe to RSS feed'),
searchInputPlaceholder: __('Search or filter results...'),
@@ -60,8 +75,12 @@ export default {
GlTooltip: GlTooltipDirective,
},
inject: [
+ 'autocompleteAwardEmojisPath',
'calendarPath',
- 'emptyStateSvgPath',
+ 'dashboardLabelsPath',
+ 'dashboardMilestonesPath',
+ 'emptyStateWithFilterSvgPath',
+ 'emptyStateWithoutFilterSvgPath',
'hasBlockedIssuesFeature',
'hasIssuableHealthStatusFeature',
'hasIssueWeightsFeature',
@@ -117,6 +136,9 @@ export default {
this.issuesError = this.$options.i18n.errorFetchingIssues;
Sentry.captureException(error);
},
+ skip() {
+ return !this.hasSearch;
+ },
debounce: 200,
},
},
@@ -124,6 +146,25 @@ export default {
apiFilterParams() {
return convertToApiParams(this.filterTokens);
},
+ emptyStateDescription() {
+ return this.hasSearch ? this.$options.i18n.emptyStateWithFilterDescription : undefined;
+ },
+ emptyStateSvgPath() {
+ return this.hasSearch
+ ? this.emptyStateWithFilterSvgPath
+ : this.emptyStateWithoutFilterSvgPath;
+ },
+ emptyStateTitle() {
+ return this.hasSearch
+ ? this.$options.i18n.emptyStateWithFilterTitle
+ : this.$options.i18n.emptyStateWithoutFilterTitle;
+ },
+ hasSearch() {
+ return Boolean(this.searchQuery || Object.keys(this.urlFilterParams).length);
+ },
+ renderedIssues() {
+ return this.hasSearch ? this.issues : [];
+ },
searchQuery() {
return convertToSearchQuery(this.filterTokens);
},
@@ -159,12 +200,46 @@ export default {
preloadedUsers,
recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-author',
},
+ {
+ type: TOKEN_TYPE_LABEL,
+ title: TOKEN_TITLE_LABEL,
+ icon: 'labels',
+ token: LabelToken,
+ fetchLabels: this.fetchLabels,
+ recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-label',
+ },
+ {
+ type: TOKEN_TYPE_MILESTONE,
+ title: TOKEN_TITLE_MILESTONE,
+ icon: 'clock',
+ token: MilestoneToken,
+ fetchMilestones: this.fetchMilestones,
+ recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-milestone',
+ shouldSkipSort: true,
+ },
];
+ if (this.isSignedIn) {
+ tokens.push({
+ type: TOKEN_TYPE_MY_REACTION,
+ title: TOKEN_TITLE_MY_REACTION,
+ icon: 'thumb-up',
+ token: EmojiToken,
+ unique: true,
+ fetchEmojis: this.fetchEmojis,
+ recentSuggestionsStorageKey: 'dashboard-issues-recent-tokens-my_reaction',
+ });
+ }
+
+ tokens.sort((a, b) => a.title.localeCompare(b.title));
+
return tokens;
},
showPaginationControls() {
- return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);
+ return (
+ this.renderedIssues.length > 0 &&
+ (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage)
+ );
},
sortOptions() {
return getSortOptions({
@@ -185,7 +260,34 @@ export default {
};
},
},
+ created() {
+ this.autocompleteCache = new AutocompleteCache();
+ },
methods: {
+ fetchEmojis(search) {
+ return this.autocompleteCache.fetch({
+ url: this.autocompleteAwardEmojisPath,
+ cacheName: 'emojis',
+ searchProperty: 'name',
+ search,
+ });
+ },
+ fetchLabels(search) {
+ return this.autocompleteCache.fetch({
+ url: this.dashboardLabelsPath,
+ cacheName: 'labels',
+ searchProperty: 'title',
+ search,
+ });
+ },
+ fetchMilestones(search) {
+ return this.autocompleteCache.fetch({
+ url: this.dashboardMilestonesPath,
+ cacheName: 'milestones',
+ searchProperty: 'title',
+ search,
+ });
+ },
fetchUsers(search) {
return axios.get('/-/autocomplete/users.json', { params: { active: true, search } });
},
@@ -266,7 +368,7 @@ export default {
:has-scoped-labels-feature="hasScopedLabelsFeature"
:initial-filter-value="filterTokens"
:initial-sort-by="sortKey"
- :issuables="issues"
+ :issuables="renderedIssues"
:issuables-loading="$apollo.queries.issues.loading"
namespace="dashboard"
recent-searches-storage-key="issues"
@@ -307,7 +409,11 @@ export default {
</template>
<template #empty-state>
- <gl-empty-state :svg-path="emptyStateSvgPath" :title="$options.i18n.emptyStateTitle" />
+ <gl-empty-state
+ :description="emptyStateDescription"
+ :svg-path="emptyStateSvgPath"
+ :title="emptyStateTitle"
+ />
</template>
</issuable-list>
</template>
diff --git a/app/assets/javascripts/issues/dashboard/index.js b/app/assets/javascripts/issues/dashboard/index.js
index e3e5cc614cb..005ab5ce3b0 100644
--- a/app/assets/javascripts/issues/dashboard/index.js
+++ b/app/assets/javascripts/issues/dashboard/index.js
@@ -14,8 +14,12 @@ export function mountIssuesDashboardApp() {
Vue.use(VueApollo);
const {
+ autocompleteAwardEmojisPath,
calendarPath,
- emptyStateSvgPath,
+ dashboardLabelsPath,
+ dashboardMilestonesPath,
+ emptyStateWithFilterSvgPath,
+ emptyStateWithoutFilterSvgPath,
hasBlockedIssuesFeature,
hasIssuableHealthStatusFeature,
hasIssueWeightsFeature,
@@ -33,8 +37,12 @@ export function mountIssuesDashboardApp() {
defaultClient: createDefaultClient(),
}),
provide: {
+ autocompleteAwardEmojisPath,
calendarPath,
- emptyStateSvgPath,
+ dashboardLabelsPath,
+ dashboardMilestonesPath,
+ emptyStateWithFilterSvgPath,
+ emptyStateWithoutFilterSvgPath,
hasBlockedIssuesFeature: parseBoolean(hasBlockedIssuesFeature),
hasIssuableHealthStatusFeature: parseBoolean(hasIssuableHealthStatusFeature),
hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature),
diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
index 8ffcb456755..43b8804108c 100644
--- a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
+++ b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
@@ -7,8 +7,14 @@ query getDashboardIssues(
$search: String
$sort: IssueSort
$state: IssuableState
+ $assigneeId: String
$assigneeUsernames: [String!]
$authorUsername: String
+ $labelName: [String]
+ $milestoneTitle: [String]
+ $milestoneWildcardId: MilestoneWildcardId
+ $myReactionEmoji: String
+ $not: NegatedIssueFilterInput
$afterCursor: String
$beforeCursor: String
$firstPageSize: Int
@@ -18,8 +24,14 @@ query getDashboardIssues(
search: $search
sort: $sort
state: $state
+ assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
authorUsername: $authorUsername
+ labelName: $labelName
+ milestoneTitle: $milestoneTitle
+ milestoneWildcardId: $milestoneWildcardId
+ myReactionEmoji: $myReactionEmoji
+ not: $not
after: $afterCursor
before: $beforeCursor
first: $firstPageSize
diff --git a/app/assets/javascripts/issues/dashboard/utils.js b/app/assets/javascripts/issues/dashboard/utils.js
new file mode 100644
index 00000000000..6fa95b38649
--- /dev/null
+++ b/app/assets/javascripts/issues/dashboard/utils.js
@@ -0,0 +1,23 @@
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import { MAX_LIST_SIZE } from '~/issues/list/constants';
+import axios from '~/lib/utils/axios_utils';
+
+export class AutocompleteCache {
+ constructor() {
+ this.cache = {};
+ }
+
+ fetch({ url, cacheName, searchProperty, search }) {
+ if (this.cache[cacheName]) {
+ const data = search
+ ? fuzzaldrinPlus.filter(this.cache[cacheName], search, { key: searchProperty })
+ : this.cache[cacheName].slice(0, MAX_LIST_SIZE);
+ return Promise.resolve(data);
+ }
+
+ return axios.get(url).then(({ data }) => {
+ this.cache[cacheName] = data;
+ return data.slice(0, MAX_LIST_SIZE);
+ });
+ }
+}