feat(KAN-29): rework backlog ticket tree
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
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))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user