diff --git a/src/App.js b/src/App.js index 9d0d5fc..ad0526e 100644 --- a/src/App.js +++ b/src/App.js @@ -101,6 +101,7 @@ function App() { const [notificationMessage, setNotificationMessage] = useState(''); const [notificationError, setNotificationError] = useState(''); const [notificationPanelOpen, setNotificationPanelOpen] = useState(false); + const [copyFeedback, setCopyFeedback] = useState(''); const minSelectableDate = useMemo(() => startOfDay(new Date()), []); const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag']; @@ -990,6 +991,36 @@ function App() { } }, [activeRangePicker, activeRangeEntry]); + const ntfyPreviewUrl = useMemo(() => { + if ( + !notificationCapabilities.ntfy.enabled || + !notificationCapabilities.ntfy.serverUrl || + !notificationSettings.ntfy.topic + ) { + return null; + } + const server = notificationCapabilities.ntfy.serverUrl.replace(/\/+$/, ''); + if (!server) { + return null; + } + const sanitizedTopic = notificationSettings.ntfy.topic.trim().replace(/^-+|-+$/g, ''); + if (!sanitizedTopic) { + return null; + } + const prefix = (notificationCapabilities.ntfy.topicPrefix || '').replace(/^-+|-+$/g, ''); + const separator = prefix && sanitizedTopic ? '-' : ''; + const slug = `${prefix}${separator}${sanitizedTopic}` || sanitizedTopic || prefix; + if (!slug) { + return null; + } + return `${server}/${slug}`; + }, [ + notificationCapabilities.ntfy.enabled, + notificationCapabilities.ntfy.serverUrl, + notificationCapabilities.ntfy.topicPrefix, + notificationSettings.ntfy.topic + ]); + const handleStoreSelection = async (store) => { const storeId = String(store.id); const existing = configMap.get(storeId); @@ -1115,6 +1146,33 @@ function App() { } }; + const copyToClipboard = async (text) => { + if (!text) { + return; + } + setCopyFeedback(''); + try { + if (navigator?.clipboard?.writeText) { + await navigator.clipboard.writeText(text); + } else { + const tempInput = document.createElement('textarea'); + tempInput.value = text; + tempInput.style.position = 'fixed'; + tempInput.style.top = '-9999px'; + document.body.appendChild(tempInput); + tempInput.focus(); + tempInput.select(); + document.execCommand('copy'); + document.body.removeChild(tempInput); + } + setCopyFeedback('Link kopiert!'); + setTimeout(() => setCopyFeedback(''), 2000); + } catch (err) { + setCopyFeedback(`Kopieren fehlgeschlagen: ${err.message}`); + setTimeout(() => setCopyFeedback(''), 3000); + } + }; + const handleAdminSettingChange = (field, value, isNumber = false) => { setAdminSettings((prev) => { if (!prev) { @@ -1390,70 +1448,6 @@ function App() { -
- - {!availableCollapsed && ( -
- {stores.length === 0 && ( -
Noch keine Betriebe geladen. Aktualisiere nach dem Login.
- )} - {stores.map((store) => { - const storeId = String(store.id); - const entry = configMap.get(storeId); - const isVisible = entry && !entry.hidden; - const needsRestore = entry && entry.hidden; - const blockedByNoPickups = store.hasPickupSlots === false; - let statusLabel = 'Hinzufügen'; - let statusClass = 'text-blue-600'; - if (isVisible) { - statusLabel = 'Bereits in Konfiguration'; - statusClass = 'text-gray-500'; - } else if (needsRestore) { - statusLabel = 'Ausgeblendet – erneut hinzufügen'; - statusClass = 'text-amber-600'; - } - if (blockedByNoPickups) { - statusLabel = 'Keine Pickups – automatisch verborgen'; - statusClass = 'text-red-600'; - } - return ( - - ); - })} -
- )} -
- {notificationPanelOpen && (
{notificationError && ( @@ -1513,6 +1507,29 @@ function App() { Präfix: {notificationCapabilities.ntfy.topicPrefix} (Bindestrich wird automatisch ergänzt)

)} + {ntfyPreviewUrl && ( +
+ + {ntfyPreviewUrl} + + +
+ )} + {copyFeedback && ( +

{copyFeedback}

+ )}
)} +
+ + {!availableCollapsed && ( +
+ {stores.length === 0 && ( +
Noch keine Betriebe geladen. Aktualisiere nach dem Login.
+ )} + {stores.map((store) => { + const storeId = String(store.id); + const entry = configMap.get(storeId); + const isVisible = entry && !entry.hidden; + const needsRestore = entry && entry.hidden; + const blockedByNoPickups = store.hasPickupSlots === false; + let statusLabel = 'Hinzufügen'; + let statusClass = 'text-blue-600'; + if (isVisible) { + statusLabel = 'Bereits in Konfiguration'; + statusClass = 'text-gray-500'; + } else if (needsRestore) { + statusLabel = 'Ausgeblendet – erneut hinzufügen'; + statusClass = 'text-amber-600'; + } + if (blockedByNoPickups) { + statusLabel = 'Keine Pickups – automatisch verborgen'; + statusClass = 'text-red-600'; + } + return ( + + ); + })} +
+ )} +
+ )} +