151 lines
5.0 KiB
TypeScript
151 lines
5.0 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { X, Send } from 'lucide-react';
|
|
import type { Settings } from '../types';
|
|
import { updateSettings, testTelegram } from '../api/client';
|
|
|
|
interface Props {
|
|
settings: Settings | null;
|
|
onClose: () => void;
|
|
onSaved: (s: Settings) => void;
|
|
}
|
|
|
|
export default function SettingsModal({ settings, onClose, onSaved }: Props) {
|
|
const [token, setToken] = useState('');
|
|
const [chatId, setChatId] = useState('');
|
|
const [saving, setSaving] = useState(false);
|
|
const [testing, setTesting] = useState(false);
|
|
const [error, setError] = useState('');
|
|
const [testMsg, setTestMsg] = useState('');
|
|
|
|
useEffect(() => {
|
|
if (settings) {
|
|
setToken(settings.telegram_bot_token ?? '');
|
|
setChatId(settings.telegram_chat_id ?? '');
|
|
}
|
|
}, [settings]);
|
|
|
|
const handleSave = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
setTestMsg('');
|
|
setSaving(true);
|
|
try {
|
|
const updated = await updateSettings({
|
|
telegram_bot_token: token.trim(),
|
|
telegram_chat_id: chatId.trim(),
|
|
});
|
|
onSaved(updated);
|
|
} catch (e: unknown) {
|
|
setError(e instanceof Error ? e.message : 'Failed to save settings');
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const handleTest = async () => {
|
|
setTestMsg('');
|
|
setError('');
|
|
setTesting(true);
|
|
try {
|
|
// Save first so the test uses current values
|
|
await updateSettings({
|
|
telegram_bot_token: token.trim(),
|
|
telegram_chat_id: chatId.trim(),
|
|
});
|
|
const result = await testTelegram();
|
|
if (result.success) {
|
|
setTestMsg('✓ Test message sent! Check your Telegram.');
|
|
} else {
|
|
setError(result.error ?? 'Test failed — check your token and chat ID.');
|
|
}
|
|
} catch (e: unknown) {
|
|
setError(e instanceof Error ? e.message : 'Test failed');
|
|
} finally {
|
|
setTesting(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="modal-backdrop" onClick={e => e.target === e.currentTarget && onClose()}>
|
|
<div className="modal" role="dialog" aria-modal="true" aria-labelledby="settings-title">
|
|
<div className="modal-header">
|
|
<div>
|
|
<div className="modal-title" id="settings-title">Telegram Settings</div>
|
|
<div className="modal-subtitle">Configure your alert notifications</div>
|
|
</div>
|
|
<button className="modal-close" onClick={onClose} aria-label="Close">
|
|
<X size={18} />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="info-box">
|
|
<strong>Getting your Chat ID</strong><br />
|
|
Your Telegram <em>username</em> (@ALWISPER) can't be used directly — you need your numeric
|
|
chat ID. The easiest way: message{' '}
|
|
<strong>@userinfobot</strong> on Telegram and it will reply with your numeric ID.
|
|
Then paste it below.
|
|
</div>
|
|
|
|
{error && <div className="form-error">{error}</div>}
|
|
{testMsg && <div className="success-box">{testMsg}</div>}
|
|
|
|
<form onSubmit={handleSave}>
|
|
<div className="form-group">
|
|
<label className="form-label" htmlFor="tg-token">Bot Token</label>
|
|
<input
|
|
id="tg-token"
|
|
className="form-input"
|
|
type="text"
|
|
placeholder="1234567890:AABBCCDDEEFFaabbccddeeff..."
|
|
value={token}
|
|
onChange={e => setToken(e.target.value)}
|
|
autoComplete="off"
|
|
spellCheck={false}
|
|
/>
|
|
<div className="form-hint">
|
|
From <strong>@BotFather</strong> → your bot → API Token.
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-group">
|
|
<label className="form-label" htmlFor="tg-chatid">Chat ID (numeric)</label>
|
|
<input
|
|
id="tg-chatid"
|
|
className="form-input"
|
|
type="text"
|
|
placeholder="123456789"
|
|
value={chatId}
|
|
onChange={e => setChatId(e.target.value)}
|
|
autoComplete="off"
|
|
spellCheck={false}
|
|
/>
|
|
<div className="form-hint">
|
|
Message <strong>@userinfobot</strong> to get your numeric ID.
|
|
</div>
|
|
</div>
|
|
|
|
<div className="form-actions">
|
|
<button
|
|
type="button"
|
|
className="btn-secondary"
|
|
disabled={testing || saving || !token || !chatId}
|
|
onClick={handleTest}
|
|
>
|
|
{testing
|
|
? <><div className="spinner" style={{ width: 13, height: 13 }} /> Sending…</>
|
|
: <><Send size={13} /> Test Alert</>
|
|
}
|
|
</button>
|
|
<button type="button" className="btn-secondary" onClick={onClose} disabled={saving}>
|
|
Cancel
|
|
</button>
|
|
<button type="submit" className="btn-primary" disabled={saving}>
|
|
{saving ? <><div className="spinner" style={{ width: 14, height: 14 }} /> Saving…</> : 'Save'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|