644 lines
22 KiB
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.<region>.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>
|