debian-mirror-gitlab/app/assets/javascripts/issues/list/utils.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

344 lines
8.7 KiB
JavaScript
Raw Normal View History

2022-07-16 23:28:13 +05:30
import { createTerm } from '@gitlab/ui/src/components/base/filtered_search/filtered_search_utils';
2022-04-04 11:22:00 +05:30
import { isPositiveInteger } from '~/lib/utils/number_utils';
2022-06-21 17:19:12 +05:30
import { getParameterByName } from '~/lib/utils/url_utility';
2022-04-04 11:22:00 +05:30
import { __ } from '~/locale';
import {
FILTERED_SEARCH_TERM,
2023-03-04 22:38:38 +05:30
OPERATOR_NOT,
2023-01-13 00:05:48 +05:30
OPERATOR_OR,
TOKEN_TYPE_ASSIGNEE,
2023-03-04 22:38:38 +05:30
TOKEN_TYPE_AUTHOR,
2023-01-13 00:05:48 +05:30
TOKEN_TYPE_CONFIDENTIAL,
TOKEN_TYPE_ITERATION,
TOKEN_TYPE_MILESTONE,
TOKEN_TYPE_RELEASE,
TOKEN_TYPE_TYPE,
2023-03-17 16:20:25 +05:30
TOKEN_TYPE_HEALTH,
TOKEN_TYPE_LABEL,
2022-04-04 11:22:00 +05:30
} from '~/vue_shared/components/filtered_search_bar/constants';
2023-05-27 22:25:52 +05:30
import { DEFAULT_PAGE_SIZE } from '~/vue_shared/issuable/list/constants';
2021-06-08 01:23:25 +05:30
import {
2023-03-04 22:38:38 +05:30
ALTERNATIVE_FILTER,
2021-09-30 23:02:18 +05:30
API_PARAM,
2022-01-26 12:08:38 +05:30
BLOCKING_ISSUES_ASC,
2021-06-08 01:23:25 +05:30
BLOCKING_ISSUES_DESC,
2023-03-04 22:38:38 +05:30
CLOSED_AT_ASC,
CLOSED_AT_DESC,
2021-06-08 01:23:25 +05:30
CREATED_ASC,
CREATED_DESC,
DUE_DATE_ASC,
DUE_DATE_DESC,
filters,
2023-03-04 22:38:38 +05:30
HEALTH_STATUS_ASC,
HEALTH_STATUS_DESC,
2021-09-30 23:02:18 +05:30
LABEL_PRIORITY_ASC,
2021-06-08 01:23:25 +05:30
LABEL_PRIORITY_DESC,
MILESTONE_DUE_ASC,
MILESTONE_DUE_DESC,
NORMAL_FILTER,
2022-06-21 17:19:12 +05:30
PARAM_ASSIGNEE_ID,
2021-06-08 01:23:25 +05:30
POPULARITY_ASC,
POPULARITY_DESC,
2021-09-30 23:02:18 +05:30
PRIORITY_ASC,
2021-06-08 01:23:25 +05:30
PRIORITY_DESC,
2021-09-30 23:02:18 +05:30
RELATIVE_POSITION_ASC,
2021-06-08 01:23:25 +05:30
SPECIAL_FILTER,
2022-06-21 17:19:12 +05:30
specialFilterValues,
2021-12-11 22:18:48 +05:30
TITLE_ASC,
TITLE_DESC,
2021-06-08 01:23:25 +05:30
UPDATED_ASC,
UPDATED_DESC,
2021-09-30 23:02:18 +05:30
URL_PARAM,
2021-06-08 01:23:25 +05:30
urlSortParams,
WEIGHT_ASC,
WEIGHT_DESC,
2022-04-04 11:22:00 +05:30
} from './constants';
2021-06-08 01:23:25 +05:30
2022-07-23 23:45:48 +05:30
export const getInitialPageParams = (
2022-08-13 15:12:31 +05:30
pageSize,
2023-05-27 22:25:52 +05:30
firstPageSize = pageSize ?? DEFAULT_PAGE_SIZE,
2022-07-23 23:45:48 +05:30
lastPageSize,
afterCursor,
beforeCursor,
) => ({
firstPageSize: lastPageSize ? undefined : firstPageSize,
lastPageSize,
2022-05-07 20:08:51 +05:30
afterCursor,
beforeCursor,
});
2021-10-27 15:23:28 +05:30
2021-06-08 01:23:25 +05:30
export const getSortKey = (sort) =>
2021-09-30 23:02:18 +05:30
Object.keys(urlSortParams).find((key) => urlSortParams[key] === sort);
2021-06-08 01:23:25 +05:30
2022-04-04 11:22:00 +05:30
export const isSortKey = (sort) => Object.keys(urlSortParams).includes(sort);
2021-06-08 01:23:25 +05:30
2023-03-04 22:38:38 +05:30
export const getSortOptions = ({
hasBlockedIssuesFeature,
hasIssuableHealthStatusFeature,
hasIssueWeightsFeature,
}) => {
2021-06-08 01:23:25 +05:30
const sortOptions = [
{
id: 1,
title: __('Priority'),
sortDirection: {
2021-09-30 23:02:18 +05:30
ascending: PRIORITY_ASC,
2021-06-08 01:23:25 +05:30
descending: PRIORITY_DESC,
},
},
{
id: 2,
title: __('Created date'),
sortDirection: {
ascending: CREATED_ASC,
descending: CREATED_DESC,
},
},
{
id: 3,
2022-03-02 08:16:31 +05:30
title: __('Updated date'),
2021-06-08 01:23:25 +05:30
sortDirection: {
ascending: UPDATED_ASC,
descending: UPDATED_DESC,
},
},
{
id: 4,
2022-08-13 15:12:31 +05:30
title: __('Closed date'),
sortDirection: {
2023-03-04 22:38:38 +05:30
ascending: CLOSED_AT_ASC,
descending: CLOSED_AT_DESC,
2022-08-13 15:12:31 +05:30
},
},
{
id: 5,
2021-06-08 01:23:25 +05:30
title: __('Milestone due date'),
sortDirection: {
ascending: MILESTONE_DUE_ASC,
descending: MILESTONE_DUE_DESC,
},
},
{
2022-08-13 15:12:31 +05:30
id: 6,
2021-06-08 01:23:25 +05:30
title: __('Due date'),
sortDirection: {
ascending: DUE_DATE_ASC,
descending: DUE_DATE_DESC,
},
},
{
2022-08-13 15:12:31 +05:30
id: 7,
2021-06-08 01:23:25 +05:30
title: __('Popularity'),
sortDirection: {
ascending: POPULARITY_ASC,
descending: POPULARITY_DESC,
},
},
{
2022-08-13 15:12:31 +05:30
id: 8,
2021-06-08 01:23:25 +05:30
title: __('Label priority'),
sortDirection: {
2021-09-30 23:02:18 +05:30
ascending: LABEL_PRIORITY_ASC,
2021-06-08 01:23:25 +05:30
descending: LABEL_PRIORITY_DESC,
},
},
{
2022-08-13 15:12:31 +05:30
id: 9,
2021-06-08 01:23:25 +05:30
title: __('Manual'),
sortDirection: {
2021-09-30 23:02:18 +05:30
ascending: RELATIVE_POSITION_ASC,
descending: RELATIVE_POSITION_ASC,
2021-06-08 01:23:25 +05:30
},
},
2021-12-11 22:18:48 +05:30
{
2022-08-13 15:12:31 +05:30
id: 10,
2021-12-11 22:18:48 +05:30
title: __('Title'),
sortDirection: {
ascending: TITLE_ASC,
descending: TITLE_DESC,
},
},
2021-06-08 01:23:25 +05:30
];
2023-03-04 22:38:38 +05:30
if (hasIssuableHealthStatusFeature) {
sortOptions.push({
id: sortOptions.length + 1,
title: __('Health'),
sortDirection: {
ascending: HEALTH_STATUS_ASC,
descending: HEALTH_STATUS_DESC,
},
});
}
2021-06-08 01:23:25 +05:30
if (hasIssueWeightsFeature) {
sortOptions.push({
2021-12-11 22:18:48 +05:30
id: sortOptions.length + 1,
2021-06-08 01:23:25 +05:30
title: __('Weight'),
sortDirection: {
ascending: WEIGHT_ASC,
descending: WEIGHT_DESC,
},
});
}
if (hasBlockedIssuesFeature) {
sortOptions.push({
2021-12-11 22:18:48 +05:30
id: sortOptions.length + 1,
2021-06-08 01:23:25 +05:30
title: __('Blocking'),
sortDirection: {
2022-01-26 12:08:38 +05:30
ascending: BLOCKING_ISSUES_ASC,
2021-06-08 01:23:25 +05:30
descending: BLOCKING_ISSUES_DESC,
},
});
}
return sortOptions;
};
const tokenTypes = Object.keys(filters);
const getUrlParams = (tokenType) =>
2021-09-30 23:02:18 +05:30
Object.values(filters[tokenType][URL_PARAM]).flatMap((filterObj) => Object.values(filterObj));
2021-06-08 01:23:25 +05:30
const urlParamKeys = tokenTypes.flatMap(getUrlParams);
const getTokenTypeFromUrlParamKey = (urlParamKey) =>
tokenTypes.find((tokenType) => getUrlParams(tokenType).includes(urlParamKey));
const getOperatorFromUrlParamKey = (tokenType, urlParamKey) =>
2021-09-30 23:02:18 +05:30
Object.entries(filters[tokenType][URL_PARAM]).find(([, filterObj]) =>
2021-06-08 01:23:25 +05:30
Object.values(filterObj).includes(urlParamKey),
)[0];
const convertToFilteredTokens = (locationSearch) =>
Array.from(new URLSearchParams(locationSearch).entries())
.filter(([key]) => urlParamKeys.includes(key))
.map(([key, data]) => {
const type = getTokenTypeFromUrlParamKey(key);
const operator = getOperatorFromUrlParamKey(type, key);
return {
type,
value: { data, operator },
};
});
const convertToFilteredSearchTerms = (locationSearch) =>
new URLSearchParams(locationSearch)
.get('search')
?.split(' ')
.map((word) => ({
type: FILTERED_SEARCH_TERM,
value: {
data: word,
},
})) || [];
export const getFilterTokens = (locationSearch) => {
if (!locationSearch) {
2022-07-16 23:28:13 +05:30
return [createTerm()];
2021-06-08 01:23:25 +05:30
}
const filterTokens = convertToFilteredTokens(locationSearch);
const searchTokens = convertToFilteredSearchTerms(locationSearch);
2022-07-16 23:28:13 +05:30
const tokens = filterTokens.concat(searchTokens);
return tokens.length ? tokens : [createTerm()];
2021-06-08 01:23:25 +05:30
};
2023-03-04 22:38:38 +05:30
const isSpecialFilter = (type, data) => {
2022-06-21 17:19:12 +05:30
const isAssigneeIdParam =
2023-03-04 22:38:38 +05:30
type === TOKEN_TYPE_ASSIGNEE &&
2022-06-21 17:19:12 +05:30
isPositiveInteger(data) &&
getParameterByName(PARAM_ASSIGNEE_ID) === data;
2023-03-04 22:38:38 +05:30
return specialFilterValues.includes(data) || isAssigneeIdParam;
};
const getFilterType = ({ type, value: { data, operator } }) => {
const isUnionedAuthor = type === TOKEN_TYPE_AUTHOR && operator === OPERATOR_OR;
2023-03-17 16:20:25 +05:30
const isUnionedLabel = type === TOKEN_TYPE_LABEL && operator === OPERATOR_OR;
2022-06-21 17:19:12 +05:30
2023-03-17 16:20:25 +05:30
if (isUnionedAuthor || isUnionedLabel) {
2023-03-04 22:38:38 +05:30
return ALTERNATIVE_FILTER;
}
if (isSpecialFilter(type, data)) {
return SPECIAL_FILTER;
}
return NORMAL_FILTER;
2022-06-21 17:19:12 +05:30
};
2021-06-08 01:23:25 +05:30
2021-12-11 22:18:48 +05:30
const wildcardTokens = [TOKEN_TYPE_ITERATION, TOKEN_TYPE_MILESTONE, TOKEN_TYPE_RELEASE];
2021-10-27 15:23:28 +05:30
const isWildcardValue = (tokenType, value) =>
2022-06-21 17:19:12 +05:30
wildcardTokens.includes(tokenType) && specialFilterValues.includes(value);
2021-10-27 15:23:28 +05:30
2023-03-17 16:20:25 +05:30
const isHealthStatusSpecialFilter = (tokenType, value) =>
tokenType === TOKEN_TYPE_HEALTH && specialFilterValues.includes(value);
2021-10-27 15:23:28 +05:30
const requiresUpperCaseValue = (tokenType, value) =>
2023-03-17 16:20:25 +05:30
tokenType === TOKEN_TYPE_TYPE ||
isWildcardValue(tokenType, value) ||
isHealthStatusSpecialFilter(tokenType, value);
2021-10-27 15:23:28 +05:30
2021-12-11 22:18:48 +05:30
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;
};
2021-09-30 23:02:18 +05:30
export const convertToApiParams = (filterTokens) => {
2023-05-27 22:25:52 +05:30
const params = new Map();
const not = new Map();
const or = new Map();
2021-09-30 23:02:18 +05:30
filterTokens
.filter((token) => token.type !== FILTERED_SEARCH_TERM)
.forEach((token) => {
2023-03-04 22:38:38 +05:30
const filterType = getFilterType(token);
const apiField = filters[token.type][API_PARAM][filterType];
2023-01-13 00:05:48 +05:30
let obj;
2023-03-04 22:38:38 +05:30
if (token.value.operator === OPERATOR_NOT) {
2023-01-13 00:05:48 +05:30
obj = not;
} else if (token.value.operator === OPERATOR_OR) {
obj = or;
} else {
obj = params;
}
2021-10-27 15:23:28 +05:30
const data = formatData(token);
2023-05-27 22:25:52 +05:30
obj.set(apiField, obj.has(apiField) ? [obj.get(apiField), data].flat() : data);
2021-09-30 23:02:18 +05:30
});
2023-05-27 22:25:52 +05:30
if (not.size) {
params.set('not', Object.fromEntries(not));
2023-01-13 00:05:48 +05:30
}
2023-05-27 22:25:52 +05:30
if (or.size) {
params.set('or', Object.fromEntries(or));
2023-01-13 00:05:48 +05:30
}
2023-05-27 22:25:52 +05:30
return Object.fromEntries(params);
2021-09-30 23:02:18 +05:30
};
2023-05-27 22:25:52 +05:30
export const convertToUrlParams = (filterTokens) => {
const urlParamsMap = filterTokens
2021-06-08 01:23:25 +05:30
.filter((token) => token.type !== FILTERED_SEARCH_TERM)
.reduce((acc, token) => {
2023-03-04 22:38:38 +05:30
const filterType = getFilterType(token);
const urlParam = filters[token.type][URL_PARAM][token.value.operator]?.[filterType];
2023-05-27 22:25:52 +05:30
return acc.set(
urlParam,
acc.has(urlParam) ? [acc.get(urlParam), token.value.data].flat() : token.value.data,
);
}, new Map());
return Object.fromEntries(urlParamsMap);
};
2021-06-08 01:23:25 +05:30
export const convertToSearchQuery = (filterTokens) =>
filterTokens
.filter((token) => token.type === FILTERED_SEARCH_TERM && token.value.data)
.map((token) => token.value.data)
2023-03-04 22:38:38 +05:30
.join(' ') || undefined;