feat: add script dry-run functionality and enhance script management

- Introduced a new dry-run endpoint for executing scripts without saving changes.
- Implemented a dedicated ScriptDryRunPage for user interaction with dry-run functionality.
- Enhanced ScriptsPage to support navigation to the dry-run feature.
- Added LogViewer component for displaying logs during script execution.
- Created ScriptEditPage for editing scripts with integrated dry-run capabilities.
- Updated workflows to include new scripts for fetching and notifying data.
- Improved overall user experience with better navigation and error handling.
This commit is contained in:
2026-08-14 15:37:30 +07:00
parent 389f94f950
commit 8519c92ed1
17 changed files with 810 additions and 176 deletions
+4 -3
View File
@@ -1,19 +1,20 @@
import Editor from "@monaco-editor/react";
import { useTheme } from "../theme.jsx";
export function CodeEditor({ language, value, onChange, height = "50vh" }) {
export function CodeEditor({ language, value, onChange, height = "50vh", readOnly = false }) {
const { theme } = useTheme();
const isMobile = typeof window !== "undefined" && window.innerWidth < 768;
return (
<div className="overflow-hidden rounded-box border border-base-300">
<div className="overflow-hidden rounded-box border border-base-300 h-full min-h-0">
<Editor
height={height}
language={language}
value={value}
onChange={(v) => onChange(v ?? "")}
onChange={readOnly ? undefined : (v) => onChange(v ?? "")}
theme={theme === "dark" ? "vs-dark" : "light"}
options={{
readOnly,
minimap: { enabled: !isMobile },
lineNumbers: "on",
tabSize: 2,
+49
View File
@@ -0,0 +1,49 @@
import { useState } from "react";
import { levelName } from "../lib/format.jsx";
export function LogViewer({ logs = [], className = "" }) {
const [wordWrap, setWordWrap] = useState(true);
return (
<section className={`flex min-h-0 flex-col ${className}`}>
<div className="mb-2 flex shrink-0 items-center justify-between gap-2">
<h2 className="font-semibold text-sm">Logs</h2>
<label className="label cursor-pointer gap-2 py-0">
<span className="label-text text-sm">Word wrap</span>
<input
type="checkbox"
className="checkbox checkbox-sm"
checked={wordWrap}
onChange={(e) => setWordWrap(e.target.checked)}
/>
</label>
</div>
<div className="mockup-code min-h-0 flex-1 overflow-auto text-xs">
{logs.length === 0 ? (
<div className="flex gap-3 px-5 py-0.5 font-mono opacity-50">
<span className="w-12 shrink-0 text-right">—</span>
<span>no logs</span>
</div>
) : (
logs.map((l, i) => (
<div key={l.id ?? i} className="flex gap-3 px-5 py-0.5 font-mono">
<span className="w-12 shrink-0 text-right opacity-50">
{levelName(l.level)}
</span>
<span
className={
wordWrap
? "min-w-0 flex-1 whitespace-pre-wrap break-words"
: "whitespace-pre"
}
>
{l.ts} {l.msg ?? ""}
{l.payload ? ` ${JSON.stringify(l.payload)}` : ""}
</span>
</div>
))
)}
</div>
</section>
);
}
@@ -46,7 +46,7 @@ export function MermaidDiagram({ chart, scriptIds = {} }) {
el.style.cursor = "pointer";
el.setAttribute("title", script);
el.addEventListener("click", () => {
navigate(`/scripts?edit=${encodeURIComponent(script)}`);
navigate(`/scripts/${encodeURIComponent(script)}/edit`);
});
}
})