Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.
This commit is contained in:
192
pages/notification/queue/index.vue
Normal file
192
pages/notification/queue/index.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb />
|
||||
|
||||
<!-- Header Section -->
|
||||
<rs-card class="mb-6">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<Icon class="mr-2 text-primary" name="ic:outline-schedule"></Icon>
|
||||
<h1 class="text-xl font-bold text-primary">Queue</h1>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="text-gray-600">Manage notification queues and scheduled tasks.</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Basic Stats -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||
<rs-card v-for="(stat, index) in queueStats" :key="index">
|
||||
<div class="p-4 text-center">
|
||||
<div v-if="isLoading" class="animate-pulse">
|
||||
<div class="h-8 bg-gray-200 rounded w-24 mx-auto mb-2"></div>
|
||||
<div class="h-4 bg-gray-200 rounded w-32 mx-auto"></div>
|
||||
</div>
|
||||
<template v-else>
|
||||
<h3 :class="['text-2xl font-bold', stat.colorClass]">
|
||||
{{ stat.value }}
|
||||
</h3>
|
||||
<p class="text-sm text-gray-600">{{ stat.label }}</p>
|
||||
</template>
|
||||
</div>
|
||||
</rs-card>
|
||||
</div>
|
||||
|
||||
<!-- Main Features -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<rs-card
|
||||
v-for="(feature, index) in features"
|
||||
:key="index"
|
||||
class="cursor-pointer hover:shadow-md transition-shadow"
|
||||
@click="navigateTo(feature.path)"
|
||||
>
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<Icon class="mr-2 text-primary" :name="feature.icon"></Icon>
|
||||
<h3 class="font-semibold text-primary">{{ feature.title }}</h3>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="text-gray-600 text-sm">{{ feature.description }}</p>
|
||||
</template>
|
||||
<template #footer>
|
||||
<rs-button variant="outline" size="sm" class="w-full"> Open </rs-button>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
|
||||
<!-- Error Alert -->
|
||||
<rs-alert v-if="error" variant="danger" class="mt-4">
|
||||
{{ error }}
|
||||
</rs-alert>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Queue & Scheduler",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Notification",
|
||||
path: "/notification",
|
||||
},
|
||||
{
|
||||
name: "Queue",
|
||||
path: "/notification/queue",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const { $swal } = useNuxtApp();
|
||||
|
||||
// Reactive state
|
||||
const isLoading = ref(true);
|
||||
const error = ref(null);
|
||||
const queueStats = ref([]);
|
||||
|
||||
// Hardcoded features
|
||||
const features = ref([
|
||||
{
|
||||
title: "Queue Monitor",
|
||||
description: "View and manage current notification queues",
|
||||
icon: "ic:outline-monitor",
|
||||
path: "/notification/queue/monitor",
|
||||
},
|
||||
{
|
||||
title: "Performance",
|
||||
description: "Check system performance and metrics",
|
||||
icon: "ic:outline-speed",
|
||||
path: "/notification/queue/performance",
|
||||
},
|
||||
// {
|
||||
// title: "Batch Processing",
|
||||
// description: "Process notifications in batches",
|
||||
// icon: "ic:outline-batch-prediction",
|
||||
// path: "/notification/queue/batch",
|
||||
// },
|
||||
{
|
||||
title: "Failed Jobs",
|
||||
description: "Handle and retry failed notifications",
|
||||
icon: "ic:outline-refresh",
|
||||
path: "/notification/queue/retry",
|
||||
},
|
||||
]);
|
||||
|
||||
// Fetch queue statistics
|
||||
async function fetchQueueStats() {
|
||||
try {
|
||||
const { data } = await useFetch("/api/notifications/queue/stats", {
|
||||
method: "GET",
|
||||
});
|
||||
|
||||
console.log(data.value);
|
||||
|
||||
if (data.value?.success) {
|
||||
queueStats.value = [
|
||||
{
|
||||
value: data.value.data.pending,
|
||||
label: "Pending Jobs",
|
||||
colorClass: "text-primary",
|
||||
},
|
||||
{
|
||||
value: data.value.data.completed,
|
||||
label: "Completed Today",
|
||||
colorClass: "text-green-600",
|
||||
},
|
||||
{
|
||||
value: data.value.data.failed,
|
||||
label: "Failed Jobs",
|
||||
colorClass: "text-red-600",
|
||||
},
|
||||
];
|
||||
} else {
|
||||
throw new Error(data.value?.message || "Failed to fetch queue statistics");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Error fetching queue stats:", err);
|
||||
error.value = "Failed to load queue statistics. Please try again later.";
|
||||
|
||||
// Set default values for stats
|
||||
queueStats.value = [
|
||||
{ value: "-", label: "Pending Jobs", colorClass: "text-primary" },
|
||||
{ value: "-", label: "Completed Today", colorClass: "text-green-600" },
|
||||
{ value: "-", label: "Failed Jobs", colorClass: "text-red-600" },
|
||||
];
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize data
|
||||
onMounted(async () => {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
error.value = null;
|
||||
await fetchQueueStats();
|
||||
} catch (err) {
|
||||
console.error("Error initializing queue page:", err);
|
||||
error.value = "Failed to initialize the page. Please refresh to try again.";
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Auto-refresh stats every 30 seconds
|
||||
let refreshInterval;
|
||||
onMounted(() => {
|
||||
refreshInterval = setInterval(async () => {
|
||||
await fetchQueueStats();
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
Reference in New Issue
Block a user