merge newui branch

This commit is contained in:
Gani Georgiev
2026-04-18 16:29:34 +03:00
parent 58f605e90c
commit 4c44044c0c
804 changed files with 58660 additions and 56663 deletions

View 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" }),
),
),
);
});
},
);
}

View 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()),
),
);
}