Files
Nas-Notification/pages/notification/delivery/index.vue

644 lines
22 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">Delivery Settings</h1>
</div>
</template>
<template #body>
<p class="text-gray-600">
Configure email, push notification, and SMS delivery settings.
</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>
<!-- Channel Configuration -->
<div class="space-y-8 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="flex flex-col gap-4">
<div class="flex items-center justify-between">
<span class="font-medium">Enable Email Delivery</span>
<FormKit type="toggle" v-model="emailConfig.enabled" />
</div>
<FormKit
type="select"
label="Email Provider"
v-model="emailConfig.provider"
:options="emailProviders"
:disabled="!emailConfig.enabled"
class="w-full"
/>
<!-- Provider Configuration -->
<div class="space-y-4">
<!-- Mailtrap Configuration -->
<div v-if="emailConfig.provider === 'mailtrap'">
<!-- Mailtrap Info Banner -->
<div class="p-4 bg-blue-50 border border-blue-200 rounded-lg mb-4">
<div class="flex items-start gap-3">
<Icon name="ic:outline-info" class="text-blue-600 text-xl mt-0.5"></Icon>
<div class="text-sm">
<p class="font-semibold text-blue-900 mb-1">Mailtrap SMTP Configuration</p>
<p class="text-blue-700">
Use <code class="px-1 py-0.5 bg-blue-100 rounded">live.smtp.mailtrap.io</code> for production sending.
Port 587 (recommended) with STARTTLS.
</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormKit
type="text"
label="SMTP Host"
v-model="emailConfig.config.host"
:disabled="!emailConfig.enabled"
help="Use: live.smtp.mailtrap.io"
/>
<FormKit
type="number"
label="SMTP Port"
v-model="emailConfig.config.port"
:disabled="!emailConfig.enabled"
help="Recommended: 587"
/>
<FormKit
type="text"
label="SMTP Username"
v-model="emailConfig.config.user"
:disabled="!emailConfig.enabled"
help="Usually: apismtp@mailtrap.io"
/>
<FormKit
type="password"
label="SMTP Password / API Token"
v-model="emailConfig.config.pass"
:disabled="!emailConfig.enabled"
help="Your Mailtrap API token"
validation="required"
/>
<FormKit
type="email"
label="Sender Email"
v-model="emailConfig.config.senderEmail"
:disabled="!emailConfig.enabled"
help="Email address that will appear as the sender of notifications"
validation="required|email"
/>
<FormKit
type="text"
label="Sender Name (Optional)"
v-model="emailConfig.config.senderName"
:disabled="!emailConfig.enabled"
help="Name that will appear as the sender"
/>
</div>
</div>
<!-- AWS SES Configuration -->
<div v-if="emailConfig.provider === 'aws-ses'">
<!-- AWS SES Info Banner -->
<div class="p-4 bg-orange-50 border border-orange-200 rounded-lg mb-4">
<div class="flex items-start gap-3">
<Icon name="ic:outline-info" class="text-orange-600 text-xl mt-0.5"></Icon>
<div class="text-sm">
<p class="font-semibold text-orange-900 mb-1">AWS SES SMTP Configuration</p>
<p class="text-orange-700">
Use region-specific SMTP endpoint: <code class="px-1 py-0.5 bg-orange-100 rounded">email-smtp.&lt;region&gt;.amazonaws.com</code>
(e.g., email-smtp.us-east-1.amazonaws.com). Port 587 with STARTTLS.
</p>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormKit
type="text"
label="SMTP Host"
v-model="emailConfig.config.host"
:disabled="!emailConfig.enabled"
help="e.g., email-smtp.us-east-1.amazonaws.com"
placeholder="email-smtp.us-east-1.amazonaws.com"
/>
<FormKit
type="number"
label="SMTP Port"
v-model="emailConfig.config.port"
:disabled="!emailConfig.enabled"
help="Use 587 (STARTTLS) or 465 (TLS)"
/>
<FormKit
type="text"
label="SMTP Username"
v-model="emailConfig.config.user"
:disabled="!emailConfig.enabled"
help="Your AWS SES SMTP username (not IAM user)"
placeholder="AKIAIOSFODNN7EXAMPLE"
/>
<FormKit
type="password"
label="SMTP Password"
v-model="emailConfig.config.pass"
:disabled="!emailConfig.enabled"
help="Your AWS SES SMTP password (not secret key)"
validation="required"
/>
<FormKit
type="email"
label="Sender Email"
v-model="emailConfig.config.senderEmail"
:disabled="!emailConfig.enabled"
help="Must be verified in AWS SES"
validation="required|email"
/>
<FormKit
type="text"
label="Sender Name (Optional)"
v-model="emailConfig.config.senderName"
:disabled="!emailConfig.enabled"
help="Name that will appear as the sender"
/>
<FormKit
type="text"
label="AWS Region"
v-model="emailConfig.config.region"
:disabled="!emailConfig.enabled"
help="AWS region where SES is configured"
placeholder="us-east-1"
/>
<FormKit
type="text"
label="Configuration Set (Optional)"
v-model="emailConfig.config.configurationSet"
:disabled="!emailConfig.enabled"
help="AWS SES configuration set for tracking"
/>
</div>
</div>
</div>
<!-- Add more providers as needed -->
<div class="mt-6 p-4 bg-gray-50 rounded-lg flex flex-wrap gap-8">
<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 class="flex justify-end">
<rs-button @click="saveEmailConfig" :disabled="isLoadingEmail">
<Icon
:name="isLoadingEmail ? 'ic:outline-refresh' : 'ic:outline-save'"
class="mr-1"
:class="{ 'animate-spin': isLoadingEmail }"
/>
{{ isLoadingEmail ? "Saving..." : "Save" }}
</rs-button>
</div>
</div>
</template>
</rs-card>
<!-- Push 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="flex flex-col gap-4">
<div class="flex items-center justify-between">
<span class="font-medium">Enable Push Notifications</span>
<FormKit type="toggle" v-model="pushConfig.enabled" />
</div>
<FormKit
type="select"
label="Push Provider"
v-model="pushConfig.provider"
:options="pushProviders"
:disabled="!pushConfig.enabled"
class="w-full"
/>
<!-- Provider-specific fields -->
<div
v-if="pushConfig.provider === 'firebase'"
class="grid grid-cols-1 md:grid-cols-2 gap-4"
>
<FormKit
type="text"
label="API Key"
v-model="pushConfig.config.apiKey"
:disabled="!pushConfig.enabled"
/>
<FormKit
type="text"
label="Project ID"
v-model="pushConfig.config.projectId"
:disabled="!pushConfig.enabled"
/>
</div>
<!-- Add more providers as needed -->
<div class="mt-6 p-4 bg-gray-50 rounded-lg flex flex-wrap gap-8">
<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 class="flex justify-end">
<rs-button @click="savePushConfig" :disabled="isLoadingPush">
<Icon
:name="isLoadingPush ? 'ic:outline-refresh' : 'ic:outline-save'"
class="mr-1"
:class="{ 'animate-spin': isLoadingPush }"
/>
{{ isLoadingPush ? "Saving..." : "Save" }}
</rs-button>
</div>
</div>
</template>
</rs-card>
<!-- SMS 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-sms"></Icon>
<h2 class="text-lg font-semibold text-primary">SMS Configuration</h2>
</div>
<rs-badge :variant="smsConfig.enabled ? 'success' : 'secondary'">
{{ smsConfig.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="flex flex-col gap-4">
<div class="flex items-center justify-between">
<span class="font-medium">Enable SMS Delivery</span>
<FormKit type="toggle" v-model="smsConfig.enabled" />
</div>
<FormKit
type="select"
label="SMS Provider"
v-model="smsConfig.provider"
:options="smsProviders"
:disabled="!smsConfig.enabled"
class="w-full"
/>
<!-- Provider-specific fields -->
<div
v-if="smsConfig.provider === 'twilio'"
class="grid grid-cols-1 md:grid-cols-2 gap-4"
>
<FormKit
type="text"
label="Account SID"
v-model="smsConfig.config.accountSid"
:disabled="!smsConfig.enabled"
/>
<FormKit
type="password"
label="Auth Token"
v-model="smsConfig.config.authToken"
:disabled="!smsConfig.enabled"
/>
<FormKit
type="text"
label="From Number"
v-model="smsConfig.config.from"
:disabled="!smsConfig.enabled"
/>
</div>
<!-- Add more providers as needed -->
<div class="mt-6 p-4 bg-gray-50 rounded-lg flex flex-wrap gap-8">
<div>
<span class="text-gray-600">Status:</span>
<span class="ml-2 font-medium">{{ smsConfig.status }}</span>
</div>
<div>
<span class="text-gray-600">Success Rate:</span>
<span class="ml-2 font-medium">{{ smsConfig.successRate }}%</span>
</div>
</div>
<div class="flex justify-end">
<rs-button @click="saveSmsConfig" :disabled="isLoadingSms">
<Icon
:name="isLoadingSms ? 'ic:outline-refresh' : 'ic:outline-save'"
class="mr-1"
:class="{ 'animate-spin': isLoadingSms }"
/>
{{ isLoadingSms ? "Saving..." : "Save" }}
</rs-button>
</div>
</div>
</template>
</rs-card>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import { useToast } from "@/composables/useToast";
import { useNotificationDelivery } from "@/composables/useNotificationDelivery";
definePageMeta({
title: "Notification Delivery",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Notification",
path: "/notification",
},
{
name: "Delivery",
path: "/notification/delivery",
type: "current",
},
],
});
// Email Configuration
const emailConfig = ref({
enabled: true,
provider: "mailtrap",
config: {
host: "live.smtp.mailtrap.io",
port: 587,
user: "apismtp@mailtrap.io",
pass: "",
senderEmail: "",
senderName: "",
},
status: "Connected",
successRate: 99.2,
});
const emailProviders = [
{ label: "Mailtrap", value: "mailtrap" },
{ label: "AWS SES", value: "aws-ses" }
];
// Push Configuration
const pushConfig = ref({
enabled: true,
provider: "firebase",
config: {
apiKey: "",
projectId: "",
},
status: "Connected",
successRate: 95.8,
});
const pushProviders = [{ label: "Firebase FCM", value: "firebase" }];
// Add SMS config and providers
const smsConfig = ref({
enabled: false,
provider: "twilio",
config: {
accountSid: "",
authToken: "",
from: "",
},
status: "Not Configured",
successRate: 0,
});
const smsProviders = [
{ label: "Twilio", value: "twilio" },
// Add more providers as needed
];
const isLoadingEmail = ref(false);
const isLoadingPush = ref(false);
const isLoadingSms = ref(false);
const toast = useToast();
const {
isLoading,
error,
fetchDeliveryStats,
fetchEmailConfig,
fetchPushConfig,
fetchSmsConfig,
fetchDeliverySettings,
updateEmailConfig,
updatePushConfig,
updateSmsConfig,
updateDeliverySettings,
} = useNotificationDelivery();
// Store provider-specific configs
const providerConfigs = ref({
mailtrap: null,
'aws-ses': null
});
// Methods
async function refreshData() {
try {
isLoading.value = true;
const [emailData, pushData, smsData] = await Promise.all([
fetchEmailConfig(),
fetchPushConfig(),
fetchSmsConfig(),
]);
// Store all provider configs
if (emailData.providers) {
providerConfigs.value = emailData.providers;
}
// Update email config with active provider
const activeProviderKey = emailData.activeProvider || emailData.provider;
const activeProviderData = emailData.providers?.[activeProviderKey] || emailData;
emailConfig.value = {
enabled: activeProviderData.enabled,
provider: activeProviderKey,
config: activeProviderData.config || {},
status: activeProviderData.status,
successRate: activeProviderData.successRate,
};
// Update push config
pushConfig.value = {
enabled: pushData.enabled,
provider: pushData.provider,
config: pushData.config || {},
status: pushData.status,
successRate: pushData.successRate,
};
// Update SMS config
smsConfig.value = {
enabled: smsData.enabled,
provider: smsData.provider,
config: smsData.config || {},
status: smsData.status,
successRate: smsData.successRate,
};
} catch (err) {
console.error("Error refreshing data:", err);
toast.error(err.message || "Failed to refresh data");
} finally {
isLoading.value = false;
}
}
async function saveEmailConfig() {
try {
isLoadingEmail.value = true;
await updateEmailConfig({
enabled: emailConfig.value.enabled,
provider: emailConfig.value.provider,
config: emailConfig.value.config,
});
toast.success("Email settings saved!");
} catch (err) {
toast.error(err.message || "Failed to save email settings");
} finally {
isLoadingEmail.value = false;
}
}
async function savePushConfig() {
try {
isLoadingPush.value = true;
await updatePushConfig({
enabled: pushConfig.value.enabled,
provider: pushConfig.value.provider,
config: pushConfig.value.config,
});
toast.success("Push settings saved!");
} catch (err) {
toast.error(err.message || "Failed to save push settings");
} finally {
isLoadingPush.value = false;
}
}
async function saveSmsConfig() {
try {
isLoadingSms.value = true;
await updateSmsConfig({
enabled: smsConfig.value.enabled,
provider: smsConfig.value.provider,
config: smsConfig.value.config,
});
toast.success("SMS settings saved!");
} catch (err) {
toast.error(err.message || "Failed to save SMS settings");
} finally {
isLoadingSms.value = false;
}
}
// Watch for provider changes and load saved config
watch(() => emailConfig.value.provider, (newProvider) => {
if (providerConfigs.value[newProvider]) {
// Load saved config for this provider
const savedConfig = providerConfigs.value[newProvider];
emailConfig.value.config = savedConfig.config || {};
emailConfig.value.status = savedConfig.status;
emailConfig.value.successRate = savedConfig.successRate;
} else {
// Load default config for new provider
if (newProvider === 'mailtrap') {
emailConfig.value.config = {
host: 'live.smtp.mailtrap.io',
port: 587,
user: 'apismtp@mailtrap.io',
pass: '',
senderEmail: '',
senderName: '',
};
} else if (newProvider === 'aws-ses') {
emailConfig.value.config = {
host: '',
port: 587,
user: '',
pass: '',
senderEmail: '',
senderName: '',
region: 'us-east-1',
configurationSet: '',
};
}
}
});
// Load initial data
onMounted(() => {
refreshData();
});
</script>
<style lang="scss" scoped></style>