feat(queue): erweiterte Queue mit Cron-Jobs + Tasks, Prioritäten, Delete/Priority API
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
ArrowDown,
|
||||
Trash2,
|
||||
Zap,
|
||||
RefreshCw,
|
||||
} from '@lucide/vue'
|
||||
import type { QueueItem } from '../../composables/useDashboardData'
|
||||
import Button from '@/components/ui/button/Button.vue'
|
||||
@@ -107,6 +108,7 @@ function onDragEnd(): void {
|
||||
>
|
||||
<div class="queue-item-body">
|
||||
<div class="queue-item-head">
|
||||
<span class="queue-source-badge" :class="item.source">{{ item.source }}</span>
|
||||
<Badge
|
||||
variant="outline"
|
||||
class="text-[7px] font-bold uppercase tracking-wider py-0 px-1.5 border"
|
||||
@@ -124,8 +126,8 @@ function onDragEnd(): void {
|
||||
</div>
|
||||
|
||||
<div class="queue-actions">
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 text-[#6b7385] hover:text-[#e8eaf0]" title="Execute now" @click.stop="emit('executeNow', item.id)">
|
||||
<Zap :size="12" />
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 text-[#6b7385] hover:text-[#e8eaf0]" title="Cycle priority" @click.stop="emit('changePriority', item.id, item.priority === 'high' ? 'medium' : item.priority === 'medium' ? 'low' : 'high')">
|
||||
<RefreshCw :size="12" />
|
||||
</Button>
|
||||
<Button variant="ghost" size="icon" class="h-5 w-5 text-[#6b7385] hover:text-[#e8eaf0]" title="Move up" :disabled="idx === 0" @click.stop="emit('moveUp', item.id)">
|
||||
<ArrowUp :size="12" />
|
||||
@@ -257,6 +259,27 @@ function onDragEnd(): void {
|
||||
color: #6b7385;
|
||||
}
|
||||
|
||||
/* Source badge */
|
||||
.queue-source-badge {
|
||||
font-size: 7px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
padding: 0 4px;
|
||||
border-radius: 3px;
|
||||
line-height: 14px;
|
||||
}
|
||||
.queue-source-badge.cron {
|
||||
color: #22c55e;
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
border: 1px solid rgba(34, 197, 94, 0.2);
|
||||
}
|
||||
.queue-source-badge.task {
|
||||
color: #a78bfa;
|
||||
background: rgba(167, 139, 250, 0.1);
|
||||
border: 1px solid rgba(167, 139, 250, 0.2);
|
||||
}
|
||||
|
||||
/* Transition */
|
||||
.queue-expand-enter-active,
|
||||
.queue-expand-leave-active {
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface QueueItem {
|
||||
text: string
|
||||
priority: 'high' | 'medium' | 'low'
|
||||
waitTime: string
|
||||
source: 'cron' | 'task'
|
||||
}
|
||||
|
||||
// ── API Response Interfaces ──
|
||||
@@ -101,6 +102,9 @@ interface DashboardQueueItem {
|
||||
id: string
|
||||
name: string
|
||||
status: string
|
||||
priority: string
|
||||
source: string
|
||||
waitTime: string
|
||||
}
|
||||
|
||||
interface DashboardTaskResponse {
|
||||
@@ -340,10 +344,11 @@ async function fetchQueue(): Promise<void> {
|
||||
queue.value = data.map((item) => ({
|
||||
id: item.id,
|
||||
text: item.name,
|
||||
priority: (item.status === 'high' || item.status === 'medium' || item.status === 'low')
|
||||
? item.status as 'high' | 'medium' | 'low'
|
||||
priority: (item.priority === 'high' || item.priority === 'medium' || item.priority === 'low')
|
||||
? item.priority as 'high' | 'medium' | 'low'
|
||||
: 'medium',
|
||||
waitTime: '--',
|
||||
waitTime: item.waitTime || '--',
|
||||
source: (item.source === 'cron' || item.source === 'task') ? item.source as 'cron' | 'task' : 'cron',
|
||||
}))
|
||||
} catch {
|
||||
// API unreachable – keep current values
|
||||
@@ -443,9 +448,24 @@ async function sendChatMessage(text: string): Promise<void> {
|
||||
|
||||
// ── Queue Operations ──
|
||||
|
||||
function removeQueueItem(id: string): void {
|
||||
const idx = queue.value.findIndex(q => q.id === id)
|
||||
if (idx !== -1) queue.value.splice(idx, 1)
|
||||
async function removeQueueItem(id: string): Promise<void> {
|
||||
const item = queue.value.find(q => q.id === id)
|
||||
if (!item) return
|
||||
|
||||
try {
|
||||
const res = await apiFetch(`/api/dashboard/queue/${encodeURIComponent(id)}?source=${item.source}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
if (!res.ok) {
|
||||
console.warn('[Dashboard] Failed to remove queue item', id, res.status)
|
||||
return
|
||||
}
|
||||
// Remove from local state on success
|
||||
const idx = queue.value.findIndex(q => q.id === id)
|
||||
if (idx !== -1) queue.value.splice(idx, 1)
|
||||
} catch {
|
||||
console.warn('[Dashboard] Error removing queue item', id)
|
||||
}
|
||||
}
|
||||
|
||||
function moveQueueItem(fromIdx: number, toIdx: number): void {
|
||||
@@ -454,9 +474,40 @@ function moveQueueItem(fromIdx: number, toIdx: number): void {
|
||||
queue.value.splice(toIdx, 0, item)
|
||||
}
|
||||
|
||||
function changeQueuePriority(id: string, priority: QueueItem['priority']): void {
|
||||
async function changeQueuePriority(id: string, priority: QueueItem['priority']): Promise<void> {
|
||||
const item = queue.value.find(q => q.id === id)
|
||||
if (item) item.priority = priority
|
||||
if (!item) return
|
||||
|
||||
// For cron jobs, just update locally (gateway manages its own priorities)
|
||||
if (item.source === 'cron') {
|
||||
item.priority = priority
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await apiFetch(`/api/dashboard/queue/${encodeURIComponent(id)}/priority`, {
|
||||
method: 'PUT',
|
||||
})
|
||||
if (!res.ok) {
|
||||
console.warn('[Dashboard] Failed to change priority', id, res.status)
|
||||
return
|
||||
}
|
||||
// Update priority from API response
|
||||
const data = await res.json()
|
||||
if (data.priority) {
|
||||
const normalized = data.priority.toLowerCase()
|
||||
if (normalized === 'high' || normalized === 'medium' || normalized === 'low') {
|
||||
item.priority = normalized as 'high' | 'medium' | 'low'
|
||||
} else {
|
||||
// Fallback: cycle locally
|
||||
item.priority = priority
|
||||
}
|
||||
} else {
|
||||
item.priority = priority
|
||||
}
|
||||
} catch {
|
||||
console.warn('[Dashboard] Error changing priority', id)
|
||||
}
|
||||
}
|
||||
|
||||
// ── Runtime ──
|
||||
|
||||
Reference in New Issue
Block a user