'use client'; import * as React from 'react'; import { useState, useEffect } from 'react'; import { AdminService } from '@/lib/api/services/admin'; import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter, } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Textarea } from '@/components/ui/textarea'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { AlertCircle, Save, RefreshCw, Eye, EyeOff } from 'lucide-react'; import { toast } from 'sonner'; import { Switch } from '@/components/ui/switch'; interface SettingsData { name?: string; description?: string; npub?: string; nsec?: string; upstream_api_key?: string; http_url?: string; onion_url?: string; cashu_mints?: string[]; relays?: string[]; [key: string]: unknown; } const HANDLED_KEYS = [ 'name', 'description', 'http_url', 'onion_url', 'npub', 'nsec', 'cashu_mints', 'relays', 'admin_password', 'id', 'updated_at', ]; const IGNORED_KEYS = [ 'upstream_base_url', 'upstream_api_key', 'upstream_provider_fee', 'exchange_fee', 'models_path', ]; interface PasswordData { current_password: string; new_password: string; confirm_password: string; } export function AdminSettings() { const [settings, setSettings] = useState({}); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); const [showSecrets, setShowSecrets] = useState(false); const [newMint, setNewMint] = useState(''); const [newRelay, setNewRelay] = useState(''); const [passwordData, setPasswordData] = useState({ current_password: '', new_password: '', confirm_password: '', }); const [passwordError, setPasswordError] = useState(''); const [passwordSaving, setPasswordSaving] = useState(false); useEffect(() => { loadSettings(); }, []); const loadSettings = async () => { try { setLoading(true); setError(''); const data = await AdminService.getSettings(); setSettings(data as SettingsData); } catch (err) { const message = err instanceof Error ? err.message : 'Failed to load settings'; setError(message); toast.error(message); } finally { setLoading(false); } }; const handleSave = async () => { try { setSaving(true); setError(''); const updatedData = await AdminService.updateSettings(settings); setSettings(updatedData as SettingsData); toast.success('Settings saved successfully'); } catch (err) { const message = err instanceof Error ? err.message : 'Failed to save settings'; setError(message); toast.error(message); } finally { setSaving(false); } }; const handlePasswordUpdate = async () => { try { setPasswordSaving(true); setPasswordError(''); if (passwordData.new_password !== passwordData.confirm_password) { setPasswordError('New passwords do not match'); return; } if (passwordData.new_password.length < 6) { setPasswordError('New password must be at least 6 characters'); return; } const { apiClient } = await import('@/lib/api/client'); await apiClient.patch('/admin/api/password', { current_password: passwordData.current_password, new_password: passwordData.new_password, }); setPasswordData({ current_password: '', new_password: '', confirm_password: '', }); toast.success('Password updated successfully'); } catch (err) { const message = err instanceof Error ? err.message : 'Failed to update password'; setPasswordError(message); toast.error(message); } finally { setPasswordSaving(false); } }; const handleInputChange = (field: string, value: unknown) => { setSettings((prev) => ({ ...prev, [field]: value, })); }; const addMint = () => { if (newMint.trim()) { setSettings((prev) => ({ ...prev, cashu_mints: [...(prev.cashu_mints || []), newMint.trim()], })); setNewMint(''); } }; const removeMint = (index: number) => { setSettings((prev) => ({ ...prev, cashu_mints: prev.cashu_mints?.filter((_, i) => i !== index) || [], })); }; const addRelay = () => { if (newRelay.trim()) { setSettings((prev) => ({ ...prev, relays: [...(prev.relays || []), newRelay.trim()], })); setNewRelay(''); } }; const removeRelay = (index: number) => { setSettings((prev) => ({ ...prev, relays: prev.relays?.filter((_, i) => i !== index) || [], })); }; const renderSecretField = ( field: string, label: string, placeholder?: string ) => { const value = (settings[field] as string) || ''; const displayValue = showSecrets ? value : value ? '••••••••' : ''; return (
handleInputChange(field, e.target.value)} placeholder={placeholder} className='flex-1' />
); }; const renderDynamicField = (key: string, value: unknown) => { const label = key .split('_') .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .join(' '); if (typeof value === 'boolean') { return (
handleInputChange(key, checked)} />
); } if (typeof value === 'number') { return (
{ const val = e.target.value === '' ? 0 : Number(e.target.value); handleInputChange(key, val); }} />
); } if (Array.isArray(value)) { const strValue = value.join(', '); return (