33 lines
2.1 KiB
TypeScript
33 lines
2.1 KiB
TypeScript
import type { JiraSettings } from "../api/types.ts";
|
|
import type { BoardSnapshot } from "../api/types.ts";
|
|
|
|
export type Opt = { value: string; label: string };
|
|
const uniq = (xs: (string | null | undefined)[]) => [...new Set(xs.filter((x): x is string => !!x))].sort((a, b) => a.localeCompare(b, "ru"));
|
|
const isEpic = (t: string | null) => /^(Фича|Epic|Эпик)$/i.test(t ?? "");
|
|
|
|
export const epicNames = (snap: BoardSnapshot | undefined): Record<string, string> =>
|
|
Object.fromEntries((snap?.issues ?? []).filter((i) => isEpic(i.issue_type)).map((i) => [i.key, i.summary || i.key]));
|
|
|
|
export const dueOf = (i: { planned_end: string | null; duedate: string | null }) => (i.planned_end || i.duedate || "").slice(0, 10);
|
|
|
|
// T13.1: значение фильтра из адреса, которого нет среди вариантов (закрытый спринт, ушедший исполнитель), видно в списке;
|
|
// пока снимок доски не загружен, пометку не ставим (R-4)
|
|
export const withCurrent = (opts: Opt[], value: string | undefined, loaded: boolean): Opt[] =>
|
|
!value || opts.some((o) => o.value === value) ? opts
|
|
: [{ value, label: loaded ? `${value} (нет на доске)` : value }, ...opts];
|
|
|
|
// варианты выпадающих списков фильтров: из снимка /board и словарей настроек, без новых маршрутов API
|
|
export function filterOptions(snap: BoardSnapshot | undefined, s: JiraSettings | undefined) {
|
|
const issues = snap?.issues ?? [];
|
|
const names = epicNames(snap);
|
|
const opt = (xs: string[]): Opt[] => xs.map((v) => ({ value: v, label: v }));
|
|
return {
|
|
sprint: opt(uniq(issues.map((i) => i.sprint))),
|
|
assignee: opt(uniq(issues.map((i) => i.assignee))),
|
|
work_kind: opt(uniq([...Object.keys(s?.kind_labels ?? {}), ...issues.map((i) => i.work_kind)])),
|
|
status: opt(uniq(issues.map((i) => i.status))),
|
|
label: opt(uniq(issues.flatMap((i) => i.labels))),
|
|
epic: uniq(issues.map((i) => i.epic_key)).map((k) => ({ value: k, label: names[k] ?? k })),
|
|
};
|
|
}
|