You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

149 lines
5.0 KiB

<script setup lang="ts">
interface TaskRow {
id: string
name: string
cronExpression: string
type: string
enabled: number
}
const { data, refresh } = await useHttpFetch("/api/scheduler/tasks")
const stats = await useHttpFetch("/api/scheduler/stats")
const taskList = computed<TaskRow[]>(() => (data.value as any)?.list ?? [])
const registeredFunctions = computed<string[]>(() => (data.value as any)?.registeredFunctions ?? [])
const statsData = computed(() => (stats.data.value ?? {}) as {
totalTasks: number
enabledTasks: number
activeJobs: number
last24hExecutions: number
})
const showCreateModal = ref(false)
const editingTask = ref<TaskRow | null>(null)
function statusBadge(enabled: number) {
return enabled
? { label: "Active", color: "success" as const }
: { label: "Paused", color: "neutral" as const }
}
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">{{ statsData.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">{{ statsData.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">{{ statsData.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">{{ statsData.last24hExecutions ?? 0 }}</div>
</div>
</div>
<!-- Task table -->
<div class="rounded-lg border overflow-hidden">
<table class="w-full">
<thead>
<tr class="bg-gray-50 border-b">
<th class="text-left px-4 py-3 text-sm font-medium text-gray-500">Name</th>
<th class="text-left px-4 py-3 text-sm font-medium text-gray-500">Cron</th>
<th class="text-left px-4 py-3 text-sm font-medium text-gray-500">Type</th>
<th class="text-left px-4 py-3 text-sm font-medium text-gray-500">Status</th>
<th class="text-right px-4 py-3 text-sm font-medium text-gray-500">Actions</th>
</tr>
</thead>
<tbody>
<tr v-if="taskList.length === 0">
<td colspan="5" class="text-center px-4 py-8 text-sm text-gray-400">No tasks yet</td>
</tr>
<tr
v-for="t in taskList"
:key="t.id"
class="border-b last:border-b-0 hover:bg-gray-50"
>
<td class="px-4 py-3 text-sm">{{ t.name }}</td>
<td class="px-4 py-3 text-sm font-mono">{{ t.cronExpression }}</td>
<td class="px-4 py-3 text-sm">
<UBadge :color="t.type === 'function' ? 'info' : 'primary'" variant="subtle" size="sm">
{{ t.type }}
</UBadge>
</td>
<td class="px-4 py-3">
<UBadge :color="statusBadge(t.enabled).color" variant="subtle" size="sm">
{{ statusBadge(t.enabled).label }}
</UBadge>
</td>
<td class="px-4 py-3">
<div class="flex gap-1 justify-end">
<UButton size="xs" variant="ghost" @click="handleTrigger(t.id)">Trigger</UButton>
<UButton size="xs" variant="ghost" @click="handleToggle(t.id, !t.enabled)">
{{ t.enabled ? 'Pause' : 'Resume' }}
</UButton>
<UButton size="xs" variant="ghost" :to="`/admin/scheduler/${t.id}`">Detail</UButton>
<UButton size="xs" variant="ghost" @click="openEdit(t)">Edit</UButton>
<UButton size="xs" variant="ghost" color="error" @click="handleDelete(t.id)">Delete</UButton>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Create/Edit Modal -->
<SchedulerTaskModal
v-if="showCreateModal"
:task="editingTask"
:registered-functions="registeredFunctions"
@close="onModalClose"
/>
</div>
</template>