- cards grouped by collect / download / organize / maintain / agent checks: switch, in-place interval or cron edit, last result and duration, next-run countdown, failure reason, run now, skip next, reset to default, history drawer - top stats (enabled / failed / running / total) and a scheduler-offline notice; refreshed by the schedule.changed SSE event with a 30s fallback - agent check cards show goal template, trigger, budget and allowlist size; create / edit dialog (read-only capabilities, budget, trigger, interval, report to inbox) - nav entry in the system group, route and smoke route Co-Authored-By: Claude Opus 5.5 <[email protected]>
87 lines
2.5 KiB
TypeScript
87 lines
2.5 KiB
TypeScript
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
import type {
|
|
AgentCheckCreate,
|
|
ScheduleBoard,
|
|
ScheduledJob,
|
|
ScheduledJobRun,
|
|
ScheduleRunNowResult,
|
|
ScheduleUpdate,
|
|
} from "@umt/contracts";
|
|
import { apiData } from "~/lib/api.ts";
|
|
import { qk } from "~/lib/query-keys.ts";
|
|
|
|
// 定时任务看板(W7-C):GET /schedules 一次取全部卡片;写操作成功后整块失效(SSE schedule.changed 也会触发)。
|
|
|
|
const AREA = "schedules";
|
|
|
|
export function useScheduleBoard() {
|
|
return useQuery({
|
|
queryKey: qk.list(AREA),
|
|
queryFn: ({ signal }) => apiData<ScheduleBoard>("/schedules", { signal }),
|
|
// SSE 是主路径,30 秒兜底
|
|
refetchInterval: 30_000,
|
|
});
|
|
}
|
|
|
|
export function useScheduleRuns(id: string | null, enabled = true) {
|
|
return useQuery({
|
|
queryKey: qk.sub(AREA, "runs", id ?? ""),
|
|
queryFn: ({ signal }) =>
|
|
apiData<{ items: ScheduledJobRun[] }>("/schedules/{id}/runs", {
|
|
pathParams: { id: id ?? "" },
|
|
params: { limit: 50 },
|
|
signal,
|
|
}),
|
|
enabled: !!id && enabled,
|
|
});
|
|
}
|
|
|
|
function useInvalidate() {
|
|
const qc = useQueryClient();
|
|
return () => qc.invalidateQueries({ queryKey: qk.all(AREA) });
|
|
}
|
|
|
|
export function useUpdateSchedule() {
|
|
const invalidate = useInvalidate();
|
|
return useMutation({
|
|
mutationFn: ({ id, body }: { id: string; body: ScheduleUpdate }) =>
|
|
apiData<ScheduledJob>("/schedules/{id}", { method: "PATCH", pathParams: { id }, body }),
|
|
onSettled: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useRunScheduleNow() {
|
|
const invalidate = useInvalidate();
|
|
return useMutation({
|
|
mutationFn: ({ id }: { id: string }) =>
|
|
apiData<ScheduleRunNowResult>("/schedules/{id}/run-now", { method: "POST", pathParams: { id } }),
|
|
onSettled: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useSkipSchedule() {
|
|
const invalidate = useInvalidate();
|
|
return useMutation({
|
|
mutationFn: ({ id }: { id: string }) =>
|
|
apiData<ScheduledJob>("/schedules/{id}/skip", { method: "POST", pathParams: { id } }),
|
|
onSettled: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useCreateAgentCheck() {
|
|
const invalidate = useInvalidate();
|
|
return useMutation({
|
|
mutationFn: (body: AgentCheckCreate) => apiData<ScheduledJob>("/schedules", { method: "POST", body }),
|
|
onSettled: invalidate,
|
|
});
|
|
}
|
|
|
|
export function useDeleteSchedule() {
|
|
const invalidate = useInvalidate();
|
|
return useMutation({
|
|
mutationFn: ({ id }: { id: string }) =>
|
|
apiData<{ ok: true }>("/schedules/{id}", { method: "DELETE", pathParams: { id } }),
|
|
onSettled: invalidate,
|
|
});
|
|
}
|