115 lines
3.1 KiB
TypeScript
115 lines
3.1 KiB
TypeScript
import type {
|
|
BacklogFilterState,
|
|
BacklogTicketTreeNode,
|
|
} from '@/types/backlog';
|
|
import type { TicketModel } from '@/types/models/ticket-model';
|
|
|
|
export function buildBacklogTicketTree(
|
|
tickets: TicketModel[],
|
|
): BacklogTicketTreeNode[] {
|
|
const nodesByTicketId = new Map<number, BacklogTicketTreeNode>();
|
|
|
|
for (const ticket of tickets) {
|
|
nodesByTicketId.set(ticket.id, {
|
|
children: [],
|
|
matchesFilters: true,
|
|
ticket,
|
|
});
|
|
}
|
|
|
|
const roots: BacklogTicketTreeNode[] = [];
|
|
|
|
for (const ticket of tickets) {
|
|
const node = nodesByTicketId.get(ticket.id);
|
|
|
|
if (!node) {
|
|
continue;
|
|
}
|
|
|
|
const parentNode =
|
|
ticket.parent_ticket_id === null
|
|
? undefined
|
|
: nodesByTicketId.get(ticket.parent_ticket_id);
|
|
|
|
if (parentNode) {
|
|
parentNode.children.push(node);
|
|
} else {
|
|
roots.push(node);
|
|
}
|
|
}
|
|
|
|
return roots;
|
|
}
|
|
|
|
export function filterBacklogTicketTree(
|
|
nodes: BacklogTicketTreeNode[],
|
|
filters: BacklogFilterState,
|
|
): BacklogTicketTreeNode[] {
|
|
return nodes.flatMap((node) => {
|
|
const children = filterBacklogTicketTree(node.children, filters);
|
|
const matchesFilters = ticketMatchesFilters(node.ticket, filters);
|
|
|
|
if (!matchesFilters && children.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
return [
|
|
{
|
|
...node,
|
|
children,
|
|
matchesFilters,
|
|
},
|
|
];
|
|
});
|
|
}
|
|
|
|
export function countBacklogTreeTickets(
|
|
nodes: BacklogTicketTreeNode[],
|
|
): number {
|
|
return nodes.reduce(
|
|
(count, node) => count + 1 + countBacklogTreeTickets(node.children),
|
|
0,
|
|
);
|
|
}
|
|
|
|
export function expandableBacklogTicketIds(
|
|
nodes: BacklogTicketTreeNode[],
|
|
): number[] {
|
|
return nodes.flatMap((node) => [
|
|
...(node.children.length > 0 ? [node.ticket.id] : []),
|
|
...expandableBacklogTicketIds(node.children),
|
|
]);
|
|
}
|
|
|
|
function ticketMatchesFilters(
|
|
ticket: TicketModel,
|
|
filters: BacklogFilterState,
|
|
): boolean {
|
|
const query = filters.query.trim().toLocaleLowerCase();
|
|
const searchableText = [
|
|
ticket.ticket_key,
|
|
ticket.title,
|
|
ticket.description,
|
|
ticket.ticket_type.name,
|
|
ticket.ticket_priority.name,
|
|
ticket.ticket_status?.name,
|
|
ticket.assignee?.name,
|
|
]
|
|
.filter((value): value is string => Boolean(value))
|
|
.join(' ')
|
|
.toLocaleLowerCase();
|
|
|
|
return (
|
|
(query === '' || searchableText.includes(query)) &&
|
|
(filters.typeId === '' || ticket.ticket_type_id === filters.typeId) &&
|
|
(filters.priorityId === '' ||
|
|
ticket.ticket_priority_id === filters.priorityId) &&
|
|
(filters.statusId === '' ||
|
|
ticket.ticket_status_id === filters.statusId) &&
|
|
(filters.assigneeId === '' ||
|
|
(filters.assigneeId === 'unassigned'
|
|
? ticket.assignee_user_id === null
|
|
: ticket.assignee_user_id === filters.assigneeId))
|
|
);
|
|
}
|