187 lines
6.3 KiB
Svelte
187 lines
6.3 KiB
Svelte
<!-- [DEF:TaskManagementPage:Component] -->
|
|
<!--
|
|
@SEMANTICS: tasks, management, history, logs
|
|
@PURPOSE: Page for managing and monitoring tasks.
|
|
@LAYER: Page
|
|
@RELATION: USES -> TaskList
|
|
@RELATION: USES -> TaskLogViewer
|
|
-->
|
|
<script>
|
|
import { onMount, onDestroy } from 'svelte';
|
|
import { getTasks, createTask, getEnvironmentsList } from '../../lib/api';
|
|
import { addToast } from '../../lib/toasts';
|
|
import TaskList from '../../components/TaskList.svelte';
|
|
import TaskLogViewer from '../../components/TaskLogViewer.svelte';
|
|
|
|
let tasks = [];
|
|
let environments = [];
|
|
let loading = true;
|
|
let selectedTaskId = null;
|
|
let pollInterval;
|
|
let showBackupModal = false;
|
|
let selectedEnvId = '';
|
|
|
|
// [DEF:loadInitialData:Function]
|
|
/**
|
|
* @purpose Loads tasks and environments on page initialization.
|
|
* @pre API must be reachable.
|
|
* @post tasks and environments variables are populated.
|
|
*/
|
|
async function loadInitialData() {
|
|
console.log("[loadInitialData][Action] Loading initial tasks and environments");
|
|
try {
|
|
loading = true;
|
|
const [tasksData, envsData] = await Promise.all([
|
|
getTasks(),
|
|
getEnvironmentsList()
|
|
]);
|
|
tasks = tasksData;
|
|
environments = envsData;
|
|
console.log(`[loadInitialData][Coherence:OK] Data loaded context={{'tasks': ${tasks.length}, 'envs': ${environments.length}}}`);
|
|
} catch (error) {
|
|
console.error(`[loadInitialData][Coherence:Failed] Failed to load tasks data context={{'error': '${error.message}'}}`);
|
|
} finally {
|
|
loading = false;
|
|
}
|
|
}
|
|
// [/DEF:loadInitialData:Function]
|
|
|
|
// [DEF:refreshTasks:Function]
|
|
/**
|
|
* @purpose Periodically refreshes the task list.
|
|
* @pre API must be reachable.
|
|
* @post tasks variable is updated if data is valid.
|
|
*/
|
|
async function refreshTasks() {
|
|
try {
|
|
const data = await getTasks();
|
|
// Ensure we don't try to parse HTML as JSON if the route returns 404
|
|
if (Array.isArray(data)) {
|
|
tasks = data;
|
|
}
|
|
} catch (error) {
|
|
console.error(`[refreshTasks][Coherence:Failed] Failed to refresh tasks context={{'error': '${error.message}'}}`);
|
|
}
|
|
}
|
|
// [/DEF:refreshTasks:Function]
|
|
|
|
// [DEF:handleSelectTask:Function]
|
|
/**
|
|
* @purpose Updates the selected task ID when a task is clicked.
|
|
* @pre event.detail.id must be provided.
|
|
* @post selectedTaskId is updated.
|
|
*/
|
|
function handleSelectTask(event) {
|
|
selectedTaskId = event.detail.id;
|
|
console.log(`[handleSelectTask][Action] Task selected context={{'taskId': '${selectedTaskId}'}}`);
|
|
}
|
|
// [/DEF:handleSelectTask:Function]
|
|
|
|
// [DEF:handleRunBackup:Function]
|
|
/**
|
|
* @purpose Triggers a manual backup task for the selected environment.
|
|
* @pre selectedEnvId must not be empty.
|
|
* @post Backup task is created and task list is refreshed.
|
|
*/
|
|
async function handleRunBackup() {
|
|
if (!selectedEnvId) {
|
|
addToast('Please select an environment', 'error');
|
|
return;
|
|
}
|
|
|
|
console.log(`[handleRunBackup][Action] Starting backup for env context={{'envId': '${selectedEnvId}'}}`);
|
|
try {
|
|
const task = await createTask('superset-backup', { environment_id: selectedEnvId });
|
|
addToast('Backup task started', 'success');
|
|
showBackupModal = false;
|
|
selectedTaskId = task.id;
|
|
await refreshTasks();
|
|
console.log(`[handleRunBackup][Coherence:OK] Backup task created context={{'taskId': '${task.id}'}}`);
|
|
} catch (error) {
|
|
console.error(`[handleRunBackup][Coherence:Failed] Failed to start backup context={{'error': '${error.message}'}}`);
|
|
}
|
|
}
|
|
// [/DEF:handleRunBackup:Function]
|
|
|
|
onMount(() => {
|
|
loadInitialData();
|
|
pollInterval = setInterval(refreshTasks, 3000);
|
|
});
|
|
|
|
onDestroy(() => {
|
|
if (pollInterval) clearInterval(pollInterval);
|
|
});
|
|
</script>
|
|
|
|
<div class="container mx-auto p-4 max-w-6xl">
|
|
<div class="flex justify-between items-center mb-6">
|
|
<h1 class="text-2xl font-bold text-gray-800">Task Management</h1>
|
|
<button
|
|
on:click={() => showBackupModal = true}
|
|
class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md shadow-sm transition duration-150 font-medium"
|
|
>
|
|
Run Backup
|
|
</button>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
<div class="lg:col-span-1">
|
|
<h2 class="text-lg font-semibold mb-3 text-gray-700">Recent Tasks</h2>
|
|
<TaskList {tasks} {loading} on:select={handleSelectTask} />
|
|
</div>
|
|
|
|
<div class="lg:col-span-2">
|
|
<h2 class="text-lg font-semibold mb-3 text-gray-700">Task Details & Logs</h2>
|
|
{#if selectedTaskId}
|
|
<div class="bg-white rounded-lg shadow-lg h-[600px] flex flex-col">
|
|
<TaskLogViewer
|
|
taskId={selectedTaskId}
|
|
taskStatus={tasks.find(t => t.id === selectedTaskId)?.status}
|
|
inline={true}
|
|
/>
|
|
</div>
|
|
{:else}
|
|
<div class="bg-gray-50 border-2 border-dashed border-gray-300 rounded-lg h-[600px] flex items-center justify-center text-gray-500">
|
|
<p>Select a task to view logs and details</p>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{#if showBackupModal}
|
|
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
|
|
<div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md">
|
|
<h3 class="text-xl font-bold mb-4">Run Manual Backup</h3>
|
|
<div class="mb-4">
|
|
<label for="env-select" class="block text-sm font-medium text-gray-700 mb-1">Target Environment</label>
|
|
<select
|
|
id="env-select"
|
|
bind:value={selectedEnvId}
|
|
class="w-full border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 p-2 border"
|
|
>
|
|
<option value="" disabled>-- Select Environment --</option>
|
|
{#each environments as env}
|
|
<option value={env.id}>{env.name}</option>
|
|
{/each}
|
|
</select>
|
|
</div>
|
|
<div class="flex justify-end space-x-3">
|
|
<button
|
|
on:click={() => showBackupModal = false}
|
|
class="px-4 py-2 text-gray-700 hover:bg-gray-100 rounded-md transition"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
on:click={handleRunBackup}
|
|
class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition"
|
|
>
|
|
Start Backup
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<!-- [/DEF:TaskManagementPage:Component] --> |