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:
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.vue622
1 files changed, 417 insertions, 205 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 57c5107fcbb..93ba338a6b3 100644
--- a/app/assets/javascripts/issues_list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue
@@ -1,56 +1,72 @@
<script>
-import { GlButton, GlEmptyState, GlIcon, GlLink, GlSprintf, GlTooltipDirective } from '@gitlab/ui';
+import {
+ GlButton,
+ GlEmptyState,
+ GlFilteredSearchToken,
+ GlIcon,
+ GlLink,
+ GlSprintf,
+ GlTooltipDirective,
+} from '@gitlab/ui';
+import fuzzaldrinPlus from 'fuzzaldrin-plus';
import { toNumber } from 'lodash';
import createFlash from '~/flash';
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 {
+ API_PARAM,
+ apiSortParams,
CREATED_DESC,
+ i18n,
+ MAX_LIST_SIZE,
PAGE_SIZE,
- RELATIVE_POSITION_ASC,
- sortOptions,
- sortParams,
+ PARAM_DUE_DATE,
+ PARAM_PAGE,
+ PARAM_SORT,
+ PARAM_STATE,
+ RELATIVE_POSITION_DESC,
+ UPDATED_DESC,
+ URL_PARAM,
+ urlSortParams,
} from '~/issues_list/constants';
+import {
+ convertToParams,
+ convertToSearchQuery,
+ getDueDateValue,
+ getFilterTokens,
+ getSortKey,
+ getSortOptions,
+} from '~/issues_list/utils';
import axios from '~/lib/utils/axios_utils';
import { convertObjectPropsToCamelCase, getParameterByName } from '~/lib/utils/common_utils';
-import { __, s__ } from '~/locale';
+import {
+ DEFAULT_NONE_ANY,
+ OPERATOR_IS_ONLY,
+ 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_WEIGHT,
+} from '~/vue_shared/components/filtered_search_bar/constants';
+import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue';
+import EpicToken from '~/vue_shared/components/filtered_search_bar/tokens/epic_token.vue';
+import IterationToken from '~/vue_shared/components/filtered_search_bar/tokens/iteration_token.vue';
+import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue';
+import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue';
+import WeightToken from '~/vue_shared/components/filtered_search_bar/tokens/weight_token.vue';
import eventHub from '../eventhub';
import IssueCardTimeInfo from './issue_card_time_info.vue';
export default {
- CREATED_DESC,
+ i18n,
IssuableListTabs,
- PAGE_SIZE,
- sortOptions,
- sortParams,
- i18n: {
- calendarLabel: __('Subscribe to calendar'),
- jiraIntegrationMessage: s__(
- 'JiraService|%{jiraDocsLinkStart}Enable the Jira integration%{jiraDocsLinkEnd} to view your Jira issues in GitLab.',
- ),
- jiraIntegrationSecondaryMessage: s__('JiraService|This feature requires a Premium plan.'),
- jiraIntegrationTitle: s__('JiraService|Using Jira for issue tracking?'),
- newIssueLabel: __('New issue'),
- noClosedIssuesTitle: __('There are no closed issues'),
- noOpenIssuesDescription: __('To keep this project going, create a new issue'),
- noOpenIssuesTitle: __('There are no open issues'),
- noIssuesSignedInDescription: __(
- 'Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable.',
- ),
- noIssuesSignedInTitle: __(
- 'The Issue Tracker is the place to add things that need to be improved or solved in a project',
- ),
- noIssuesSignedOutButtonText: __('Register / Sign In'),
- noIssuesSignedOutDescription: __(
- 'The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project.',
- ),
- noIssuesSignedOutTitle: __('There are no issues to show'),
- noSearchResultsDescription: __('To widen your search, change or remove filters above'),
- noSearchResultsTitle: __('Sorry, your filter produced no results'),
- reorderError: __('An error occurred while reordering issues.'),
- rssLabel: __('Subscribe to RSS feed'),
- },
components: {
CsvImportExportButtons,
GlButton,
@@ -58,6 +74,7 @@ export default {
GlIcon,
GlLink,
GlSprintf,
+ IssuableByEmail,
IssuableList,
IssueCardTimeInfo,
BlockingIssuesCount: () => import('ee_component/issues/components/blocking_issues_count.vue'),
@@ -66,6 +83,12 @@ export default {
GlTooltip: GlTooltipDirective,
},
inject: {
+ autocompleteAwardEmojisPath: {
+ default: '',
+ },
+ autocompleteUsersPath: {
+ default: '',
+ },
calendarPath: {
default: '',
},
@@ -81,12 +104,24 @@ export default {
exportCsvPath: {
default: '',
},
- fullPath: {
+ groupEpicsPath: {
default: '',
},
+ hasBlockedIssuesFeature: {
+ default: false,
+ },
hasIssues: {
default: false,
},
+ hasIssueWeightsFeature: {
+ default: false,
+ },
+ hasMultipleIssueAssigneesFeature: {
+ default: false,
+ },
+ initialEmail: {
+ default: '',
+ },
isSignedIn: {
default: false,
},
@@ -99,6 +134,18 @@ export default {
newIssuePath: {
default: '',
},
+ projectIterationsPath: {
+ default: '',
+ },
+ projectLabelsPath: {
+ default: '',
+ },
+ projectMilestonesPath: {
+ default: '',
+ },
+ projectPath: {
+ default: '',
+ },
rssPath: {
default: '',
},
@@ -110,51 +157,143 @@ export default {
},
},
data() {
- const orderBy = getParameterByName('order_by');
- const sort = getParameterByName('sort');
- const sortKey = Object.keys(sortParams).find(
- (key) => sortParams[key].order_by === orderBy && sortParams[key].sort === sort,
- );
-
- const search = getParameterByName('search') || '';
- const tokens = search.split(' ').map((searchWord) => ({
- type: 'filtered-search-term',
- value: {
- data: searchWord,
- },
- }));
+ const state = getParameterByName(PARAM_STATE);
+ const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC;
return {
+ dueDateFilter: getDueDateValue(getParameterByName(PARAM_DUE_DATE)),
exportCsvPathWithQuery: this.getExportCsvPathWithQuery(),
- filters: sortParams[sortKey] || {},
- filterTokens: tokens,
+ filterTokens: getFilterTokens(window.location.search),
isLoading: false,
issues: [],
- page: toNumber(getParameterByName('page')) || 1,
+ page: toNumber(getParameterByName(PARAM_PAGE)) || 1,
showBulkEditSidebar: false,
- sortKey: sortKey || CREATED_DESC,
- state: getParameterByName('state') || IssuableStates.Opened,
+ sortKey: getSortKey(getParameterByName(PARAM_SORT)) || defaultSortKey,
+ state: state || IssuableStates.Opened,
totalIssues: 0,
};
},
computed: {
+ isBulkEditButtonDisabled() {
+ return this.showBulkEditSidebar || !this.issues.length;
+ },
isManualOrdering() {
- return this.sortKey === RELATIVE_POSITION_ASC;
+ return this.sortKey === RELATIVE_POSITION_DESC;
},
isOpenTab() {
return this.state === IssuableStates.Opened;
},
+ apiFilterParams() {
+ return convertToParams(this.filterTokens, API_PARAM);
+ },
+ urlFilterParams() {
+ return convertToParams(this.filterTokens, URL_PARAM);
+ },
searchQuery() {
- return (
- this.filterTokens
- .map((searchTerm) => searchTerm.value.data)
- .filter((searchWord) => Boolean(searchWord))
- .join(' ') || undefined
- );
+ return convertToSearchQuery(this.filterTokens) || undefined;
+ },
+ searchTokens() {
+ const tokens = [
+ {
+ type: 'author_username',
+ title: TOKEN_TITLE_AUTHOR,
+ icon: 'pencil',
+ token: AuthorToken,
+ dataType: 'user',
+ unique: true,
+ defaultAuthors: [],
+ fetchAuthors: this.fetchUsers,
+ },
+ {
+ type: 'assignee_username',
+ title: TOKEN_TITLE_ASSIGNEE,
+ icon: 'user',
+ token: AuthorToken,
+ dataType: 'user',
+ unique: !this.hasMultipleIssueAssigneesFeature,
+ defaultAuthors: DEFAULT_NONE_ANY,
+ fetchAuthors: this.fetchUsers,
+ },
+ {
+ type: 'milestone',
+ title: TOKEN_TITLE_MILESTONE,
+ icon: 'clock',
+ token: MilestoneToken,
+ unique: true,
+ defaultMilestones: [],
+ fetchMilestones: this.fetchMilestones,
+ },
+ {
+ type: 'labels',
+ title: TOKEN_TITLE_LABEL,
+ icon: 'labels',
+ token: LabelToken,
+ defaultLabels: [],
+ fetchLabels: this.fetchLabels,
+ },
+ {
+ type: 'my_reaction_emoji',
+ title: TOKEN_TITLE_MY_REACTION,
+ icon: 'thumb-up',
+ token: EmojiToken,
+ unique: true,
+ operators: OPERATOR_IS_ONLY,
+ fetchEmojis: this.fetchEmojis,
+ },
+ {
+ type: 'confidential',
+ title: TOKEN_TITLE_CONFIDENTIAL,
+ icon: 'eye-slash',
+ token: GlFilteredSearchToken,
+ unique: true,
+ operators: OPERATOR_IS_ONLY,
+ options: [
+ { icon: 'eye-slash', value: 'yes', title: this.$options.i18n.confidentialYes },
+ { icon: 'eye', value: 'no', title: this.$options.i18n.confidentialNo },
+ ],
+ },
+ ];
+
+ if (this.projectIterationsPath) {
+ tokens.push({
+ type: 'iteration',
+ title: TOKEN_TITLE_ITERATION,
+ icon: 'iteration',
+ token: IterationToken,
+ unique: true,
+ fetchIterations: this.fetchIterations,
+ });
+ }
+
+ if (this.groupEpicsPath) {
+ tokens.push({
+ type: 'epic_id',
+ title: TOKEN_TITLE_EPIC,
+ icon: 'epic',
+ token: EpicToken,
+ unique: true,
+ fetchEpics: this.fetchEpics,
+ });
+ }
+
+ if (this.hasIssueWeightsFeature) {
+ tokens.push({
+ type: 'weight',
+ title: TOKEN_TITLE_WEIGHT,
+ icon: 'weight',
+ token: WeightToken,
+ unique: true,
+ });
+ }
+
+ return tokens;
},
showPaginationControls() {
return this.issues.length > 0;
},
+ sortOptions() {
+ return getSortOptions(this.hasIssueWeightsFeature, this.hasBlockedIssuesFeature);
+ },
tabCounts() {
return Object.values(IssuableStates).reduce(
(acc, state) => ({
@@ -166,24 +305,65 @@ export default {
},
urlParams() {
return {
+ due_date: this.dueDateFilter,
page: this.page,
search: this.searchQuery,
state: this.state,
- ...this.filters,
+ ...urlSortParams[this.sortKey],
+ ...this.urlFilterParams,
};
},
},
+ created() {
+ this.cache = {};
+ },
mounted() {
- eventHub.$on('issuables:toggleBulkEdit', (showBulkEditSidebar) => {
- this.showBulkEditSidebar = showBulkEditSidebar;
- });
+ eventHub.$on('issuables:toggleBulkEdit', this.toggleBulkEditSidebar);
this.fetchIssues();
},
beforeDestroy() {
- // eslint-disable-next-line @gitlab/no-global-event-off
- eventHub.$off('issuables:toggleBulkEdit');
+ eventHub.$off('issuables:toggleBulkEdit', this.toggleBulkEditSidebar);
},
methods: {
+ fetchWithCache(path, cacheName, searchKey, search, wrapData = false) {
+ if (this.cache[cacheName]) {
+ const data = search
+ ? fuzzaldrinPlus.filter(this.cache[cacheName], search, { key: searchKey })
+ : this.cache[cacheName].slice(0, MAX_LIST_SIZE);
+ return wrapData ? Promise.resolve({ data }) : Promise.resolve(data);
+ }
+
+ return axios.get(path).then(({ data }) => {
+ this.cache[cacheName] = data;
+ const result = data.slice(0, MAX_LIST_SIZE);
+ return wrapData ? { data: result } : result;
+ });
+ },
+ fetchEmojis(search) {
+ return this.fetchWithCache(this.autocompleteAwardEmojisPath, 'emojis', 'name', search);
+ },
+ async fetchEpics(search) {
+ const epics = await this.fetchWithCache(this.groupEpicsPath, 'epics');
+ if (!search) {
+ return epics.slice(0, MAX_LIST_SIZE);
+ }
+ const number = Number(search);
+ return Number.isNaN(number)
+ ? fuzzaldrinPlus.filter(epics, search, { key: 'title' })
+ : epics.filter((epic) => epic.id === number);
+ },
+ fetchLabels(search) {
+ return this.fetchWithCache(this.projectLabelsPath, 'labels', 'title', search);
+ },
+ fetchMilestones(search) {
+ return this.fetchWithCache(this.projectMilestonesPath, 'milestones', 'title', search, true);
+ },
+ fetchIterations(search) {
+ return axios.get(this.projectIterationsPath, { params: { search } });
+ },
+ fetchUsers(search) {
+ return axios.get(this.autocompleteUsersPath, { params: { search } });
+ },
fetchIssues() {
if (!this.hasIssues) {
return undefined;
@@ -194,12 +374,14 @@ export default {
return axios
.get(this.endpoint, {
params: {
+ due_date: this.dueDateFilter,
page: this.page,
- per_page: this.$options.PAGE_SIZE,
+ per_page: PAGE_SIZE,
search: this.searchQuery,
state: this.state,
with_labels_details: true,
- ...this.filters,
+ ...apiSortParams[this.sortKey],
+ ...this.apiFilterParams,
},
})
.then(({ data, headers }) => {
@@ -209,7 +391,7 @@ export default {
this.exportCsvPathWithQuery = this.getExportCsvPathWithQuery();
})
.catch(() => {
- createFlash({ message: __('An error occurred while loading issues') });
+ createFlash({ message: this.$options.i18n.errorFetchingIssues });
})
.finally(() => {
this.isLoading = false;
@@ -218,6 +400,15 @@ export default {
getExportCsvPathWithQuery() {
return `${this.exportCsvPath}${window.location.search}`;
},
+ getStatus(issue) {
+ if (issue.closedAt && issue.movedToId) {
+ return this.$options.i18n.closedMoved;
+ }
+ if (issue.closedAt) {
+ return this.$options.i18n.closed;
+ }
+ return undefined;
+ },
handleUpdateLegacyBulkEdit() {
// If "select all" checkbox was checked, wait for all checkboxes
// to be checked before updating IssuableBulkUpdateSidebar class
@@ -225,7 +416,18 @@ export default {
eventHub.$emit('issuables:updateBulkEdit');
});
},
- handleBulkUpdateClick() {
+ async handleBulkUpdateClick() {
+ if (!this.hasInitBulkEdit) {
+ const initBulkUpdateSidebar = await import('~/issuable_init_bulk_update_sidebar');
+ initBulkUpdateSidebar.default.init('issuable_');
+
+ const usersSelect = await import('~/users_select');
+ const UsersSelect = usersSelect.default;
+ new UsersSelect(); // eslint-disable-line no-new
+
+ this.hasInitBulkEdit = true;
+ }
+
eventHub.$emit('issuables:enableBulkEdit');
},
handleClickTab(state) {
@@ -278,151 +480,161 @@ export default {
},
handleSort(value) {
this.sortKey = value;
- this.filters = sortParams[value];
this.fetchIssues();
},
+ toggleBulkEditSidebar(showBulkEditSidebar) {
+ this.showBulkEditSidebar = showBulkEditSidebar;
+ },
},
};
</script>
<template>
- <issuable-list
- v-if="hasIssues"
- :namespace="fullPath"
- recent-searches-storage-key="issues"
- :search-input-placeholder="__('Search or filter results…')"
- :search-tokens="[]"
- :initial-filter-value="filterTokens"
- :sort-options="$options.sortOptions"
- :initial-sort-by="sortKey"
- :issuables="issues"
- :tabs="$options.IssuableListTabs"
- :current-tab="state"
- :tab-counts="tabCounts"
- :issuables-loading="isLoading"
- :is-manual-ordering="isManualOrdering"
- :show-bulk-edit-sidebar="showBulkEditSidebar"
- :show-pagination-controls="showPaginationControls"
- :total-items="totalIssues"
- :current-page="page"
- :previous-page="page - 1"
- :next-page="page + 1"
- :url-params="urlParams"
- @click-tab="handleClickTab"
- @filter="handleFilter"
- @page-change="handlePageChange"
- @reorder="handleReorder"
- @sort="handleSort"
- @update-legacy-bulk-edit="handleUpdateLegacyBulkEdit"
- >
- <template #nav-actions>
- <gl-button
- v-gl-tooltip
- :href="rssPath"
- icon="rss"
- :title="$options.i18n.rssLabel"
- :aria-label="$options.i18n.rssLabel"
- />
- <gl-button
- v-gl-tooltip
- :href="calendarPath"
- icon="calendar"
- :title="$options.i18n.calendarLabel"
- :aria-label="$options.i18n.calendarLabel"
- />
- <csv-import-export-buttons
- class="gl-mr-3"
- :export-csv-path="exportCsvPathWithQuery"
- :issuable-count="totalIssues"
- />
- <gl-button
- v-if="canBulkUpdate"
- :disabled="showBulkEditSidebar"
- @click="handleBulkUpdateClick"
- >
- {{ __('Edit issues') }}
- </gl-button>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
- {{ $options.i18n.newIssueLabel }}
- </gl-button>
- </template>
+ <div v-if="hasIssues">
+ <issuable-list
+ :namespace="projectPath"
+ recent-searches-storage-key="issues"
+ :search-input-placeholder="$options.i18n.searchPlaceholder"
+ :search-tokens="searchTokens"
+ :initial-filter-value="filterTokens"
+ :sort-options="sortOptions"
+ :initial-sort-by="sortKey"
+ :issuables="issues"
+ :tabs="$options.IssuableListTabs"
+ :current-tab="state"
+ :tab-counts="tabCounts"
+ :issuables-loading="isLoading"
+ :is-manual-ordering="isManualOrdering"
+ :show-bulk-edit-sidebar="showBulkEditSidebar"
+ :show-pagination-controls="showPaginationControls"
+ :total-items="totalIssues"
+ :current-page="page"
+ :previous-page="page - 1"
+ :next-page="page + 1"
+ :url-params="urlParams"
+ @click-tab="handleClickTab"
+ @filter="handleFilter"
+ @page-change="handlePageChange"
+ @reorder="handleReorder"
+ @sort="handleSort"
+ @update-legacy-bulk-edit="handleUpdateLegacyBulkEdit"
+ >
+ <template #nav-actions>
+ <gl-button
+ v-gl-tooltip
+ :href="rssPath"
+ icon="rss"
+ :title="$options.i18n.rssLabel"
+ :aria-label="$options.i18n.rssLabel"
+ />
+ <gl-button
+ v-gl-tooltip
+ :href="calendarPath"
+ icon="calendar"
+ :title="$options.i18n.calendarLabel"
+ :aria-label="$options.i18n.calendarLabel"
+ />
+ <csv-import-export-buttons
+ v-if="isSignedIn"
+ class="gl-mr-3"
+ :export-csv-path="exportCsvPathWithQuery"
+ :issuable-count="totalIssues"
+ />
+ <gl-button
+ v-if="canBulkUpdate"
+ :disabled="isBulkEditButtonDisabled"
+ @click="handleBulkUpdateClick"
+ >
+ {{ $options.i18n.editIssues }}
+ </gl-button>
+ <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ {{ $options.i18n.newIssueLabel }}
+ </gl-button>
+ </template>
- <template #timeframe="{ issuable = {} }">
- <issue-card-time-info :issue="issuable" />
- </template>
+ <template #timeframe="{ issuable = {} }">
+ <issue-card-time-info :issue="issuable" />
+ </template>
- <template #statistics="{ issuable = {} }">
- <li
- v-if="issuable.mergeRequestsCount"
- v-gl-tooltip
- class="gl-display-none gl-sm-display-block"
- :title="__('Related merge requests')"
- data-testid="issuable-mr"
- >
- <gl-icon name="merge-request" />
- {{ issuable.mergeRequestsCount }}
- </li>
- <li
- v-if="issuable.upvotes"
- v-gl-tooltip
- class="gl-display-none gl-sm-display-block"
- :title="__('Upvotes')"
- data-testid="issuable-upvotes"
- >
- <gl-icon name="thumb-up" />
- {{ issuable.upvotes }}
- </li>
- <li
- v-if="issuable.downvotes"
- v-gl-tooltip
- class="gl-display-none gl-sm-display-block"
- :title="__('Downvotes')"
- data-testid="issuable-downvotes"
- >
- <gl-icon name="thumb-down" />
- {{ issuable.downvotes }}
- </li>
- <blocking-issues-count
- class="gl-display-none gl-sm-display-block"
- :blocking-issues-count="issuable.blockingIssuesCount"
- :is-list-item="true"
- />
- </template>
+ <template #status="{ issuable = {} }">
+ {{ getStatus(issuable) }}
+ </template>
- <template #empty-state>
- <gl-empty-state
- v-if="searchQuery"
- :description="$options.i18n.noSearchResultsDescription"
- :title="$options.i18n.noSearchResultsTitle"
- :svg-path="emptyStateSvgPath"
- >
- <template #actions>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
- {{ $options.i18n.newIssueLabel }}
- </gl-button>
- </template>
- </gl-empty-state>
+ <template #statistics="{ issuable = {} }">
+ <li
+ v-if="issuable.mergeRequestsCount"
+ v-gl-tooltip
+ class="gl-display-none gl-sm-display-block"
+ :title="$options.i18n.relatedMergeRequests"
+ data-testid="issuable-mr"
+ >
+ <gl-icon name="merge-request" />
+ {{ issuable.mergeRequestsCount }}
+ </li>
+ <li
+ v-if="issuable.upvotes"
+ v-gl-tooltip
+ class="gl-display-none gl-sm-display-block"
+ :title="$options.i18n.upvotes"
+ data-testid="issuable-upvotes"
+ >
+ <gl-icon name="thumb-up" />
+ {{ issuable.upvotes }}
+ </li>
+ <li
+ v-if="issuable.downvotes"
+ v-gl-tooltip
+ class="gl-display-none gl-sm-display-block"
+ :title="$options.i18n.downvotes"
+ data-testid="issuable-downvotes"
+ >
+ <gl-icon name="thumb-down" />
+ {{ issuable.downvotes }}
+ </li>
+ <blocking-issues-count
+ class="gl-display-none gl-sm-display-block"
+ :blocking-issues-count="issuable.blockingIssuesCount"
+ :is-list-item="true"
+ />
+ </template>
- <gl-empty-state
- v-else-if="isOpenTab"
- :description="$options.i18n.noOpenIssuesDescription"
- :title="$options.i18n.noOpenIssuesTitle"
- :svg-path="emptyStateSvgPath"
- >
- <template #actions>
- <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
- {{ $options.i18n.newIssueLabel }}
- </gl-button>
- </template>
- </gl-empty-state>
+ <template #empty-state>
+ <gl-empty-state
+ v-if="searchQuery"
+ :description="$options.i18n.noSearchResultsDescription"
+ :title="$options.i18n.noSearchResultsTitle"
+ :svg-path="emptyStateSvgPath"
+ >
+ <template #actions>
+ <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ {{ $options.i18n.newIssueLabel }}
+ </gl-button>
+ </template>
+ </gl-empty-state>
- <gl-empty-state
- v-else
- :title="$options.i18n.noClosedIssuesTitle"
- :svg-path="emptyStateSvgPath"
- />
- </template>
- </issuable-list>
+ <gl-empty-state
+ v-else-if="isOpenTab"
+ :description="$options.i18n.noOpenIssuesDescription"
+ :title="$options.i18n.noOpenIssuesTitle"
+ :svg-path="emptyStateSvgPath"
+ >
+ <template #actions>
+ <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
+ {{ $options.i18n.newIssueLabel }}
+ </gl-button>
+ </template>
+ </gl-empty-state>
+
+ <gl-empty-state
+ v-else
+ :title="$options.i18n.noClosedIssuesTitle"
+ :svg-path="emptyStateSvgPath"
+ />
+ </template>
+ </issuable-list>
+
+ <issuable-by-email v-if="initialEmail" class="gl-text-center gl-pt-5 gl-pb-7" />
+ </div>
<div v-else-if="isSignedIn">
<gl-empty-state