import { isClosed } from "../../../api/closed.ts"; import type { IssueDetail } from "../../../api/types.ts"; export type LinkGroupId = "depends" | "blocks" | "related" | "other"; export interface CardLink { key: string; summary: string | null; status: string | null; tag: string | null } const GROUPS: [LinkGroupId, string][] = [["depends", "Зависит от"], ["blocks", "Блокирует"], ["related", "Связана с"], ["other", "Прочие"]]; const BLOCK_TAGS: Record = { "блокирует начало": "начало", "блокирует завершение": "завершение" }; const byKey = (a: CardLink, b: CardLink) => a.key.localeCompare(b.key, "ru", { numeric: true }) || (a.tag ?? "").localeCompare(b.tag ?? "", "ru"); // связи карточки по смыслу (design 17): логическое имя типа — из link_types настроек (имя типа Jira → логическое) export function groupLinks(d: Pick, linkTypes: Record): [LinkGroupId, string, CardLink[]][] { const logical = new Map(Object.entries(linkTypes).map(([name, jira]) => [jira, name])); const groups: Record = { depends: [], blocks: [], related: [], other: [] }; const add = (dir: "out" | "in", key: string, jiraType: string, summary: string | null, status: string | null) => { const name = logical.get(jiraType); if (name && BLOCK_TAGS[name]) groups[dir === "in" ? "depends" : "blocks"].push({ key, summary, status, tag: BLOCK_TAGS[name] }); else if (name === "связанные") { if (!groups.related.some((l) => l.key === key)) groups.related.push({ key, summary, status, tag: null }); } else groups.other.push({ key, summary, status, tag: `${dir === "out" ? "→" : "←"} ${name ?? jiraType}` }); }; for (const l of d.links_out) add("out", l.target_key, l.link_type, l.summary, l.status); for (const l of d.links_in) add("in", l.source_key, l.link_type, l.summary, l.status); return GROUPS.filter(([id]) => groups[id].length).map(([id, title]) => [id, title, groups[id].sort(byKey)]); } // просрочено — срок раньше сегодня, а статус Jira не из closed_statuses (hide-closed; было — по статусу по факту, R-2 #37) export function isOverdue(due: string, status: string | null | undefined, closed: Set, today: string): boolean { return !!due && due < today && !isClosed(status, closed); } export const todayISO = (d = new Date()): string => `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;