Files
shopdb-flask/frontend/src/views/settings/EmailSettings.vue

261 lines
8.6 KiB
Vue

<template>
<div>
<div class="page-header">
<h2>Email / SMTP</h2>
</div>
<div class="section-card">
<div class="setting-group">
<h3>Email Notifications</h3>
<p class="setting-description">
Configure SMTP settings to enable email notifications for alerts, toner reports,
and other system notifications.
</p>
<div class="setting-row">
<label class="toggle-label">
<span>Enable Email Notifications</span>
<button
class="toggle-btn"
:class="{ active: settings.smtp_enabled }"
@click="toggleSetting('smtp_enabled')"
:disabled="saving"
>
<span class="toggle-slider"></span>
</button>
</label>
</div>
<template v-if="settings.smtp_enabled">
<div class="settings-grid">
<div class="setting-row">
<label>
<span>SMTP Host</span>
<input
type="text"
v-model="settings.smtp_host"
placeholder="smtp.example.com"
@blur="saveSetting('smtp_host', settings.smtp_host)"
:disabled="saving"
>
</label>
</div>
<div class="setting-row">
<label>
<span>SMTP Port</span>
<input
type="number"
v-model="settings.smtp_port"
placeholder="587"
@blur="saveSetting('smtp_port', settings.smtp_port)"
:disabled="saving"
>
</label>
</div>
<div class="setting-row">
<label>
<span>Username</span>
<input
type="text"
v-model="settings.smtp_username"
placeholder="username"
@blur="saveSetting('smtp_username', settings.smtp_username)"
:disabled="saving"
>
</label>
</div>
<div class="setting-row">
<label>
<span>Password</span>
<input
type="password"
v-model="settings.smtp_password"
placeholder="password"
@blur="saveSetting('smtp_password', settings.smtp_password)"
:disabled="saving"
>
</label>
</div>
</div>
<div class="setting-row">
<label class="toggle-label">
<span>Use TLS Encryption</span>
<button
class="toggle-btn"
:class="{ active: settings.smtp_use_tls }"
@click="toggleSetting('smtp_use_tls')"
:disabled="saving"
>
<span class="toggle-slider"></span>
</button>
</label>
</div>
<div class="settings-grid">
<div class="setting-row">
<label>
<span>From Address</span>
<input
type="email"
v-model="settings.smtp_from_address"
placeholder="noreply@example.com"
@blur="saveSetting('smtp_from_address', settings.smtp_from_address)"
:disabled="saving"
>
</label>
</div>
<div class="setting-row">
<label>
<span>From Name</span>
<input
type="text"
v-model="settings.smtp_from_name"
placeholder="ShopDB"
@blur="saveSetting('smtp_from_name', settings.smtp_from_name)"
:disabled="saving"
>
</label>
</div>
</div>
<div class="setting-row full-width">
<label>
<span>Default Alert Recipients</span>
<input
type="text"
v-model="settings.alert_recipients"
placeholder="user1@example.com, user2@example.com"
@blur="saveSetting('alert_recipients', settings.alert_recipients)"
:disabled="saving"
>
<small class="input-hint">Comma-separated list of email addresses</small>
</label>
</div>
<div class="setting-row full-width">
<label>
<span>Site Base URL</span>
<input
type="text"
v-model="settings.site_base_url"
placeholder="https://shopdb.example.net/ops"
@blur="saveSetting('site_base_url', settings.site_base_url)"
:disabled="saving"
>
<small class="input-hint">Public URL of this site; used to build links in emails</small>
</label>
</div>
<div class="setting-row full-width">
<label>
<span>Alert Webhook URL</span>
<input
type="text"
v-model="settings.alert_webhook_url"
placeholder="https://... (Microsoft Teams Incoming Webhook or Workflow)"
@blur="saveSetting('alert_webhook_url', settings.alert_webhook_url)"
:disabled="saving"
>
<small class="input-hint">Alerts also POST here (Teams, etc.). Blank = email only.</small>
</label>
</div>
<div class="setting-row full-width">
<label>
<span>Alert Webhook Format</span>
<select
v-model="settings.alert_webhook_format"
@change="saveSetting('alert_webhook_format', settings.alert_webhook_format)"
:disabled="saving"
>
<option value="teams">Teams - Incoming Webhook (MessageCard)</option>
<option value="adaptivecard">Teams - Workflow (Adaptive Card)</option>
<option value="json">Generic JSON</option>
</select>
<small class="input-hint">Payload format for the webhook above</small>
</label>
</div>
<div class="status-indicator">
<span class="status-dot" :class="smtpStatus"></span>
<span>{{ smtpMessage }}</span>
</div>
<button
class="test-btn"
@click="testEmail"
:disabled="saving || testingEmail || !canTestEmail"
>
{{ testingEmail ? 'Sending...' : 'Send Test Email' }}
</button>
</template>
</div>
</div>
<div v-if="error" class="error-message">{{ error }}</div>
<div v-if="success" class="settings-success">{{ success }}</div>
</div>
</template>
<script setup>
import { onMounted, ref, computed } from 'vue'
import { useSystemSettings } from '../../composables/systemSettings'
import { apiError } from '../../utils/apiError'
import { settingsApi } from '../../api'
const {
settings, saving, error, success,
loadSettings, saveSetting, toggleSetting,
} = useSystemSettings()
const testingEmail = ref(false)
// Per-page connection status (stays with the page, not the composable).
const smtpStatus = computed(() => {
if (!settings.smtp_enabled) return 'inactive'
if (!settings.smtp_host || !settings.smtp_from_address) return 'warning'
return 'pending'
})
const smtpMessage = computed(() => {
if (!settings.smtp_enabled) return 'Disabled'
if (!settings.smtp_host) return 'SMTP host not configured'
if (!settings.smtp_from_address) return 'From address not configured'
return 'Configured - use test button to verify'
})
const canTestEmail = computed(() => {
return settings.smtp_host && settings.smtp_from_address && settings.alert_recipients
})
async function testEmail() {
testingEmail.value = true
error.value = ''
success.value = ''
try {
// Send to the configured Alert Recipients (the endpoint splits the list).
const response = await settingsApi.testEmail(settings.alert_recipients)
const result = response.data?.data || {}
if (result.sent) {
success.value = `Test email sent to ${settings.alert_recipients}`
} else {
error.value = result.error
? `Test email failed: ${result.error}`
: (response.data?.message || 'Email is not configured')
}
} catch (e) {
error.value = apiError(e, 'Failed to send test email')
} finally {
testingEmail.value = false
setTimeout(() => { success.value = '' }, 4000)
}
}
onMounted(loadSettings)
</script>