import React from 'react' import { authApi, notificationsApi } from '../../api/client' import { Save } from 'lucide-react' import type { TranslationFn } from '../../types' import type { useAdmin } from './useAdmin' import AdminNotificationsPanel from './AdminNotificationsPanel' interface AdminNotificationsTabProps { admin: ReturnType t: TranslationFn } // "Notifications" admin tab: email/webhook/ntfy channel toggles, SMTP credentials, // trip reminders, admin webhook + ntfy targets, and the per-event preference matrix. // Derives channel state from smtpValues exactly as the original inline IIFE did. export default function AdminNotificationsTab({ admin, t }: AdminNotificationsTabProps): React.ReactElement { const { toast, smtpValues, setSmtpValues, smtpLoaded, setTripRemindersEnabled } = admin // Derive active channels from smtpValues.notification_channels (plural) // with fallback to notification_channel (singular) for existing installs const rawChannels = smtpValues.notification_channels ?? smtpValues.notification_channel ?? 'none' const activeChans = rawChannels === 'none' ? [] : rawChannels.split(',').map((c: string) => c.trim()) const emailActive = activeChans.includes('email') const webhookActive = activeChans.includes('webhook') const ntfyActive = activeChans.includes('ntfy') const tripRemindersActive = smtpValues.notify_trip_reminder !== 'false' const setChannels = async (email: boolean, webhook: boolean, ntfy: boolean) => { const chans = [email && 'email', webhook && 'webhook', ntfy && 'ntfy'].filter(Boolean).join(',') || 'none' setSmtpValues(prev => ({ ...prev, notification_channels: chans })) try { await authApi.updateAppSettings({ notification_channels: chans }) } catch { // Revert state on failure const reverted = [emailActive && 'email', webhookActive && 'webhook', ntfyActive && 'ntfy'].filter(Boolean).join(',') || 'none' setSmtpValues(prev => ({ ...prev, notification_channels: reverted })) toast.error(t('common.error')) } } const smtpConfigured = !!(smtpValues.smtp_host?.trim()) const saveNotifications = async () => { // Saves credentials only — channel activation is auto-saved by the toggle const notifKeys = ['smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', 'smtp_from', 'smtp_skip_tls_verify'] const payload: Record = {} for (const k of notifKeys) { if (smtpValues[k] !== undefined) payload[k] = smtpValues[k] } try { await authApi.updateAppSettings(payload) toast.success(t('admin.notifications.saved')) authApi.getAppConfig().then((c: { trip_reminders_enabled?: boolean }) => { if (c?.trip_reminders_enabled !== undefined) setTripRemindersEnabled(c.trip_reminders_enabled) }).catch(() => {}) } catch { toast.error(t('common.error')) } } return (<>
{/* Email Panel */}

{t('admin.notifications.emailPanel.title')}

{t('admin.smtp.hint')}

{smtpLoaded && [ { key: 'smtp_host', label: 'SMTP Host', placeholder: 'mail.example.com' }, { key: 'smtp_port', label: 'SMTP Port', placeholder: '587' }, { key: 'smtp_user', label: 'SMTP User', placeholder: 'trek@example.com' }, { key: 'smtp_pass', label: 'SMTP Password', placeholder: '••••••••', type: 'password' }, { key: 'smtp_from', label: 'From Address', placeholder: 'trek@example.com' }, ].map(field => (
setSmtpValues(prev => ({ ...prev, [field.key]: e.target.value }))} placeholder={field.placeholder} className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-slate-400 focus:border-transparent" />
))}
Skip TLS certificate check

Enable for self-signed certificates on local mail servers

{/* Webhook Panel */}

{t('admin.notifications.webhookPanel.title')}

{t('admin.webhook.hint')}

{/* Ntfy Panel */}

{t('admin.notifications.ntfy')}

{t('admin.ntfy.hint') || 'Allow users to configure their own ntfy topics for push notifications.'}

{/* In-App Panel */}

{t('admin.notifications.inappPanel.title')}

{t('admin.notifications.inappPanel.hint')}

{/* Trip Reminders Toggle */}

{t('admin.notifications.tripReminders.title')}

{t('admin.notifications.tripReminders.hint')}

{/* Admin Webhook Panel */}

{t('admin.notifications.adminWebhookPanel.title')}

{t('admin.notifications.adminWebhookPanel.hint')}

{smtpLoaded && (
setSmtpValues(prev => ({ ...prev, admin_webhook_url: e.target.value }))} placeholder={smtpValues.admin_webhook_url === '••••••••' ? '••••••••' : 'https://discord.com/api/webhooks/...'} className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-slate-400 focus:border-transparent" />
)}
{/* Admin Ntfy Panel */}

{t('admin.notifications.adminNtfyPanel.title')}

{t('admin.notifications.adminNtfyPanel.hint')}

{smtpLoaded && ( <>
setSmtpValues(prev => ({ ...prev, admin_ntfy_server: e.target.value }))} placeholder={t('admin.notifications.adminNtfyPanel.serverPlaceholder')} className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-slate-400 focus:border-transparent" />

{t('admin.notifications.adminNtfyPanel.serverHint')}

setSmtpValues(prev => ({ ...prev, admin_ntfy_topic: e.target.value }))} placeholder={t('admin.notifications.adminNtfyPanel.topicPlaceholder')} className="w-full px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-slate-400 focus:border-transparent" />
setSmtpValues(prev => ({ ...prev, admin_ntfy_token: e.target.value }))} placeholder={smtpValues.admin_ntfy_token === '••••••••' ? '••••••••' : ''} className="flex-1 px-3 py-2 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-slate-400 focus:border-transparent" /> {smtpValues.admin_ntfy_token === '••••••••' && ( )}
)}
) }