2026-10-28 12:48:40 +04:00

60 lines
3.0 KiB
TypeScript

import { useMemo } from "react";
import { useSearchParams } from "react-router";
import { Checkbox } from "../../../ui/index.ts";
import type { FeedResult, FeedSource, FeedSourceInput } from "../../types.ts";
import { useChanges } from "../api/queries.ts";
import type { ChangeKind } from "../api/types.ts";
import { groupByIssue, KINDS, kindsParam, parseKinds } from "./events.ts";
import { FeedGroup } from "./FeedGroup.tsx";
import "./feed.css";
// виды событий — настройка источника, хранится в адресе (`kinds=`); пустой набор — явное «none», иначе адрес вернул бы умолчание
function Kinds() {
const [params, setParams] = useSearchParams();
const kinds = useMemo(() => parseKinds(params.get("kinds")), [params]);
const toggle = (k: ChangeKind, on: boolean) => {
const next = on ? [...kinds, k] : kinds.filter((x) => x !== k);
const value = next.length ? kindsParam(next) : "none";
setParams((p) => {
const n = new URLSearchParams(p);
if (value) n.set("kinds", value); else n.delete("kinds");
return n;
});
};
return (
<div className="feed__kinds" role="group" aria-label="Виды событий">
{KINDS.map((k) => (
<Checkbox key={k.value} label={k.label} checked={kinds.includes(k.value)}
onChange={(e) => toggle(k.value, e.currentTarget.checked)} />
))}
</div>
);
}
const EMPTY: FeedResult = { groups: [], count: 0, window: null, pending: false, error: null, truncated: false, notice: null };
function useJiraFeed(input: FeedSourceInput): FeedResult {
const [params] = useSearchParams();
const kinds = useMemo(() => parseKinds(params.get("kinds")), [params]);
// границы считает запрос в момент выполнения (C-1): ключ кэша — проект, период и виды
const q = useChanges({ project: input.project.label, period: input.period, kinds: kinds.join(",") }, input.bounds,
input.enabled && kinds.length > 0);
const data = input.enabled && kinds.length > 0 ? q.data : undefined;
const updatedAt = q.dataUpdatedAt;
const groups = useMemo(() => {
const now = new Date(updatedAt || Date.now()); // «текущие сутки» для времени событий — на момент ответа
return groupByIssue(data?.changes ?? []).map((g) => ({
key: g.key, at: g.events[0]?.at ?? "", node: <FeedGroup group={g} now={now} />,
}));
}, [data, updatedAt]);
if (!input.enabled) return EMPTY;
if (!kinds.length) return { ...EMPTY, notice: "выберите хотя бы один вид" };
return {
groups, count: data?.changes.length ?? 0, window: data ? { since: data.since, until: data.until } : null,
pending: q.isPending, truncated: !!data?.truncated,
error: q.isError ? { message: (q.error as Error).message, retry: () => void q.refetch() } : null, notice: null,
};
}
export const jiraFeed: FeedSource = { available: () => true, useFeed: useJiraFeed, Controls: Kinds };