482 lines
14 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-send"></Icon>
<h1 class="text-xl font-bold text-primary">Notification Delivery</h1>
</div>
</template>
<template #body>
<p class="text-gray-600">
Configure and monitor email and push notification delivery.
</p>
</template>
</rs-card>
<!-- Loading Overlay -->
<div
v-if="isLoading"
class="fixed inset-0 blur-lg bg-opacity-50 z-50 flex items-center justify-center"
>
<div class="bg-white rounded-lg p-6 flex items-center space-x-4">
<Icon name="ic:outline-refresh" class="text-primary animate-spin" size="24" />
<span class="text-gray-700">Loading...</span>
</div>
</div>
<!-- Error Alert -->
<rs-alert
v-if="error"
variant="danger"
class="mb-6"
dismissible
@dismiss="error = null"
>
<template #icon>
<Icon name="ic:outline-error" />
</template>
{{ error }}
</rs-alert>
<!-- Quick Stats -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
<rs-card v-for="(stat, index) in stats" :key="index" class="text-center relative">
<div
v-if="isLoading"
class="absolute inset-0 bg-gray-50 bg-opacity-75 flex items-center justify-center"
>
<Icon name="ic:outline-refresh" class="text-primary animate-spin" size="24" />
</div>
<div class="p-6">
<Icon class="text-primary text-4xl mb-3" :name="getIconForStat(index)"></Icon>
<div class="font-bold text-2xl text-primary mb-1">
{{ stat.value }}
</div>
<div class="text-sm text-gray-600">{{ getLabelForStat(index) }}</div>
</div>
</rs-card>
</div>
<!-- Channel Configuration -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<!-- Email Configuration -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-email"></Icon>
<h2 class="text-lg font-semibold text-primary">Email Configuration</h2>
</div>
<rs-badge :variant="emailConfig.enabled ? 'success' : 'secondary'">
{{ emailConfig.enabled ? "Active" : "Disabled" }}
</rs-badge>
</div>
</template>
<template #body>
<div v-if="isLoading" class="flex items-center justify-center py-8">
<Icon name="ic:outline-refresh" class="text-primary animate-spin" size="24" />
</div>
<div v-else class="space-y-4">
<div class="flex items-center justify-between">
<span class="font-medium">Enable Email Delivery</span>
<FormKit type="toggle" v-model="emailConfig.enabled" />
</div>
<div>
<FormKit
type="select"
label="Email Provider"
v-model="emailConfig.provider"
:options="emailProviders"
:disabled="!emailConfig.enabled"
/>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="text-gray-600">Status:</span>
<span class="ml-2 font-medium">{{ emailConfig.status }}</span>
</div>
<div>
<span class="text-gray-600">Success Rate:</span>
<span class="ml-2 font-medium">{{ emailConfig.successRate }}%</span>
</div>
</div>
</div>
</div>
</template>
</rs-card>
<!-- Push Notification Configuration -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-notifications"></Icon>
<h2 class="text-lg font-semibold text-primary">
Push Notification Configuration
</h2>
</div>
<rs-badge :variant="pushConfig.enabled ? 'success' : 'secondary'">
{{ pushConfig.enabled ? "Active" : "Disabled" }}
</rs-badge>
</div>
</template>
<template #body>
<div v-if="isLoading" class="flex items-center justify-center py-8">
<Icon name="ic:outline-refresh" class="text-primary animate-spin" size="24" />
</div>
<div v-else class="space-y-4">
<div class="flex items-center justify-between">
<span class="font-medium">Enable Push Notifications</span>
<FormKit type="toggle" v-model="pushConfig.enabled" />
</div>
<div>
<FormKit
type="select"
label="Push Provider"
v-model="pushConfig.provider"
:options="pushProviders"
:disabled="!pushConfig.enabled"
/>
</div>
<div class="p-4 bg-gray-50 rounded-lg">
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="text-gray-600">Status:</span>
<span class="ml-2 font-medium">{{ pushConfig.status }}</span>
</div>
<div>
<span class="text-gray-600">Success Rate:</span>
<span class="ml-2 font-medium">{{ pushConfig.successRate }}%</span>
</div>
</div>
</div>
</div>
</template>
</rs-card>
</div>
<!-- Basic Settings -->
<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-settings"></Icon>
<h2 class="text-lg font-semibold text-primary">Delivery Settings</h2>
</div>
<rs-button
size="sm"
variant="primary"
@click="saveSettings"
:disabled="isLoading"
>
<Icon
:name="isLoading ? 'ic:outline-refresh' : 'ic:outline-save'"
class="mr-1"
:class="{ 'animate-spin': isLoading }"
/>
{{ isLoading ? "Saving..." : "Save Settings" }}
</rs-button>
</div>
</template>
<template #body>
<div v-if="isLoading" class="flex items-center justify-center py-8">
<Icon name="ic:outline-refresh" class="text-primary animate-spin" size="24" />
</div>
<div v-else class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-4">
<div class="flex items-center justify-between">
<span class="font-medium">Auto Retry Failed Deliveries</span>
<FormKit type="toggle" v-model="settings.autoRetry" />
</div>
<div class="flex items-center justify-between">
<span class="font-medium">Enable Fallback Delivery</span>
<FormKit type="toggle" v-model="settings.enableFallback" />
</div>
<div>
<FormKit
type="number"
label="Max Retry Attempts"
v-model="settings.maxRetries"
min="0"
max="5"
/>
</div>
</div>
<div class="space-y-4">
<div>
<FormKit
type="number"
label="Retry Delay (seconds)"
v-model="settings.retryDelay"
min="1"
max="300"
/>
</div>
<div>
<FormKit
type="select"
label="Delivery Priority"
v-model="settings.priority"
:options="priorityOptions"
/>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">Enable Delivery Reports</span>
<FormKit type="toggle" v-model="settings.enableReports" />
</div>
</div>
</div>
</template>
</rs-card>
</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { useToast } from "@/composables/useToast";
import { useNotificationDelivery } from "@/composables/useNotificationDelivery";
definePageMeta({
title: "Notification Delivery",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Dashboard",
path: "/dashboard",
},
{
name: "Notification",
path: "/notification",
},
{
name: "Delivery",
path: "/notification/delivery",
type: "current",
},
],
});
// Stats
const stats = ref([
{
key: "emailsSent",
value: "12.5K",
label: "Emails Sent",
icon: "ic:outline-email",
},
{
key: "pushSent",
value: "8.3K",
label: "Push Sent",
icon: "ic:outline-notifications",
},
{
key: "successRate",
value: 98.5,
label: "Success Rate",
icon: "ic:outline-check-circle",
},
{ key: "failed", value: 156, label: "Failed", icon: "ic:outline-error" },
]);
// Email Configuration
const emailConfig = ref({
enabled: true,
provider: "nodemailer",
status: "Connected",
successRate: 99.2,
});
const emailProviders = [{ label: "Nodemailer", value: "nodemailer" }];
// Push Configuration
const pushConfig = ref({
enabled: true,
provider: "firebase",
status: "Connected",
successRate: 95.8,
});
const pushProviders = [{ label: "Firebase FCM", value: "firebase" }];
// Delivery Status
const emailStatus = ref({
delivered: "11.8K",
failed: "42",
});
const pushStatus = ref({
delivered: "7.9K",
failed: "114",
});
// Settings
const settings = ref({
autoRetry: true,
enableFallback: true,
maxRetries: 3,
retryDelay: 30,
priority: "normal",
enableReports: true,
});
const priorityOptions = [
{ label: "High Priority", value: "high" },
{ label: "Normal Priority", value: "normal" },
{ label: "Low Priority", value: "low" },
];
// Use composables
const toast = useToast();
const {
isLoading,
error,
fetchDeliveryStats,
fetchEmailConfig,
fetchPushConfig,
fetchDeliverySettings,
updateEmailConfig,
updatePushConfig,
updateDeliverySettings,
} = useNotificationDelivery();
// Methods
async function refreshData() {
try {
isLoading.value = true;
const [statsData, emailData, pushData, settingsData] = await Promise.all([
fetchDeliveryStats(),
fetchEmailConfig(),
fetchPushConfig(),
fetchDeliverySettings(),
]);
// Update stats
stats.value = [
{
key: "emailsSent",
value: formatNumber(statsData.emailsSent),
label: "Emails Sent",
icon: "ic:outline-email",
},
{
key: "pushSent",
value: formatNumber(statsData.pushSent),
label: "Push Sent",
icon: "ic:outline-notifications",
},
{
key: "successRate",
value: statsData.successRate.toFixed(1),
label: "Success Rate",
icon: "ic:outline-check-circle",
},
{
key: "failed",
value: formatNumber(statsData.failed),
label: "Failed",
icon: "ic:outline-error",
},
];
// Update email config
emailConfig.value = {
enabled: emailData.enabled,
provider: emailData.provider,
status: emailData.status,
successRate: emailData.successRate,
};
// Update push config
pushConfig.value = {
enabled: pushData.enabled,
provider: pushData.provider,
status: pushData.status,
successRate: pushData.successRate,
};
// Update settings
settings.value = {
autoRetry: settingsData.autoRetry,
enableFallback: settingsData.enableFallback,
maxRetries: settingsData.maxRetries,
retryDelay: settingsData.retryDelay,
priority: settingsData.priority,
enableReports: settingsData.enableReports,
};
// toast.success("Data refreshed successfully");
} catch (err) {
console.error("Error refreshing data:", err);
toast.error(err.message || "Failed to refresh data");
} finally {
isLoading.value = false;
}
}
async function saveSettings() {
try {
isLoading.value = true;
// Update email configuration if changed
if (emailConfig.value.enabled !== undefined) {
await updateEmailConfig(emailConfig.value);
}
// Update push configuration if changed
if (pushConfig.value.enabled !== undefined) {
await updatePushConfig(pushConfig.value);
}
// Update delivery settings
await updateDeliverySettings(settings.value);
toast.success("Settings saved successfully");
} catch (err) {
console.error("Error saving settings:", err);
toast.error(err.message || "Failed to save settings");
} finally {
isLoading.value = false;
}
}
// Format number helper
function formatNumber(num) {
if (num >= 1000) {
return (num / 1000).toFixed(1) + "K";
}
return num.toString();
}
// Helper functions for stats
function getIconForStat(index) {
return stats.value[index]?.icon || "ic:outline-email";
}
function getLabelForStat(index) {
return stats.value[index]?.label || "";
}
// Load initial data
onMounted(() => {
refreshData();
});
</script>
<style lang="scss" scoped></style>