diff --git a/hifi-src/src/App.jsx b/hifi-src/src/App.jsx index 682c5e4..d95057e 100644 --- a/hifi-src/src/App.jsx +++ b/hifi-src/src/App.jsx @@ -37,6 +37,7 @@ import AdminUsers from './pages/admin/AdminUsers.jsx'; import PermissionGroups from './pages/admin/PermissionGroups.jsx'; import SettingsLayout from './pages/admin/settings/SettingsLayout.jsx'; import WebsiteSettings from './pages/admin/settings/WebsiteSettings.jsx'; +import EmailSettings from './pages/admin/settings/EmailSettings.jsx'; import DatabaseSettings from './pages/admin/settings/DatabaseSettings.jsx'; import ExportImportSettings from './pages/admin/settings/ExportImportSettings.jsx'; import MaintenanceSettings from './pages/admin/settings/MaintenanceSettings.jsx'; @@ -89,6 +90,7 @@ export default function App() { > } /> } /> + } /> } /> } /> } /> diff --git a/hifi-src/src/pages/admin/settings/EmailSettings.jsx b/hifi-src/src/pages/admin/settings/EmailSettings.jsx new file mode 100644 index 0000000..686bf8f --- /dev/null +++ b/hifi-src/src/pages/admin/settings/EmailSettings.jsx @@ -0,0 +1,297 @@ +import { useEffect, useState } from 'react'; +import { api } from '../../../api/client.js'; + +const emptyForm = { + mail_host: '', + mail_port: 587, + mail_username: '', + mail_password: '', + mail_encryption: 'tls', + mail_from_email: '', + mail_from_name: '', + mail_notify_email: '', + mail_customer_subject: '', + mail_customer_body: '', + mail_owner_subject: '', + mail_owner_body: '', +}; + +const PLACEHOLDERS = [ + ['{{name}}', 'Name des Kunden'], + ['{{email}}', 'E-Mail des Kunden'], + ['{{phone}}', 'Telefonnummer'], + ['{{vin}}', 'Fahrgestellnummer (FIN)'], + ['{{message}}', 'Nachricht des Kunden'], + ['{{brand}}', 'Marke'], + ['{{model}}', 'Modell'], + ['{{package}}', 'Paket'], + ['{{product}}', 'Produkt'], + ['{{upgrades}}', 'Gewählte Upgrades'], +]; + +function PlaceholderLegend() { + return ( +
+ {PLACEHOLDERS.map(([token, label]) => ( + + {token} + + ))} +
+ ); +} + +export default function EmailSettings() { + const [form, setForm] = useState(emptyForm); + const [hasPassword, setHasPassword] = useState(false); + const [loading, setLoading] = useState(true); + const [showPassword, setShowPassword] = useState(false); + + const [saveBusy, setSaveBusy] = useState(false); + const [saveError, setSaveError] = useState(''); + const [saveSuccess, setSaveSuccess] = useState(false); + + const [testBusy, setTestBusy] = useState(false); + const [testError, setTestError] = useState(''); + const [testSuccess, setTestSuccess] = useState(''); + + useEffect(() => { + api + .get('/settings/mail') + .then((res) => { + setForm({ ...res, mail_password: '' }); + setHasPassword(res.has_password); + }) + .finally(() => setLoading(false)); + }, []); + + const update = (field, value) => { + setForm((f) => ({ ...f, [field]: value })); + setSaveSuccess(false); + }; + + const handleSave = async (e) => { + e.preventDefault(); + setSaveBusy(true); + setSaveError(''); + setSaveSuccess(false); + try { + const res = await api.post('/settings/mail', form); + setForm({ ...res, mail_password: '' }); + setHasPassword(res.has_password); + setSaveSuccess(true); + } catch (err) { + setSaveError(err.message); + } finally { + setSaveBusy(false); + } + }; + + const handleTest = async () => { + setTestBusy(true); + setTestError(''); + setTestSuccess(''); + try { + const res = await api.post('/settings/mail/test', form); + setTestSuccess(`Testmail gesendet an ${res.sent_to}.`); + } catch (err) { + setTestError(err.message); + } finally { + setTestBusy(false); + } + }; + + if (loading) { + return

Lädt…

; + } + + return ( +
+
+

SMTP-Server

+

+ Zugangsdaten deines Mail-Postfachs oder Transactional-Mail-Anbieters. Solange kein Server eingetragen ist, + werden keine E-Mails verschickt (Kontaktanfragen werden trotzdem in der Datenbank gespeichert). +

+
+
+
+ + update('mail_host', e.target.value)} + placeholder="smtp.beispiel.de" + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+
+ + update('mail_port', e.target.value)} + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+
+
+ + +
+
+ + update('mail_username', e.target.value)} + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+
+ +
+ update('mail_password', e.target.value)} + placeholder={hasPassword ? '••••••••' : ''} + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> + +
+
+
+ + update('mail_from_email', e.target.value)} + placeholder="info@hifi-planet-amorbach.de" + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+
+ + update('mail_from_name', e.target.value)} + placeholder="HifiPlanet" + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+
+ +
+ {testError &&

{testError}

} + {testSuccess &&

{testSuccess}

} + +
+
+ +
+

Empfänger

+

+ An diese Adresse geht die Benachrichtigung bei jeder neuen Kontaktanfrage. +

+ update('mail_notify_email', e.target.value)} + placeholder="info@hifi-planet-amorbach.de" + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+ +
+

E-Mail an den Kunden (Bestätigung)

+

+ Geht automatisch an den Kunden, sobald er eine Kontaktanfrage abschickt. +

+
+
+ + update('mail_customer_subject', e.target.value)} + className="w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm dark:border-neutral-700 dark:bg-neutral-900" + /> +
+
+ +