1<script> 2import { GlFilteredSearchToken } from '@gitlab/ui'; 3import fuzzaldrinPlus from 'fuzzaldrin-plus'; 4import { mapActions } from 'vuex'; 5import { orderBy } from 'lodash'; 6import BoardFilteredSearch from 'ee_else_ce/boards/components/board_filtered_search.vue'; 7import { BoardType } from '~/boards/constants'; 8import axios from '~/lib/utils/axios_utils'; 9import { joinPaths } from '~/lib/utils/url_utility'; 10import issueBoardFilters from '~/boards/issue_board_filters'; 11import { TYPE_USER } from '~/graphql_shared/constants'; 12import { convertToGraphQLId } from '~/graphql_shared/utils'; 13import { __ } from '~/locale'; 14import { 15 TOKEN_TITLE_MY_REACTION, 16 OPERATOR_IS_AND_IS_NOT, 17 OPERATOR_IS_ONLY, 18} from '~/vue_shared/components/filtered_search_bar/constants'; 19import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'; 20import EmojiToken from '~/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue'; 21import LabelToken from '~/vue_shared/components/filtered_search_bar/tokens/label_token.vue'; 22import MilestoneToken from '~/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue'; 23import ReleaseToken from '~/vue_shared/components/filtered_search_bar/tokens/release_token.vue'; 24 25export default { 26 types: { 27 ISSUE: 'ISSUE', 28 INCIDENT: 'INCIDENT', 29 }, 30 i18n: { 31 search: __('Search'), 32 epic: __('Epic'), 33 label: __('Label'), 34 author: __('Author'), 35 assignee: __('Assignee'), 36 type: __('Type'), 37 incident: __('Incident'), 38 issue: __('Issue'), 39 milestone: __('Milestone'), 40 release: __('Release'), 41 confidential: __('Confidential'), 42 }, 43 components: { BoardFilteredSearch }, 44 inject: ['isSignedIn', 'releasesFetchPath'], 45 props: { 46 fullPath: { 47 type: String, 48 required: true, 49 }, 50 boardType: { 51 type: String, 52 required: true, 53 }, 54 }, 55 computed: { 56 isGroupBoard() { 57 return this.boardType === BoardType.group; 58 }, 59 epicsGroupPath() { 60 return this.isGroupBoard 61 ? this.fullPath 62 : this.fullPath.slice(0, this.fullPath.lastIndexOf('/')); 63 }, 64 tokensCE() { 65 const { 66 label, 67 author, 68 assignee, 69 issue, 70 incident, 71 type, 72 milestone, 73 release, 74 confidential, 75 } = this.$options.i18n; 76 const { types } = this.$options; 77 const { fetchAuthors, fetchLabels } = issueBoardFilters( 78 this.$apollo, 79 this.fullPath, 80 this.boardType, 81 ); 82 83 const tokens = [ 84 { 85 icon: 'user', 86 title: assignee, 87 type: 'assignee', 88 operators: OPERATOR_IS_AND_IS_NOT, 89 token: AuthorToken, 90 unique: true, 91 fetchAuthors, 92 preloadedAuthors: this.preloadedAuthors(), 93 }, 94 { 95 icon: 'pencil', 96 title: author, 97 type: 'author', 98 operators: OPERATOR_IS_AND_IS_NOT, 99 symbol: '@', 100 token: AuthorToken, 101 unique: true, 102 fetchAuthors, 103 preloadedAuthors: this.preloadedAuthors(), 104 }, 105 { 106 icon: 'labels', 107 title: label, 108 type: 'label', 109 operators: OPERATOR_IS_AND_IS_NOT, 110 token: LabelToken, 111 unique: false, 112 symbol: '~', 113 fetchLabels, 114 }, 115 ...(this.isSignedIn 116 ? [ 117 { 118 type: 'my-reaction', 119 title: TOKEN_TITLE_MY_REACTION, 120 icon: 'thumb-up', 121 token: EmojiToken, 122 unique: true, 123 fetchEmojis: (search = '') => { 124 // TODO: Switch to GraphQL query when backend is ready: https://gitlab.com/gitlab-org/gitlab/-/issues/339694 125 return axios 126 .get(`${gon.relative_url_root || ''}/-/autocomplete/award_emojis`) 127 .then(({ data }) => { 128 if (search) { 129 return { 130 data: fuzzaldrinPlus.filter(data, search, { 131 key: ['name'], 132 }), 133 }; 134 } 135 return { data }; 136 }); 137 }, 138 }, 139 { 140 type: 'confidential', 141 icon: 'eye-slash', 142 title: confidential, 143 unique: true, 144 token: GlFilteredSearchToken, 145 operators: OPERATOR_IS_ONLY, 146 options: [ 147 { icon: 'eye-slash', value: 'yes', title: __('Yes') }, 148 { icon: 'eye', value: 'no', title: __('No') }, 149 ], 150 }, 151 ] 152 : []), 153 { 154 type: 'milestone', 155 title: milestone, 156 icon: 'clock', 157 symbol: '%', 158 token: MilestoneToken, 159 unique: true, 160 fetchMilestones: this.fetchMilestones, 161 }, 162 { 163 icon: 'issues', 164 title: type, 165 type: 'type', 166 token: GlFilteredSearchToken, 167 unique: true, 168 options: [ 169 { icon: 'issue-type-issue', value: types.ISSUE, title: issue }, 170 { icon: 'issue-type-incident', value: types.INCIDENT, title: incident }, 171 ], 172 }, 173 { 174 type: 'release', 175 title: release, 176 icon: 'rocket', 177 token: ReleaseToken, 178 fetchReleases: (search) => { 179 // TODO: Switch to GraphQL query when backend is ready: https://gitlab.com/gitlab-org/gitlab/-/issues/337686 180 return axios 181 .get(joinPaths(gon.relative_url_root, this.releasesFetchPath)) 182 .then(({ data }) => { 183 if (search) { 184 return fuzzaldrinPlus.filter(data, search, { 185 key: ['tag'], 186 }); 187 } 188 return data; 189 }); 190 }, 191 }, 192 ]; 193 194 return orderBy(tokens, ['title']); 195 }, 196 tokens() { 197 return this.tokensCE; 198 }, 199 }, 200 methods: { 201 ...mapActions(['fetchMilestones']), 202 preloadedAuthors() { 203 return gon?.current_user_id 204 ? [ 205 { 206 id: convertToGraphQLId(TYPE_USER, gon.current_user_id), 207 name: gon.current_user_fullname, 208 username: gon.current_username, 209 avatarUrl: gon.current_user_avatar_url, 210 }, 211 ] 212 : []; 213 }, 214 }, 215}; 216</script> 217 218<template> 219 <board-filtered-search data-testid="issue-board-filtered-search" :tokens="tokens" /> 220</template> 221