1 changed files with 128 additions and 0 deletions
@ -0,0 +1,128 @@ |
|||
<script setup lang="ts"> |
|||
const { data, refresh } = await useHttpFetch("/api/scheduler/tasks") |
|||
const stats = await useHttpFetch("/api/scheduler/stats") |
|||
|
|||
const showCreateModal = ref(false) |
|||
const editingTask = ref<any>(null) |
|||
|
|||
const columns = [ |
|||
{ key: "name", label: "Name" }, |
|||
{ key: "cronExpression", label: "Cron" }, |
|||
{ key: "type", label: "Type" }, |
|||
{ key: "enabled", label: "Status" }, |
|||
{ key: "actions", label: "" }, |
|||
] |
|||
|
|||
function statusBadge(enabled: number) { |
|||
return enabled ? { label: "Active", color: "green" } : { label: "Paused", color: "gray" } |
|||
} |
|||
|
|||
async function handleDelete(id: string) { |
|||
await $fetch(`/api/scheduler/tasks/${id}`, { method: "DELETE" }) |
|||
refresh() |
|||
} |
|||
|
|||
async function handleToggle(id: string, enabled: boolean) { |
|||
await $fetch(`/api/scheduler/tasks/${id}/toggle`, { |
|||
method: "POST", |
|||
body: { enabled }, |
|||
}) |
|||
refresh() |
|||
} |
|||
|
|||
async function handleTrigger(id: string) { |
|||
await $fetch(`/api/scheduler/tasks/${id}/trigger`, { method: "POST" }) |
|||
} |
|||
|
|||
function openEdit(task: any) { |
|||
editingTask.value = task |
|||
showCreateModal.value = true |
|||
} |
|||
|
|||
function openCreate() { |
|||
editingTask.value = null |
|||
showCreateModal.value = true |
|||
} |
|||
|
|||
function onModalClose() { |
|||
showCreateModal.value = false |
|||
editingTask.value = null |
|||
refresh() |
|||
} |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="p-6 max-w-6xl mx-auto"> |
|||
<div class="flex items-center justify-between mb-6"> |
|||
<h1 class="text-2xl font-bold">Scheduled Tasks</h1> |
|||
<UButton @click="openCreate">Create Task</UButton> |
|||
</div> |
|||
|
|||
<!-- Stats bar --> |
|||
<div class="grid grid-cols-4 gap-4 mb-6"> |
|||
<div class="rounded-lg border p-4"> |
|||
<div class="text-sm text-gray-500">Total</div> |
|||
<div class="text-2xl font-bold">{{ stats?.data?.totalTasks ?? 0 }}</div> |
|||
</div> |
|||
<div class="rounded-lg border p-4"> |
|||
<div class="text-sm text-gray-500">Active</div> |
|||
<div class="text-2xl font-bold">{{ stats?.data?.enabledTasks ?? 0 }}</div> |
|||
</div> |
|||
<div class="rounded-lg border p-4"> |
|||
<div class="text-sm text-gray-500">Jobs Running</div> |
|||
<div class="text-2xl font-bold">{{ stats?.data?.activeJobs ?? 0 }}</div> |
|||
</div> |
|||
<div class="rounded-lg border p-4"> |
|||
<div class="text-sm text-gray-500">24h Executions</div> |
|||
<div class="text-2xl font-bold">{{ stats?.data?.last24hExecutions ?? 0 }}</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- Task table --> |
|||
<div class="rounded-lg border"> |
|||
<UTable :rows="data?.list ?? []" :columns="columns"> |
|||
<template #enabled-data="{ row }"> |
|||
<UBadge :color="statusBadge(row.enabled).color" variant="subtle"> |
|||
{{ statusBadge(row.enabled).label }} |
|||
</UBadge> |
|||
</template> |
|||
<template #actions-data="{ row }"> |
|||
<div class="flex gap-1 justify-end"> |
|||
<UButton |
|||
size="xs" |
|||
variant="ghost" |
|||
@click="handleTrigger(row.id)" |
|||
>Trigger</UButton> |
|||
<UButton |
|||
size="xs" |
|||
variant="ghost" |
|||
@click="handleToggle(row.id, !row.enabled)" |
|||
> |
|||
{{ row.enabled ? "Pause" : "Resume" }} |
|||
</UButton> |
|||
<UButton |
|||
size="xs" |
|||
variant="ghost" |
|||
:to="`/admin/scheduler/${row.id}`" |
|||
>Detail</UButton> |
|||
<UButton size="xs" variant="ghost" @click="openEdit(row)">Edit</UButton> |
|||
<UButton |
|||
size="xs" |
|||
variant="ghost" |
|||
color="red" |
|||
@click="handleDelete(row.id)" |
|||
>Delete</UButton> |
|||
</div> |
|||
</template> |
|||
</UTable> |
|||
</div> |
|||
|
|||
<!-- Create/Edit Modal --> |
|||
<SchedulerTaskModal |
|||
v-if="showCreateModal" |
|||
:task="editingTask" |
|||
:registered-functions="data?.registeredFunctions ?? []" |
|||
@close="onModalClose" |
|||
/> |
|||
</div> |
|||
</template> |
|||
Loading…
Reference in new issue