212 lines
6.1 KiB
Vue
212 lines
6.1 KiB
Vue
<template>
|
|
<div>
|
|
<LayoutsBreadcrumb />
|
|
|
|
<!-- Header Section -->
|
|
<rs-card class="mb-6">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-primary" name="ic:outline-speed"></Icon>
|
|
<h1 class="text-xl font-bold text-primary">Performance</h1>
|
|
</div>
|
|
<rs-button
|
|
variant="outline"
|
|
size="sm"
|
|
@click="refreshMetrics"
|
|
:loading="isLoading"
|
|
>
|
|
<Icon class="mr-1" name="ic:outline-refresh"></Icon>
|
|
Refresh
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<p class="text-gray-600">Monitor system performance and metrics.</p>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Error Alert -->
|
|
<rs-alert v-if="error" variant="danger" class="mb-6">
|
|
{{ error }}
|
|
</rs-alert>
|
|
|
|
<!-- Loading State -->
|
|
<div v-if="isLoading" class="flex justify-center items-center py-12 text-gray-600">
|
|
<Icon name="ic:outline-sync" class="w-6 h-6 animate-spin mr-2" />
|
|
Loading...
|
|
</div>
|
|
|
|
<div v-else>
|
|
<!-- Key Metrics -->
|
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
|
|
<rs-card>
|
|
<div class="p-4 text-center">
|
|
<h3 class="text-2xl font-bold text-blue-600">
|
|
{{ metrics.throughput }}
|
|
</h3>
|
|
<p class="text-sm text-gray-600">Messages/min</p>
|
|
</div>
|
|
</rs-card>
|
|
<rs-card>
|
|
<div class="p-4 text-center">
|
|
<h3 class="text-2xl font-bold text-green-600">{{ metrics.uptime }}%</h3>
|
|
<p class="text-sm text-gray-600">System Uptime</p>
|
|
</div>
|
|
</rs-card>
|
|
<rs-card>
|
|
<div class="p-4 text-center">
|
|
<h3 class="text-2xl font-bold text-purple-600">
|
|
{{ metrics.workers }}
|
|
</h3>
|
|
<p class="text-sm text-gray-600">Active Workers</p>
|
|
</div>
|
|
</rs-card>
|
|
<rs-card>
|
|
<div class="p-4 text-center">
|
|
<h3 class="text-2xl font-bold text-orange-600">{{ metrics.queueLoad }}%</h3>
|
|
<p class="text-sm text-gray-600">Queue Load</p>
|
|
</div>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Performance Summary -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<!-- Throughput Summary -->
|
|
<rs-card>
|
|
<template #header>
|
|
<h3 class="text-lg font-semibold text-primary">Throughput Summary</h3>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div class="flex justify-between items-center p-3 bg-gray-50 rounded">
|
|
<span class="text-gray-600">Current Rate</span>
|
|
<span class="font-bold">{{ throughput.current }}/min</span>
|
|
</div>
|
|
<div class="flex justify-between items-center p-3 bg-gray-50 rounded">
|
|
<span class="text-gray-600">Peak Today</span>
|
|
<span class="font-bold">{{ throughput.peak }}/min</span>
|
|
</div>
|
|
<div class="flex justify-between items-center p-3 bg-gray-50 rounded">
|
|
<span class="text-gray-600">Average</span>
|
|
<span class="font-bold">{{ throughput.average }}/min</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- System Status -->
|
|
<rs-card>
|
|
<template #header>
|
|
<h3 class="text-lg font-semibold text-primary">System Status</h3>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div class="flex justify-between items-center p-3 bg-gray-50 rounded">
|
|
<span class="text-gray-600">Uptime Today</span>
|
|
<span class="font-bold text-green-600"
|
|
>{{ systemStatus.uptimeToday }}%</span
|
|
>
|
|
</div>
|
|
<div class="flex justify-between items-center p-3 bg-gray-50 rounded">
|
|
<span class="text-gray-600">Response Time</span>
|
|
<span class="font-bold">{{ systemStatus.responseTime }}ms</span>
|
|
</div>
|
|
<div class="flex justify-between items-center p-3 bg-gray-50 rounded">
|
|
<span class="text-gray-600">Error Rate</span>
|
|
<span class="font-bold text-red-600">{{ systemStatus.errorRate }}%</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { onMounted } from "vue";
|
|
|
|
definePageMeta({
|
|
title: "Performance",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{
|
|
name: "Notification",
|
|
path: "/notification",
|
|
},
|
|
{
|
|
name: "Queue",
|
|
path: "/notification/queue",
|
|
},
|
|
{
|
|
name: "Performance",
|
|
path: "/notification/queue/performance",
|
|
},
|
|
],
|
|
});
|
|
|
|
// Reactive state
|
|
const isLoading = ref(true);
|
|
const error = ref(null);
|
|
const metrics = ref({
|
|
throughput: "0",
|
|
uptime: "0",
|
|
workers: "0",
|
|
queueLoad: "0",
|
|
});
|
|
|
|
const throughput = ref({
|
|
current: "0",
|
|
peak: "0",
|
|
average: "0",
|
|
});
|
|
|
|
const systemStatus = ref({
|
|
uptimeToday: "0",
|
|
responseTime: "0",
|
|
errorRate: "0",
|
|
});
|
|
|
|
// Fetch metrics from API
|
|
const fetchMetrics = async () => {
|
|
try {
|
|
isLoading.value = true;
|
|
error.value = null;
|
|
|
|
const response = await useFetch("/api/notifications/queue/performance", {
|
|
method: "GET",
|
|
});
|
|
|
|
if (response.error.value) {
|
|
throw new Error(response.error.value.data?.message || "Failed to fetch metrics");
|
|
}
|
|
|
|
const data = response.data.value?.data;
|
|
if (data) {
|
|
metrics.value = data.metrics;
|
|
throughput.value = data.throughput;
|
|
systemStatus.value = data.systemStatus;
|
|
}
|
|
} catch (err) {
|
|
console.error("Error fetching metrics:", err);
|
|
error.value = err.message;
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
};
|
|
|
|
// Refresh metrics
|
|
const refreshMetrics = () => {
|
|
fetchMetrics();
|
|
};
|
|
|
|
// Fetch metrics on component mount
|
|
onMounted(() => {
|
|
fetchMetrics();
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss" scoped></style>
|