Files
Pickup-Config/src/components/NotificationPanel.js
2025-11-10 21:30:43 +01:00

251 lines
9.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const NotificationPanel = ({
error,
message,
settings,
capabilities,
loading,
dirty,
saving,
onReset,
onSave,
onFieldChange,
onSendTest,
onCopyLink,
copyFeedback,
ntfyPreviewUrl,
location,
locationLoading,
locationSaving,
locationError,
onDetectLocation,
onClearLocation
}) => {
return (
<div className="mb-6 border border-gray-200 rounded-lg p-4 bg-gray-50">
{error && (
<div className="mb-4 bg-red-100 border border-red-300 text-red-700 px-4 py-2 rounded">
{error}
</div>
)}
{message && (
<div className="mb-4 bg-green-100 border border-green-300 text-green-700 px-4 py-2 rounded">
{message}
</div>
)}
<div className="grid md:grid-cols-2 gap-4">
<div className="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
<div className="flex items-center justify-between mb-3">
<div>
<h3 className="text-lg font-semibold text-gray-800">ntfy</h3>
<p className="text-xs text-gray-500">
Push aufs Handy über die ntfy-App oder Browser (Themenkanal notwendig).
</p>
</div>
<label className="inline-flex items-center space-x-2 text-sm text-gray-700">
<input
type="checkbox"
checked={settings.ntfy.enabled}
onChange={(event) => onFieldChange('ntfy', 'enabled', event.target.checked)}
disabled={!capabilities.ntfy.enabled}
className="h-4 w-4 text-blue-600 rounded focus:ring-blue-500"
/>
<span>Aktiv</span>
</label>
</div>
{!capabilities.ntfy.enabled ? (
<p className="text-sm text-gray-500">
Diese Option wurde vom Admin deaktiviert. Bitte frage nach, wenn du ntfy nutzen möchtest.
</p>
) : (
<div className="space-y-3">
<div>
<label className="block text-xs font-semibold text-gray-600 mb-1">Topic / Kanal</label>
<input
type="text"
value={settings.ntfy.topic}
onChange={(event) => onFieldChange('ntfy', 'topic', event.target.value)}
placeholder="z. B. mein-pickup-topic"
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
disabled={!settings.ntfy.enabled}
/>
</div>
{capabilities.ntfy.serverUrl && (
<p className="text-xs text-gray-500">
Server: {capabilities.ntfy.serverUrl} (vom Admin festgelegt)
</p>
)}
{capabilities.ntfy.topicPrefix && (
<p className="text-xs text-gray-500">
Präfix: {capabilities.ntfy.topicPrefix} (Bindestrich wird automatisch ergänzt)
</p>
)}
{ntfyPreviewUrl && (
<div className="flex items-center gap-2 text-xs text-gray-600">
<a
href={ntfyPreviewUrl}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800 break-all"
>
{ntfyPreviewUrl}
</a>
<button
type="button"
onClick={() => onCopyLink(ntfyPreviewUrl)}
className="border border-gray-300 rounded px-2 py-0.5 text-gray-600 hover:text-blue-700 hover:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-400"
title="In Zwischenablage kopieren"
>
Kopieren
</button>
</div>
)}
{copyFeedback && <p className="text-xs text-green-600">{copyFeedback}</p>}
<button
type="button"
onClick={() => onSendTest('ntfy')}
disabled={!settings.ntfy.enabled || saving}
className="text-sm text-blue-600 hover:text-blue-800"
>
Test senden
</button>
</div>
)}
</div>
<div className="bg-white border border-gray-200 rounded-lg p-4 shadow-sm">
<div className="flex items-center justify-between mb-3">
<div>
<h3 className="text-lg font-semibold text-gray-800">Telegram</h3>
<p className="text-xs text-gray-500">
Nutze den Bot des Admins, um Nachrichten direkt in Telegram zu erhalten.
</p>
</div>
<label className="inline-flex items-center space-x-2 text-sm text-gray-700">
<input
type="checkbox"
checked={settings.telegram.enabled}
onChange={(event) => onFieldChange('telegram', 'enabled', event.target.checked)}
disabled={!capabilities.telegram.enabled}
className="h-4 w-4 text-blue-600 rounded focus:ring-blue-500"
/>
<span>Aktiv</span>
</label>
</div>
{!capabilities.telegram.enabled ? (
<p className="text-sm text-gray-500">
Telegram-Benachrichtigungen sind derzeit deaktiviert oder der Bot ist nicht konfiguriert.
</p>
) : (
<div className="space-y-3">
<div>
<label className="block text-xs font-semibold text-gray-600 mb-1">Chat-ID</label>
<input
type="text"
value={settings.telegram.chatId}
onChange={(event) => onFieldChange('telegram', 'chatId', event.target.value)}
placeholder="z. B. 123456789"
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
disabled={!settings.telegram.enabled}
/>
<p className="text-xs text-gray-500 mt-1">
Tipp: Schreibe dem Telegram-Bot und nutze{' '}
<a
href="https://t.me/userinfobot"
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 hover:text-blue-800"
>
@userinfobot
</a>{' '}
oder ein Chat-ID-Tool.
</p>
</div>
<button
type="button"
onClick={() => onSendTest('telegram')}
disabled={!settings.telegram.enabled || saving}
className="text-sm text-blue-600 hover:text-blue-800"
>
Test senden
</button>
</div>
)}
</div>
</div>
<div className="mt-6 border border-blue-100 bg-blue-50 rounded-lg p-4">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div>
<h3 className="text-lg font-semibold text-blue-900">Standort für Entfernungssortierung</h3>
<p className="text-sm text-blue-800">
Der Standort wird für die Entfernungskalkulation im Monitoring genutzt. Nur für dich sichtbar.
</p>
</div>
<div className="flex gap-2">
<button
type="button"
onClick={() => onDetectLocation?.()}
className="px-4 py-2 rounded-md bg-blue-600 text-white text-sm disabled:opacity-60"
disabled={!onDetectLocation || locationLoading || locationSaving}
>
{locationSaving ? 'Speichere...' : 'Standort ermitteln'}
</button>
{location && (
<button
type="button"
onClick={() => onClearLocation?.()}
className="px-4 py-2 rounded-md border border-blue-300 text-blue-700 text-sm bg-white disabled:opacity-60"
disabled={!onClearLocation || locationSaving}
>
Entfernen
</button>
)}
</div>
</div>
<div className="mt-3 text-sm text-blue-900">
{locationLoading ? (
<p>Standort wird geladen...</p>
) : location ? (
<p>
Aktueller Standort: {location.lat.toFixed(4)}, {location.lon.toFixed(4)}
{location.label && (
<span className="ml-1 font-semibold text-blue-900"> {location.label}</span>
)}{' '}
(
{location.updatedAt ? new Date(location.updatedAt).toLocaleString('de-DE') : 'Zeit unbekannt'})
</p>
) : (
<p>Kein Standort hinterlegt.</p>
)}
</div>
{locationError && <p className="mt-2 text-sm text-red-600">{locationError}</p>}
</div>
<div className="flex items-center justify-end mt-4 gap-3">
<button
type="button"
onClick={onReset}
className="text-sm text-gray-600 hover:text-gray-900"
disabled={loading}
>
Zurücksetzen
</button>
<button
type="button"
onClick={onSave}
disabled={!dirty || saving}
className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors disabled:opacity-60"
>
{saving ? 'Speichere...' : 'Benachrichtigungen speichern'}
</button>
</div>
</div>
);
};
export default NotificationPanel;