Alerts: backup-contact phrasing; correct backup countdown; include per-position pay link; deep-link route rendering in-place; single-position view via deep-link; ARB alias; backup test message alignment
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { NotificationSettings, NotificationProvider, SchedulerProvider } from "@/types/notifications";
|
||||
import { scheduleTestNotification } from "@/utils/scheduler";
|
||||
import { scheduleTestNotification, scheduleTestBackupNotification } from "@/utils/scheduler";
|
||||
|
||||
export interface SettingsModalProps {
|
||||
open: boolean;
|
||||
@@ -28,6 +28,8 @@ const defaultSettings: NotificationSettings = {
|
||||
schedyBaseUrl: ENV_SCHEDY,
|
||||
schedyApiKey: ENV_SCHEDY_API_KEY,
|
||||
email: "",
|
||||
backupEmail: "",
|
||||
backupDelayDays: 1,
|
||||
daysBefore: 10,
|
||||
};
|
||||
|
||||
@@ -37,13 +39,12 @@ export default function SettingsModal({ open, initial, onClose, onSave }: Settin
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
const base: any = initial || defaultSettings;
|
||||
const next: any = { ...base };
|
||||
if (next.scheduler !== 'schedy') next.scheduler = 'schedy';
|
||||
for (const k of ['cronhostApiKey', 'kronosBaseUrl', 'schedifyBaseUrl', 'schedifyApiKey', 'schedifyWebhookId']) {
|
||||
if (k in next) delete next[k];
|
||||
}
|
||||
setForm(next);
|
||||
// Merge defaults to ensure newly added fields (e.g., backupDelayDays) are populated
|
||||
const merged = { ...defaultSettings, ...(initial || {}) } as NotificationSettings;
|
||||
// Re-apply env defaults if user hasn't set values
|
||||
if (!merged.ntfyServer) merged.ntfyServer = ENV_NTFY;
|
||||
if (!merged.schedyBaseUrl) merged.schedyBaseUrl = ENV_SCHEDY;
|
||||
setForm(merged);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open, initial]);
|
||||
@@ -55,6 +56,12 @@ export default function SettingsModal({ open, initial, onClose, onSave }: Settin
|
||||
if (!form.email) return true;
|
||||
const db = form.daysBefore ?? 10;
|
||||
if (Number(db) < 0) return true;
|
||||
// if backupEmail provided, enforce min backupDelayDays >= 1 (treat empty/NaN as invalid)
|
||||
const hasBackup = Boolean((form.backupEmail || '').trim());
|
||||
if (hasBackup) {
|
||||
const bdd = Number(form.backupDelayDays);
|
||||
if (!Number.isFinite(bdd) || bdd < 1) return true;
|
||||
}
|
||||
if (!provider) return true;
|
||||
if (!scheduler) return true;
|
||||
if (provider === 'ntfy') return !(form.ntfyServer && form.ntfyTopic);
|
||||
@@ -80,6 +87,16 @@ export default function SettingsModal({ open, initial, onClose, onSave }: Settin
|
||||
}
|
||||
};
|
||||
|
||||
const onTestBackup = async () => {
|
||||
setTestStatus('Scheduling backup test…');
|
||||
try {
|
||||
const { jobId } = await scheduleTestBackupNotification(form);
|
||||
setTestStatus(`Backup test scheduled (job ${jobId}). You should receive a backup email shortly.`);
|
||||
} catch (e: any) {
|
||||
setTestStatus(`Backup test failed: ${e?.message || String(e)}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
@@ -146,6 +163,16 @@ export default function SettingsModal({ open, initial, onClose, onSave }: Settin
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span className="text-gray-300">Backup Email (optional)</span>
|
||||
<input
|
||||
className="mt-1 w-full rounded border border-gray-700 bg-gray-800 px-2 py-1 text-gray-100"
|
||||
placeholder="backup@example.com"
|
||||
value={form.backupEmail || ''}
|
||||
onChange={(e) => setForm((f) => ({ ...f, backupEmail: e.target.value }))}
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<span className="text-gray-300">Days before liquidation</span>
|
||||
<input
|
||||
@@ -156,7 +183,16 @@ export default function SettingsModal({ open, initial, onClose, onSave }: Settin
|
||||
onChange={(e) => setForm((f) => ({ ...f, daysBefore: Number(e.target.value) }))}
|
||||
/>
|
||||
</label>
|
||||
<div />
|
||||
<label>
|
||||
<span className="text-gray-300">Backup delay (days after first email)</span>
|
||||
<input
|
||||
type="number"
|
||||
min={1}
|
||||
className="mt-1 w-full rounded border border-gray-700 bg-gray-800 px-2 py-1 text-gray-100"
|
||||
value={form.backupDelayDays ?? 1}
|
||||
onChange={(e) => setForm((f) => ({ ...f, backupDelayDays: Number(e.target.value) }))}
|
||||
/>
|
||||
</label>
|
||||
|
||||
{provider === 'ntfy' && (
|
||||
<>
|
||||
@@ -260,6 +296,14 @@ export default function SettingsModal({ open, initial, onClose, onSave }: Settin
|
||||
>
|
||||
Send test alert
|
||||
</button>
|
||||
<button
|
||||
className="rounded bg-teal-600 px-3 py-1.5 disabled:opacity-50"
|
||||
disabled={!canTest || !(form.backupEmail || '').trim()}
|
||||
onClick={onTestBackup}
|
||||
title={!((form.backupEmail || '').trim()) ? 'Set a backup email first' : ''}
|
||||
>
|
||||
Send backup test
|
||||
</button>
|
||||
<button
|
||||
className="rounded bg-indigo-600 px-3 py-1.5 disabled:opacity-50"
|
||||
disabled={saveDisabled}
|
||||
|
||||
Reference in New Issue
Block a user