818 lines
25 KiB
Vue
818 lines
25 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-extension"></Icon>
|
|
<h1 class="text-xl font-bold text-primary">Provider Management</h1>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<p class="text-gray-600">
|
|
Configure and manage third-party notification service providers. Set up
|
|
credentials, fallback rules, and monitor provider performance across all
|
|
channels.
|
|
</p>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Provider Overview Stats -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
|
|
<rs-card
|
|
v-for="(stat, index) in providerStats"
|
|
: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="stat.bgColor"
|
|
>
|
|
<Icon class="text-3xl" :name="stat.icon" :class="stat.iconColor"></Icon>
|
|
</div>
|
|
<div class="flex-1 truncate">
|
|
<span class="block font-bold text-2xl leading-tight" :class="stat.textColor">
|
|
{{ stat.value }}
|
|
</span>
|
|
<span class="text-sm font-medium text-gray-600">
|
|
{{ stat.title }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Providers by Channel -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
|
|
<!-- Email Providers -->
|
|
<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 Providers</h2>
|
|
</div>
|
|
<rs-button size="sm" variant="primary-outline" @click="addProvider('email')">
|
|
<Icon class="mr-1" name="ic:outline-add"></Icon>
|
|
Add Provider
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div
|
|
v-for="provider in emailProviders"
|
|
:key="provider.id"
|
|
class="border border-gray-200 rounded-lg p-4"
|
|
>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-2xl" :name="provider.icon"></Icon>
|
|
<div>
|
|
<div class="font-semibold">{{ provider.name }}</div>
|
|
<div class="text-sm text-gray-600">{{ provider.description }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<rs-badge
|
|
:variant="provider.status === 'active' ? 'success' : 'danger'"
|
|
size="sm"
|
|
>
|
|
{{ provider.status }}
|
|
</rs-badge>
|
|
<rs-button
|
|
size="sm"
|
|
variant="secondary-outline"
|
|
@click="configureProvider(provider)"
|
|
>
|
|
<Icon name="ic:outline-settings"></Icon>
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-3 gap-4 text-sm">
|
|
<div>
|
|
<div class="text-gray-600">Success Rate</div>
|
|
<div class="font-semibold">{{ provider.successRate }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Quota Used</div>
|
|
<div class="font-semibold">{{ provider.quotaUsed }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Priority</div>
|
|
<div class="font-semibold">{{ provider.priority }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- SMS Providers -->
|
|
<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 Providers</h2>
|
|
</div>
|
|
<rs-button size="sm" variant="primary-outline" @click="addProvider('sms')">
|
|
<Icon class="mr-1" name="ic:outline-add"></Icon>
|
|
Add Provider
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div
|
|
v-for="provider in smsProviders"
|
|
:key="provider.id"
|
|
class="border border-gray-200 rounded-lg p-4"
|
|
>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-2xl" :name="provider.icon"></Icon>
|
|
<div>
|
|
<div class="font-semibold">{{ provider.name }}</div>
|
|
<div class="text-sm text-gray-600">{{ provider.description }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<rs-badge
|
|
:variant="provider.status === 'active' ? 'success' : 'danger'"
|
|
size="sm"
|
|
>
|
|
{{ provider.status }}
|
|
</rs-badge>
|
|
<rs-button
|
|
size="sm"
|
|
variant="secondary-outline"
|
|
@click="configureProvider(provider)"
|
|
>
|
|
<Icon name="ic:outline-settings"></Icon>
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-3 gap-4 text-sm">
|
|
<div>
|
|
<div class="text-gray-600">Success Rate</div>
|
|
<div class="font-semibold">{{ provider.successRate }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Quota Used</div>
|
|
<div class="font-semibold">{{ provider.quotaUsed }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Priority</div>
|
|
<div class="font-semibold">{{ provider.priority }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Push Providers -->
|
|
<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 Providers</h2>
|
|
</div>
|
|
<rs-button size="sm" variant="primary-outline" @click="addProvider('push')">
|
|
<Icon class="mr-1" name="ic:outline-add"></Icon>
|
|
Add Provider
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div
|
|
v-for="provider in pushProviders"
|
|
:key="provider.id"
|
|
class="border border-gray-200 rounded-lg p-4"
|
|
>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-2xl" :name="provider.icon"></Icon>
|
|
<div>
|
|
<div class="font-semibold">{{ provider.name }}</div>
|
|
<div class="text-sm text-gray-600">{{ provider.description }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<rs-badge
|
|
:variant="provider.status === 'active' ? 'success' : 'danger'"
|
|
size="sm"
|
|
>
|
|
{{ provider.status }}
|
|
</rs-badge>
|
|
<rs-button
|
|
size="sm"
|
|
variant="secondary-outline"
|
|
@click="configureProvider(provider)"
|
|
>
|
|
<Icon name="ic:outline-settings"></Icon>
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-3 gap-4 text-sm">
|
|
<div>
|
|
<div class="text-gray-600">Success Rate</div>
|
|
<div class="font-semibold">{{ provider.successRate }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Quota Used</div>
|
|
<div class="font-semibold">{{ provider.quotaUsed }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Priority</div>
|
|
<div class="font-semibold">{{ provider.priority }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Webhook Providers -->
|
|
<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-webhook"></Icon>
|
|
<h2 class="text-lg font-semibold text-primary">Webhook Endpoints</h2>
|
|
</div>
|
|
<rs-button
|
|
size="sm"
|
|
variant="primary-outline"
|
|
@click="addProvider('webhook')"
|
|
>
|
|
<Icon class="mr-1" name="ic:outline-add"></Icon>
|
|
Add Webhook
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div
|
|
v-for="provider in webhookProviders"
|
|
:key="provider.id"
|
|
class="border border-gray-200 rounded-lg p-4"
|
|
>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div class="flex items-center">
|
|
<Icon class="mr-2 text-2xl" :name="provider.icon"></Icon>
|
|
<div>
|
|
<div class="font-semibold">{{ provider.name }}</div>
|
|
<div class="text-sm text-gray-600 font-mono">{{ provider.url }}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<rs-badge
|
|
:variant="provider.status === 'active' ? 'success' : 'danger'"
|
|
size="sm"
|
|
>
|
|
{{ provider.status }}
|
|
</rs-badge>
|
|
<rs-button
|
|
size="sm"
|
|
variant="secondary-outline"
|
|
@click="configureProvider(provider)"
|
|
>
|
|
<Icon name="ic:outline-settings"></Icon>
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-3 gap-4 text-sm">
|
|
<div>
|
|
<div class="text-gray-600">Success Rate</div>
|
|
<div class="font-semibold">{{ provider.successRate }}%</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Avg Response</div>
|
|
<div class="font-semibold">{{ provider.avgResponse }}ms</div>
|
|
</div>
|
|
<div>
|
|
<div class="text-gray-600">Last Used</div>
|
|
<div class="font-semibold">{{ provider.lastUsed }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
</div>
|
|
|
|
<!-- Fallback Configuration -->
|
|
<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-alt-route"></Icon>
|
|
<h2 class="text-lg font-semibold text-primary">Fallback Configuration</h2>
|
|
</div>
|
|
<rs-button
|
|
size="sm"
|
|
variant="primary-outline"
|
|
@click="showFallbackModal = true"
|
|
>
|
|
<Icon class="mr-1" name="ic:outline-add"></Icon>
|
|
Add Rule
|
|
</rs-button>
|
|
</div>
|
|
</template>
|
|
<template #body>
|
|
<div class="space-y-4">
|
|
<div
|
|
v-for="rule in fallbackRules"
|
|
:key="rule.id"
|
|
class="border border-gray-200 rounded-lg p-4"
|
|
>
|
|
<div class="flex items-center justify-between mb-3">
|
|
<div>
|
|
<div class="font-semibold">{{ rule.name }}</div>
|
|
<div class="text-sm text-gray-600">{{ rule.description }}</div>
|
|
</div>
|
|
<div class="flex items-center gap-2">
|
|
<rs-badge :variant="rule.enabled ? 'success' : 'secondary'" size="sm">
|
|
{{ rule.enabled ? "Active" : "Disabled" }}
|
|
</rs-badge>
|
|
<rs-button
|
|
size="sm"
|
|
variant="secondary-outline"
|
|
@click="editFallbackRule(rule)"
|
|
>
|
|
<Icon name="ic:outline-edit"></Icon>
|
|
</rs-button>
|
|
</div>
|
|
</div>
|
|
<div class="text-sm space-y-2">
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-gray-600">Primary:</span>
|
|
<span class="font-medium">{{ rule.primary }}</span>
|
|
<Icon name="ic:outline-arrow-forward" class="text-gray-400"></Icon>
|
|
<span class="text-gray-600">Fallback:</span>
|
|
<span class="font-medium">{{ rule.fallback }}</span>
|
|
</div>
|
|
<div>
|
|
<span class="text-gray-600">Trigger:</span>
|
|
<span class="font-medium">{{ rule.trigger }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</rs-card>
|
|
|
|
<!-- Provider Configuration Modal -->
|
|
<rs-modal v-model="showProviderModal" size="lg">
|
|
<template #header>
|
|
<h3 class="text-lg font-semibold">
|
|
{{ selectedProvider ? "Configure" : "Add" }} Provider
|
|
</h3>
|
|
</template>
|
|
<template #body>
|
|
<div v-if="selectedProvider" class="space-y-6">
|
|
<!-- Basic Info -->
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<FormKit
|
|
type="text"
|
|
v-model="providerForm.name"
|
|
label="Provider Name"
|
|
placeholder="Enter provider name"
|
|
/>
|
|
<FormKit
|
|
type="select"
|
|
v-model="providerForm.channel"
|
|
label="Channel"
|
|
:options="channelOptions"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Provider-specific Configuration -->
|
|
<div v-if="providerForm.channel === 'email'">
|
|
<h4 class="font-semibold mb-3">Email Configuration</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<FormKit
|
|
type="select"
|
|
v-model="providerForm.provider"
|
|
label="Provider Type"
|
|
:options="emailProviderOptions"
|
|
/>
|
|
<FormKit
|
|
type="password"
|
|
v-model="providerForm.apiKey"
|
|
label="API Key"
|
|
placeholder="Enter API key"
|
|
/>
|
|
<FormKit
|
|
type="password"
|
|
v-model="providerForm.apiSecret"
|
|
label="API Secret"
|
|
placeholder="Enter API secret"
|
|
/>
|
|
<FormKit
|
|
type="text"
|
|
v-model="providerForm.fromEmail"
|
|
label="From Email"
|
|
placeholder="noreply@example.com"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="providerForm.channel === 'sms'">
|
|
<h4 class="font-semibold mb-3">SMS Configuration</h4>
|
|
<div class="grid grid-cols-2 gap-4">
|
|
<FormKit
|
|
type="select"
|
|
v-model="providerForm.provider"
|
|
label="Provider Type"
|
|
:options="smsProviderOptions"
|
|
/>
|
|
<FormKit
|
|
type="text"
|
|
v-model="providerForm.accountSid"
|
|
label="Account SID"
|
|
placeholder="Enter account SID"
|
|
/>
|
|
<FormKit
|
|
type="password"
|
|
v-model="providerForm.authToken"
|
|
label="Auth Token"
|
|
placeholder="Enter auth token"
|
|
/>
|
|
<FormKit
|
|
type="text"
|
|
v-model="providerForm.fromNumber"
|
|
label="From Number"
|
|
placeholder="+1234567890"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Priority & Limits -->
|
|
<div>
|
|
<h4 class="font-semibold mb-3">Priority & Limits</h4>
|
|
<div class="grid grid-cols-3 gap-4">
|
|
<FormKit
|
|
type="number"
|
|
v-model="providerForm.priority"
|
|
label="Priority"
|
|
placeholder="1-10"
|
|
min="1"
|
|
max="10"
|
|
/>
|
|
<FormKit
|
|
type="number"
|
|
v-model="providerForm.rateLimit"
|
|
label="Rate Limit (per minute)"
|
|
placeholder="100"
|
|
/>
|
|
<FormKit
|
|
type="number"
|
|
v-model="providerForm.dailyQuota"
|
|
label="Daily Quota"
|
|
placeholder="10000"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Test Connection -->
|
|
<div class="border border-gray-200 rounded-lg p-4">
|
|
<h4 class="font-semibold mb-3">Test Connection</h4>
|
|
<div class="flex items-center gap-3">
|
|
<rs-button variant="secondary" @click="testProviderConnection">
|
|
<Icon class="mr-1" name="ic:outline-wifi-tethering"></Icon>
|
|
Test Connection
|
|
</rs-button>
|
|
<div v-if="connectionTestResult" class="flex items-center gap-2">
|
|
<Icon
|
|
:name="
|
|
connectionTestResult.success
|
|
? 'ic:outline-check-circle'
|
|
: 'ic:outline-error'
|
|
"
|
|
:class="
|
|
connectionTestResult.success ? 'text-green-500' : 'text-red-500'
|
|
"
|
|
></Icon>
|
|
<span
|
|
:class="
|
|
connectionTestResult.success ? 'text-green-600' : 'text-red-600'
|
|
"
|
|
>
|
|
{{ connectionTestResult.message }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<template #footer>
|
|
<div class="flex justify-end gap-2">
|
|
<rs-button variant="outline" @click="showProviderModal = false"
|
|
>Cancel</rs-button
|
|
>
|
|
<rs-button variant="primary" @click="saveProvider">Save Provider</rs-button>
|
|
</div>
|
|
</template>
|
|
</rs-modal>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
definePageMeta({
|
|
title: "Provider Management",
|
|
middleware: ["auth"],
|
|
requiresAuth: true,
|
|
breadcrumb: [
|
|
{
|
|
name: "Dashboard",
|
|
path: "/dashboard",
|
|
},
|
|
{
|
|
name: "Notification",
|
|
path: "/notification",
|
|
},
|
|
{
|
|
name: "Delivery Engine",
|
|
path: "/notification/delivery",
|
|
},
|
|
{
|
|
name: "Providers",
|
|
path: "/notification/delivery/providers",
|
|
type: "current",
|
|
},
|
|
],
|
|
});
|
|
|
|
import { ref, reactive } from "vue";
|
|
|
|
// Modal states
|
|
const showProviderModal = ref(false);
|
|
const showFallbackModal = ref(false);
|
|
const selectedProvider = ref(null);
|
|
const connectionTestResult = ref(null);
|
|
|
|
// Provider form
|
|
const providerForm = reactive({
|
|
name: "",
|
|
channel: "",
|
|
provider: "",
|
|
apiKey: "",
|
|
apiSecret: "",
|
|
accountSid: "",
|
|
authToken: "",
|
|
fromEmail: "",
|
|
fromNumber: "",
|
|
priority: 5,
|
|
rateLimit: 100,
|
|
dailyQuota: 10000,
|
|
});
|
|
|
|
// Statistics
|
|
const providerStats = ref([
|
|
{
|
|
title: "Active Providers",
|
|
value: "12",
|
|
icon: "ic:outline-verified",
|
|
bgColor: "bg-green-100",
|
|
iconColor: "text-green-600",
|
|
textColor: "text-green-600",
|
|
},
|
|
{
|
|
title: "Total Messages Today",
|
|
value: "28.5K",
|
|
icon: "ic:outline-send",
|
|
bgColor: "bg-blue-100",
|
|
iconColor: "text-blue-600",
|
|
textColor: "text-blue-600",
|
|
},
|
|
{
|
|
title: "Avg Success Rate",
|
|
value: "98.2%",
|
|
icon: "ic:outline-trending-up",
|
|
bgColor: "bg-purple-100",
|
|
iconColor: "text-purple-600",
|
|
textColor: "text-purple-600",
|
|
},
|
|
{
|
|
title: "Failed Providers",
|
|
value: "1",
|
|
icon: "ic:outline-error",
|
|
bgColor: "bg-red-100",
|
|
iconColor: "text-red-600",
|
|
textColor: "text-red-600",
|
|
},
|
|
]);
|
|
|
|
// Provider data
|
|
const emailProviders = ref([
|
|
{
|
|
id: 1,
|
|
name: "SendGrid",
|
|
description: "Primary email delivery service",
|
|
icon: "ic:outline-email",
|
|
status: "active",
|
|
successRate: 99.2,
|
|
quotaUsed: 65,
|
|
priority: 1,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Mailgun",
|
|
description: "Fallback email service",
|
|
icon: "ic:outline-email",
|
|
status: "active",
|
|
successRate: 98.8,
|
|
quotaUsed: 23,
|
|
priority: 2,
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "AWS SES",
|
|
description: "Cost-effective bulk email",
|
|
icon: "ic:outline-cloud",
|
|
status: "inactive",
|
|
successRate: 97.5,
|
|
quotaUsed: 5,
|
|
priority: 3,
|
|
},
|
|
]);
|
|
|
|
const smsProviders = ref([
|
|
{
|
|
id: 4,
|
|
name: "Twilio",
|
|
description: "Primary SMS service",
|
|
icon: "ic:outline-sms",
|
|
status: "active",
|
|
successRate: 97.8,
|
|
quotaUsed: 78,
|
|
priority: 1,
|
|
},
|
|
{
|
|
id: 5,
|
|
name: "Nexmo",
|
|
description: "International SMS fallback",
|
|
icon: "ic:outline-sms",
|
|
status: "active",
|
|
successRate: 96.5,
|
|
quotaUsed: 34,
|
|
priority: 2,
|
|
},
|
|
]);
|
|
|
|
const pushProviders = ref([
|
|
{
|
|
id: 6,
|
|
name: "Firebase FCM",
|
|
description: "Android push notifications",
|
|
icon: "ic:outline-notifications",
|
|
status: "active",
|
|
successRate: 95.4,
|
|
quotaUsed: 45,
|
|
priority: 1,
|
|
},
|
|
{
|
|
id: 7,
|
|
name: "Apple APNs",
|
|
description: "iOS push notifications",
|
|
icon: "ic:outline-phone-iphone",
|
|
status: "active",
|
|
successRate: 94.8,
|
|
quotaUsed: 52,
|
|
priority: 1,
|
|
},
|
|
]);
|
|
|
|
const webhookProviders = ref([
|
|
{
|
|
id: 8,
|
|
name: "CRM Webhook",
|
|
url: "https://api.crm.com/webhooks/delivery",
|
|
icon: "ic:outline-webhook",
|
|
status: "active",
|
|
successRate: 99.1,
|
|
avgResponse: 150,
|
|
lastUsed: "2 min ago",
|
|
},
|
|
{
|
|
id: 9,
|
|
name: "Analytics Webhook",
|
|
url: "https://analytics.example.com/webhook",
|
|
icon: "ic:outline-analytics",
|
|
status: "active",
|
|
successRate: 98.7,
|
|
avgResponse: 89,
|
|
lastUsed: "5 min ago",
|
|
},
|
|
]);
|
|
|
|
// Fallback rules
|
|
const fallbackRules = ref([
|
|
{
|
|
id: 1,
|
|
name: "Email Provider Failover",
|
|
description: "Switch from SendGrid to Mailgun on failure",
|
|
primary: "SendGrid",
|
|
fallback: "Mailgun",
|
|
trigger: "API error or rate limit exceeded",
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "SMS Provider Failover",
|
|
description: "Switch from Twilio to Nexmo on failure",
|
|
primary: "Twilio",
|
|
fallback: "Nexmo",
|
|
trigger: "Delivery failure or timeout",
|
|
enabled: true,
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Email to SMS Fallback",
|
|
description: "Send SMS if email delivery fails",
|
|
primary: "Email Channel",
|
|
fallback: "SMS Channel",
|
|
trigger: "Hard bounce or 30s timeout",
|
|
enabled: false,
|
|
},
|
|
]);
|
|
|
|
// Form options
|
|
const channelOptions = [
|
|
{ label: "Email", value: "email" },
|
|
{ label: "SMS", value: "sms" },
|
|
{ label: "Push Notification", value: "push" },
|
|
{ label: "Webhook", value: "webhook" },
|
|
];
|
|
|
|
const emailProviderOptions = [
|
|
{ label: "SendGrid", value: "sendgrid" },
|
|
{ label: "Mailgun", value: "mailgun" },
|
|
{ label: "AWS SES", value: "ses" },
|
|
{ label: "Postmark", value: "postmark" },
|
|
];
|
|
|
|
const smsProviderOptions = [
|
|
{ label: "Twilio", value: "twilio" },
|
|
{ label: "Nexmo", value: "nexmo" },
|
|
{ label: "CM.com", value: "cm" },
|
|
];
|
|
|
|
// Methods
|
|
function addProvider(channel) {
|
|
selectedProvider.value = null;
|
|
providerForm.channel = channel;
|
|
providerForm.name = "";
|
|
providerForm.provider = "";
|
|
showProviderModal.value = true;
|
|
}
|
|
|
|
function configureProvider(provider) {
|
|
selectedProvider.value = provider;
|
|
// Populate form with provider data
|
|
providerForm.name = provider.name;
|
|
showProviderModal.value = true;
|
|
}
|
|
|
|
function saveProvider() {
|
|
console.log("Saving provider:", providerForm);
|
|
showProviderModal.value = false;
|
|
// Reset form
|
|
Object.keys(providerForm).forEach((key) => {
|
|
if (typeof providerForm[key] === "string") providerForm[key] = "";
|
|
if (typeof providerForm[key] === "number") providerForm[key] = 0;
|
|
});
|
|
}
|
|
|
|
function testProviderConnection() {
|
|
console.log("Testing provider connection...");
|
|
connectionTestResult.value = null;
|
|
|
|
// Simulate connection test
|
|
setTimeout(() => {
|
|
connectionTestResult.value = {
|
|
success: Math.random() > 0.3,
|
|
message:
|
|
Math.random() > 0.3
|
|
? "Connection successful"
|
|
: "Connection failed - Check credentials",
|
|
};
|
|
}, 2000);
|
|
}
|
|
|
|
function editFallbackRule(rule) {
|
|
console.log("Editing fallback rule:", rule);
|
|
// Implementation for editing fallback rules
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped></style>
|