Files
Project-Monitor/frontend/src/routes/scripts/[id]/+page.svelte
Sami Abuzakuk c957d839dd Small fixes
2025-11-01 16:15:35 +01:00

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>