feat(settings): improve email and push notification settings validation and state management
This commit is contained in:
+40
-33
@@ -456,8 +456,13 @@ function AppContent() {
|
||||
async function saveSettings(e: React.FormEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
// Auto-disable email if no recipient is set
|
||||
const effectiveEmailEnabled = settings.emailEnabled && !!settings.notificationEmail?.trim();
|
||||
// Auto-disable push if no URL is set
|
||||
const effectiveShoutrrrEnabled = settings.shoutrrrEnabled && !!settings.shoutrrrUrl?.trim();
|
||||
|
||||
// Validate email if email notifications are enabled
|
||||
if (settings.emailEnabled && settings.notificationEmail) {
|
||||
if (effectiveEmailEnabled && settings.notificationEmail) {
|
||||
const emailRegex = /^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$/i;
|
||||
if (!emailRegex.test(settings.notificationEmail)) {
|
||||
setTestEmailResult({ success: false, message: "Invalid email address" });
|
||||
@@ -469,14 +474,14 @@ function AppContent() {
|
||||
setTestEmailResult(null);
|
||||
|
||||
const payload = {
|
||||
emailEnabled: settings.emailEnabled,
|
||||
emailEnabled: effectiveEmailEnabled,
|
||||
notificationEmail: settings.notificationEmail,
|
||||
reminderDaysBefore: settings.reminderDaysBefore,
|
||||
repeatDailyReminders: settings.repeatDailyReminders,
|
||||
lowStockDays: settings.lowStockDays,
|
||||
normalStockDays: settings.normalStockDays,
|
||||
highStockDays: settings.highStockDays,
|
||||
shoutrrrEnabled: settings.shoutrrrEnabled,
|
||||
shoutrrrEnabled: effectiveShoutrrrEnabled,
|
||||
shoutrrrUrl: settings.shoutrrrUrl,
|
||||
// Granular notification settings
|
||||
emailStockReminders: settings.emailStockReminders,
|
||||
@@ -500,8 +505,15 @@ function AppContent() {
|
||||
body: JSON.stringify(payload),
|
||||
}).catch(() => null);
|
||||
|
||||
// Update local state with effective values
|
||||
const updatedSettings = {
|
||||
...settings,
|
||||
emailEnabled: effectiveEmailEnabled,
|
||||
shoutrrrEnabled: effectiveShoutrrrEnabled
|
||||
};
|
||||
setSettings(updatedSettings);
|
||||
setSettingsSaving(false);
|
||||
setSavedSettings(settings);
|
||||
setSavedSettings(updatedSettings);
|
||||
setSettingsSaved(true);
|
||||
}
|
||||
|
||||
@@ -1574,7 +1586,7 @@ function AppContent() {
|
||||
<div className="matrix-row">
|
||||
<div className="matrix-label">{t('settings.notifications.stockReminders')}</div>
|
||||
<div className="matrix-cell">
|
||||
<label className="toggle-switch small">
|
||||
<label className={`toggle-switch small${!settings.emailEnabled ? ' disabled' : ''}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.emailStockReminders}
|
||||
@@ -1585,7 +1597,7 @@ function AppContent() {
|
||||
</label>
|
||||
</div>
|
||||
<div className="matrix-cell">
|
||||
<label className="toggle-switch small">
|
||||
<label className={`toggle-switch small${!settings.shoutrrrEnabled ? ' disabled' : ''}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.shoutrrrStockReminders}
|
||||
@@ -1599,7 +1611,7 @@ function AppContent() {
|
||||
<div className="matrix-row">
|
||||
<div className="matrix-label">{t('settings.notifications.intakeReminders')}</div>
|
||||
<div className="matrix-cell">
|
||||
<label className="toggle-switch small">
|
||||
<label className={`toggle-switch small${!settings.emailEnabled ? ' disabled' : ''}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.emailIntakeReminders}
|
||||
@@ -1610,7 +1622,7 @@ function AppContent() {
|
||||
</label>
|
||||
</div>
|
||||
<div className="matrix-cell">
|
||||
<label className="toggle-switch small">
|
||||
<label className={`toggle-switch small${!settings.shoutrrrEnabled ? ' disabled' : ''}`}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.shoutrrrIntakeReminders}
|
||||
@@ -1644,22 +1656,19 @@ function AppContent() {
|
||||
<div className="setting-group">
|
||||
<label className="full">
|
||||
<span className="field-label">{t('settings.email.recipient')}</span>
|
||||
<input
|
||||
type="email"
|
||||
value={settings.notificationEmail}
|
||||
onChange={(e) => setSettings({ ...settings, notificationEmail: e.target.value })}
|
||||
placeholder="your@email.com"
|
||||
pattern="[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$"
|
||||
autoComplete="email"
|
||||
/>
|
||||
<div className="input-with-tooltip">
|
||||
<input
|
||||
type="email"
|
||||
value={settings.notificationEmail}
|
||||
onChange={(e) => setSettings({ ...settings, notificationEmail: e.target.value })}
|
||||
placeholder="your@email.com"
|
||||
pattern="[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$"
|
||||
autoComplete="email"
|
||||
/>
|
||||
<span className="info-tooltip" data-tooltip={`SMTP: ${settings.smtpHost || t('settings.email.notConfigured')}:${settings.smtpPort}${settings.hasSmtpPassword ? '\nPassword: ✓' : ''}`}>ⓘ</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="smtp-info">
|
||||
<span className="smtp-summary">
|
||||
SMTP: {settings.smtpHost || t('settings.email.notConfigured')}:{settings.smtpPort}
|
||||
{settings.hasSmtpPassword && " ✓"}
|
||||
</span>
|
||||
</div>
|
||||
<div className="setting-actions">
|
||||
<button type="button" className="ghost" onClick={testEmail} disabled={testingEmail || !settings.notificationEmail}>
|
||||
{testingEmail ? t('common.sending') : t('common.test')}
|
||||
@@ -1691,19 +1700,17 @@ function AppContent() {
|
||||
<div className="setting-group">
|
||||
<label className="full">
|
||||
<span className="field-label">{t('settings.push.url')}</span>
|
||||
<input
|
||||
type="url"
|
||||
value={settings.shoutrrrUrl}
|
||||
onChange={(e) => setSettings({ ...settings, shoutrrrUrl: e.target.value })}
|
||||
placeholder="https://ntfy.sh/your-topic"
|
||||
/>
|
||||
<div className="input-with-tooltip">
|
||||
<input
|
||||
type="url"
|
||||
value={settings.shoutrrrUrl}
|
||||
onChange={(e) => setSettings({ ...settings, shoutrrrUrl: e.target.value })}
|
||||
placeholder="https://ntfy.sh/your-topic"
|
||||
/>
|
||||
<span className="info-tooltip" data-tooltip={t('settings.push.supports')}>ⓘ</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div className="smtp-info">
|
||||
<span className="smtp-summary">
|
||||
{t('settings.push.supports')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="setting-actions">
|
||||
<button type="button" className="ghost" onClick={testShoutrrr} disabled={testingShoutrrr || !settings.shoutrrrUrl}>
|
||||
{testingShoutrrr ? t('common.sending') : t('common.test')}
|
||||
|
||||
@@ -1213,8 +1213,8 @@ textarea {
|
||||
white-space: pre-line;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
width: max-content;
|
||||
max-width: 300px;
|
||||
text-align: left;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||||
border: 1px solid var(--border-primary);
|
||||
|
||||
Reference in New Issue
Block a user