2025-05-31 10:07:32 +08:00

183 lines
6.0 KiB
Vue

<script setup>
import { ref, onMounted } from 'vue';
import { useToast } from 'vue-toastification';
const toast = useToast();
const loading = ref(true);
const refreshInterval = ref(null);
// Use useFetch for queue data
const { data: queueData, refresh: refreshQueueData } = await useFetch('/api/queue/asnaf-analysis', {
onResponse({ response }) {
loading.value = false;
},
onResponseError({ error }) {
console.error('Error fetching queue data:', error);
toast.error('Gagal memuat data queue');
loading.value = false;
}
});
// Start auto-refresh
onMounted(() => {
// Refresh every 30 seconds
refreshInterval.value = setInterval(() => {
refreshQueueData();
}, 30000);
});
// Clean up interval on component unmount
onUnmounted(() => {
if (refreshInterval.value) {
clearInterval(refreshInterval.value);
}
});
// Retry failed job
async function retryJob(jobId) {
try {
await useFetch(`/api/queue/asnaf-analysis/${jobId}/retry`, {
method: 'POST',
onResponse() {
toast.success('Job akan diproses semula');
refreshQueueData();
},
onResponseError({ error }) {
console.error('Error retrying job:', error);
toast.error('Gagal memproses semula job');
}
});
} catch (error) {
console.error('Error retrying job:', error);
toast.error('Gagal memproses semula job');
}
}
// Remove job
async function removeJob(jobId) {
try {
await useFetch(`/api/queue/asnaf-analysis/${jobId}`, {
method: 'DELETE',
onResponse() {
toast.success('Job telah dipadam');
refreshQueueData();
},
onResponseError({ error }) {
console.error('Error removing job:', error);
toast.error('Gagal memadam job');
}
});
} catch (error) {
console.error('Error removing job:', error);
toast.error('Gagal memadam job');
}
}
// Page metadata
definePageMeta({
title: "Queue Analisis Asnaf",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Dashboard",
path: "/",
},
{
name: "BF-PRF",
path: "/BF-PRF",
},
{
name: "Asnaf",
path: "/BF-PRF/AS",
},
{
name: "Queue",
path: "/BF-PRF/AS/QUEUE",
},
],
});
</script>
<template>
<div class="space-y-6">
<LayoutsBreadcrumb />
<div class="flex justify-between items-center">
<h1 class="text-2xl font-bold text-primary">Queue Analisis Asnaf</h1>
<rs-button variant="primary" @click="refreshQueueData">
<Icon name="mdi:refresh" size="18" class="mr-1" />
Refresh
</rs-button>
</div>
<!-- Loading state -->
<div v-if="loading" class="flex justify-center items-center py-20">
<div class="text-center">
<Loading />
<p class="mt-4 text-gray-600">Memuat data queue...</p>
</div>
</div>
<div v-else>
<rs-card>
<template #body>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead>
<tr>
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Asnaf ID</th>
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Created</th>
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Updated</th>
<th class="px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="job in queueData" :key="job.id" class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ job.id }}</td>
<td class="px-6 py-4 whitespace-nowrap">
<rs-badge
:variant="job.status === 'completed' ? 'success' :
job.status === 'failed' ? 'danger' :
job.status === 'active' ? 'primary' :
'warning'">
{{ job.status }}
</rs-badge>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">{{ job.data.asnafId }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ new Date(job.timestamp).toLocaleString('ms-MY') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ new Date(job.processedOn || job.finishedOn).toLocaleString('ms-MY') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
<div class="flex space-x-2">
<rs-button
v-if="job.status === 'failed'"
variant="warning"
size="sm"
@click="retryJob(job.id)">
<Icon name="mdi:refresh" size="16" class="mr-1" />
Retry
</rs-button>
<rs-button
variant="danger"
size="sm"
@click="removeJob(job.id)">
<Icon name="mdi:delete" size="16" class="mr-1" />
Remove
</rs-button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
</rs-card>
</div>
</div>
</template>