60 lines
3.0 KiB
TypeScript
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 };
|