500 lines
16 KiB
Vue
500 lines
16 KiB
Vue
<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-bar-chart"></Icon>
|
|
<h1 class="text-xl font-bold text-primary">Analytics Dashboard</h1>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<p class="text-gray-600">
|
|
Visual analytics and metrics for notification performance, delivery rates, and user engagement trends.
|
|
Monitor key performance indicators and analyze patterns to optimize your notification strategy.
|
|
</p>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Key Metrics Summary -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
|
|
<rs-card
|
|
v-for="(metric, index) in keyMetrics"
|
|
: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 bg-primary/20 rounded-2xl transition-all duration-300 hover:bg-primary/30"
|
|
>
|
|
<Icon class="text-primary text-3xl" :name="metric.icon"></Icon>
|
|
</div>
|
|
<div class="flex-1 truncate">
|
|
<span class="block font-bold text-2xl leading-tight text-primary">
|
|
{{ metric.value }}
|
|
</span>
|
|
<span class="text-sm font-medium text-gray-600">
|
|
{{ metric.title }}
|
|
</span>
|
|
<div class="flex items-center mt-1">
|
|
<Icon
|
|
:name="metric.trend === 'up' ? 'ic:outline-trending-up' : 'ic:outline-trending-down'"
|
|
:class="metric.trend === 'up' ? 'text-green-500' : 'text-red-500'"
|
|
class="text-sm mr-1"
|
|
/>
|
|
<span
|
|
:class="metric.trend === 'up' ? 'text-green-600' : 'text-red-600'"
|
|
class="text-xs font-medium"
|
|
>
|
|
{{ metric.change }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Time Range Filter -->
|
|
<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">Analytics Period</h3>
|
|
<FormKit
|
|
type="select"
|
|
v-model="selectedPeriod"
|
|
:options="periodOptions"
|
|
outer-class="mb-0"
|
|
@input="updateAnalytics"
|
|
/>
|
|
</div>
|
|
<rs-button variant="primary-outline" size="sm" @click="refreshAnalytics">
|
|
<Icon name="ic:outline-refresh" class="mr-1"/> Refresh Data
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Main Analytics Charts -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon name="ic:outline-bar-chart" class="mr-2 text-primary"/>
|
|
<h3 class="text-lg font-semibold text-primary">Delivery Rate Analysis</h3>
|
|
</div>
|
|
<rs-button variant="outline" size="sm" @click="exportChart('delivery')">
|
|
<Icon name="ic:outline-file-download" class="mr-1"/> Export
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<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-assessment" class="text-4xl text-gray-400 mb-2"/>
|
|
<p class="text-gray-500">Chart Implementation Pending</p>
|
|
<p class="text-sm text-gray-400 mt-1">Line chart showing delivery success over time</p>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 grid grid-cols-3 gap-4 text-center">
|
|
<div>
|
|
<div class="text-2xl font-bold text-green-600">98.2%</div>
|
|
<div class="text-sm text-gray-600">Success Rate</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-2xl font-bold text-red-600">1.5%</div>
|
|
<div class="text-sm text-gray-600">Failed Rate</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-2xl font-bold text-yellow-600">0.3%</div>
|
|
<div class="text-sm text-gray-600">Bounce Rate</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon name="ic:outline-open-in-new" class="mr-2 text-primary"/>
|
|
<h3 class="text-lg font-semibold text-primary">Open Rate Metrics</h3>
|
|
</div>
|
|
<rs-button variant="outline" size="sm" @click="exportChart('openrate')">
|
|
<Icon name="ic:outline-file-download" class="mr-1"/> Export
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<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-assessment" class="text-4xl text-gray-400 mb-2"/>
|
|
<p class="text-gray-500">Chart Implementation Pending</p>
|
|
<p class="text-sm text-gray-400 mt-1">Pie chart showing open rates by channel</p>
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 space-y-2">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 bg-blue-500 rounded-full mr-2"></div>
|
|
<span class="text-sm">Email</span>
|
|
</div>
|
|
<span class="text-sm font-medium">24.5%</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 bg-green-500 rounded-full mr-2"></div>
|
|
<span class="text-sm">Push Notification</span>
|
|
</div>
|
|
<span class="text-sm font-medium">18.7%</span>
|
|
</div>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<div class="w-3 h-3 bg-yellow-500 rounded-full mr-2"></div>
|
|
<span class="text-sm">SMS</span>
|
|
</div>
|
|
<span class="text-sm font-medium">45.2%</span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- User Engagement Trends -->
|
|
<rs-card class="mb-6">
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon name="ic:outline-trending-up" class="mr-2 text-primary"/>
|
|
<h3 class="text-lg font-semibold text-primary">User Engagement Trends</h3>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<FormKit
|
|
type="select"
|
|
v-model="selectedChannel"
|
|
:options="channelFilterOptions"
|
|
outer-class="mb-0"
|
|
@input="updateEngagementChart"
|
|
/>
|
|
<rs-button variant="outline" size="sm" @click="exportChart('engagement')">
|
|
<Icon name="ic:outline-file-download" class="mr-1"/> Export
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="h-80 bg-gray-100 dark:bg-gray-800 flex items-center justify-center rounded">
|
|
<div class="text-center">
|
|
<Icon name="ic:outline-assessment" class="text-4xl text-gray-400 mb-2"/>
|
|
<p class="text-gray-500">Chart Implementation Pending</p>
|
|
<p class="text-sm text-gray-400 mt-1">Multi-line chart showing engagement metrics over time</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Performance Breakdown -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center">
|
|
<Icon name="ic:outline-schedule" class="mr-2 text-primary"/>
|
|
<h3 class="text-lg font-semibold text-primary">Peak Activity Hours</h3>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="h-48 bg-gray-100 dark:bg-gray-800 flex items-center justify-center rounded mb-4">
|
|
<div class="text-center">
|
|
<Icon name="ic:outline-access-time" class="text-3xl text-gray-400 mb-2"/>
|
|
<p class="text-gray-500">Heatmap Implementation Pending</p>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<div class="text-center">
|
|
<div class="text-xl font-bold text-primary">2:00 PM</div>
|
|
<div class="text-sm text-gray-600">Peak Hour</div>
|
|
</div>
|
|
<div class="text-center">
|
|
<div class="text-xl font-bold text-primary">3:47</div>
|
|
<div class="text-sm text-gray-600">Avg Response Time (s)</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center">
|
|
<Icon name="ic:outline-device-hub" class="mr-2 text-primary"/>
|
|
<h3 class="text-lg font-semibold text-primary">Channel Performance</h3>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div
|
|
v-for="channel in channelPerformance"
|
|
:key="channel.name"
|
|
class="p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
|
|
>
|
|
<div class="flex items-center justify-between mb-2">
|
|
<div class="flex items-center">
|
|
<Icon :name="channel.icon" class="mr-2 text-primary"/>
|
|
<span class="font-medium">{{ channel.name }}</span>
|
|
</div>
|
|
<span class="text-sm font-bold text-primary">{{ channel.successRate }}%</span>
|
|
</div>
|
|
<div class="w-full bg-gray-200 rounded-full h-2">
|
|
<div
|
|
class="bg-primary h-2 rounded-full transition-all duration-300"
|
|
:style="{ width: channel.successRate + '%' }"
|
|
></div>
|
|
</div>
|
|
<div class="flex justify-between text-xs text-gray-500 mt-1">
|
|
<span>{{ channel.sent }} sent</span>
|
|
<span>{{ channel.failed }} failed</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Recent Analytics Events -->
|
|
<rs-card>
|
|
<template #header>
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center">
|
|
<Icon name="ic:outline-insights" class="mr-2 text-primary"/>
|
|
<h3 class="text-lg font-semibold text-primary">Recent Analytics Events</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="(event, index) in recentEvents"
|
|
: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': event.type === 'success',
|
|
'bg-yellow-500': event.type === 'warning',
|
|
'bg-red-500': event.type === 'error',
|
|
'bg-blue-500': event.type === 'info',
|
|
}"
|
|
></div>
|
|
<div>
|
|
<p class="font-medium">{{ event.title }}</p>
|
|
<p class="text-sm text-gray-600">{{ event.description }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="text-right">
|
|
<p class="text-sm font-medium">{{ event.value }}</p>
|
|
<p class="text-xs text-gray-500">{{ event.time }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
definePageMeta({
|
|
title: "Analytics Dashboard",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{
|
|
name: "Dashboard",
|
|
path: "/dashboard",
|
|
},
|
|
{
|
|
name: "Notification",
|
|
path: "/notification",
|
|
},
|
|
{
|
|
name: "Logs & Audit Trail",
|
|
path: "/notification/log-audit",
|
|
},
|
|
{
|
|
name: "Analytics",
|
|
path: "/notification/log-audit/analytics",
|
|
type: "current"
|
|
},
|
|
],
|
|
});
|
|
|
|
import { ref, computed } from 'vue'
|
|
|
|
// Time period selection
|
|
const selectedPeriod = ref('7d')
|
|
const periodOptions = [
|
|
{ label: 'Last 24 Hours', value: '1d' },
|
|
{ label: 'Last 7 Days', value: '7d' },
|
|
{ label: 'Last 30 Days', value: '30d' },
|
|
{ label: 'Last 90 Days', value: '90d' },
|
|
{ label: 'Last 12 Months', value: '12m' },
|
|
]
|
|
|
|
// Channel filter for engagement chart
|
|
const selectedChannel = ref('all')
|
|
const channelFilterOptions = [
|
|
{ label: 'All Channels', value: 'all' },
|
|
{ label: 'Email', value: 'email' },
|
|
{ label: 'SMS', value: 'sms' },
|
|
{ label: 'Push Notification', value: 'push' },
|
|
{ label: 'Webhook', value: 'webhook' },
|
|
]
|
|
|
|
// Key metrics data
|
|
const keyMetrics = computed(() => [
|
|
{
|
|
title: "Total Sent",
|
|
value: "127,847",
|
|
icon: "ic:outline-send",
|
|
trend: "up",
|
|
change: "+12.5%"
|
|
},
|
|
{
|
|
title: "Success Rate",
|
|
value: "98.2%",
|
|
icon: "ic:outline-check-circle",
|
|
trend: "up",
|
|
change: "+2.1%"
|
|
},
|
|
{
|
|
title: "Open Rate",
|
|
value: "24.7%",
|
|
icon: "ic:outline-open-in-new",
|
|
trend: "down",
|
|
change: "-1.3%"
|
|
},
|
|
{
|
|
title: "Click Rate",
|
|
value: "4.8%",
|
|
icon: "ic:outline-touch-app",
|
|
trend: "up",
|
|
change: "+0.7%"
|
|
},
|
|
])
|
|
|
|
// Channel performance data
|
|
const channelPerformance = ref([
|
|
{
|
|
name: "Email",
|
|
icon: "ic:outline-email",
|
|
successRate: 98,
|
|
sent: "45,230",
|
|
failed: "912"
|
|
},
|
|
{
|
|
name: "SMS",
|
|
icon: "ic:outline-sms",
|
|
successRate: 97,
|
|
sent: "32,156",
|
|
failed: "987"
|
|
},
|
|
{
|
|
name: "Push Notification",
|
|
icon: "ic:outline-notifications",
|
|
successRate: 99,
|
|
sent: "89,421",
|
|
failed: "645"
|
|
},
|
|
{
|
|
name: "Webhook",
|
|
icon: "ic:outline-webhook",
|
|
successRate: 95,
|
|
sent: "12,340",
|
|
failed: "617"
|
|
},
|
|
])
|
|
|
|
// Recent analytics events
|
|
const recentEvents = ref([
|
|
{
|
|
title: "High Open Rate Detected",
|
|
description: "Email campaign achieved 45% open rate",
|
|
value: "45.2%",
|
|
time: "2 minutes ago",
|
|
type: "success"
|
|
},
|
|
{
|
|
title: "SMS Delivery Spike",
|
|
description: "SMS volume increased by 300%",
|
|
value: "+300%",
|
|
time: "15 minutes ago",
|
|
type: "info"
|
|
},
|
|
{
|
|
title: "Push Notification Failure",
|
|
description: "Batch delivery failed for iOS devices",
|
|
value: "2,456 failed",
|
|
time: "1 hour ago",
|
|
type: "error"
|
|
},
|
|
{
|
|
title: "Webhook Timeout Warning",
|
|
description: "Response time exceeded threshold",
|
|
value: "8.5s avg",
|
|
time: "2 hours ago",
|
|
type: "warning"
|
|
},
|
|
{
|
|
title: "Peak Activity Period",
|
|
description: "Highest activity recorded today",
|
|
value: "15,420/hr",
|
|
time: "3 hours ago",
|
|
type: "info"
|
|
},
|
|
])
|
|
|
|
// Methods
|
|
const updateAnalytics = () => {
|
|
console.log('Updating analytics for period:', selectedPeriod.value)
|
|
// Simulate data refresh
|
|
}
|
|
|
|
const updateEngagementChart = () => {
|
|
console.log('Updating engagement chart for channel:', selectedChannel.value)
|
|
// Simulate chart update
|
|
}
|
|
|
|
const refreshAnalytics = () => {
|
|
console.log('Refreshing analytics data...')
|
|
// Simulate refresh
|
|
}
|
|
|
|
const exportChart = (chartType) => {
|
|
console.log(`Exporting ${chartType} chart...`)
|
|
alert(`Exporting ${chartType} chart. (Implementation pending)`)
|
|
}
|
|
</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;
|
|
}
|
|
</style> |