first commit
This commit is contained in:
352
src/pages/Settings.jsx
Normal file
352
src/pages/Settings.jsx
Normal file
@@ -0,0 +1,352 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Save, Key, Globe, DollarSign, Bell, Shield, Database } from 'lucide-react';
|
||||
|
||||
const Settings = () => {
|
||||
const [settings, setSettings] = useState({
|
||||
paymentApiKey: '',
|
||||
webhookUrl: '',
|
||||
currency: 'USD',
|
||||
notifications: {
|
||||
email: true,
|
||||
sms: false,
|
||||
webhook: true
|
||||
},
|
||||
security: {
|
||||
twoFactor: false,
|
||||
sessionTimeout: 30
|
||||
},
|
||||
data: {
|
||||
retentionPeriod: 365,
|
||||
autoBackup: true
|
||||
}
|
||||
});
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [activeTab, setActiveTab] = useState('payment');
|
||||
|
||||
useEffect(() => {
|
||||
loadSettings();
|
||||
}, []);
|
||||
|
||||
const loadSettings = () => {
|
||||
const savedSettings = localStorage.getItem('adminSettings');
|
||||
if (savedSettings) {
|
||||
setSettings(JSON.parse(savedSettings));
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (section, field, value) => {
|
||||
setSettings(prev => ({
|
||||
...prev,
|
||||
[section]: {
|
||||
...prev[section],
|
||||
[field]: value
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const handleDirectChange = (field, value) => {
|
||||
setSettings(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSave = async () => {
|
||||
setLoading(true);
|
||||
setMessage('');
|
||||
|
||||
try {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
localStorage.setItem('adminSettings', JSON.stringify(settings));
|
||||
setMessage('Settings saved successfully!');
|
||||
|
||||
setTimeout(() => setMessage(''), 3000);
|
||||
} catch (error) {
|
||||
setMessage('Failed to save settings');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const tabs = [
|
||||
{ id: 'payment', label: 'Payment', icon: DollarSign },
|
||||
{ id: 'notifications', label: 'Notifications', icon: Bell },
|
||||
{ id: 'security', label: 'Security', icon: Shield },
|
||||
{ id: 'data', label: 'Data', icon: Database }
|
||||
];
|
||||
|
||||
const currencies = [
|
||||
{ value: 'USD', label: 'US Dollar (USD)' },
|
||||
{ value: 'EUR', label: 'Euro (EUR)' },
|
||||
{ value: 'GBP', label: 'British Pound (GBP)' },
|
||||
{ value: 'CAD', label: 'Canadian Dollar (CAD)' },
|
||||
{ value: 'AUD', label: 'Australian Dollar (AUD)' },
|
||||
{ value: 'JPY', label: 'Japanese Yen (JPY)' }
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-6">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Settings</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400">Configure your payment system preferences</p>
|
||||
</div>
|
||||
|
||||
{/* Message */}
|
||||
{message && (
|
||||
<div className={`mb-6 p-4 rounded-lg ${
|
||||
message.includes('success')
|
||||
? 'bg-green-50 dark:bg-green-900 text-green-700 dark:text-green-300 border border-green-200 dark:border-green-700'
|
||||
: 'bg-red-50 dark:bg-red-900 text-red-700 dark:text-red-300 border border-red-200 dark:border-red-700'
|
||||
}`}>
|
||||
{message}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
{/* Sidebar */}
|
||||
<div className="lg:col-span-1">
|
||||
<nav className="space-y-1">
|
||||
{tabs.map((tab) => {
|
||||
const Icon = tab.icon;
|
||||
return (
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`w-full flex items-center px-3 py-2 text-sm font-medium rounded-lg transition-colors duration-200 ${
|
||||
activeTab === tab.id
|
||||
? 'bg-primary-100 text-primary-700 dark:bg-primary-900 dark:text-primary-300'
|
||||
: 'text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-800'
|
||||
}`}
|
||||
>
|
||||
<Icon className="mr-3 h-5 w-5" />
|
||||
{tab.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="lg:col-span-3">
|
||||
<div className="card p-6">
|
||||
{/* Payment Settings */}
|
||||
{activeTab === 'payment' && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-6">Payment Configuration</h3>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
<Key className="inline h-4 w-4 mr-1" />
|
||||
Payment API Key
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
value={settings.paymentApiKey}
|
||||
onChange={(e) => handleDirectChange('paymentApiKey', e.target.value)}
|
||||
className="input-field"
|
||||
placeholder="Enter your payment API key"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Your secure API key for payment processing
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
<Globe className="inline h-4 w-4 mr-1" />
|
||||
Webhook URL
|
||||
</label>
|
||||
<input
|
||||
type="url"
|
||||
value={settings.webhookUrl}
|
||||
onChange={(e) => handleDirectChange('webhookUrl', e.target.value)}
|
||||
className="input-field"
|
||||
placeholder="https://your-domain.com/webhook"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
URL to receive payment notifications
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
<DollarSign className="inline h-4 w-4 mr-1" />
|
||||
Default Currency
|
||||
</label>
|
||||
<select
|
||||
value={settings.currency}
|
||||
onChange={(e) => handleDirectChange('currency', e.target.value)}
|
||||
className="input-field"
|
||||
>
|
||||
{currencies.map(currency => (
|
||||
<option key={currency.value} value={currency.value}>
|
||||
{currency.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Notification Settings */}
|
||||
{activeTab === 'notifications' && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-6">Notification Preferences</h3>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Email Notifications</label>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Receive notifications via email</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.email}
|
||||
onChange={(e) => handleInputChange('notifications', 'email', e.target.checked)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-300 dark:peer-focus:ring-primary-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">SMS Notifications</label>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Receive notifications via SMS</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.sms}
|
||||
onChange={(e) => handleInputChange('notifications', 'sms', e.target.checked)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-300 dark:peer-focus:ring-primary-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Webhook Notifications</label>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Send notifications to webhook URL</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.notifications.webhook}
|
||||
onChange={(e) => handleInputChange('notifications', 'webhook', e.target.checked)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-300 dark:peer-focus:ring-primary-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Security Settings */}
|
||||
{activeTab === 'security' && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-6">Security Settings</h3>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Two-Factor Authentication</label>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Enable 2FA for enhanced security</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.security.twoFactor}
|
||||
onChange={(e) => handleInputChange('security', 'twoFactor', e.target.checked)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-300 dark:peer-focus:ring-primary-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Session Timeout (minutes)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
min="5"
|
||||
max="480"
|
||||
value={settings.security.sessionTimeout}
|
||||
onChange={(e) => handleInputChange('security', 'sessionTimeout', parseInt(e.target.value))}
|
||||
className="input-field w-32"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
Automatically log out after inactivity
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Data Settings */}
|
||||
{activeTab === 'data' && (
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-6">Data Management</h3>
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
Data Retention Period (days)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
min="30"
|
||||
max="2555"
|
||||
value={settings.data.retentionPeriod}
|
||||
onChange={(e) => handleInputChange('data', 'retentionPeriod', parseInt(e.target.value))}
|
||||
className="input-field w-32"
|
||||
/>
|
||||
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
How long to keep transaction data
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Automatic Backup</label>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Automatically backup data daily</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={settings.data.autoBackup}
|
||||
onChange={(e) => handleInputChange('data', 'autoBackup', e.target.checked)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary-300 dark:peer-focus:ring-primary-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Save Button */}
|
||||
<div className="mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={loading}
|
||||
className="btn-primary flex items-center"
|
||||
>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
{loading ? 'Saving...' : 'Save Settings'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Settings;
|
||||
Reference in New Issue
Block a user