Pushover: Emergency-Priorität mit Retry, has_pushover Badge in Admin
This commit is contained in:
@@ -757,30 +757,46 @@ function NachrichtenWidget({ unreadMsgs, setActive }) {
|
||||
|
||||
// ── Pushover Settings ─────────────────────────────────────────────────────────
|
||||
function PushoverSettings({ toast }) {
|
||||
const [form, setForm] = useState({ user_key:'', app_token:'' });
|
||||
const [form, setForm] = useState({ user_key:'', app_token:'', retry: '', expire: '' });
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [hasSaved, setHasSaved] = useState(false);
|
||||
const [emergency,setEmergency]= useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
api('/tools/nachrichten/pushover')
|
||||
.then(d => {
|
||||
const v = { user_key: d.user_key||'', app_token: d.app_token||'' };
|
||||
const v = { user_key: d.user_key||'', app_token: d.app_token||'',
|
||||
retry: d.retry||'', expire: d.expire||'' };
|
||||
setForm(v);
|
||||
setHasSaved(!!(v.user_key && v.app_token));
|
||||
setEmergency(!!(d.retry && d.expire));
|
||||
}).catch(()=>{});
|
||||
}, []);
|
||||
|
||||
const save = async () => {
|
||||
if (!form.user_key.trim() || !form.app_token.trim()) { toast('Beide Felder ausfüllen', 'error'); return; }
|
||||
setBusy(true);
|
||||
try { await api('/tools/nachrichten/pushover', { body: form }); toast('Gespeichert'); setHasSaved(true); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
try {
|
||||
await api('/tools/nachrichten/pushover', { body: {
|
||||
...form,
|
||||
retry: emergency ? (parseInt(form.retry) || 60) : null,
|
||||
expire: emergency ? (parseInt(form.expire) || 3600) : null,
|
||||
}});
|
||||
toast('Gespeichert'); setHasSaved(true);
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
setBusy(false);
|
||||
};
|
||||
|
||||
const test = async () => {
|
||||
setBusy(true);
|
||||
try { await api('/tools/nachrichten/pushover/test', { method:'POST' }); toast('Testbenachrichtigung gesendet ✓'); }
|
||||
try { await api('/tools/nachrichten/pushover/test', { method:'POST', body:{ emergency: false } }); toast('Test gesendet ✓ (normale Priorität)'); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
setBusy(false);
|
||||
};
|
||||
|
||||
const testEmergency = async () => {
|
||||
setBusy(true);
|
||||
try { await api('/tools/nachrichten/pushover/test', { method:'POST', body:{ emergency: true } }); toast('🚨 Emergency-Test gesendet – quittiere in der Pushover-App!'); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
setBusy(false);
|
||||
};
|
||||
@@ -788,8 +804,8 @@ function PushoverSettings({ toast }) {
|
||||
const clear = async () => {
|
||||
try {
|
||||
await api('/tools/nachrichten/pushover', { method:'DELETE' });
|
||||
setForm({ user_key:'', app_token:'' });
|
||||
setHasSaved(false);
|
||||
setForm({ user_key:'', app_token:'', retry:'', expire:'' });
|
||||
setHasSaved(false); setEmergency(false);
|
||||
toast('Pushover entfernt');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
};
|
||||
@@ -797,12 +813,47 @@ function PushoverSettings({ toast }) {
|
||||
return (
|
||||
<div>
|
||||
<p style={{ color:'rgba(255,255,255,0.42)', fontSize:11, fontFamily:'monospace', marginBottom:12, lineHeight:1.7 }}>
|
||||
Push-Benachrichtigungen bei neuen Nachrichten via{' '}
|
||||
Push-Benachrichtigungen via{' '}
|
||||
<a href="https://pushover.net" target="_blank" rel="noopener noreferrer" style={{ color:'#4ecdc4' }}>pushover.net</a>.
|
||||
Du brauchst einen Account und eine eigene App (kostenlos).
|
||||
</p>
|
||||
<Field label="USER KEY" value={form.user_key} onChange={e=>setForm(p=>({...p,user_key:e.target.value}))} />
|
||||
<Field label="APP TOKEN" value={form.app_token} onChange={e=>setForm(p=>({...p,app_token:e.target.value}))} />
|
||||
|
||||
{/* Emergency / Wiederholung */}
|
||||
<div style={{ marginBottom:14, background:'rgba(255,255,255,0.03)', border:'1px solid rgba(255,255,255,0.08)',
|
||||
borderRadius:8, padding:'10px 12px' }}>
|
||||
<label style={{ display:'flex', alignItems:'center', gap:8, cursor:'pointer', marginBottom: emergency ? 10 : 0 }}>
|
||||
<input type="checkbox" checked={emergency} onChange={e=>setEmergency(e.target.checked)}
|
||||
style={{ width:14, height:14, accentColor:'#ff6b9d', cursor:'pointer' }}/>
|
||||
<span style={{ color:'rgba(255,255,255,0.7)', fontFamily:'monospace', fontSize:11 }}>
|
||||
🚨 Wiederholen bis Quittierung (Emergency-Priorität)
|
||||
</span>
|
||||
</label>
|
||||
{emergency && (
|
||||
<div style={{ display:'grid', gridTemplateColumns:'1fr 1fr', gap:8, marginTop:6 }}>
|
||||
<div>
|
||||
<label style={{ ...S.head, display:'block', marginBottom:3, fontSize:9 }}>WIEDERHOLUNG (SEK)</label>
|
||||
<input type="number" value={form.retry||60} min={30} max={3600}
|
||||
onChange={e=>setForm(p=>({...p,retry:e.target.value}))}
|
||||
style={{ ...S.inp, fontSize:13 }}/>
|
||||
<div style={{ color:'rgba(255,255,255,0.2)', fontFamily:'monospace', fontSize:8, marginTop:2 }}>
|
||||
min. 30s · z.B. 60 = jede Minute
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ ...S.head, display:'block', marginBottom:3, fontSize:9 }}>MAX. DAUER (SEK)</label>
|
||||
<input type="number" value={form.expire||3600} min={60} max={86400}
|
||||
onChange={e=>setForm(p=>({...p,expire:e.target.value}))}
|
||||
style={{ ...S.inp, fontSize:13 }}/>
|
||||
<div style={{ color:'rgba(255,255,255,0.2)', fontFamily:'monospace', fontSize:8, marginTop:2 }}>
|
||||
z.B. 3600 = max. 1 Stunde
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div style={{ display:'flex', gap:8 }}>
|
||||
<button onClick={save} disabled={busy}
|
||||
style={{ ...S.btn('#4ecdc4'), flex:1, textAlign:'center', padding:'10px 0', opacity:busy?0.5:1 }}>
|
||||
@@ -810,11 +861,15 @@ function PushoverSettings({ toast }) {
|
||||
</button>
|
||||
{hasSaved && (
|
||||
<button onClick={test} disabled={busy}
|
||||
style={{ ...S.btn('#ffe66d', true), padding:'0 14px', opacity:busy?0.5:1 }}>Test</button>
|
||||
style={{ ...S.btn('#ffe66d', true), padding:'0 10px', opacity:busy?0.5:1, fontSize:10 }}>Test</button>
|
||||
)}
|
||||
{hasSaved && emergency && (
|
||||
<button onClick={testEmergency} disabled={busy}
|
||||
style={{ ...S.btn('#ff6b9d', true), padding:'0 10px', opacity:busy?0.5:1, fontSize:10 }}>🚨 Test</button>
|
||||
)}
|
||||
{hasSaved && (
|
||||
<button onClick={clear}
|
||||
style={{ ...S.btn('#ff6b9d', true), padding:'0 14px' }}>✕</button>
|
||||
style={{ ...S.btn('#ff6b9d', true), padding:'0 10px' }}>✕</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1499,6 +1554,15 @@ function UserManagement({ toast }) {
|
||||
border:`1px solid ${u.role==='admin'?'rgba(255,230,109,0.3)':'rgba(78,205,196,0.3)'}`,
|
||||
borderRadius:4, padding:'1px 6px',
|
||||
}}>{u.role}</span>
|
||||
{!!u.has_pushover && (
|
||||
<span title="Pushover eingerichtet" style={{
|
||||
marginLeft:4, fontSize:9, fontFamily:'monospace',
|
||||
color:'rgba(255,107,157,0.8)',
|
||||
background:'rgba(255,107,157,0.1)',
|
||||
border:'1px solid rgba(255,107,157,0.25)',
|
||||
borderRadius:4, padding:'1px 6px',
|
||||
}}>🔔 Push</span>
|
||||
)}
|
||||
{u.last_active_at && (
|
||||
<span style={{
|
||||
marginLeft:6, fontSize:9, fontFamily:'monospace',
|
||||
|
||||
Reference in New Issue
Block a user