summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/issues_list/components/issues_list_app.vue
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/issues_list/components/issues_list_app.vue')
-rw-r--r--app/assets/javascripts/issues_list/components/issues_list_app.vue171
1 files changed, 87 insertions, 84 deletions
diff --git a/app/assets/javascripts/issues_list/components/issues_list_app.vue b/app/assets/javascripts/issues_list/components/issues_list_app.vue
index 7f2082e5b90..6ced1080b71 100644
--- a/app/assets/javascripts/issues_list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue
@@ -8,17 +8,20 @@ import {
GlSprintf,
GlTooltipDirective,
} from '@gitlab/ui';
+import * as Sentry from '@sentry/browser';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
+import { orderBy } from 'lodash';
import getIssuesQuery from 'ee_else_ce/issues_list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues_list/queries/get_issues_counts.query.graphql';
-import createFlash from '~/flash';
+import IssueCardTimeInfo from 'ee_else_ce/issues_list/components/issue_card_time_info.vue';
+import createFlash, { FLASH_TYPES } from '~/flash';
import { TYPE_USER } from '~/graphql_shared/constants';
import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { ITEM_TYPE } from '~/groups/constants';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import IssuableByEmail from '~/issuable/components/issuable_by_email.vue';
-import IssuableList from '~/issuable_list/components/issuable_list_root.vue';
-import { IssuableListTabs, IssuableStates } from '~/issuable_list/constants';
+import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_root.vue';
+import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/constants';
import {
CREATED_DESC,
i18n,
@@ -31,14 +34,11 @@ import {
TOKEN_TYPE_ASSIGNEE,
TOKEN_TYPE_AUTHOR,
TOKEN_TYPE_CONFIDENTIAL,
- TOKEN_TYPE_EPIC,
- TOKEN_TYPE_ITERATION,
TOKEN_TYPE_LABEL,
TOKEN_TYPE_MILESTONE,
TOKEN_TYPE_MY_REACTION,
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_TYPE,
- TOKEN_TYPE_WEIGHT,
UPDATED_DESC,
urlSortParams,
} from '~/issues_list/constants';
@@ -61,39 +61,29 @@ import {
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
TOKEN_TITLE_CONFIDENTIAL,
- TOKEN_TITLE_EPIC,
- TOKEN_TITLE_ITERATION,
TOKEN_TITLE_LABEL,
TOKEN_TITLE_MILESTONE,
TOKEN_TITLE_MY_REACTION,
TOKEN_TITLE_RELEASE,
TOKEN_TITLE_TYPE,
- TOKEN_TITLE_WEIGHT,
} from '~/vue_shared/components/filtered_search_bar/constants';
import eventHub from '../eventhub';
import reorderIssuesMutation from '../queries/reorder_issues.mutation.graphql';
-import searchIterationsQuery from '../queries/search_iterations.query.graphql';
import searchLabelsQuery from '../queries/search_labels.query.graphql';
import searchMilestonesQuery from '../queries/search_milestones.query.graphql';
import searchUsersQuery from '../queries/search_users.query.graphql';
-import IssueCardTimeInfo from './issue_card_time_info.vue';
import NewIssueDropdown from './new_issue_dropdown.vue';
const AuthorToken = () =>
import('~/vue_shared/components/filtered_search_bar/tokens/author_token.vue');
const EmojiToken = () =>
import('~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue');
-const EpicToken = () => import('~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue');
-const IterationToken = () =>
- import('~/vue_shared/components/filtered_search_bar/tokens/iteration_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');
const ReleaseToken = () =>
import('~/vue_shared/components/filtered_search_bar/tokens/release_token.vue');
-const WeightToken = () =>
- import('~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue');
export default {
i18n,
@@ -109,7 +99,6 @@ export default {
IssuableList,
IssueCardTimeInfo,
NewIssueDropdown,
- BlockingIssuesCount: () => import('ee_component/issues/components/blocking_issues_count.vue'),
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -133,9 +122,6 @@ export default {
fullPath: {
default: '',
},
- groupPath: {
- default: '',
- },
hasAnyIssues: {
default: false,
},
@@ -148,15 +134,18 @@ export default {
hasIssueWeightsFeature: {
default: false,
},
- hasIterationsFeature: {
- default: false,
- },
hasMultipleIssueAssigneesFeature: {
default: false,
},
initialEmail: {
default: '',
},
+ isAnonymousSearchDisabled: {
+ default: false,
+ },
+ isIssueRepositioningDisabled: {
+ default: false,
+ },
isProject: {
default: false,
},
@@ -182,21 +171,43 @@ export default {
default: '',
},
},
+ props: {
+ eeSearchTokens: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
data() {
const state = getParameterByName(PARAM_STATE);
- const sortKey = getSortKey(getParameterByName(PARAM_SORT));
const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC;
+ let sortKey = getSortKey(getParameterByName(PARAM_SORT)) || defaultSortKey;
+
+ if (this.isIssueRepositioningDisabled && sortKey === RELATIVE_POSITION_ASC) {
+ this.showIssueRepositioningMessage();
+ sortKey = defaultSortKey;
+ }
+
+ const isSearchDisabled =
+ this.isAnonymousSearchDisabled &&
+ !this.isSignedIn &&
+ window.location.search.includes('search=');
+
+ if (isSearchDisabled) {
+ this.showAnonymousSearchingMessage();
+ }
return {
dueDateFilter: getDueDateValue(getParameterByName(PARAM_DUE_DATE)),
exportCsvPathWithQuery: this.getExportCsvPathWithQuery(),
- filterTokens: getFilterTokens(window.location.search),
+ filterTokens: isSearchDisabled ? [] : getFilterTokens(window.location.search),
issues: [],
issuesCounts: {},
+ issuesError: null,
pageInfo: {},
pageParams: getInitialPageParams(sortKey),
showBulkEditSidebar: false,
- sortKey: sortKey || defaultSortKey,
+ sortKey,
state: state || IssuableStates.Opened,
};
},
@@ -214,7 +225,8 @@ export default {
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
},
error(error) {
- createFlash({ message: this.$options.i18n.errorFetchingIssues, captureError: true, error });
+ this.issuesError = this.$options.i18n.errorFetchingIssues;
+ Sentry.captureException(error);
},
skip() {
return !this.hasAnyIssues;
@@ -230,7 +242,8 @@ export default {
return data[this.namespace] ?? {};
},
error(error) {
- createFlash({ message: this.$options.i18n.errorFetchingCounts, captureError: true, error });
+ this.issuesError = this.$options.i18n.errorFetchingCounts;
+ Sentry.captureException(error);
},
skip() {
return !this.hasAnyIssues;
@@ -306,6 +319,7 @@ export default {
unique: true,
defaultAuthors: [],
fetchAuthors: this.fetchUsers,
+ recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-author`,
preloadedAuthors,
},
{
@@ -317,6 +331,7 @@ export default {
unique: !this.hasMultipleIssueAssigneesFeature,
defaultAuthors: DEFAULT_NONE_ANY,
fetchAuthors: this.fetchUsers,
+ recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-assignee`,
preloadedAuthors,
},
{
@@ -325,6 +340,7 @@ export default {
icon: 'clock',
token: MilestoneToken,
fetchMilestones: this.fetchMilestones,
+ recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-milestone`,
},
{
type: TOKEN_TYPE_LABEL,
@@ -333,6 +349,7 @@ export default {
token: LabelToken,
defaultLabels: DEFAULT_NONE_ANY,
fetchLabels: this.fetchLabels,
+ recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-label`,
},
{
type: TOKEN_TYPE_TYPE,
@@ -354,6 +371,7 @@ export default {
icon: 'rocket',
token: ReleaseToken,
fetchReleases: this.fetchReleases,
+ recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-release`,
});
}
@@ -365,6 +383,7 @@ export default {
token: EmojiToken,
unique: true,
fetchEmojis: this.fetchEmojis,
+ recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-my_reaction`,
});
tokens.push({
@@ -381,42 +400,13 @@ export default {
});
}
- if (this.hasIterationsFeature) {
- tokens.push({
- type: TOKEN_TYPE_ITERATION,
- title: TOKEN_TITLE_ITERATION,
- icon: 'iteration',
- token: IterationToken,
- fetchIterations: this.fetchIterations,
- });
+ if (this.eeSearchTokens.length) {
+ tokens.push(...this.eeSearchTokens);
}
- if (this.groupPath) {
- tokens.push({
- type: TOKEN_TYPE_EPIC,
- title: TOKEN_TITLE_EPIC,
- icon: 'epic',
- token: EpicToken,
- unique: true,
- symbol: '&',
- idProperty: 'id',
- useIdValue: true,
- recentSuggestionsStorageKey: `${this.fullPath}-issues-recent-tokens-epic_id`,
- fullPath: this.groupPath,
- });
- }
+ tokens.sort((a, b) => a.title.localeCompare(b.title));
- if (this.hasIssueWeightsFeature) {
- tokens.push({
- type: TOKEN_TYPE_WEIGHT,
- title: TOKEN_TITLE_WEIGHT,
- icon: 'weight',
- token: WeightToken,
- unique: true,
- });
- }
-
- return tokens;
+ return orderBy(tokens, ['title']);
},
showPaginationControls() {
return this.issues.length > 0 && (this.pageInfo.hasNextPage || this.pageInfo.hasPreviousPage);
@@ -481,7 +471,12 @@ export default {
query: searchLabelsQuery,
variables: { fullPath: this.fullPath, search, isProject: this.isProject },
})
- .then(({ data }) => data[this.namespace]?.labels.nodes);
+ .then(({ data }) => data[this.namespace]?.labels.nodes)
+ .then((labels) =>
+ // TODO remove once we can search by title-only on the backend
+ // https://gitlab.com/gitlab-org/gitlab/-/issues/346353
+ labels.filter((label) => label.title.toLowerCase().includes(search.toLowerCase())),
+ );
},
fetchMilestones(search) {
return this.$apollo
@@ -491,20 +486,6 @@ export default {
})
.then(({ data }) => data[this.namespace]?.milestones.nodes);
},
- fetchIterations(search) {
- const id = Number(search);
- const variables =
- !search || Number.isNaN(id)
- ? { fullPath: this.fullPath, search, isProject: this.isProject }
- : { fullPath: this.fullPath, id, isProject: this.isProject };
-
- return this.$apollo
- .query({
- query: searchIterationsQuery,
- variables,
- })
- .then(({ data }) => data[this.namespace]?.iterations.nodes);
- },
fetchUsers(search) {
return this.$apollo
.query({
@@ -537,7 +518,7 @@ export default {
async handleBulkUpdateClick() {
if (!this.hasInitBulkEdit) {
const initBulkUpdateSidebar = await import(
- '~/issuable_bulk_update_sidebar/issuable_init_bulk_update_sidebar'
+ '~/issuable/bulk_update_sidebar/issuable_init_bulk_update_sidebar'
);
initBulkUpdateSidebar.default.init('issuable_');
@@ -556,7 +537,14 @@ export default {
}
this.state = state;
},
+ handleDismissAlert() {
+ this.issuesError = null;
+ },
handleFilter(filter) {
+ if (this.isAnonymousSearchDisabled && !this.isSignedIn) {
+ this.showAnonymousSearchingMessage();
+ return;
+ }
this.pageParams = getInitialPageParams(this.sortKey);
this.filterTokens = filter;
},
@@ -607,15 +595,33 @@ export default {
});
})
.catch((error) => {
- createFlash({ message: this.$options.i18n.reorderError, captureError: true, error });
+ this.issuesError = this.$options.i18n.reorderError;
+ Sentry.captureException(error);
});
},
handleSort(sortKey) {
+ if (this.isIssueRepositioningDisabled && sortKey === RELATIVE_POSITION_ASC) {
+ this.showIssueRepositioningMessage();
+ return;
+ }
+
if (this.sortKey !== sortKey) {
this.pageParams = getInitialPageParams(sortKey);
}
this.sortKey = sortKey;
},
+ showAnonymousSearchingMessage() {
+ createFlash({
+ message: this.$options.i18n.anonymousSearchingMessage,
+ type: FLASH_TYPES.NOTICE,
+ });
+ },
+ showIssueRepositioningMessage() {
+ createFlash({
+ message: this.$options.i18n.issueRepositioningMessage,
+ type: FLASH_TYPES.NOTICE,
+ });
+ },
toggleBulkEditSidebar(showBulkEditSidebar) {
this.showBulkEditSidebar = showBulkEditSidebar;
},
@@ -634,6 +640,7 @@ export default {
:sort-options="sortOptions"
:initial-sort-by="sortKey"
:issuables="issues"
+ :error="issuesError"
label-filter-param="label_name"
:tabs="$options.IssuableListTabs"
:current-tab="state"
@@ -647,6 +654,7 @@ export default {
:has-previous-page="pageInfo.hasPreviousPage"
:url-params="urlParams"
@click-tab="handleClickTab"
+ @dismiss-alert="handleDismissAlert"
@filter="handleFilter"
@next-page="handleNextPage"
@previous-page="handlePreviousPage"
@@ -727,12 +735,7 @@ export default {
<gl-icon name="thumb-down" />
{{ issuable.downvotes }}
</li>
- <blocking-issues-count
- class="blocking-issues gl-display-none gl-sm-display-block"
- :blocking-issues-count="issuable.blockingCount"
- :is-list-item="true"
- data-testid="blocking-issues"
- />
+ <slot :issuable="issuable"></slot>
</template>
<template #empty-state>