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:
689
pages/notification/log-audit/monitoring.vue
Normal file
689
pages/notification/log-audit/monitoring.vue
Normal file
@@ -0,0 +1,689 @@
|
||||
<template>
|
||||
<div>
|
||||
<LayoutsBreadcrumb />
|
||||
|
||||
<!-- Page Info Card -->
|
||||
<rs-card class="mb-6">
|
||||
<template #header>
|
||||
<div class="flex items-center">
|
||||
<Icon class="mr-2 text-primary" name="ic:outline-monitor"></Icon>
|
||||
<h1 class="text-xl font-bold text-primary">Real-Time Monitoring</h1>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<p class="text-gray-600">
|
||||
Live monitoring of notification system performance with real-time alerts and
|
||||
system health indicators. Track ongoing activities, monitor system load, and
|
||||
receive immediate notifications about issues.
|
||||
</p>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- System Status Overview -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
|
||||
<rs-card
|
||||
v-for="(status, index) in systemStatus"
|
||||
:key="index"
|
||||
class="transition-all duration-300 hover:shadow-lg"
|
||||
>
|
||||
<div class="pt-5 pb-3 px-5 flex items-center gap-4">
|
||||
<div
|
||||
class="p-5 flex justify-center items-center rounded-2xl transition-all duration-300"
|
||||
:class="
|
||||
status.status === 'healthy'
|
||||
? 'bg-green-100'
|
||||
: status.status === 'warning'
|
||||
? 'bg-yellow-100'
|
||||
: 'bg-red-100'
|
||||
"
|
||||
>
|
||||
<Icon
|
||||
class="text-3xl"
|
||||
:class="
|
||||
status.status === 'healthy'
|
||||
? 'text-green-600'
|
||||
: status.status === 'warning'
|
||||
? 'text-yellow-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
:name="status.icon"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1 truncate">
|
||||
<span class="block font-bold text-2xl leading-tight text-primary">
|
||||
{{ status.value }}
|
||||
</span>
|
||||
<span class="text-sm font-medium text-gray-600">
|
||||
{{ status.title }}
|
||||
</span>
|
||||
<div class="flex items-center mt-1">
|
||||
<div
|
||||
class="w-2 h-2 rounded-full mr-2"
|
||||
:class="
|
||||
status.status === 'healthy'
|
||||
? 'bg-green-500'
|
||||
: status.status === 'warning'
|
||||
? 'bg-yellow-500'
|
||||
: 'bg-red-500'
|
||||
"
|
||||
></div>
|
||||
<span
|
||||
class="text-xs font-medium capitalize"
|
||||
:class="
|
||||
status.status === 'healthy'
|
||||
? 'text-green-600'
|
||||
: status.status === 'warning'
|
||||
? 'text-yellow-600'
|
||||
: 'text-red-600'
|
||||
"
|
||||
>
|
||||
{{ status.status }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</rs-card>
|
||||
</div>
|
||||
|
||||
<!-- Real-Time Controls -->
|
||||
<rs-card class="mb-6">
|
||||
<template #body>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-4">
|
||||
<h3 class="text-lg font-semibold text-primary">Monitoring Controls</h3>
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="w-3 h-3 rounded-full animate-pulse"
|
||||
:class="isMonitoring ? 'bg-green-500' : 'bg-gray-400'"
|
||||
></div>
|
||||
<span class="text-sm font-medium">
|
||||
{{ isMonitoring ? "Live" : "Paused" }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<FormKit
|
||||
type="select"
|
||||
v-model="refreshInterval"
|
||||
:options="refreshOptions"
|
||||
outer-class="mb-0"
|
||||
@input="updateRefreshInterval"
|
||||
/>
|
||||
<rs-button
|
||||
:variant="isMonitoring ? 'danger-outline' : 'primary'"
|
||||
size="sm"
|
||||
@click="toggleMonitoring"
|
||||
>
|
||||
<Icon
|
||||
:name="isMonitoring ? 'ic:outline-pause' : 'ic:outline-play-arrow'"
|
||||
class="mr-1"
|
||||
/>
|
||||
{{ isMonitoring ? "Pause" : "Start" }}
|
||||
</rs-button>
|
||||
<rs-button variant="primary-outline" size="sm" @click="refreshData">
|
||||
<Icon name="ic:outline-refresh" class="mr-1" /> Refresh
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- System Performance Dashboard -->
|
||||
<rs-card class="mb-6">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Icon name="ic:outline-speed" class="mr-2 text-primary" />
|
||||
<h3 class="text-lg font-semibold text-primary">System Performance</h3>
|
||||
</div>
|
||||
<rs-button variant="outline" size="sm" @click="exportPerformanceData">
|
||||
<Icon name="ic:outline-file-download" class="mr-1" /> Export Data
|
||||
</rs-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
|
||||
<!-- CPU Usage -->
|
||||
<div class="text-center">
|
||||
<div class="relative mx-auto w-32 h-32 mb-4">
|
||||
<div
|
||||
class="w-full h-full bg-gray-200 rounded-full flex items-center justify-center"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-primary">
|
||||
{{ performanceMetrics.cpu }}%
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">CPU</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Memory Usage -->
|
||||
<div class="text-center">
|
||||
<div class="relative mx-auto w-32 h-32 mb-4">
|
||||
<div
|
||||
class="w-full h-full bg-gray-200 rounded-full flex items-center justify-center"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-primary">
|
||||
{{ performanceMetrics.memory }}%
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">Memory</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Queue Load -->
|
||||
<div class="text-center">
|
||||
<div class="relative mx-auto w-32 h-32 mb-4">
|
||||
<div
|
||||
class="w-full h-full bg-gray-200 rounded-full flex items-center justify-center"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="text-2xl font-bold text-primary">
|
||||
{{ performanceMetrics.queueLoad }}%
|
||||
</div>
|
||||
<div class="text-xs text-gray-600">Queue Load</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Performance Chart Placeholder -->
|
||||
<div
|
||||
class="h-64 bg-gray-100 dark:bg-gray-800 flex items-center justify-center rounded"
|
||||
>
|
||||
<div class="text-center">
|
||||
<Icon name="ic:outline-show-chart" class="text-4xl text-gray-400 mb-2" />
|
||||
<p class="text-gray-500">Real-time Performance Chart</p>
|
||||
<p class="text-sm text-gray-400 mt-1">
|
||||
Implementation pending for live performance metrics
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Live Activity & Alerts Grid -->
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
||||
<!-- Live Activity Feed -->
|
||||
<rs-card>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Icon name="ic:outline-notifications-active" class="mr-2 text-primary" />
|
||||
<h3 class="text-lg font-semibold text-primary">Live Activity Feed</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<rs-button variant="outline" size="sm" @click="clearActivityFeed">
|
||||
<Icon name="ic:outline-clear" class="mr-1" /> Clear
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="h-96 overflow-y-auto space-y-3">
|
||||
<div
|
||||
v-for="(activity, index) in liveActivityFeed"
|
||||
:key="index"
|
||||
class="flex items-start p-3 bg-gray-50 dark:bg-gray-800 rounded-lg transition-all duration-300 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<div
|
||||
class="w-3 h-3 rounded-full mr-3 mt-2 flex-shrink-0"
|
||||
:class="{
|
||||
'bg-green-500': activity.type === 'success',
|
||||
'bg-blue-500': activity.type === 'info',
|
||||
'bg-yellow-500': activity.type === 'warning',
|
||||
'bg-red-500': activity.type === 'error',
|
||||
}"
|
||||
></div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="font-medium text-sm">{{ activity.action }}</p>
|
||||
<p class="text-xs text-gray-600 mt-1">{{ activity.details }}</p>
|
||||
<div class="flex items-center mt-2 text-xs text-gray-500">
|
||||
<Icon name="ic:outline-access-time" class="mr-1" />
|
||||
<span>{{ activity.timestamp }}</span>
|
||||
<span class="mx-2">•</span>
|
||||
<span>{{ activity.source }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="liveActivityFeed.length === 0"
|
||||
class="text-center py-8 text-gray-500"
|
||||
>
|
||||
<Icon name="ic:outline-wifi-tethering" class="text-3xl mb-2 mx-auto" />
|
||||
<p>Waiting for live activity...</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Error Alerts -->
|
||||
<rs-card>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Icon name="ic:outline-warning" class="mr-2 text-primary" />
|
||||
<h3 class="text-lg font-semibold text-primary">Error Alerts</h3>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<rs-badge
|
||||
:variant="
|
||||
errorAlerts.filter((a) => a.severity === 'critical').length > 0
|
||||
? 'danger'
|
||||
: 'secondary'
|
||||
"
|
||||
size="sm"
|
||||
>
|
||||
{{ errorAlerts.length }} Active
|
||||
</rs-badge>
|
||||
<rs-button variant="outline" size="sm" @click="acknowledgeAllAlerts">
|
||||
<Icon name="ic:outline-check" class="mr-1" /> Acknowledge All
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="h-96 overflow-y-auto space-y-3">
|
||||
<div
|
||||
v-for="(alert, index) in errorAlerts"
|
||||
:key="index"
|
||||
class="p-3 rounded-lg border-l-4 transition-all duration-300 hover:shadow-sm"
|
||||
:class="{
|
||||
'bg-red-50 border-red-400': alert.severity === 'critical',
|
||||
'bg-yellow-50 border-yellow-400': alert.severity === 'warning',
|
||||
'bg-blue-50 border-blue-400': alert.severity === 'info',
|
||||
}"
|
||||
>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center">
|
||||
<Icon
|
||||
:name="
|
||||
alert.severity === 'critical'
|
||||
? 'ic:outline-error'
|
||||
: alert.severity === 'warning'
|
||||
? 'ic:outline-warning'
|
||||
: 'ic:outline-info'
|
||||
"
|
||||
:class="{
|
||||
'text-red-600': alert.severity === 'critical',
|
||||
'text-yellow-600': alert.severity === 'warning',
|
||||
'text-blue-600': alert.severity === 'info',
|
||||
}"
|
||||
class="mr-2"
|
||||
/>
|
||||
<span class="font-medium text-sm">{{ alert.title }}</span>
|
||||
</div>
|
||||
<p class="text-xs text-gray-600 mt-1">{{ alert.description }}</p>
|
||||
<div class="flex items-center mt-2 text-xs text-gray-500">
|
||||
<span>{{ alert.timestamp }}</span>
|
||||
<span class="mx-2">•</span>
|
||||
<span>{{ alert.component }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<rs-button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@click="acknowledgeAlert(index)"
|
||||
class="ml-2"
|
||||
>
|
||||
<Icon name="ic:outline-check" />
|
||||
</rs-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="errorAlerts.length === 0" class="text-center py-8 text-gray-500">
|
||||
<Icon
|
||||
name="ic:outline-check-circle"
|
||||
class="text-3xl mb-2 mx-auto text-green-500"
|
||||
/>
|
||||
<p>No active alerts</p>
|
||||
<p class="text-sm text-gray-400 mt-1">All systems operating normally</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
|
||||
<!-- Queue Status -->
|
||||
<rs-card class="mb-6">
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Icon name="ic:outline-queue" class="mr-2 text-primary" />
|
||||
<h3 class="text-lg font-semibold text-primary">Queue Status</h3>
|
||||
</div>
|
||||
<rs-button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@click="navigateTo('/notification/queue-scheduler/monitor')"
|
||||
>
|
||||
View Queue Monitor
|
||||
</rs-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<div
|
||||
v-for="queue in queueStatus"
|
||||
:key="queue.name"
|
||||
class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="font-medium">{{ queue.name }}</span>
|
||||
<rs-badge
|
||||
:variant="
|
||||
queue.status === 'active'
|
||||
? 'success'
|
||||
: queue.status === 'warning'
|
||||
? 'warning'
|
||||
: 'danger'
|
||||
"
|
||||
size="sm"
|
||||
>
|
||||
{{ queue.status }}
|
||||
</rs-badge>
|
||||
</div>
|
||||
<div class="text-2xl font-bold text-primary mb-1">{{ queue.count }}</div>
|
||||
<div class="text-sm text-gray-600">{{ queue.description }}</div>
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 mt-2">
|
||||
<div
|
||||
class="h-2 rounded-full transition-all duration-300"
|
||||
:class="
|
||||
queue.status === 'active'
|
||||
? 'bg-green-500'
|
||||
: queue.status === 'warning'
|
||||
? 'bg-yellow-500'
|
||||
: 'bg-red-500'
|
||||
"
|
||||
:style="{ width: queue.utilization + '%' }"
|
||||
></div>
|
||||
</div>
|
||||
<div class="text-xs text-gray-500 mt-1">
|
||||
{{ queue.utilization }}% utilized
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
|
||||
<!-- Recent Logs -->
|
||||
<rs-card>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center">
|
||||
<Icon name="ic:outline-history" class="mr-2 text-primary" />
|
||||
<h3 class="text-lg font-semibold text-primary">Recent Activity Logs</h3>
|
||||
</div>
|
||||
<rs-button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@click="navigateTo('/notification/log-audit/logs')"
|
||||
>
|
||||
View All Logs
|
||||
</rs-button>
|
||||
</div>
|
||||
</template>
|
||||
<template #body>
|
||||
<div class="space-y-3">
|
||||
<div
|
||||
v-for="(log, index) in recentLogs"
|
||||
:key="index"
|
||||
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
||||
>
|
||||
<div class="flex items-center">
|
||||
<div
|
||||
class="w-3 h-3 rounded-full mr-3"
|
||||
:class="{
|
||||
'bg-green-500': log.status === 'sent' || log.status === 'created',
|
||||
'bg-yellow-500': log.status === 'queued',
|
||||
'bg-red-500': log.status === 'failed',
|
||||
'bg-blue-500': log.status === 'opened',
|
||||
}"
|
||||
></div>
|
||||
<div>
|
||||
<p class="font-medium">{{ log.action }}</p>
|
||||
<p class="text-sm text-gray-600">{{ log.description }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-sm font-medium capitalize">{{ log.status }}</p>
|
||||
<p class="text-xs text-gray-500">{{ log.time }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</rs-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
definePageMeta({
|
||||
title: "Real-Time Monitoring",
|
||||
middleware: ["auth"],
|
||||
requiresAuth: true,
|
||||
breadcrumb: [
|
||||
{
|
||||
name: "Dashboard",
|
||||
path: "/dashboard",
|
||||
},
|
||||
{
|
||||
name: "Notification",
|
||||
path: "/notification",
|
||||
},
|
||||
{
|
||||
name: "Logs & Audit Trail",
|
||||
path: "/notification/log-audit",
|
||||
},
|
||||
{
|
||||
name: "Monitoring",
|
||||
path: "/notification/log-audit/monitoring",
|
||||
type: "current",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
import { ref, computed, onMounted, onUnmounted } from "vue";
|
||||
|
||||
// Use the notification logs composable
|
||||
const {
|
||||
monitoringData,
|
||||
monitoringLoading,
|
||||
monitoringError,
|
||||
fetchMonitoringData,
|
||||
formatTimeAgo,
|
||||
} = useNotificationLogs();
|
||||
|
||||
// Monitoring state
|
||||
const isMonitoring = ref(true);
|
||||
const refreshInterval = ref("5s");
|
||||
const refreshIntervalId = ref(null);
|
||||
|
||||
const refreshOptions = [
|
||||
{ label: "1 second", value: "1s" },
|
||||
{ label: "5 seconds", value: "5s" },
|
||||
{ label: "10 seconds", value: "10s" },
|
||||
{ label: "30 seconds", value: "30s" },
|
||||
{ label: "1 minute", value: "1m" },
|
||||
];
|
||||
|
||||
// System status data - will be updated from API
|
||||
const systemStatus = computed(
|
||||
() =>
|
||||
monitoringData.value?.systemStatus || [
|
||||
{
|
||||
title: "System Health",
|
||||
value: "Healthy",
|
||||
icon: "ic:outline-favorite",
|
||||
status: "healthy",
|
||||
},
|
||||
{
|
||||
title: "Throughput",
|
||||
value: "0/hr",
|
||||
icon: "ic:outline-speed",
|
||||
status: "healthy",
|
||||
},
|
||||
{
|
||||
title: "Error Rate",
|
||||
value: "0.00%",
|
||||
icon: "ic:outline-error-outline",
|
||||
status: "healthy",
|
||||
},
|
||||
{
|
||||
title: "Response Time",
|
||||
value: "0ms",
|
||||
icon: "ic:outline-timer",
|
||||
status: "healthy",
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
// Performance metrics - will be updated from API
|
||||
const performanceMetrics = computed(
|
||||
() =>
|
||||
monitoringData.value?.performanceMetrics || {
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
queueLoad: 0,
|
||||
}
|
||||
);
|
||||
|
||||
// Live activity feed - will be updated from API
|
||||
const liveActivityFeed = computed(() => monitoringData.value?.recentActivity || []);
|
||||
|
||||
// Error alerts - will be updated from API
|
||||
const errorAlerts = computed(() => monitoringData.value?.errorAlerts || []);
|
||||
|
||||
// Queue status - will be updated from API
|
||||
const queueStatus = computed(() => monitoringData.value?.queueStatus || []);
|
||||
|
||||
// Recent logs - using the same activity feed
|
||||
const recentLogs = computed(() => liveActivityFeed.value);
|
||||
|
||||
// Methods
|
||||
const toggleMonitoring = () => {
|
||||
isMonitoring.value = !isMonitoring.value;
|
||||
if (isMonitoring.value) {
|
||||
startMonitoring();
|
||||
} else {
|
||||
stopMonitoring();
|
||||
}
|
||||
};
|
||||
|
||||
const updateRefreshInterval = () => {
|
||||
if (isMonitoring.value) {
|
||||
stopMonitoring();
|
||||
startMonitoring();
|
||||
}
|
||||
};
|
||||
|
||||
const startMonitoring = () => {
|
||||
const intervalMs =
|
||||
{
|
||||
"1s": 1000,
|
||||
"5s": 5000,
|
||||
"10s": 10000,
|
||||
"30s": 30000,
|
||||
"1m": 60000,
|
||||
}[refreshInterval.value] || 5000;
|
||||
|
||||
// Fetch immediately
|
||||
fetchMonitoringData();
|
||||
|
||||
// Then set up the interval
|
||||
refreshIntervalId.value = setInterval(async () => {
|
||||
await fetchMonitoringData();
|
||||
}, intervalMs);
|
||||
};
|
||||
|
||||
const stopMonitoring = () => {
|
||||
if (refreshIntervalId.value) {
|
||||
clearInterval(refreshIntervalId.value);
|
||||
refreshIntervalId.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
const refreshData = async () => {
|
||||
await fetchMonitoringData();
|
||||
};
|
||||
|
||||
const clearActivityFeed = () => {
|
||||
// For now, just refresh the data - in a real app, you might have an API endpoint to clear the feed
|
||||
fetchMonitoringData();
|
||||
};
|
||||
|
||||
const acknowledgeAlert = (index) => {
|
||||
// In a real app, you would call an API to acknowledge the alert
|
||||
errorAlerts.value.splice(index, 1);
|
||||
};
|
||||
|
||||
const acknowledgeAllAlerts = () => {
|
||||
// In a real app, you would call an API to acknowledge all alerts
|
||||
errorAlerts.value = [];
|
||||
};
|
||||
|
||||
const exportPerformanceData = () => {
|
||||
console.log("Exporting performance data...");
|
||||
alert("Exporting performance data. (Implementation pending)");
|
||||
};
|
||||
|
||||
// Lifecycle
|
||||
onMounted(() => {
|
||||
if (isMonitoring.value) {
|
||||
startMonitoring();
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
stopMonitoring();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// Custom styles for FormKit consistency
|
||||
:deep(.formkit-outer) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.formkit-label) {
|
||||
font-weight: 500;
|
||||
color: rgb(107 114 128);
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
:deep(.formkit-input) {
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
// Badge component styles (if RsBadge doesn't exist, these can be adjusted)
|
||||
.rs-badge {
|
||||
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
|
||||
}
|
||||
|
||||
.rs-badge.variant-success {
|
||||
@apply bg-green-100 text-green-800;
|
||||
}
|
||||
|
||||
.rs-badge.variant-danger {
|
||||
@apply bg-red-100 text-red-800;
|
||||
}
|
||||
|
||||
.rs-badge.variant-warning {
|
||||
@apply bg-yellow-100 text-yellow-800;
|
||||
}
|
||||
|
||||
.rs-badge.variant-info {
|
||||
@apply bg-blue-100 text-blue-800;
|
||||
}
|
||||
|
||||
.rs-badge.variant-secondary {
|
||||
@apply bg-gray-100 text-gray-800;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user