diff options
Diffstat (limited to 'app/assets/javascripts/incidents/components/incidents_list.vue')
-rw-r--r-- | app/assets/javascripts/incidents/components/incidents_list.vue | 196 |
1 files changed, 169 insertions, 27 deletions
diff --git a/app/assets/javascripts/incidents/components/incidents_list.vue b/app/assets/javascripts/incidents/components/incidents_list.vue index 670c42cbdac..78eb828fd19 100644 --- a/app/assets/javascripts/incidents/components/incidents_list.vue +++ b/app/assets/javascripts/incidents/components/incidents_list.vue @@ -8,7 +8,6 @@ import { GlAvatar, GlTooltipDirective, GlButton, - GlSearchBoxByType, GlIcon, GlPagination, GlTabs, @@ -16,18 +15,35 @@ import { GlBadge, GlEmptyState, } from '@gitlab/ui'; -import { debounce } from 'lodash'; +import Api from '~/api'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; +import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { convertToSnakeCase } from '~/lib/utils/text_utility'; -import { s__ } from '~/locale'; -import { mergeUrlParams, joinPaths, visitUrl } from '~/lib/utils/url_utility'; +import { s__, __ } from '~/locale'; +import { urlParamsToObject } from '~/lib/utils/common_utils'; +import { + visitUrl, + mergeUrlParams, + joinPaths, + updateHistory, + setUrlParams, +} from '~/lib/utils/url_utility'; import getIncidents from '../graphql/queries/get_incidents.query.graphql'; import getIncidentsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql'; import SeverityToken from '~/sidebar/components/severity/severity.vue'; import { INCIDENT_SEVERITY } from '~/sidebar/components/severity/constants'; -import { I18N, DEFAULT_PAGE_SIZE, INCIDENT_SEARCH_DELAY, INCIDENT_STATUS_TABS } from '../constants'; +import { + I18N, + DEFAULT_PAGE_SIZE, + INCIDENT_STATUS_TABS, + TH_CREATED_AT_TEST_ID, + TH_SEVERITY_TEST_ID, + TH_PUBLISHED_TEST_ID, + INCIDENT_DETAILS_PATH, +} from '../constants'; -const TH_TEST_ID = { 'data-testid': 'incident-management-created-at-sort' }; const tdClass = 'table-col gl-display-flex d-md-table-cell gl-align-items-center gl-white-space-nowrap'; const thClass = 'gl-hover-bg-blue-50'; @@ -49,8 +65,10 @@ export default { { key: 'severity', label: s__('IncidentManagement|Severity'), - thClass: `gl-pointer-events-none`, - tdClass, + thClass, + tdClass: `${tdClass} sortable-cell`, + sortable: true, + thAttr: TH_SEVERITY_TEST_ID, }, { key: 'title', @@ -64,7 +82,7 @@ export default { thClass, tdClass: `${tdClass} sortable-cell`, sortable: true, - thAttr: TH_TEST_ID, + thAttr: TH_CREATED_AT_TEST_ID, }, { key: 'assignees', @@ -82,7 +100,6 @@ export default { GlAvatar, GlButton, TimeAgoTooltip, - GlSearchBoxByType, GlIcon, GlPagination, GlTabs, @@ -91,10 +108,12 @@ export default { GlBadge, GlEmptyState, SeverityToken, + FilteredSearchBar, }, directives: { GlTooltip: GlTooltipDirective, }, + mixins: [glFeatureFlagsMixin()], inject: [ 'projectPath', 'newIssuePath', @@ -103,6 +122,9 @@ export default { 'issuePath', 'publishedAvailable', 'emptyListSvgPath', + 'textQuery', + 'authorUsernamesQuery', + 'assigneeUsernamesQuery', ], apollo: { incidents: { @@ -118,6 +140,8 @@ export default { lastPageSize: this.pagination.lastPageSize, prevPageCursor: this.pagination.prevPageCursor, nextPageCursor: this.pagination.nextPageCursor, + authorUsername: this.authorUsername, + assigneeUsernames: this.assigneeUsernames, }; }, update({ project: { issues: { nodes = [], pageInfo = {} } = {} } = {} }) { @@ -135,6 +159,8 @@ export default { variables() { return { searchTerm: this.searchTerm, + authorUsername: this.authorUsername, + assigneeUsernames: this.assigneeUsernames, projectPath: this.projectPath, issueTypes: ['INCIDENT'], }; @@ -149,7 +175,7 @@ export default { errored: false, isErrorAlertDismissed: false, redirecting: false, - searchTerm: '', + searchTerm: this.textQuery, pagination: initialPaginationState, incidents: {}, sort: 'created_desc', @@ -157,6 +183,9 @@ export default { sortDesc: true, statusFilter: '', filteredByStatus: '', + authorUsername: this.authorUsernamesQuery, + assigneeUsernames: this.assigneeUsernamesQuery, + filterParams: {}, }; }, computed: { @@ -208,7 +237,10 @@ export default { { key: 'published', label: s__('IncidentManagement|Published'), - thClass: 'gl-pointer-events-none', + thClass, + tdClass: `${tdClass} sortable-cell`, + sortable: true, + thAttr: TH_PUBLISHED_TEST_ID, }, ], ] @@ -242,15 +274,59 @@ export default { btnText: createIncidentBtnLabel, }; }, + filteredSearchTokens() { + return [ + { + type: 'author_username', + icon: 'user', + title: __('Author'), + unique: true, + symbol: '@', + token: AuthorToken, + operators: [{ value: '=', description: __('is'), default: 'true' }], + fetchPath: this.projectPath, + fetchAuthors: Api.projectUsers.bind(Api), + }, + { + type: 'assignee_username', + icon: 'user', + title: __('Assignees'), + unique: true, + symbol: '@', + token: AuthorToken, + operators: [{ value: '=', description: __('is'), default: 'true' }], + fetchPath: this.projectPath, + fetchAuthors: Api.projectUsers.bind(Api), + }, + ]; + }, + filteredSearchValue() { + const value = []; + + if (this.authorUsername) { + value.push({ + type: 'author_username', + value: { data: this.authorUsername }, + }); + } + + if (this.assigneeUsernames) { + value.push({ + type: 'assignee_username', + value: { data: this.assigneeUsernames }, + }); + } + + if (this.searchTerm) { + value.push(this.searchTerm); + } + + return value; + }, }, methods: { - onInputChange: debounce(function debounceSearch(input) { - const trimmedInput = input.trim(); - if (trimmedInput !== this.searchTerm) { - this.searchTerm = trimmedInput; - } - }, INCIDENT_SEARCH_DELAY), filterIncidentsByStatus(tabIndex) { + this.resetPagination(); const { filters, status } = this.$options.statusTabs[tabIndex]; this.statusFilter = filters; this.filteredByStatus = status; @@ -259,7 +335,10 @@ export default { return Boolean(assignees.nodes?.length); }, navigateToIncidentDetails({ iid }) { - return visitUrl(joinPaths(this.issuePath, iid)); + const path = this.glFeatures.issuesIncidentDetails + ? joinPaths(this.issuePath, INCIDENT_DETAILS_PATH) + : this.issuePath; + return visitUrl(joinPaths(path, iid)); }, handlePageChange(page) { const { startCursor, endCursor } = this.incidents.pageInfo; @@ -284,14 +363,73 @@ export default { this.pagination = initialPaginationState; }, fetchSortedData({ sortBy, sortDesc }) { - const sortingDirection = sortDesc ? 'desc' : 'asc'; - const sortingColumn = convertToSnakeCase(sortBy).replace(/_.*/, ''); + const sortingDirection = sortDesc ? 'DESC' : 'ASC'; + const sortingColumn = convertToSnakeCase(sortBy) + .replace(/_.*/, '') + .toUpperCase(); + this.resetPagination(); this.sort = `${sortingColumn}_${sortingDirection}`; }, getSeverity(severity) { return INCIDENT_SEVERITY[severity]; }, + handleFilterIncidents(filters) { + this.resetPagination(); + const filterParams = { authorUsername: '', assigneeUsername: '', search: '' }; + + filters.forEach(filter => { + if (typeof filter === 'object') { + switch (filter.type) { + case 'author_username': + filterParams.authorUsername = filter.value.data; + break; + case 'assignee_username': + filterParams.assigneeUsername = filter.value.data; + break; + case 'filtered-search-term': + if (filter.value.data !== '') filterParams.search = filter.value.data; + break; + default: + break; + } + } + }); + + this.filterParams = filterParams; + this.updateUrl(); + this.searchTerm = filterParams?.search; + this.authorUsername = filterParams?.authorUsername; + this.assigneeUsernames = filterParams?.assigneeUsername; + }, + updateUrl() { + const queryParams = urlParamsToObject(window.location.search); + const { authorUsername, assigneeUsername, search } = this.filterParams || {}; + + if (authorUsername) { + queryParams.author_username = authorUsername; + } else { + delete queryParams.author_username; + } + + if (assigneeUsername) { + queryParams.assignee_username = assigneeUsername; + } else { + delete queryParams.assignee_username; + } + + if (search) { + queryParams.search = search; + } else { + delete queryParams.search; + } + + updateHistory({ + url: setUrlParams(queryParams, window.location.href, true), + title: document.title, + replace: true, + }); + }, }, }; </script> @@ -331,12 +469,16 @@ export default { </gl-button> </div> - <div class="gl-bg-gray-10 gl-p-5 gl-border-b-solid gl-border-b-1 gl-border-gray-100"> - <gl-search-box-by-type - :value="searchTerm" - class="gl-bg-white" - :placeholder="$options.i18n.searchPlaceholder" - @input="onInputChange" + <div class="filtered-search-wrapper"> + <filtered-search-bar + :namespace="projectPath" + :search-input-placeholder="$options.i18n.searchPlaceholder" + :tokens="filteredSearchTokens" + :initial-filter-value="filteredSearchValue" + initial-sortby="created_desc" + recent-searches-storage-key="incidents" + class="row-content-block" + @onFilter="handleFilterIncidents" /> </div> |