271 lines
7.4 KiB
Svelte
271 lines
7.4 KiB
Svelte
<script lang="ts">
|
|
import {
|
|
updateScript,
|
|
deleteScript,
|
|
deleteLog,
|
|
executeScript,
|
|
fetchScriptById,
|
|
fetchLogs
|
|
} from '$lib/api';
|
|
import type { Script, Log } from '$lib/api';
|
|
import CodeMirror from 'svelte-codemirror-editor';
|
|
import { python } from '@codemirror/lang-python';
|
|
import { onMount } from 'svelte';
|
|
|
|
export let params: { id: string };
|
|
let script: Script | null = null;
|
|
let logs: Log[] = [];
|
|
let updatedTitle: string = '';
|
|
let updatedContent: string = '';
|
|
let updatedEnabled: boolean = false;
|
|
let loading: boolean = true;
|
|
let errorMsg: string | null = null;
|
|
|
|
onMount(async () => {
|
|
try {
|
|
const fetchedScript = await fetchScriptById(parseInt(params.id));
|
|
if (!fetchedScript) {
|
|
errorMsg = 'Script not found';
|
|
loading = false;
|
|
return;
|
|
}
|
|
script = fetchedScript;
|
|
updatedTitle = script.name || '';
|
|
updatedContent = script.script_content || '';
|
|
updatedEnabled = script.enabled || false;
|
|
const fetchedLogs: Log[] = (await fetchLogs(script.id)).sort(
|
|
(a, b) => new Date(b.created_at!).getTime() - new Date(a.created_at!).getTime()
|
|
);
|
|
logs = fetchedLogs;
|
|
} catch (err) {
|
|
errorMsg = 'Failed to fetch script data - ' + err;
|
|
}
|
|
loading = false;
|
|
});
|
|
|
|
let isEditMode: boolean = false;
|
|
|
|
let selectedLog: Log | null = null;
|
|
|
|
function openLogPopup(log: Log) {
|
|
selectedLog = log;
|
|
}
|
|
|
|
function closeLogPopup() {
|
|
selectedLog = null;
|
|
}
|
|
|
|
async function handleExecuteScript() {
|
|
try {
|
|
await executeScript(script!.id);
|
|
window.showNotification('success', 'Script executed successfully!');
|
|
// Reload the list of logs after execution
|
|
logs = (await fetchLogs(script!.id)).sort(
|
|
(a, b) => new Date(b.created_at!).getTime() - new Date(a.created_at!).getTime()
|
|
);
|
|
} catch (err) {
|
|
window.showNotification('error', 'Failed to execute script. ' + err);
|
|
}
|
|
}
|
|
|
|
async function handleUpdateScript() {
|
|
if (script) {
|
|
try {
|
|
const updatedScript = await updateScript(script.id, {
|
|
name: updatedTitle,
|
|
script_content: updatedContent,
|
|
enabled: updatedEnabled
|
|
});
|
|
script = updatedScript;
|
|
window.showNotification('success', 'Script updated successfully!');
|
|
isEditMode = false;
|
|
} catch (err) {
|
|
window.showNotification('error', 'Failed to update script. ' + err);
|
|
}
|
|
}
|
|
}
|
|
|
|
async function handleDeleteLog(logId: number) {
|
|
try {
|
|
await deleteLog(script!.id, logId);
|
|
logs = logs.filter((log) => log.id !== logId);
|
|
window.showNotification('success', 'Log deleted successfully!');
|
|
} catch (err) {
|
|
window.showNotification('error', 'Failed to delete log. ' + err);
|
|
}
|
|
}
|
|
|
|
// Delete the script
|
|
async function handleDeleteScript() {
|
|
if (script) {
|
|
try {
|
|
await deleteScript(script.id);
|
|
window.location.href = '/scripts'; // Redirect to scripts list after deletion
|
|
} catch (err) {
|
|
window.showNotification('error', 'Failed to delete script. ' + err);
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<main class="p-4">
|
|
<!-- Removed local notification container as notifications are now global -->
|
|
|
|
{#if loading}
|
|
<p>Loading...</p>
|
|
{:else if errorMsg}
|
|
<p class="text-red-500">{errorMsg}</p>
|
|
{:else if script}
|
|
{#if isEditMode}
|
|
<input
|
|
type="text"
|
|
bind:value={updatedTitle}
|
|
required
|
|
class="text-2xl font-bold mb-4 w-full p-2 border rounded"
|
|
/>
|
|
|
|
<CodeMirror bind:value={updatedContent} lang={python()} />
|
|
|
|
<div class="mt-4 flex items-center space-x-2">
|
|
<input
|
|
id="enabled"
|
|
type="checkbox"
|
|
bind:checked={updatedEnabled}
|
|
class="h-5 w-5 text-blue-500 border-gray-300 rounded focus:ring focus:ring-blue-200"
|
|
/>
|
|
<label for="enabled" class="text-sm font-medium text-gray-700">Enabled</label>
|
|
</div>
|
|
|
|
<button
|
|
class="mt-4 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
|
on:click={handleUpdateScript}
|
|
aria-label="Update the script title and content"
|
|
>
|
|
Update Script
|
|
</button>
|
|
<button
|
|
class="mt-4 px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600"
|
|
on:click={() => (isEditMode = false)}
|
|
>
|
|
Cancel
|
|
</button>
|
|
{:else}
|
|
<a href="/scripts" class="text-blue-500 hover:underline mb-4 inline-block"
|
|
>← Return to Scripts</a
|
|
>
|
|
<h1 class="text-2xl font-bold mb-4">{script.name}</h1>
|
|
<pre
|
|
class="w-full p-2 border rounded font-mono text-sm bg-gray-100">{script.script_content}</pre>
|
|
<div class="mt-4 flex items-center space-x-2">
|
|
<span class="text-sm font-medium text-gray-700">Enabled:</span>
|
|
<span
|
|
class="px-2 py-1 text-xs font-semibold rounded"
|
|
class:bg-green-100={script.enabled}
|
|
class:bg-red-100={!script.enabled}
|
|
class:text-green-700={script.enabled}
|
|
class:text-red-700={!script.enabled}
|
|
>
|
|
{script.enabled ? 'Yes' : 'No'}
|
|
</span>
|
|
</div>
|
|
<div class="mt-4 flex justify-between">
|
|
<div class="flex space-x-2">
|
|
<button
|
|
class="px-4 py-2 bg-green-500 text-white rounded hover:bg-green-600"
|
|
on:click={handleExecuteScript}
|
|
aria-label="Execute the script"
|
|
>
|
|
Execute Script
|
|
</button>
|
|
<button
|
|
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
|
|
on:click={() => (isEditMode = true)}
|
|
>
|
|
Edit Script
|
|
</button>
|
|
</div>
|
|
<button
|
|
class="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
|
on:click={handleDeleteScript}
|
|
>
|
|
Delete Script
|
|
</button>
|
|
</div>
|
|
{/if}
|
|
{/if}
|
|
|
|
<section class="mt-8">
|
|
<h2 class="text-xl font-bold mb-4">Logs</h2>
|
|
<ul class="space-y-4">
|
|
{#each logs as log (log.id)}
|
|
<li
|
|
class="p-2 rounded flex justify-between items-center border-s-slate-400 border-1"
|
|
class:bg-red-100={log.error_code !== 0}
|
|
class:bg-gray-50={log.error_code === 0}
|
|
>
|
|
<button class="p-2 w-full" on:click={() => openLogPopup(log)}>
|
|
<div class="z">
|
|
<p class="text-sm text-left">
|
|
{log.error_code !== 0
|
|
? log.error_message.split('\n')[0]
|
|
: log.message.split('\n')[0]}
|
|
</p>
|
|
<p class="text-xs text-gray-500 text-left">{log.created_at}</p>
|
|
</div>
|
|
</button>
|
|
<button
|
|
class="px-2 py-1 bg-red-500 text-white rounded hover:bg-red-600 block"
|
|
on:click|stopPropagation={() => handleDeleteLog(log.id)}
|
|
>
|
|
Delete
|
|
</button>
|
|
</li>
|
|
{/each}
|
|
</ul>
|
|
|
|
{#if selectedLog}
|
|
<div
|
|
class="fixed inset-0 bg-opacity-30 backdrop-blur-sm flex items-center justify-center z-50"
|
|
>
|
|
<div class="bg-white p-6 rounded shadow-lg w-3/4 max-w-2xl max-h-[80vh] overflow-y-auto">
|
|
<h3 class="text-lg font-bold mb-4">Log Details</h3>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Message:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.message}</pre>
|
|
</div>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Error Code:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.error_code}</pre>
|
|
</div>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Error Message:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.error_message ||
|
|
'N/A'}</pre>
|
|
</div>
|
|
<div class="mb-4">
|
|
<p class="font-semibold">Created At:</p>
|
|
<pre
|
|
class="whitespace-pre-wrap bg-gray-100 p-2 rounded border">{selectedLog.created_at}</pre>
|
|
</div>
|
|
<button
|
|
class="mt-4 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600"
|
|
on:click={closeLogPopup}
|
|
>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
</section>
|
|
</main>
|
|
|
|
<style>
|
|
main {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
</style>
|