@rebasepro/studio 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ApiExplorer-pYMZflL3.js → ApiExplorer-CCtczj3F.js} +15 -28
- package/dist/ApiExplorer-CCtczj3F.js.map +1 -0
- package/dist/ApiKeysView-B66TdzhH.js +1738 -0
- package/dist/ApiKeysView-B66TdzhH.js.map +1 -0
- package/dist/BackupsView-DmSMzAbT.js +368 -0
- package/dist/BackupsView-DmSMzAbT.js.map +1 -0
- package/dist/{BranchesView-D6Sz1a5f.js → BranchesView-DP1a87E-.js} +12 -8
- package/dist/BranchesView-DP1a87E-.js.map +1 -0
- package/dist/{CronJobsView-7hjgSYId.js → CronJobsView-TDErHTuK.js} +126 -60
- package/dist/CronJobsView-TDErHTuK.js.map +1 -0
- package/dist/{JSEditor-qiuwmcvk.js → JSEditor-B59pwg4c.js} +120 -74
- package/dist/JSEditor-B59pwg4c.js.map +1 -0
- package/dist/{LogsExplorer-Xq8K4oo6.js → LogsExplorer-BKM4yfP8.js} +48 -22
- package/dist/LogsExplorer-BKM4yfP8.js.map +1 -0
- package/dist/{RLSEditor-Djh43lBE.js → RLSEditor-BlLLV6xs.js} +237 -77
- package/dist/RLSEditor-BlLLV6xs.js.map +1 -0
- package/dist/{SQLEditor-TBw_K6CW.js → SQLEditor-1An7w0j6.js} +369 -294
- package/dist/SQLEditor-1An7w0j6.js.map +1 -0
- package/dist/{SchemaVisualizer-DtDzIUNy.js → SchemaVisualizer-DCLVfA5t.js} +20 -24
- package/dist/SchemaVisualizer-DCLVfA5t.js.map +1 -0
- package/dist/{StorageView-CMG1DOVX.js → StorageView-CbsHuCBf.js} +355 -225
- package/dist/StorageView-CbsHuCBf.js.map +1 -0
- package/dist/components/ApiExplorer/TryItPanel.d.ts +6 -1
- package/dist/components/ApiKeys/CreateApiKeyDialog.d.ts +21 -2
- package/dist/components/ApiKeys/scope-words.d.ts +37 -0
- package/dist/components/ApiKeys/scopes.d.ts +113 -0
- package/dist/components/AuthSimulationSelector.d.ts +5 -1
- package/dist/components/LogsExplorer/useLogTail.d.ts +9 -0
- package/dist/components/RLSEditor/policyListing.d.ts +44 -0
- package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +0 -1
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +6 -0
- package/dist/components/load-failure.d.ts +1 -1
- package/dist/components/useRunAsUsers.d.ts +25 -0
- package/dist/index.es.js +11 -11
- package/dist/{load-failure-C6OMJSGC.js → load-failure-Bd8lFIO7.js} +2 -2
- package/dist/load-failure-Bd8lFIO7.js.map +1 -0
- package/dist/useRunAsUsers-CLaAtXt6.js +122 -0
- package/dist/useRunAsUsers-CLaAtXt6.js.map +1 -0
- package/dist/utils/sql_utils.d.ts +90 -49
- package/package.json +9 -9
- package/dist/ApiExplorer-pYMZflL3.js.map +0 -1
- package/dist/ApiKeysView-qLSs47tw.js +0 -1239
- package/dist/ApiKeysView-qLSs47tw.js.map +0 -1
- package/dist/AuthSimulationSelector-BewmhMBw.js +0 -56
- package/dist/AuthSimulationSelector-BewmhMBw.js.map +0 -1
- package/dist/BackupsView-DYTAD6aW.js +0 -253
- package/dist/BackupsView-DYTAD6aW.js.map +0 -1
- package/dist/BranchesView-D6Sz1a5f.js.map +0 -1
- package/dist/CronJobsView-7hjgSYId.js.map +0 -1
- package/dist/JSEditor-qiuwmcvk.js.map +0 -1
- package/dist/LogsExplorer-Xq8K4oo6.js.map +0 -1
- package/dist/RLSEditor-Djh43lBE.js.map +0 -1
- package/dist/SQLEditor-TBw_K6CW.js.map +0 -1
- package/dist/SchemaVisualizer-DtDzIUNy.js.map +0 -1
- package/dist/StorageView-CMG1DOVX.js.map +0 -1
- package/dist/components/ApiKeys/permissions.d.ts +0 -66
- package/dist/load-failure-C6OMJSGC.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { t as classifyLoadFailure } from "./load-failure-
|
|
1
|
+
import { t as classifyLoadFailure } from "./load-failure-Bd8lFIO7.js";
|
|
2
2
|
import { t as LoadFailureView } from "./load-failure-view-CBTB_h-h.js";
|
|
3
3
|
import { useRebaseClient, useSnackbarController, useTranslation } from "@rebasepro/app";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
|
-
import { AlertCircleIcon, Button, CalendarIcon, CheckCircleIcon, Chip, CircularProgress, HistoryIcon, IconButton, PauseIcon, PlayIcon, RefreshCwIcon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
5
|
+
import { Alert, AlertCircleIcon, Button, CalendarIcon, CheckCircleIcon, Chip, CircularProgress, HistoryIcon, IconButton, PauseIcon, PlayIcon, RefreshCwIcon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
6
6
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/components/CronJobs/CronJobsView.tsx
|
|
8
8
|
function formatDuration(ms) {
|
|
@@ -27,6 +27,49 @@ function formatRelative(iso) {
|
|
|
27
27
|
}
|
|
28
28
|
return d.toLocaleString();
|
|
29
29
|
}
|
|
30
|
+
function problemsOf(listing) {
|
|
31
|
+
const rejected = listing.rejected ?? [];
|
|
32
|
+
return {
|
|
33
|
+
rejected,
|
|
34
|
+
filesFailed: Math.max(0, (listing.skipped ?? 0) - rejected.length)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The cron files that failed to load and the jobs the scheduler refused. None
|
|
39
|
+
* of them is scheduled, and none is in the list: without this a refused job
|
|
40
|
+
* was simply absent, and a project whose only job was refused read "No cron
|
|
41
|
+
* jobs registered".
|
|
42
|
+
*/
|
|
43
|
+
function CronLoadProblems({ problems }) {
|
|
44
|
+
const { t } = useTranslation();
|
|
45
|
+
if (problems.rejected.length === 0 && problems.filesFailed === 0) return null;
|
|
46
|
+
return /* @__PURE__ */ jsx(Alert, {
|
|
47
|
+
color: "error",
|
|
48
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
49
|
+
className: "flex flex-col gap-2",
|
|
50
|
+
children: [problems.rejected.map((job) => /* @__PURE__ */ jsxs("div", {
|
|
51
|
+
className: "flex flex-col",
|
|
52
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
53
|
+
variant: "body2",
|
|
54
|
+
className: "text-[13px] font-semibold",
|
|
55
|
+
children: t("studio_cron_job_refused", { job: job.name })
|
|
56
|
+
}), /* @__PURE__ */ jsxs(Typography, {
|
|
57
|
+
variant: "caption",
|
|
58
|
+
className: "font-mono text-[12px] break-words",
|
|
59
|
+
children: [
|
|
60
|
+
job.schedule,
|
|
61
|
+
" — ",
|
|
62
|
+
job.reason
|
|
63
|
+
]
|
|
64
|
+
})]
|
|
65
|
+
}, job.id)), problems.filesFailed > 0 && /* @__PURE__ */ jsx(Typography, {
|
|
66
|
+
variant: "body2",
|
|
67
|
+
className: "text-[13px]",
|
|
68
|
+
children: t("studio_cron_files_failed", { count: problems.filesFailed })
|
|
69
|
+
})]
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
}
|
|
30
73
|
var stateColors = {
|
|
31
74
|
idle: "bg-emerald-500",
|
|
32
75
|
running: "bg-blue-500",
|
|
@@ -39,6 +82,10 @@ function CronJobsView() {
|
|
|
39
82
|
const snackbar = useSnackbarController();
|
|
40
83
|
const { t } = useTranslation();
|
|
41
84
|
const [jobs, setJobs] = useState([]);
|
|
85
|
+
const [problems, setProblems] = useState({
|
|
86
|
+
rejected: [],
|
|
87
|
+
filesFailed: 0
|
|
88
|
+
});
|
|
42
89
|
const [loading, setLoading] = useState(true);
|
|
43
90
|
const [selectedId, setSelectedId] = useState(null);
|
|
44
91
|
const [logs, setLogs] = useState([]);
|
|
@@ -68,6 +115,7 @@ function CronJobsView() {
|
|
|
68
115
|
const res = await c.cron.listJobs();
|
|
69
116
|
if (!cancelled) {
|
|
70
117
|
setJobs(res.jobs);
|
|
118
|
+
setProblems(problemsOf(res));
|
|
71
119
|
setFailure(null);
|
|
72
120
|
}
|
|
73
121
|
} catch (e) {
|
|
@@ -126,6 +174,7 @@ function CronJobsView() {
|
|
|
126
174
|
try {
|
|
127
175
|
const res = await c.cron.listJobs();
|
|
128
176
|
setJobs(res.jobs);
|
|
177
|
+
setProblems(problemsOf(res));
|
|
129
178
|
setFailure(null);
|
|
130
179
|
} catch (e) {
|
|
131
180
|
setFailure(classifyLoadFailure(e));
|
|
@@ -151,10 +200,17 @@ function CronJobsView() {
|
|
|
151
200
|
if (!c?.cron) return;
|
|
152
201
|
setTriggering(id);
|
|
153
202
|
try {
|
|
154
|
-
await c.cron.triggerJob(id);
|
|
155
|
-
|
|
203
|
+
const { log } = await c.cron.triggerJob(id);
|
|
204
|
+
const job = jobs.find((j) => j.id === id)?.name ?? id;
|
|
205
|
+
snackbarRef.current.open(log.success ? {
|
|
156
206
|
type: "success",
|
|
157
|
-
message: "
|
|
207
|
+
message: t("studio_cron_run_succeeded", { job })
|
|
208
|
+
} : {
|
|
209
|
+
type: "error",
|
|
210
|
+
message: t("studio_cron_run_failed", {
|
|
211
|
+
job,
|
|
212
|
+
error: log.error ?? "—"
|
|
213
|
+
})
|
|
158
214
|
});
|
|
159
215
|
await refreshJobs();
|
|
160
216
|
if (selectedId === id) refreshLogs(id);
|
|
@@ -196,6 +252,7 @@ function CronJobsView() {
|
|
|
196
252
|
deniedHint: t("studio_cron_denied_hint"),
|
|
197
253
|
onRetry: refreshJobs
|
|
198
254
|
});
|
|
255
|
+
const hasProblems = problems.rejected.length > 0 || problems.filesFailed > 0;
|
|
199
256
|
if (jobs.length === 0) return /* @__PURE__ */ jsxs("div", {
|
|
200
257
|
className: "flex flex-col items-center justify-center h-full gap-4 text-center p-8",
|
|
201
258
|
children: [
|
|
@@ -203,17 +260,19 @@ function CronJobsView() {
|
|
|
203
260
|
size: iconSize.medium,
|
|
204
261
|
className: "text-surface-300 dark:text-surface-600"
|
|
205
262
|
}),
|
|
206
|
-
/* @__PURE__ */ jsx(
|
|
263
|
+
hasProblems ? /* @__PURE__ */ jsx("div", {
|
|
264
|
+
className: "max-w-lg text-left",
|
|
265
|
+
children: /* @__PURE__ */ jsx(CronLoadProblems, { problems })
|
|
266
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Typography, {
|
|
207
267
|
variant: "h6",
|
|
208
268
|
color: "secondary",
|
|
209
269
|
children: t("studio_cron_empty_title")
|
|
210
|
-
}),
|
|
211
|
-
/* @__PURE__ */ jsx(Typography, {
|
|
270
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
212
271
|
variant: "body2",
|
|
213
272
|
color: "disabled",
|
|
214
273
|
className: "max-w-md",
|
|
215
274
|
children: t("studio_cron_empty_body")
|
|
216
|
-
}),
|
|
275
|
+
})] }),
|
|
217
276
|
/* @__PURE__ */ jsx("a", {
|
|
218
277
|
href: "https://rebase.pro/docs/backend/cron-jobs",
|
|
219
278
|
target: "_blank",
|
|
@@ -227,56 +286,63 @@ function CronJobsView() {
|
|
|
227
286
|
className: "flex h-full w-full overflow-hidden bg-surface-card",
|
|
228
287
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
229
288
|
className: cls("flex flex-col w-[340px] min-w-[280px] border-r h-full", defaultBorderMixin),
|
|
230
|
-
children: [
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
289
|
+
children: [
|
|
290
|
+
/* @__PURE__ */ jsxs("div", {
|
|
291
|
+
className: cls("flex items-center justify-between px-4 py-2.5 border-b bg-surface-sheet min-h-[48px]", defaultBorderMixin),
|
|
292
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
293
|
+
className: "flex items-center gap-2",
|
|
294
|
+
children: [
|
|
295
|
+
/* @__PURE__ */ jsx(CalendarIcon, {
|
|
296
|
+
size: iconSize.smallest,
|
|
297
|
+
className: "text-primary"
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ jsx(Typography, {
|
|
300
|
+
variant: "subtitle2",
|
|
301
|
+
className: "font-semibold",
|
|
302
|
+
children: t("studio_tool_cron")
|
|
303
|
+
}),
|
|
304
|
+
/* @__PURE__ */ jsx(Chip, {
|
|
305
|
+
size: "smallest",
|
|
306
|
+
className: "bg-surface-raised text-surface-600 dark:text-surface-300",
|
|
307
|
+
children: jobs.length
|
|
308
|
+
})
|
|
309
|
+
]
|
|
310
|
+
}), /* @__PURE__ */ jsx(IconButton, {
|
|
311
|
+
size: "small",
|
|
312
|
+
onClick: refreshJobs,
|
|
313
|
+
title: "Refresh",
|
|
314
|
+
children: /* @__PURE__ */ jsx(RefreshCwIcon, { size: iconSize.smallest })
|
|
315
|
+
})]
|
|
316
|
+
}),
|
|
317
|
+
hasProblems && /* @__PURE__ */ jsx("div", {
|
|
318
|
+
className: "p-2",
|
|
319
|
+
children: /* @__PURE__ */ jsx(CronLoadProblems, { problems })
|
|
320
|
+
}),
|
|
321
|
+
/* @__PURE__ */ jsx("div", {
|
|
322
|
+
className: "flex-1 overflow-y-auto p-2 space-y-1",
|
|
323
|
+
children: jobs.map((job) => /* @__PURE__ */ jsxs("div", {
|
|
324
|
+
onClick: () => setSelectedId(job.id),
|
|
325
|
+
className: cls("flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all", selectedId === job.id ? "bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30" : "hover:bg-surface-hover"),
|
|
326
|
+
children: [
|
|
327
|
+
/* @__PURE__ */ jsx("div", { className: cls("w-2 h-2 rounded-full shrink-0", stateColors[job.state] || "bg-surface-400") }),
|
|
328
|
+
/* @__PURE__ */ jsxs("div", {
|
|
329
|
+
className: "flex-1 min-w-0",
|
|
330
|
+
children: [/* @__PURE__ */ jsx(Typography, {
|
|
331
|
+
variant: "body2",
|
|
332
|
+
className: "truncate font-medium text-[13px]",
|
|
333
|
+
children: job.name
|
|
334
|
+
}), /* @__PURE__ */ jsx(Typography, {
|
|
335
|
+
variant: "caption",
|
|
336
|
+
color: "secondary",
|
|
337
|
+
className: "truncate text-[11px] font-mono",
|
|
338
|
+
children: job.schedule
|
|
339
|
+
})]
|
|
340
|
+
}),
|
|
341
|
+
job.state === "running" && /* @__PURE__ */ jsx(CircularProgress, { size: "smallest" })
|
|
342
|
+
]
|
|
343
|
+
}, job.id))
|
|
344
|
+
})
|
|
345
|
+
]
|
|
280
346
|
}), /* @__PURE__ */ jsx("div", {
|
|
281
347
|
className: "flex-1 flex flex-col min-w-0 h-full overflow-hidden",
|
|
282
348
|
children: !selectedJob ? /* @__PURE__ */ jsx("div", {
|
|
@@ -527,4 +593,4 @@ function LogRow({ log }) {
|
|
|
527
593
|
//#endregion
|
|
528
594
|
export { CronJobsView };
|
|
529
595
|
|
|
530
|
-
//# sourceMappingURL=CronJobsView-
|
|
596
|
+
//# sourceMappingURL=CronJobsView-TDErHTuK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CronJobsView-TDErHTuK.js","names":[],"sources":["../src/components/CronJobs/CronJobsView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useRef } from \"react\";\nimport {\n Alert,\n AlertCircleIcon,\n Button,\n CalendarIcon,\n Card,\n CheckCircleIcon,\n Chip,\n CircularProgress,\n cls,\n defaultBorderMixin,\n HistoryIcon,\n IconButton,\n iconSize,\n Paper,\n PauseIcon,\n PlayIcon,\n RefreshCwIcon,\n Typography\n} from \"@rebasepro/ui\";\nimport { useRebaseClient, useSnackbarController, useTranslation } from \"@rebasepro/app\";\nimport type { CronJobListing, CronJobStatus, CronJobLogEntry, RejectedCronJob } from \"@rebasepro/types\";\nimport type { RebaseClient } from \"@rebasepro/types\";\n\nimport { classifyLoadFailure, type LoadFailure } from \"../load-failure\";\nimport { LoadFailureView } from \"../load-failure-view\";\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n}\n\nfunction formatRelative(iso: string | undefined): string {\n if (!iso) return \"—\";\n const d = new Date(iso);\n const now = Date.now();\n const diff = d.getTime() - now;\n const abs = Math.abs(diff);\n if (abs < 60000) return diff > 0 ? \"in <1m\" : \"<1m ago\";\n if (abs < 3600000) { const m = Math.round(abs / 60000); return diff > 0 ? `in ${m}m` : `${m}m ago`; }\n if (abs < 86400000) { const h = Math.round(abs / 3600000); return diff > 0 ? `in ${h}h` : `${h}h ago`; }\n return d.toLocaleString();\n}\n\n/** What the listing says did not become a job — see {@link CronLoadProblems}. */\ninterface CronProblems {\n rejected: RejectedCronJob[];\n /** Cron files that failed to load: `skipped` counts these and the refused jobs together. */\n filesFailed: number;\n}\n\nfunction problemsOf(listing: CronJobListing): CronProblems {\n const rejected = listing.rejected ?? [];\n return { rejected, filesFailed: Math.max(0, (listing.skipped ?? 0) - rejected.length) };\n}\n\n/**\n * The cron files that failed to load and the jobs the scheduler refused. None\n * of them is scheduled, and none is in the list: without this a refused job\n * was simply absent, and a project whose only job was refused read \"No cron\n * jobs registered\".\n */\nfunction CronLoadProblems({ problems }: { problems: CronProblems }) {\n const { t } = useTranslation();\n if (problems.rejected.length === 0 && problems.filesFailed === 0) return null;\n return (\n <Alert color=\"error\">\n <div className=\"flex flex-col gap-2\">\n {problems.rejected.map(job => (\n <div key={job.id} className=\"flex flex-col\">\n <Typography variant=\"body2\" className=\"text-[13px] font-semibold\">\n {t(\"studio_cron_job_refused\", { job: job.name })}\n </Typography>\n <Typography variant=\"caption\" className=\"font-mono text-[12px] break-words\">\n {job.schedule} — {job.reason}\n </Typography>\n </div>\n ))}\n {problems.filesFailed > 0 && (\n <Typography variant=\"body2\" className=\"text-[13px]\">\n {t(\"studio_cron_files_failed\", { count: problems.filesFailed })}\n </Typography>\n )}\n </div>\n </Alert>\n );\n}\n\nconst stateColors: Record<string, string> = {\n idle: \"bg-emerald-500\",\nrunning: \"bg-blue-500\",\nsuccess: \"bg-emerald-500\",\n error: \"bg-red-500\",\ndisabled: \"bg-surface-400\"\n};\n\nexport function CronJobsView() {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const { t } = useTranslation();\n const [jobs, setJobs] = useState<CronJobStatus[]>([]);\n const [problems, setProblems] = useState<CronProblems>({ rejected: [], filesFailed: 0 });\n const [loading, setLoading] = useState(true);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [logs, setLogs] = useState<CronJobLogEntry[]>([]);\n const [logsLoading, setLogsLoading] = useState(false);\n /**\n * Why the selected job's executions could not be read. Shown where the\n * list would be: an empty list reads as \"this job has never run\", and the\n * snackbar that used to carry the reason was gone in four seconds.\n */\n const [logsFailure, setLogsFailure] = useState<LoadFailure | null>(null);\n const [triggering, setTriggering] = useState<string | null>(null);\n /** Why the job listing failed, classified — see `load-failure.ts`. */\n const [failure, setFailure] = useState<LoadFailure | null>(null);\n\n // Refs so effects never re-fire due to identity changes\n const clientRef = useRef(client);\n clientRef.current = client;\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n // ── Fetch jobs on mount + poll every 15s ──\n useEffect(() => {\n let cancelled = false;\n\n async function load() {\n const c = clientRef.current;\n if (!c?.cron) {\n setLoading(false);\n return;\n }\n try {\n const res = await c.cron.listJobs();\n if (!cancelled) {\n setJobs(res.jobs);\n setProblems(problemsOf(res));\n setFailure(null);\n }\n } catch (e: unknown) {\n // A snackbar over an empty list said \"No Cron Jobs Registered\"\n // about a project whose crons the caller may not read. This\n // view polls every 15s, so the toast is also gone long before\n // anyone looks.\n if (!cancelled) setFailure(classifyLoadFailure(e));\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n load();\n\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n const scheduleNext = () => {\n if (cancelled) return;\n timeoutId = setTimeout(async () => {\n if (document.visibilityState === \"visible\") {\n await load();\n }\n scheduleNext();\n }, 15_000);\n };\n\n scheduleNext();\n\n const handleVisibility = () => {\n if (document.visibilityState === \"visible\") {\n load();\n }\n };\n document.addEventListener(\"visibilitychange\", handleVisibility);\n\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n document.removeEventListener(\"visibilitychange\", handleVisibility);\n };\n }, []); // runs once\n\n // ── Fetch logs when selection changes ──\n useEffect(() => {\n if (!selectedId) {\n setLogs([]);\n setLogsFailure(null);\n return;\n }\n let cancelled = false;\n const c = clientRef.current;\n if (!c?.cron) return;\n\n setLogsLoading(true);\n setLogsFailure(null);\n c.cron.getJobLogs(selectedId, { limit: 25 })\n .then(res => { if (!cancelled) setLogs(res.logs); })\n .catch((e: unknown) => {\n if (cancelled) return;\n // Same reasoning as `refreshLogs`: an empty log list is a claim.\n setLogs([]);\n setLogsFailure(classifyLoadFailure(e));\n })\n .finally(() => { if (!cancelled) setLogsLoading(false); });\n\n return () => { cancelled = true; };\n }, [selectedId]);\n\n // ── Imperative helpers (not in any dep array) ──\n async function refreshJobs() {\n const c = clientRef.current;\n if (!c?.cron) return;\n try {\n const res = await c.cron.listJobs();\n setJobs(res.jobs);\n setProblems(problemsOf(res));\n setFailure(null);\n } catch (e: unknown) {\n // Swallowed before, which left the list showing whatever it last\n // held — or nothing — after a failed refresh. \"No cron jobs\" and\n // \"could not read the cron jobs\" are not the same statement, so the\n // reason stays on screen rather than passing through a snackbar.\n setFailure(classifyLoadFailure(e));\n }\n }\n\n async function refreshLogs(id: string) {\n const c = clientRef.current;\n if (!c?.cron) return;\n setLogsLoading(true);\n setLogsFailure(null);\n try {\n const res = await c.cron.getJobLogs(id, { limit: 25 });\n setLogs(res.logs);\n } catch (e: unknown) {\n // Clearing the list silently reads as \"this job has never run\".\n setLogs([]);\n setLogsFailure(classifyLoadFailure(e));\n }\n finally { setLogsLoading(false); }\n }\n\n const handleTrigger = async (id: string) => {\n const c = clientRef.current;\n if (!c?.cron) return;\n setTriggering(id);\n try {\n // The request returns when the run has ended, with how it went: a\n // run that threw is still a 200, so the log decides the toast.\n const { log } = await c.cron.triggerJob(id);\n const job = jobs.find(j => j.id === id)?.name ?? id;\n snackbarRef.current.open(log.success\n ? { type: \"success\", message: t(\"studio_cron_run_succeeded\", { job }) }\n : { type: \"error\", message: t(\"studio_cron_run_failed\", { job, error: log.error ?? \"—\" }) });\n await refreshJobs();\n if (selectedId === id) refreshLogs(id);\n } catch (e: unknown) {\n snackbarRef.current.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n } finally { setTriggering(null); }\n };\n\n const handleToggle = async (id: string, enabled: boolean) => {\n const c = clientRef.current;\n if (!c?.cron) return;\n try {\n await c.cron.toggleJob(id, enabled);\n snackbarRef.current.open({ type: \"success\",\nmessage: enabled ? \"Job enabled\" : \"Job paused\" });\n await refreshJobs();\n } catch (e: unknown) {\n snackbarRef.current.open({ type: \"error\",\nmessage: e instanceof Error ? e.message : String(e) });\n }\n };\n\n const selectedJob = jobs.find(j => j.id === selectedId);\n\n if (loading) return <div className=\"flex items-center justify-center h-full\"><CircularProgress/></div>;\n\n if (failure) return (\n <LoadFailureView\n failure={failure}\n title={t(\"studio_cron_read_failed\")}\n deniedTitle={t(\"studio_cron_denied_title\")}\n deniedHint={t(\"studio_cron_denied_hint\")}\n onRetry={refreshJobs}\n />\n );\n\n const hasProblems = problems.rejected.length > 0 || problems.filesFailed > 0;\n\n if (jobs.length === 0) return (\n <div className=\"flex flex-col items-center justify-center h-full gap-4 text-center p-8\">\n <CalendarIcon size={iconSize.medium} className=\"text-surface-300 dark:text-surface-600\"/>\n {hasProblems ? (\n <div className=\"max-w-lg text-left\">\n <CronLoadProblems problems={problems}/>\n </div>\n ) : (\n <>\n <Typography variant=\"h6\" color=\"secondary\">{t(\"studio_cron_empty_title\")}</Typography>\n <Typography variant=\"body2\" color=\"disabled\" className=\"max-w-md\">\n {t(\"studio_cron_empty_body\")}\n </Typography>\n </>\n )}\n <a\n href=\"https://rebase.pro/docs/backend/cron-jobs\"\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-primary text-sm underline\"\n >\n {t(\"studio_read_the_docs\")}\n </a>\n </div>\n );\n\n return (\n <div className=\"flex h-full w-full overflow-hidden bg-surface-card\">\n {/* ── Job List ── */}\n <div className={cls(\"flex flex-col w-[340px] min-w-[280px] border-r h-full\", defaultBorderMixin)}>\n <div className={cls(\"flex items-center justify-between px-4 py-2.5 border-b bg-surface-sheet min-h-[48px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2\">\n <CalendarIcon size={iconSize.smallest} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">{t(\"studio_tool_cron\")}</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-raised text-surface-600 dark:text-surface-300\">{jobs.length}</Chip>\n </div>\n <IconButton size=\"small\" onClick={refreshJobs} title=\"Refresh\"><RefreshCwIcon size={iconSize.smallest}/></IconButton>\n </div>\n {hasProblems && (\n <div className=\"p-2\">\n <CronLoadProblems problems={problems}/>\n </div>\n )}\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {jobs.map(job => (\n <div\n key={job.id}\n onClick={() => setSelectedId(job.id)}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selectedId === job.id\n ? \"bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30\"\n : \"hover:bg-surface-hover\"\n )}\n >\n <div className={cls(\"w-2 h-2 rounded-full shrink-0\", stateColors[job.state] || \"bg-surface-400\")}/>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{job.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px] font-mono\">{job.schedule}</Typography>\n </div>\n {job.state === \"running\" && <CircularProgress size=\"smallest\"/>}\n </div>\n ))}\n </div>\n </div>\n\n {/* ── Detail Panel ── */}\n <div className=\"flex-1 flex flex-col min-w-0 h-full overflow-hidden\">\n {!selectedJob ? (\n <div className=\"flex items-center justify-center h-full\">\n <Typography variant=\"body2\" color=\"disabled\">Select a cron job to view details</Typography>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between px-5 py-3 border-b bg-surface-card min-h-[56px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className={cls(\"w-2.5 h-2.5 rounded-full\", stateColors[selectedJob.state])}/>\n <div className=\"min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selectedJob.name}</Typography>\n {selectedJob.description && <Typography variant=\"caption\" color=\"secondary\" className=\"truncate\">{selectedJob.description}</Typography>}\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n <IconButton title={selectedJob.enabled ? \"Pause job\" : \"Enable job\"} size=\"small\" onClick={() => handleToggle(selectedJob.id, !selectedJob.enabled)}>\n {selectedJob.enabled ? <PauseIcon size={iconSize.small}/> : <PlayIcon size={iconSize.smallest}/>}\n </IconButton>\n <Button\n size=\"small\"\n color=\"primary\"\n onClick={() => handleTrigger(selectedJob.id)}\n disabled={triggering === selectedJob.id}\n startIcon={triggering === selectedJob.id ? <CircularProgress size=\"smallest\"/> : <PlayIcon size={iconSize.smallest}/>}\n >\n Run Now\n </Button>\n </div>\n </div>\n\n {/* Stats Cards */}\n <div className=\"px-5 py-4 bg-surface-sheet\">\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3\">\n <StatCard label=\"Schedule\" value={selectedJob.schedule} mono/>\n <StatCard label=\"Last Run\" value={formatRelative(selectedJob.lastRunAt)}/>\n <StatCard label=\"Next Run\" value={selectedJob.enabled ? formatRelative(selectedJob.nextRunAt) : \"Paused\"}/>\n <StatCard label=\"Duration\" value={selectedJob.lastDurationMs !== undefined ? formatDuration(selectedJob.lastDurationMs) : \"—\"}/>\n </div>\n <div className=\"grid grid-cols-3 gap-3 mt-3\">\n <StatCard label=\"Status\" value={selectedJob.state.toUpperCase()} chipColor={selectedJob.state === \"error\" ? \"red\" : selectedJob.state === \"disabled\" ? \"gray\" : \"green\"}/>\n <StatCard label=\"Total Runs\" value={String(selectedJob.totalRuns)}/>\n <StatCard label=\"Failures\" value={String(selectedJob.totalFailures)} highlight={selectedJob.totalFailures > 0}/>\n </div>\n {selectedJob.lastError && (\n <div className=\"mt-3 p-3 rounded-lg bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800/50\">\n <div className=\"flex items-center gap-2 mb-1\">\n <AlertCircleIcon size={iconSize.smallest} className=\"text-red-500\"/>\n <Typography variant=\"caption\" className=\"font-semibold text-red-700 dark:text-red-400\">Last Error</Typography>\n </div>\n <Typography variant=\"caption\" className=\"font-mono text-red-600 dark:text-red-300 text-[11px] break-all\">{selectedJob.lastError}</Typography>\n </div>\n )}\n </div>\n\n {/* Logs Section */}\n <div className={cls(\"flex items-center justify-between px-5 py-2 border-y bg-surface-card\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2\">\n <HistoryIcon size={iconSize.smallest} className=\"text-surface-400\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold text-[13px]\">Execution History</Typography>\n </div>\n <IconButton size=\"small\" onClick={() => refreshLogs(selectedJob.id)} title=\"Refresh logs\"><RefreshCwIcon size={iconSize.smallest}/></IconButton>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n {logsLoading ? (\n <div className=\"flex justify-center p-8\"><CircularProgress size=\"small\"/></div>\n ) : logsFailure ? (\n <LoadFailureView\n failure={logsFailure}\n title={t(\"studio_cron_logs_read_failed\")}\n deniedTitle={t(\"studio_cron_logs_denied_title\")}\n deniedHint={t(\"studio_cron_denied_hint\")}\n onRetry={() => refreshLogs(selectedJob.id)}\n />\n ) : logs.length === 0 ? (\n <div className=\"flex items-center justify-center h-32\">\n <Typography variant=\"body2\" color=\"disabled\">No executions yet</Typography>\n </div>\n ) : (\n <div className=\"divide-y divide-surface-100 dark:divide-surface-950\">\n {logs.map((log, idx) => (\n <LogRow key={idx} log={log}/>\n ))}\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n );\n}\n\nfunction StatCard({ label, value, mono, chipColor, highlight }: {\n label: string; value: string; mono?: boolean; chipColor?: string; highlight?: boolean;\n}) {\n return (\n <div className={cls(\"px-3 py-2 rounded-lg border bg-surface-card\", defaultBorderMixin)}>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[10px] uppercase tracking-wider font-medium\">{label}</Typography>\n <Typography variant=\"body2\" className={cls(\n \"mt-0.5 font-semibold text-[13px]\",\n mono && \"font-mono\",\n highlight && \"text-red-500 dark:text-red-400\",\n chipColor === \"red\" && \"text-red-500\",\n chipColor === \"green\" && \"text-emerald-500\",\n chipColor === \"gray\" && \"text-surface-400\"\n )}>{value}</Typography>\n </div>\n );\n}\n\nfunction LogRow({ log }: { log: CronJobLogEntry }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div className=\"px-5 py-2.5 hover:bg-surface-hover transition-colors\">\n <div className=\"flex items-center gap-3 cursor-pointer\" onClick={() => setExpanded(!expanded)}>\n {log.success\n ? <CheckCircleIcon size={iconSize.smallest} className=\"text-emerald-500 shrink-0\"/>\n : <AlertCircleIcon size={iconSize.smallest} className=\"text-red-500 shrink-0\"/>}\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"caption\" className=\"font-mono text-[11px] text-surface-500\">{new Date(log.startedAt).toLocaleString()}</Typography>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n {log.manual && <Chip size=\"smallest\" className=\"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300\">manual</Chip>}\n <Typography variant=\"caption\" className=\"font-mono text-[11px]\">{formatDuration(log.durationMs)}</Typography>\n <svg className={cls(\"w-3 h-3 transition-transform text-surface-400\", expanded && \"rotate-180\")} fill=\"currentColor\" viewBox=\"0 0 20 20\"><path d=\"M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z\"/></svg>\n </div>\n </div>\n {expanded && (\n <div className=\"mt-2 ml-6 space-y-2\">\n {log.error && (\n <div className=\"p-2 rounded bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800/50\">\n <Typography variant=\"caption\" className=\"font-mono text-[11px] text-red-600 dark:text-red-300 break-all\">{log.error}</Typography>\n </div>\n )}\n {log.logs.length > 0 && (\n <div className=\"p-2 rounded bg-surface-sheet border border-hairline-strong max-h-40 overflow-auto\">\n {log.logs.map((line, i) => (\n <div key={i} className=\"font-mono text-[11px] text-surface-600 dark:text-surface-400 leading-relaxed\">{line}</div>\n ))}\n </div>\n )}\n {log.result !== undefined && (\n <div className=\"p-2 rounded bg-surface-sheet border border-hairline-strong\">\n <Typography variant=\"caption\" className=\"text-[10px] uppercase tracking-wider text-surface-400 mb-1 block\">Result</Typography>\n <pre className=\"font-mono text-[11px] text-surface-600 dark:text-surface-400 whitespace-pre-wrap break-all\">{JSON.stringify(log.result, null, 2)}</pre>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|