debian-mirror-gitlab/app/assets/javascripts/boards/boards_util.js

317 lines
8.6 KiB
JavaScript
Raw Normal View History

2021-04-29 21:17:54 +05:30
import { sortBy, cloneDeep } from 'lodash';
2022-05-07 20:08:51 +05:30
import { TYPE_BOARD, TYPE_ITERATION, TYPE_MILESTONE, TYPE_USER } from '~/graphql_shared/constants';
import { isGid, convertToGraphQLId } from '~/graphql_shared/utils';
2022-01-26 12:08:38 +05:30
import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants';
2020-10-24 23:57:45 +05:30
2019-07-31 22:56:46 +05:30
export function getMilestone() {
return null;
}
2021-02-22 17:27:13 +05:30
export function updateListPosition(listObj) {
const { listType } = listObj;
let { position } = listObj;
if (listType === ListType.closed) {
position = Infinity;
} else if (listType === ListType.backlog) {
position = -Infinity;
}
return { ...listObj, position };
}
2021-01-03 14:25:43 +05:30
export function formatBoardLists(lists) {
2021-02-22 17:27:13 +05:30
return lists.nodes.reduce((map, list) => {
2021-01-03 14:25:43 +05:30
return {
...map,
2021-02-22 17:27:13 +05:30
[list.id]: updateListPosition(list),
2021-01-03 14:25:43 +05:30
};
}, {});
}
2020-11-24 15:15:51 +05:30
export function formatIssue(issue) {
2021-02-22 17:27:13 +05:30
return {
2020-11-24 15:15:51 +05:30
...issue,
labels: issue.labels?.nodes || [],
assignees: issue.assignees?.nodes || [],
2021-02-22 17:27:13 +05:30
};
2020-11-24 15:15:51 +05:30
}
2020-10-24 23:57:45 +05:30
export function formatListIssues(listIssues) {
2021-04-17 20:07:23 +05:30
const boardItems = {};
2020-11-24 15:15:51 +05:30
const listData = listIssues.nodes.reduce((map, list) => {
2021-03-08 18:12:59 +05:30
let sortedIssues = list.issues.edges.map((issueNode) => ({
2021-01-03 14:25:43 +05:30
...issueNode.node,
}));
2021-11-18 22:05:49 +05:30
if (list.listType !== ListType.closed) {
sortedIssues = sortBy(sortedIssues, 'relativePosition');
}
2021-01-03 14:25:43 +05:30
2020-10-24 23:57:45 +05:30
return {
...map,
2021-03-08 18:12:59 +05:30
[list.id]: sortedIssues.map((i) => {
2021-11-11 11:23:49 +05:30
const { id } = i;
2020-11-24 15:15:51 +05:30
2021-02-22 17:27:13 +05:30
const listIssue = {
2020-11-24 15:15:51 +05:30
...i,
labels: i.labels?.nodes || [],
assignees: i.assignees?.nodes || [],
2021-02-22 17:27:13 +05:30
};
2020-11-24 15:15:51 +05:30
2021-04-17 20:07:23 +05:30
boardItems[id] = listIssue;
2020-11-24 15:15:51 +05:30
return id;
}),
2020-10-24 23:57:45 +05:30
};
}, {});
2020-11-24 15:15:51 +05:30
2022-08-27 11:52:29 +05:30
return { listData, boardItems };
2021-01-03 14:25:43 +05:30
}
export function formatListsPageInfo(lists) {
const listData = lists.nodes.reduce((map, list) => {
return {
...map,
[list.id]: list.issues.pageInfo,
};
}, {});
return listData;
2020-11-24 15:15:51 +05:30
}
export function fullBoardId(boardId) {
2022-05-07 20:08:51 +05:30
if (!boardId) {
return null;
}
return convertToGraphQLId(TYPE_BOARD, boardId);
2020-11-24 15:15:51 +05:30
}
2021-03-08 18:12:59 +05:30
export function fullIterationId(id) {
2022-05-07 20:08:51 +05:30
return convertToGraphQLId(TYPE_ITERATION, id);
2021-03-08 18:12:59 +05:30
}
export function fullUserId(id) {
2022-05-07 20:08:51 +05:30
return convertToGraphQLId(TYPE_USER, id);
2021-03-08 18:12:59 +05:30
}
export function fullMilestoneId(id) {
2022-05-07 20:08:51 +05:30
return convertToGraphQLId(TYPE_MILESTONE, id);
2021-03-08 18:12:59 +05:30
}
2021-01-03 14:25:43 +05:30
export function fullLabelId(label) {
2021-12-11 22:18:48 +05:30
if (isGid(label.id)) {
return label.id;
}
2021-03-08 18:12:59 +05:30
if (label.project_id && label.project_id !== null) {
2021-01-03 14:25:43 +05:30
return `gid://gitlab/ProjectLabel/${label.id}`;
}
return `gid://gitlab/GroupLabel/${label.id}`;
}
2021-03-08 18:12:59 +05:30
export function formatIssueInput(issueInput, boardConfig) {
const { labelIds = [], assigneeIds = [] } = issueInput;
2022-06-21 17:19:12 +05:30
const { labels, assigneeId, milestoneId, weight } = boardConfig;
2021-03-08 18:12:59 +05:30
return {
...issueInput,
2021-11-11 11:23:49 +05:30
milestoneId:
milestoneId && milestoneId !== MilestoneIDs.ANY
? fullMilestoneId(milestoneId)
: issueInput?.milestoneId,
2021-03-08 18:12:59 +05:30
labelIds: [...labelIds, ...(labels?.map((l) => fullLabelId(l)) || [])],
assigneeIds: [...assigneeIds, ...(assigneeId ? [fullUserId(assigneeId)] : [])],
2022-06-21 17:19:12 +05:30
weight: weight > -1 ? weight : undefined,
2021-03-08 18:12:59 +05:30
};
}
2021-04-29 21:17:54 +05:30
export function shouldCloneCard(fromListType, toListType) {
const involvesClosed = fromListType === ListType.closed || toListType === ListType.closed;
const involvesBacklog = fromListType === ListType.backlog || toListType === ListType.backlog;
if (involvesClosed || involvesBacklog) {
return false;
}
if (fromListType !== toListType) {
return true;
}
return false;
}
export function getMoveData(state, params) {
const { boardItems, boardItemsByListId, boardLists } = state;
const { itemId, fromListId, toListId } = params;
const fromListType = boardLists[fromListId].listType;
const toListType = boardLists[toListId].listType;
return {
reordering: fromListId === toListId,
shouldClone: shouldCloneCard(fromListType, toListType),
itemNotInToList: !boardItemsByListId[toListId].includes(itemId),
originalIssue: cloneDeep(boardItems[itemId]),
originalIndex: boardItemsByListId[fromListId].indexOf(itemId),
...params,
};
}
2021-04-17 20:07:23 +05:30
export function moveItemListHelper(item, fromList, toList) {
2021-11-18 22:05:49 +05:30
const updatedItem = cloneDeep(item);
2021-02-22 17:27:13 +05:30
if (
toList.listType === ListType.label &&
2021-04-17 20:07:23 +05:30
!updatedItem.labels.find((label) => label.id === toList.label.id)
2021-02-22 17:27:13 +05:30
) {
2021-04-17 20:07:23 +05:30
updatedItem.labels.push(toList.label);
2020-11-24 15:15:51 +05:30
}
2021-02-22 17:27:13 +05:30
if (fromList?.label && fromList.listType === ListType.label) {
2021-04-17 20:07:23 +05:30
updatedItem.labels = updatedItem.labels.filter((label) => fromList.label.id !== label.id);
2020-11-24 15:15:51 +05:30
}
2021-02-22 17:27:13 +05:30
if (
toList.listType === ListType.assignee &&
2021-04-17 20:07:23 +05:30
!updatedItem.assignees.find((assignee) => assignee.id === toList.assignee.id)
2021-02-22 17:27:13 +05:30
) {
2021-04-17 20:07:23 +05:30
updatedItem.assignees.push(toList.assignee);
2021-02-22 17:27:13 +05:30
}
if (fromList?.assignee && fromList.listType === ListType.assignee) {
2021-04-17 20:07:23 +05:30
updatedItem.assignees = updatedItem.assignees.filter(
2021-03-08 18:12:59 +05:30
(assignee) => assignee.id !== fromList.assignee.id,
2021-02-22 17:27:13 +05:30
);
2020-11-24 15:15:51 +05:30
}
2021-02-22 17:27:13 +05:30
2021-04-17 20:07:23 +05:30
return updatedItem;
2021-02-22 17:27:13 +05:30
}
export function isListDraggable(list) {
return list.listType !== ListType.backlog && list.listType !== ListType.closed;
}
2020-11-24 15:15:51 +05:30
2021-09-04 01:27:46 +05:30
export const FiltersInfo = {
assigneeUsername: {
negatedSupport: true,
2022-01-26 12:08:38 +05:30
remap: (k, v) => (v === AssigneeFilterType.any ? 'assigneeWildcardId' : k),
2021-09-04 01:27:46 +05:30
},
assigneeId: {
// assigneeId should be renamed to assigneeWildcardId.
// Classic boards used 'assigneeId'
remap: () => 'assigneeWildcardId',
},
assigneeWildcardId: {
negatedSupport: false,
transform: (val) => val.toUpperCase(),
},
authorUsername: {
negatedSupport: true,
},
labelName: {
negatedSupport: true,
},
milestoneTitle: {
negatedSupport: true,
2022-01-26 12:08:38 +05:30
remap: (k, v) => (Object.values(MilestoneFilterType).includes(v) ? 'milestoneWildcardId' : k),
},
milestoneWildcardId: {
negatedSupport: true,
transform: (val) => val.toUpperCase(),
2021-09-04 01:27:46 +05:30
},
myReactionEmoji: {
negatedSupport: true,
},
releaseTag: {
negatedSupport: true,
},
2021-10-27 15:23:28 +05:30
types: {
negatedSupport: true,
},
2022-01-26 12:08:38 +05:30
confidential: {
negatedSupport: false,
transform: (val) => val === 'yes',
},
2021-09-04 01:27:46 +05:30
search: {
negatedSupport: false,
},
};
/**
* @param {Object} filters - ex. { search: "foobar", "not[authorUsername]": "root", }
* @returns {Object} - ex. [ ["search", "foobar", false], ["authorUsername", "root", true], ]
*/
const parseFilters = (filters) => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
const isNegated = (x) => x.startsWith('not[') && x.endsWith(']');
return Object.entries(filters).map(([k, v]) => {
const isNot = isNegated(k);
const filterKey = isNot ? k.slice(4, -1) : k;
return [filterKey, v, isNot];
});
};
/**
* Returns an object of filter key/value pairs used as variables in GraphQL requests.
* (warning: filter values are not validated)
*
* @param {Object} objParam.filters - filters extracted from url params. ex. { search: "foobar", "not[authorUsername]": "root", }
* @param {string} objParam.issuableType - issuable type e.g., issue.
* @param {Object} objParam.filterInfo - data on filters such as how to transform filter value, if filter can be negated, etc.
* @param {Object} objParam.filterFields - data on what filters are available for given issuableType (based on GraphQL schema)
*/
export const filterVariables = ({ filters, issuableType, filterInfo, filterFields }) =>
parseFilters(filters)
.map(([k, v, negated]) => {
// for legacy reasons, some filters need to be renamed to correct GraphQL fields.
const remapAvailable = filterInfo[k]?.remap;
const remappedKey = remapAvailable ? filterInfo[k].remap(k, v) : k;
return [remappedKey, v, negated];
})
.filter(([k, , negated]) => {
// remove unsupported filters (+ check if the filters support negation)
const supported = filterFields[issuableType].includes(k);
if (supported) {
return negated ? filterInfo[k].negatedSupport : true;
}
return false;
})
.map(([k, v, negated]) => {
// if the filter value needs a special transformation, apply it (e.g., capitalization)
const transform = filterInfo[k]?.transform;
const newVal = transform ? transform(v) : v;
return [k, newVal, negated];
})
.reduce(
(acc, [k, v, negated]) => {
return negated
? {
...acc,
not: {
...acc.not,
[k]: v,
},
}
: {
...acc,
[k]: v,
};
},
{ not: {} },
);
2021-06-08 01:23:25 +05:30
2021-02-22 17:27:13 +05:30
// EE-specific feature. Find the implementation in the `ee/`-folder
export function transformBoardConfig() {
return '';
2020-10-24 23:57:45 +05:30
}
2019-07-31 22:56:46 +05:30
export default {
getMilestone,
2020-11-24 15:15:51 +05:30
formatIssue,
2020-10-24 23:57:45 +05:30
formatListIssues,
2020-11-24 15:15:51 +05:30
fullBoardId,
2021-01-03 14:25:43 +05:30
fullLabelId,
2021-03-08 18:12:59 +05:30
fullIterationId,
2021-02-22 17:27:13 +05:30
isListDraggable,
2019-07-31 22:56:46 +05:30
};