merge newui branch
This commit is contained in:
113
ui/src/settings/crons/cronsList.js
Normal file
113
ui/src/settings/crons/cronsList.js
Normal file
@@ -0,0 +1,113 @@
|
||||
export function cronsList(propsArg = {}) {
|
||||
const props = store({
|
||||
reset: null,
|
||||
});
|
||||
|
||||
const watchers = app.utils.extendStore(props, propsArg);
|
||||
|
||||
const data = store({
|
||||
isLoading: false,
|
||||
isRunning: {},
|
||||
crons: [],
|
||||
});
|
||||
|
||||
async function loadCrons() {
|
||||
data.isLoading = true;
|
||||
|
||||
try {
|
||||
data.crons = await app.pb.crons.getFullList();
|
||||
data.isLoading = false;
|
||||
} catch (err) {
|
||||
if (!err.isAbort) {
|
||||
app.checkApiError(err);
|
||||
data.isLoading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function runCron(jobId) {
|
||||
if (!jobId || data.isRunning[jobId]) {
|
||||
return;
|
||||
}
|
||||
|
||||
data.isRunning[jobId] = true;
|
||||
|
||||
try {
|
||||
await app.pb.crons.run(jobId);
|
||||
app.toasts.success(`Successfully triggered "${jobId}".`);
|
||||
data.isRunning[jobId] = false;
|
||||
} catch (err) {
|
||||
if (!err.isAbort) {
|
||||
ApiClient.error(err);
|
||||
data.isRunning[jobId] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t.div(
|
||||
{
|
||||
pbEvent: "cronsList",
|
||||
className: "list",
|
||||
onmount: () => {
|
||||
watchers.push(
|
||||
watch(() => props.reset, () => {
|
||||
loadCrons();
|
||||
}),
|
||||
);
|
||||
},
|
||||
onunmount: () => {
|
||||
watchers.forEach((w) => w?.unwatch());
|
||||
},
|
||||
},
|
||||
() => {
|
||||
if (!data.isLoading || data.crons.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const skeletons = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
skeletons.push(
|
||||
t.div({ rid: "skeleton_" + i, className: "list-item" }, t.div({ className: "skeleton-loader" })),
|
||||
);
|
||||
}
|
||||
return skeletons;
|
||||
},
|
||||
t.div(
|
||||
{
|
||||
hidden: () => data.isLoading || data.crons.length,
|
||||
className: "list-item",
|
||||
},
|
||||
t.div({ className: "content block txt-hint" }, "No registered crons found."),
|
||||
),
|
||||
() => {
|
||||
return data.crons.map((cron) => {
|
||||
return t.div(
|
||||
{ className: () => `list-item ${data.isLoading ? "faded" : ""}` },
|
||||
t.div(
|
||||
{ className: "content" },
|
||||
t.span({
|
||||
className: "cron-id txt-code txt-ellipsis",
|
||||
title: () => cron.id,
|
||||
textContent: () => cron.id,
|
||||
}),
|
||||
),
|
||||
t.small({ className: "cron-expression txt-hint txt-nowrap txt-code" }, () => cron.expression),
|
||||
t.nav(
|
||||
{ hidden: () => data.isLoading, className: "actions" },
|
||||
t.button(
|
||||
{
|
||||
type: "button",
|
||||
ariaDescription: app.attrs.tooltip("Run"),
|
||||
className: () =>
|
||||
`btn sm circle secondary transparent ${data.isRunning[cron.id] ? "loading" : ""}`,
|
||||
disabled: () => data.isRunning[cron.id],
|
||||
onclick: () => runCron(cron.id),
|
||||
},
|
||||
t.i({ className: "ri-play-large-line" }),
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
63
ui/src/settings/crons/pageCronsSettings.js
Normal file
63
ui/src/settings/crons/pageCronsSettings.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import { settingsSidebar } from "../settingsSidebar";
|
||||
import { cronsList } from "./cronsList";
|
||||
|
||||
export function pageCronsSettings(route) {
|
||||
app.store.title = "Crons";
|
||||
|
||||
const data = store({
|
||||
resetList: null,
|
||||
});
|
||||
|
||||
function resetCronsList() {
|
||||
data.resetList = Date.now();
|
||||
}
|
||||
|
||||
return t.div(
|
||||
{ pbEvent: "pageCronsSettings", className: "page" },
|
||||
settingsSidebar(),
|
||||
t.div(
|
||||
{ className: "page-content full-height" },
|
||||
t.header(
|
||||
{ className: "page-header" },
|
||||
t.nav(
|
||||
{ className: "breadcrumbs" },
|
||||
t.div({ className: "breadcrumb-item" }, "Settings"),
|
||||
t.div({ className: "breadcrumb-item" }, () => app.store.title),
|
||||
),
|
||||
),
|
||||
t.div(
|
||||
{ className: "wrapper m-b-base" },
|
||||
t.div(
|
||||
{ className: "flex gap-10 m-b-sm" },
|
||||
t.div({ className: "txt-lg" }, "Registered app cron jobs"),
|
||||
app.components.refreshButton({
|
||||
className: "btn sm transparent secondary circle",
|
||||
onclick: resetCronsList,
|
||||
}),
|
||||
),
|
||||
cronsList({
|
||||
reset: () => data.resetList,
|
||||
}),
|
||||
t.div(
|
||||
{ className: "txt-sm txt-hint m-t-sm" },
|
||||
"App cron jobs can be registered only programmatically with ",
|
||||
t.a({
|
||||
href: `${import.meta.env.PB_DOCS_URL}/go-jobs-scheduling/`,
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
textContent: "Go",
|
||||
}),
|
||||
" or ",
|
||||
t.a({
|
||||
href: `${import.meta.env.PB_DOCS_URL}/js-jobs-scheduling/`,
|
||||
target: "_blank",
|
||||
rel: "noopener noreferrer",
|
||||
textContent: "JavaScript",
|
||||
}),
|
||||
".",
|
||||
),
|
||||
),
|
||||
t.footer({ className: "page-footer" }, app.components.credits()),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user