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(); 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)) ); }