Files
Alpha/src/services/hierarchy.ts
T

116 lines
3.3 KiB
TypeScript

export type ItemType = 'E' | 'F' | 'U' | 'T' | 'B'
export interface HierarchyInfo {
fullPath: string
items: { type: ItemType; number: number }[]
epicCode: string | null
featureCode: string | null
itemCode: string | null
}
const TYPE_LABELS: Record<ItemType, string> = {
E: 'Épica',
F: 'Feature',
U: 'HU',
T: 'Tarea',
B: 'Bug',
}
const TYPE_COLORS: Record<ItemType, string> = {
E: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400',
F: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
U: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',
T: 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400',
B: 'bg-rose-100 text-rose-700 dark:bg-rose-900/30 dark:text-rose-400',
}
const TYPE_ICONS: Record<ItemType, string> = {
E: '📦',
F: '⭐',
U: '📋',
T: '⚙️',
B: '🐛',
}
export function getTypeLabel(type: ItemType): string {
return TYPE_LABELS[type] || type
}
export function getTypeColor(type: ItemType): string {
return TYPE_COLORS[type] || 'bg-muted text-muted-foreground'
}
export function getTypeIcon(type: ItemType): string {
return TYPE_ICONS[type] || '📄'
}
const HIERARCHY_REGEX = /\[(([EFUTB]\d+)(?:-([EFUTB]\d+))?(?:-([EFUTB]\d+))?)\]/i
export function parseHierarchy(title: string): HierarchyInfo | null {
const match = title.match(HIERARCHY_REGEX)
if (!match) return null
const fullPath = match[1].toUpperCase()
const segments = [match[2]]
if (match[3]) segments.push(match[3])
if (match[4]) segments.push(match[4])
const items = segments.map(s => ({
type: s[0] as ItemType,
number: parseInt(s.slice(1), 10),
}))
const epic = items.find(i => i.type === 'E')
const feature = items.find(i => i.type === 'F')
const item = items[items.length - 1]
return {
fullPath,
items,
epicCode: epic ? `E${String(epic.number).padStart(2, '0')}` : null,
featureCode: feature ? `F${String(feature.number).padStart(2, '0')}` : null,
itemCode: item ? `${item.type}${String(item.number).padStart(2, '0')}` : null,
}
}
export function stripHierarchy(title: string): string {
return title.replace(HIERARCHY_REGEX, '').trim()
}
export function getItemType(title: string): ItemType {
const h = parseHierarchy(title)
if (h?.items.length) return h.items[h.items.length - 1].type
return 'U'
}
export function buildHierarchyPath(
epicNum?: number,
featureNum?: number,
itemType?: ItemType,
itemNum?: number,
): string {
const parts: string[] = []
if (epicNum !== undefined) parts.push(`E${String(epicNum).padStart(2, '0')}`)
if (featureNum !== undefined) parts.push(`F${String(featureNum).padStart(2, '0')}`)
if (itemType && itemNum !== undefined) parts.push(`${itemType}${String(itemNum).padStart(2, '0')}`)
return parts.length ? `[${parts.join('-')}]` : ''
}
export function buildFullTitle(
name: string,
epicNum?: number,
featureNum?: number,
itemType?: ItemType,
itemNum?: number,
): string {
const path = buildHierarchyPath(epicNum, featureNum, itemType, itemNum)
return path ? `${path} ${name}` : name
}
export function extractItemNumber(title: string, type: ItemType): number | null {
const h = parseHierarchy(title)
if (!h) return null
const found = h.items.find(i => i.type === type)
return found?.number ?? null
}