import React, {useState, useEffect, useContext} from 'react'; import {UserContext} from '@/common/contexts/UserContext.jsx'; import {useToast} from '@/common/contexts/ToastContext.jsx'; import {getRequest, postRequest} from '@/common/utils/RequestUtil.js'; import Button from '@/common/components/Button'; import Input from '@/common/components/Input'; import Card, {CardHeader, CardBody} from '@/common/components/Card'; import LoadingSpinner from '@/common/components/LoadingSpinner'; import PageHeader from '@/common/components/PageHeader'; import { SlidersIcon, FloppyDiskIcon, ArrowClockwiseIcon, InfoIcon, WarningIcon } from '@phosphor-icons/react'; import './styles.sass'; export const SystemSettings = () => { const toast = useToast(); const [configs, setConfigs] = useState([]); const [loading, setLoading] = useState(true); const [formData, setFormData] = useState({}); const [formErrors, setFormErrors] = useState({}); const [submitting, setSubmitting] = useState(false); const [hasChanges, setHasChanges] = useState(false); useEffect(() => { fetchConfigs(); }, []); const fetchConfigs = async () => { try { setLoading(true); const response = await getRequest('admin/config'); setConfigs(response.configs); const initialData = {}; response.configs.forEach(config => { initialData[config.key] = config.value || config.default_value || ''; }); setFormData(initialData); setHasChanges(false); } catch (error) { console.error('Failed to fetch configs:', error); toast.error('Failed to load system settings. Please try again.'); } finally { setLoading(false); } }; const handleInputChange = (key, value) => { setFormData(prev => { const newData = {...prev, [key]: value}; const originalConfig = configs.find(c => c.key === key); const hasChange = Object.keys(newData).some(k => { const original = configs.find(c => c.key === k); const origVal = original?.value || original?.default_value || ''; return newData[k] !== origVal; }); setHasChanges(hasChange); return newData; }); if (formErrors[key]) { setFormErrors(prev => ({ ...prev, [key]: '' })); } }; const validateForm = () => { const errors = {}; configs.forEach(config => { const value = formData[config.key] || ''; if (config.required && !value.trim()) { errors[config.key] = `${config.key} is required`; return; } switch (config.key) { case 'EXTERNAL_URL': if (value && !value.startsWith('http://') && !value.startsWith('https://')) { errors[config.key] = 'URL must start with http:// or https://'; } break; case 'MAX_UPLOAD_SIZE_MB': case 'BACKUP_RETENTION_DAYS': case 'SESSION_TIMEOUT_HOURS': if (value && (isNaN(value) || parseInt(value) <= 0)) { errors[config.key] = 'Must be a positive number'; } break; } }); setFormErrors(errors); return Object.keys(errors).length === 0; }; const handleSave = async () => { if (!validateForm()) { return; } setSubmitting(true); try { const savePromises = []; for (const config of configs) { const newValue = formData[config.key] || ''; const originalValue = config.value || config.default_value || ''; if (newValue !== originalValue) { savePromises.push( postRequest('admin/config', { key: config.key, value: newValue }) ); } } await Promise.all(savePromises); toast.success('System settings saved successfully'); setHasChanges(false); await fetchConfigs(); } catch (error) { console.error('Failed to save settings:', error); const errorMessage = error.error || 'Failed to save settings. Please try again.'; toast.error(errorMessage); } finally { setSubmitting(false); } }; const handleReset = async () => { if (!window.confirm('Are you sure you want to reset all settings to their current saved values? Any unsaved changes will be lost.')) { return; } await fetchConfigs(); toast.info('Settings reset to saved values'); }; const getFieldType = (key) => { switch (key) { case 'SESSION_TIMEOUT_HOURS': return 'number'; case 'EXTERNAL_URL': return 'url'; default: return 'text'; } }; const getFieldPlaceholder = (config) => { if (config.default_value) { return `Default: ${config.default_value}`; } return `Enter ${config.key.toLowerCase().replace(/_/g, ' ')}`; }; if (loading) { return (
{config.description}
No configurable settings are currently defined.