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