refactoring node 24

This commit is contained in:
2025-11-10 10:39:56 +01:00
parent 0515d3d714
commit dff35b8218
10 changed files with 1102 additions and 1019 deletions

View File

@@ -0,0 +1,253 @@
import { Link } from 'react-router-dom';
const AdminSettingsPanel = ({
adminSettings,
adminSettingsLoading,
status,
error,
onDismissError,
onSettingChange,
onIgnoredSlotChange,
onAddIgnoredSlot,
onRemoveIgnoredSlot,
onNotificationChange,
onSave
}) => {
return (
<div className="p-4 max-w-4xl mx-auto bg-white shadow-lg rounded-lg mt-4">
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-4">
<div>
<h1 className="text-2xl font-bold text-purple-900">Admin-Einstellungen</h1>
<p className="text-sm text-gray-600">Globale Abläufe und Verzögerungen für die Abhol-Automation verwalten.</p>
</div>
<Link
to="/"
className="inline-flex items-center justify-center px-4 py-2 text-sm font-medium border border-purple-200 rounded-md text-purple-700 hover:bg-purple-50 transition-colors"
>
Zur Konfiguration
</Link>
</div>
{error && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4 relative">
<span className="block sm:inline">{error}</span>
<button className="absolute top-0 bottom-0 right-0 px-4 py-3" onClick={onDismissError}>
<span className="text-xl">&times;</span>
</button>
</div>
)}
{status && (
<div className="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded mb-4 relative">
<span className="block sm:inline">{status}</span>
</div>
)}
<div className="border border-purple-200 rounded-lg p-4 bg-purple-50">
{adminSettingsLoading && <p className="text-sm text-purple-700">Lade Admin-Einstellungen...</p>}
{!adminSettingsLoading && !adminSettings && <p className="text-sm text-purple-700">Keine Admin-Einstellungen verfügbar.</p>}
{adminSettings && (
<>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Cron-Ausdruck</label>
<input
type="text"
value={adminSettings.scheduleCron}
onChange={(event) => onSettingChange('scheduleCron', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="z. B. 0 * * * *"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Initiale Verzögerung (Sek.)</label>
<div className="grid grid-cols-2 gap-2">
<input
type="number"
min="0"
value={adminSettings.initialDelayMinSeconds}
onChange={(event) => onSettingChange('initialDelayMinSeconds', event.target.value, true)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Min"
/>
<input
type="number"
min="0"
value={adminSettings.initialDelayMaxSeconds}
onChange={(event) => onSettingChange('initialDelayMaxSeconds', event.target.value, true)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Max"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Prüfverzögerung (Sek.)</label>
<div className="grid grid-cols-2 gap-2">
<input
type="number"
min="0"
value={adminSettings.randomDelayMinSeconds}
onChange={(event) => onSettingChange('randomDelayMinSeconds', event.target.value, true)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Min"
/>
<input
type="number"
min="0"
value={adminSettings.randomDelayMaxSeconds}
onChange={(event) => onSettingChange('randomDelayMaxSeconds', event.target.value, true)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Max"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Verzögerung Store-Prüfung (ms)</label>
<input
type="number"
min="0"
value={adminSettings.storePickupCheckDelayMs}
onChange={(event) => onSettingChange('storePickupCheckDelayMs', event.target.value, true)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="z. B. 3000"
/>
</div>
</div>
<div className="mb-6">
<div className="flex items-center justify-between mb-2">
<h2 className="text-lg font-semibold text-purple-900">Ignorierte Slots</h2>
<button
type="button"
onClick={onAddIgnoredSlot}
className="text-sm text-purple-700 hover:text-purple-900 flex items-center gap-1"
>
+ Slot hinzufügen
</button>
</div>
{(!adminSettings.ignoredSlots || adminSettings.ignoredSlots.length === 0) && (
<p className="text-sm text-gray-500">Keine Regeln definiert.</p>
)}
{adminSettings.ignoredSlots?.map((slot, index) => (
<div key={`${index}-${slot.storeId}`} className="grid grid-cols-1 md:grid-cols-5 gap-2 mb-2 items-center">
<input
type="text"
value={slot.storeId}
onChange={(event) => onIgnoredSlotChange(index, 'storeId', event.target.value)}
placeholder="Store-ID"
className="md:col-span-2 border rounded p-2 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
/>
<input
type="text"
value={slot.description}
onChange={(event) => onIgnoredSlotChange(index, 'description', event.target.value)}
placeholder="Beschreibung (optional)"
className="md:col-span-2 border rounded p-2 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
/>
<button
type="button"
onClick={() => onRemoveIgnoredSlot(index)}
className="text-sm text-red-600 hover:text-red-800 focus:outline-none"
>
Entfernen
</button>
</div>
))}
</div>
<div className="grid grid-cols-1 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">Konfiguration für Systembenachrichtigungen via ntfy.</p>
</div>
<label className="inline-flex items-center space-x-2 text-sm text-gray-700">
<input
type="checkbox"
checked={adminSettings.notifications?.ntfy?.enabled || false}
onChange={(event) => onNotificationChange('ntfy', 'enabled', event.target.checked)}
className="h-4 w-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span>Aktiv</span>
</label>
</div>
<div className="space-y-3">
<input
type="text"
value={adminSettings.notifications?.ntfy?.serverUrl || ''}
onChange={(event) => onNotificationChange('ntfy', 'serverUrl', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Server-URL"
/>
<input
type="text"
value={adminSettings.notifications?.ntfy?.topicPrefix || ''}
onChange={(event) => onNotificationChange('ntfy', 'topicPrefix', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Themenpräfix"
/>
<input
type="text"
value={adminSettings.notifications?.ntfy?.username || ''}
onChange={(event) => onNotificationChange('ntfy', 'username', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Benutzername"
/>
<input
type="password"
value={adminSettings.notifications?.ntfy?.password || ''}
onChange={(event) => onNotificationChange('ntfy', 'password', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Passwort"
/>
</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">Bot-Konfiguration für Telegram-Benachrichtigungen.</p>
</div>
<label className="inline-flex items-center space-x-2 text-sm text-gray-700">
<input
type="checkbox"
checked={adminSettings.notifications?.telegram?.enabled || false}
onChange={(event) => onNotificationChange('telegram', 'enabled', event.target.checked)}
className="h-4 w-4 text-purple-600 rounded focus:ring-purple-500"
/>
<span>Aktiv</span>
</label>
</div>
<input
type="text"
value={adminSettings.notifications?.telegram?.botToken || ''}
onChange={(event) => onNotificationChange('telegram', 'botToken', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="Bot-Token"
/>
</div>
</div>
<div className="flex justify-end mt-4">
<button
type="button"
onClick={onSave}
className="bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded focus:outline-none focus:ring-2 focus:ring-purple-500"
>
Admin-Einstellungen speichern
</button>
</div>
</>
)}
</div>
</div>
);
};
export default AdminSettingsPanel;