diff options
Diffstat (limited to 'app/assets/javascripts/issues_list')
11 files changed, 125 insertions, 25 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 7b51f6ee46a..7f2082e5b90 100644 --- a/app/assets/javascripts/issues_list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues_list/components/issues_list_app.vue @@ -36,6 +36,7 @@ import { TOKEN_TYPE_LABEL, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_MY_REACTION, + TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, TOKEN_TYPE_WEIGHT, UPDATED_DESC, @@ -65,6 +66,7 @@ import { 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'; @@ -88,6 +90,8 @@ 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'); @@ -165,6 +169,9 @@ export default { newIssuePath: { default: '', }, + releasesPath: { + default: '', + }, rssPath: { default: '', }, @@ -288,6 +295,7 @@ export default { avatar_url: gon.current_user_avatar_url, }); } + const tokens = [ { type: TOKEN_TYPE_AUTHOR, @@ -297,7 +305,6 @@ export default { dataType: 'user', unique: true, defaultAuthors: [], - operators: OPERATOR_IS_ONLY, fetchAuthors: this.fetchUsers, preloadedAuthors, }, @@ -317,7 +324,6 @@ export default { title: TOKEN_TITLE_MILESTONE, icon: 'clock', token: MilestoneToken, - unique: true, fetchMilestones: this.fetchMilestones, }, { @@ -333,7 +339,6 @@ export default { title: TOKEN_TITLE_TYPE, icon: 'issues', token: GlFilteredSearchToken, - operators: OPERATOR_IS_ONLY, options: [ { icon: 'issue-type-issue', title: 'issue', value: 'issue' }, { icon: 'issue-type-incident', title: 'incident', value: 'incident' }, @@ -342,6 +347,16 @@ export default { }, ]; + if (this.isProject) { + tokens.push({ + type: TOKEN_TYPE_RELEASE, + title: TOKEN_TITLE_RELEASE, + icon: 'rocket', + token: ReleaseToken, + fetchReleases: this.fetchReleases, + }); + } + if (this.isSignedIn) { tokens.push({ type: TOKEN_TYPE_MY_REACTION, @@ -349,7 +364,6 @@ export default { icon: 'thumb-up', token: EmojiToken, unique: true, - operators: OPERATOR_IS_ONLY, fetchEmojis: this.fetchEmojis, }); @@ -373,7 +387,6 @@ export default { title: TOKEN_TITLE_ITERATION, icon: 'iteration', token: IterationToken, - unique: true, fetchIterations: this.fetchIterations, }); } @@ -459,6 +472,9 @@ export default { fetchEmojis(search) { return this.fetchWithCache(this.autocompleteAwardEmojisPath, 'emojis', 'name', search); }, + fetchReleases(search) { + return this.fetchWithCache(this.releasesPath, 'releases', 'tag', search); + }, fetchLabels(search) { return this.$apollo .query({ diff --git a/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue b/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue index 037fd9be542..e749579af80 100644 --- a/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue +++ b/app/assets/javascripts/issues_list/components/new_issue_dropdown.vue @@ -71,8 +71,11 @@ export default { hasSelectedProject() { return this.selectedProject.id; }, + projectsWithIssuesEnabled() { + return this.projects.filter((project) => project.issuesEnabled); + }, showNoSearchResultsText() { - return !this.projects.length && this.search; + return !this.projectsWithIssuesEnabled.length && this.search; }, }, methods: { @@ -110,7 +113,7 @@ export default { <gl-loading-icon v-if="$apollo.queries.projects.loading" /> <template v-else> <gl-dropdown-item - v-for="project of projects" + v-for="project of projectsWithIssuesEnabled" :key="project.id" @click="selectProject(project)" > diff --git a/app/assets/javascripts/issues_list/constants.js b/app/assets/javascripts/issues_list/constants.js index 5bdc1bd9f90..da9b96d0e22 100644 --- a/app/assets/javascripts/issues_list/constants.js +++ b/app/assets/javascripts/issues_list/constants.js @@ -166,6 +166,7 @@ const LABEL_PRIORITY_ASC_SORT = 'label_priority_asc'; const POPULARITY_ASC_SORT = 'popularity_asc'; const WEIGHT_DESC_SORT = 'weight_desc'; const BLOCKING_ISSUES_DESC_SORT = 'blocking_issues_desc'; +const TITLE_ASC_SORT = 'title_asc'; const TITLE_DESC_SORT = 'title_desc'; export const urlSortParams = { @@ -187,7 +188,7 @@ export const urlSortParams = { [WEIGHT_ASC]: WEIGHT, [WEIGHT_DESC]: WEIGHT_DESC_SORT, [BLOCKING_ISSUES_DESC]: BLOCKING_ISSUES_DESC_SORT, - [TITLE_ASC]: TITLE, + [TITLE_ASC]: TITLE_ASC_SORT, [TITLE_DESC]: TITLE_DESC_SORT, }; @@ -211,6 +212,7 @@ export const TOKEN_TYPE_ASSIGNEE = 'assignee_username'; export const TOKEN_TYPE_MILESTONE = 'milestone'; export const TOKEN_TYPE_LABEL = 'labels'; export const TOKEN_TYPE_TYPE = 'type'; +export const TOKEN_TYPE_RELEASE = 'release'; export const TOKEN_TYPE_MY_REACTION = 'my_reaction_emoji'; export const TOKEN_TYPE_CONFIDENTIAL = 'confidential'; export const TOKEN_TYPE_ITERATION = 'iteration'; @@ -271,6 +273,7 @@ export const filters = { [OPERATOR_IS]: { [NORMAL_FILTER]: 'label_name[]', [SPECIAL_FILTER]: 'label_name[]', + [ALTERNATIVE_FILTER]: 'label_name', }, [OPERATOR_IS_NOT]: { [NORMAL_FILTER]: 'not[label_name][]', @@ -280,12 +283,28 @@ export const filters = { [TOKEN_TYPE_TYPE]: { [API_PARAM]: { [NORMAL_FILTER]: 'types', - [SPECIAL_FILTER]: 'types', }, [URL_PARAM]: { [OPERATOR_IS]: { [NORMAL_FILTER]: 'type[]', - [SPECIAL_FILTER]: 'type[]', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[type][]', + }, + }, + }, + [TOKEN_TYPE_RELEASE]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'releaseTag', + [SPECIAL_FILTER]: 'releaseTagWildcardId', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'release_tag', + [SPECIAL_FILTER]: 'release_tag', + }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[release_tag]', }, }, }, @@ -299,6 +318,9 @@ export const filters = { [NORMAL_FILTER]: 'my_reaction_emoji', [SPECIAL_FILTER]: 'my_reaction_emoji', }, + [OPERATOR_IS_NOT]: { + [NORMAL_FILTER]: 'not[my_reaction_emoji]', + }, }, }, [TOKEN_TYPE_CONFIDENTIAL]: { diff --git a/app/assets/javascripts/issues_list/index.js b/app/assets/javascripts/issues_list/index.js index 47af20f5271..59034964afb 100644 --- a/app/assets/javascripts/issues_list/index.js +++ b/app/assets/javascripts/issues_list/index.js @@ -24,7 +24,7 @@ export function mountJiraIssuesListApp() { } Vue.use(VueApollo); - const defaultClient = createDefaultClient({}, { assumeImmutableResults: true }); + const defaultClient = createDefaultClient(); const apolloProvider = new VueApollo({ defaultClient, }); @@ -103,7 +103,7 @@ export function mountIssuesListApp() { }, }; - const defaultClient = createDefaultClient(resolvers, { assumeImmutableResults: true }); + const defaultClient = createDefaultClient(resolvers); const apolloProvider = new VueApollo({ defaultClient, }); @@ -137,6 +137,7 @@ export function mountIssuesListApp() { newIssuePath, projectImportJiraPath, quickActionsHelpPath, + releasesPath, resetPath, rssPath, showNewIssueLink, @@ -164,6 +165,7 @@ export function mountIssuesListApp() { isSignedIn: parseBoolean(isSignedIn), jiraIntegrationPath, newIssuePath, + releasesPath, rssPath, showNewIssueLink: parseBoolean(showNewIssueLink), signInPath, diff --git a/app/assets/javascripts/issues_list/queries/get_issues.query.graphql b/app/assets/javascripts/issues_list/queries/get_issues.query.graphql index 6df72cf6596..9866efbcecc 100644 --- a/app/assets/javascripts/issues_list/queries/get_issues.query.graphql +++ b/app/assets/javascripts/issues_list/queries/get_issues.query.graphql @@ -11,9 +11,13 @@ query getIssues( $assigneeId: String $assigneeUsernames: [String!] $authorUsername: String + $confidential: Boolean $labelName: [String] $milestoneTitle: [String] $milestoneWildcardId: MilestoneWildcardId + $myReactionEmoji: String + $releaseTag: [String!] + $releaseTagWildcardId: ReleaseTagWildcardId $types: [IssueType!] $not: NegatedIssueFilterInput $beforeCursor: String @@ -30,9 +34,11 @@ query getIssues( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji types: $types not: $not before: $beforeCursor @@ -57,9 +63,13 @@ query getIssues( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not before: $beforeCursor diff --git a/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql b/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql index 7bcdbbb28fc..5e755ec5870 100644 --- a/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql +++ b/app/assets/javascripts/issues_list/queries/get_issues_counts.query.graphql @@ -5,9 +5,13 @@ query getIssuesCount( $assigneeId: String $assigneeUsernames: [String!] $authorUsername: String + $confidential: Boolean $labelName: [String] $milestoneTitle: [String] $milestoneWildcardId: MilestoneWildcardId + $myReactionEmoji: String + $releaseTag: [String!] + $releaseTagWildcardId: ReleaseTagWildcardId $types: [IssueType!] $not: NegatedIssueFilterInput ) { @@ -19,9 +23,11 @@ query getIssuesCount( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji types: $types not: $not ) { @@ -34,9 +40,11 @@ query getIssuesCount( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji types: $types not: $not ) { @@ -49,9 +57,11 @@ query getIssuesCount( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji types: $types not: $not ) { @@ -65,9 +75,13 @@ query getIssuesCount( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not ) { @@ -79,9 +93,13 @@ query getIssuesCount( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not ) { @@ -93,9 +111,13 @@ query getIssuesCount( assigneeId: $assigneeId assigneeUsernames: $assigneeUsernames authorUsername: $authorUsername + confidential: $confidential labelName: $labelName milestoneTitle: $milestoneTitle milestoneWildcardId: $milestoneWildcardId + myReactionEmoji: $myReactionEmoji + releaseTag: $releaseTag + releaseTagWildcardId: $releaseTagWildcardId types: $types not: $not ) { diff --git a/app/assets/javascripts/issues_list/queries/get_issues_list_details.query.graphql b/app/assets/javascripts/issues_list/queries/get_issues_list_details.query.graphql index 8f9b888d19b..8c95e6114d3 100644 --- a/app/assets/javascripts/issues_list/queries/get_issues_list_details.query.graphql +++ b/app/assets/javascripts/issues_list/queries/get_issues_list_details.query.graphql @@ -1,4 +1,4 @@ -query($fullPath: ID!) { +query getIssuesListDetails($fullPath: ID!) { project(fullPath: $fullPath) { issues { nodes { diff --git a/app/assets/javascripts/issues_list/queries/iteration.fragment.graphql b/app/assets/javascripts/issues_list/queries/iteration.fragment.graphql index 78a368089a8..4f7217be7f7 100644 --- a/app/assets/javascripts/issues_list/queries/iteration.fragment.graphql +++ b/app/assets/javascripts/issues_list/queries/iteration.fragment.graphql @@ -1,4 +1,10 @@ fragment Iteration on Iteration { id title + startDate + dueDate + iterationCadence { + id + title + } } diff --git a/app/assets/javascripts/issues_list/queries/search_projects.query.graphql b/app/assets/javascripts/issues_list/queries/search_projects.query.graphql index df1f330139a..75463f643a2 100644 --- a/app/assets/javascripts/issues_list/queries/search_projects.query.graphql +++ b/app/assets/javascripts/issues_list/queries/search_projects.query.graphql @@ -3,6 +3,7 @@ query searchProjects($fullPath: ID!, $search: String) { projects(search: $search, includeSubgroups: true) { nodes { id + issuesEnabled name nameWithNamespace webUrl diff --git a/app/assets/javascripts/issues_list/service_desk_helper.js b/app/assets/javascripts/issues_list/service_desk_helper.js index f96567ef53b..815f338f1a0 100644 --- a/app/assets/javascripts/issues_list/service_desk_helper.js +++ b/app/assets/javascripts/issues_list/service_desk_helper.js @@ -1,4 +1,4 @@ -import { s__ } from '~/locale'; +import { __, s__ } from '~/locale'; /** * Generates empty state messages for Service Desk issues list. @@ -20,12 +20,12 @@ export function generateMessages(emptyStateMeta) { ); const serviceDeskSupportedMessage = s__( - 'ServiceDesk|Issues created from Service Desk emails appear here. Each comment becomes part of the email conversation.', + 'ServiceDesk|Issues created from Service Desk emails will appear here. Each comment becomes part of the email conversation.', ); const commonDescription = ` <span>${serviceDeskSupportedMessage}</span> - <a href="${serviceDeskHelpPage}">${s__('Learn more.')}</a>`; + <a href="${serviceDeskHelpPage}">${__('Learn more.')}</a>`; return { serviceDeskEnabledAndCanEditProjectSettings: { @@ -60,7 +60,7 @@ export function generateMessages(emptyStateMeta) { 'ServiceDesk|To enable Service Desk on this instance, an instance administrator must first set up incoming email.', ), primaryLink: incomingEmailHelpPage, - primaryText: s__('Learn more.'), + primaryText: __('Learn more.'), }, serviceDeskIsNotEnabled: { title: s__('ServiceDesk|Service Desk is not enabled'), diff --git a/app/assets/javascripts/issues_list/utils.js b/app/assets/javascripts/issues_list/utils.js index 1d3d07475af..0e57e2bff83 100644 --- a/app/assets/javascripts/issues_list/utils.js +++ b/app/assets/javascripts/issues_list/utils.js @@ -21,9 +21,13 @@ import { RELATIVE_POSITION_ASC, SPECIAL_FILTER, SPECIAL_FILTER_VALUES, + TITLE_ASC, + TITLE_DESC, TOKEN_TYPE_ASSIGNEE, + TOKEN_TYPE_CONFIDENTIAL, TOKEN_TYPE_ITERATION, TOKEN_TYPE_MILESTONE, + TOKEN_TYPE_RELEASE, TOKEN_TYPE_TYPE, UPDATED_ASC, UPDATED_DESC, @@ -113,11 +117,19 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) descending: RELATIVE_POSITION_ASC, }, }, + { + id: 9, + title: __('Title'), + sortDirection: { + ascending: TITLE_ASC, + descending: TITLE_DESC, + }, + }, ]; if (hasIssueWeightsFeature) { sortOptions.push({ - id: 9, + id: sortOptions.length + 1, title: __('Weight'), sortDirection: { ascending: WEIGHT_ASC, @@ -128,7 +140,7 @@ export const getSortOptions = (hasIssueWeightsFeature, hasBlockedIssuesFeature) if (hasBlockedIssuesFeature) { sortOptions.push({ - id: 10, + id: sortOptions.length + 1, title: __('Blocking'), sortDirection: { ascending: BLOCKING_ISSUES_DESC, @@ -193,17 +205,23 @@ const getFilterType = (data, tokenType = '') => ? SPECIAL_FILTER : NORMAL_FILTER; +const wildcardTokens = [TOKEN_TYPE_ITERATION, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_RELEASE]; + const isWildcardValue = (tokenType, value) => - (tokenType === TOKEN_TYPE_ITERATION || tokenType === TOKEN_TYPE_MILESTONE) && - SPECIAL_FILTER_VALUES.includes(value); + wildcardTokens.includes(tokenType) && SPECIAL_FILTER_VALUES.includes(value); const requiresUpperCaseValue = (tokenType, value) => tokenType === TOKEN_TYPE_TYPE || isWildcardValue(tokenType, value); -const formatData = (token) => - requiresUpperCaseValue(token.type, token.value.data) - ? token.value.data.toUpperCase() - : token.value.data; +const formatData = (token) => { + if (requiresUpperCaseValue(token.type, token.value.data)) { + return token.value.data.toUpperCase(); + } + if (token.type === TOKEN_TYPE_CONFIDENTIAL) { + return token.value.data === 'yes'; + } + return token.value.data; +}; export const convertToApiParams = (filterTokens) => { const params = {}; |