Andrey Epifancev 2cb454af8b refactor(web): модуль jira и оболочка из списка модулей (#87)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 23:05:02 +04:00

34 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<string, string> = { "блокирует начало": "начало", "блокирует завершение": "завершение" };
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<IssueDetail, "links_out" | "links_in">, linkTypes: Record<string, string>): [LinkGroupId, string, CardLink[]][] {
const logical = new Map(Object.entries(linkTypes).map(([name, jira]) => [jira, name]));
const groups: Record<LinkGroupId, CardLink[]> = { 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<string>, 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")}`;