mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 21:48:27 +00:00
Web UI: add full cron edit parity, all-jobs run history, and compact filters (openclaw#24155) thanks @Takhoffman
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check - pnpm test:macmini Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { CronJobCreate, CronJobPatch } from "../types.js";
|
||||
import type { CronJob, CronJobCreate, CronJobPatch } from "../types.js";
|
||||
import {
|
||||
applyJobPatch,
|
||||
computeJobNextRunAtMs,
|
||||
@@ -22,6 +22,29 @@ import {
|
||||
wake,
|
||||
} from "./timer.js";
|
||||
|
||||
type CronJobsEnabledFilter = "all" | "enabled" | "disabled";
|
||||
type CronJobsSortBy = "nextRunAtMs" | "updatedAtMs" | "name";
|
||||
type CronSortDir = "asc" | "desc";
|
||||
|
||||
export type CronListPageOptions = {
|
||||
includeDisabled?: boolean;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
query?: string;
|
||||
enabled?: CronJobsEnabledFilter;
|
||||
sortBy?: CronJobsSortBy;
|
||||
sortDir?: CronSortDir;
|
||||
};
|
||||
|
||||
export type CronListPageResult = {
|
||||
jobs: ReturnType<typeof sortJobs>;
|
||||
total: number;
|
||||
offset: number;
|
||||
limit: number;
|
||||
hasMore: boolean;
|
||||
nextOffset: number | null;
|
||||
};
|
||||
|
||||
async function ensureLoadedForRead(state: CronServiceState) {
|
||||
await ensureLoaded(state, { skipRecompute: true });
|
||||
if (!state.store) {
|
||||
@@ -101,6 +124,80 @@ export async function list(state: CronServiceState, opts?: { includeDisabled?: b
|
||||
});
|
||||
}
|
||||
|
||||
function resolveEnabledFilter(opts?: CronListPageOptions): CronJobsEnabledFilter {
|
||||
if (opts?.enabled === "all" || opts?.enabled === "enabled" || opts?.enabled === "disabled") {
|
||||
return opts.enabled;
|
||||
}
|
||||
return opts?.includeDisabled ? "all" : "enabled";
|
||||
}
|
||||
|
||||
function sortJobs(jobs: CronJob[], sortBy: CronJobsSortBy, sortDir: CronSortDir) {
|
||||
const dir = sortDir === "desc" ? -1 : 1;
|
||||
return jobs.toSorted((a, b) => {
|
||||
let cmp = 0;
|
||||
if (sortBy === "name") {
|
||||
cmp = a.name.localeCompare(b.name, undefined, { sensitivity: "base" });
|
||||
} else if (sortBy === "updatedAtMs") {
|
||||
cmp = a.updatedAtMs - b.updatedAtMs;
|
||||
} else {
|
||||
const aNext = a.state.nextRunAtMs;
|
||||
const bNext = b.state.nextRunAtMs;
|
||||
if (typeof aNext === "number" && typeof bNext === "number") {
|
||||
cmp = aNext - bNext;
|
||||
} else if (typeof aNext === "number") {
|
||||
cmp = -1;
|
||||
} else if (typeof bNext === "number") {
|
||||
cmp = 1;
|
||||
} else {
|
||||
cmp = 0;
|
||||
}
|
||||
}
|
||||
if (cmp !== 0) {
|
||||
return cmp * dir;
|
||||
}
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
}
|
||||
|
||||
export async function listPage(state: CronServiceState, opts?: CronListPageOptions) {
|
||||
return await locked(state, async () => {
|
||||
await ensureLoadedForRead(state);
|
||||
const query = opts?.query?.trim().toLowerCase() ?? "";
|
||||
const enabledFilter = resolveEnabledFilter(opts);
|
||||
const sortBy = opts?.sortBy ?? "nextRunAtMs";
|
||||
const sortDir = opts?.sortDir ?? "asc";
|
||||
const source = state.store?.jobs ?? [];
|
||||
const filtered = source.filter((job) => {
|
||||
if (enabledFilter === "enabled" && !job.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (enabledFilter === "disabled" && job.enabled) {
|
||||
return false;
|
||||
}
|
||||
if (!query) {
|
||||
return true;
|
||||
}
|
||||
const haystack = [job.name, job.description ?? "", job.agentId ?? ""].join(" ").toLowerCase();
|
||||
return haystack.includes(query);
|
||||
});
|
||||
const sorted = sortJobs(filtered, sortBy, sortDir);
|
||||
const total = sorted.length;
|
||||
const offset = Math.max(0, Math.min(total, Math.floor(opts?.offset ?? 0)));
|
||||
const defaultLimit = total === 0 ? 50 : total;
|
||||
const limit = Math.max(1, Math.min(200, Math.floor(opts?.limit ?? defaultLimit)));
|
||||
const jobs = sorted.slice(offset, offset + limit);
|
||||
const nextOffset = offset + jobs.length;
|
||||
return {
|
||||
jobs,
|
||||
total,
|
||||
offset,
|
||||
limit,
|
||||
hasMore: nextOffset < total,
|
||||
nextOffset: nextOffset < total ? nextOffset : null,
|
||||
} satisfies CronListPageResult;
|
||||
});
|
||||
}
|
||||
|
||||
export async function add(state: CronServiceState, input: CronJobCreate) {
|
||||
return await locked(state, async () => {
|
||||
warnIfDisabled(state, "add");
|
||||
|
||||
Reference in New Issue
Block a user