Add new Frontend
This commit is contained in:
616
frontend/src/pages/settings/SettingsPage.tsx
Normal file
616
frontend/src/pages/settings/SettingsPage.tsx
Normal file
@@ -0,0 +1,616 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
User,
|
||||
Bell,
|
||||
Shield,
|
||||
Globe,
|
||||
Smartphone,
|
||||
Mail,
|
||||
LogOut,
|
||||
Save,
|
||||
ChevronRight,
|
||||
MapPin,
|
||||
Clock,
|
||||
DollarSign
|
||||
} from 'lucide-react';
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
interface SettingsPageProps {
|
||||
user: any;
|
||||
onLogout: () => void;
|
||||
}
|
||||
|
||||
interface UserSettings {
|
||||
fullName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
language: string;
|
||||
timezone: string;
|
||||
currency: string;
|
||||
bakeryName: string;
|
||||
bakeryAddress: string;
|
||||
businessType: string;
|
||||
}
|
||||
|
||||
interface NotificationSettings {
|
||||
emailNotifications: boolean;
|
||||
smsNotifications: boolean;
|
||||
dailyReports: boolean;
|
||||
weeklyReports: boolean;
|
||||
forecastAlerts: boolean;
|
||||
stockAlerts: boolean;
|
||||
orderReminders: boolean;
|
||||
}
|
||||
|
||||
const SettingsPage: React.FC<SettingsPageProps> = ({ user, onLogout }) => {
|
||||
const [activeTab, setActiveTab] = useState('profile');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [userSettings, setUserSettings] = useState<UserSettings>({
|
||||
fullName: user.fullName || '',
|
||||
email: user.email || '',
|
||||
phone: '',
|
||||
language: 'es',
|
||||
timezone: 'Europe/Madrid',
|
||||
currency: 'EUR',
|
||||
bakeryName: 'Mi Panadería',
|
||||
bakeryAddress: '',
|
||||
businessType: 'individual'
|
||||
});
|
||||
|
||||
const [notificationSettings, setNotificationSettings] = useState<NotificationSettings>({
|
||||
emailNotifications: true,
|
||||
smsNotifications: false,
|
||||
dailyReports: true,
|
||||
weeklyReports: true,
|
||||
forecastAlerts: true,
|
||||
stockAlerts: true,
|
||||
orderReminders: true
|
||||
});
|
||||
|
||||
const tabs = [
|
||||
{ id: 'profile', label: 'Perfil', icon: User },
|
||||
{ id: 'notifications', label: 'Notificaciones', icon: Bell },
|
||||
{ id: 'security', label: 'Seguridad', icon: Shield },
|
||||
{ id: 'preferences', label: 'Preferencias', icon: Globe },
|
||||
];
|
||||
|
||||
const handleSaveSettings = async () => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
// Simulate API call
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
toast.success('Configuración guardada exitosamente');
|
||||
} catch (error) {
|
||||
toast.error('Error al guardar la configuración');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
if (window.confirm('¿Estás seguro de que quieres cerrar sesión?')) {
|
||||
onLogout();
|
||||
}
|
||||
};
|
||||
|
||||
const renderProfileTab = () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Información Personal</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Nombre completo
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={userSettings.fullName}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, fullName: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Correo electrónico
|
||||
</label>
|
||||
<input
|
||||
type="email"
|
||||
value={userSettings.email}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, email: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Teléfono
|
||||
</label>
|
||||
<input
|
||||
type="tel"
|
||||
value={userSettings.phone}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, phone: e.target.value }))}
|
||||
placeholder="+34 600 000 000"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Información del Negocio</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Nombre de la panadería
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={userSettings.bakeryName}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, bakeryName: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Dirección
|
||||
</label>
|
||||
<div className="relative">
|
||||
<MapPin className="absolute left-3 top-3 h-5 w-5 text-gray-400" />
|
||||
<input
|
||||
type="text"
|
||||
value={userSettings.bakeryAddress}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, bakeryAddress: e.target.value }))}
|
||||
placeholder="Calle Mayor, 123, Madrid"
|
||||
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Tipo de negocio
|
||||
</label>
|
||||
<select
|
||||
value={userSettings.businessType}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, businessType: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
>
|
||||
<option value="individual">Panadería Individual</option>
|
||||
<option value="central_workshop">Obrador Central</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Horarios de Operación</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Hora de apertura
|
||||
</label>
|
||||
<input
|
||||
type="time"
|
||||
defaultValue="07:00"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Hora de cierre
|
||||
</label>
|
||||
<input
|
||||
type="time"
|
||||
defaultValue="20:00"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-3">
|
||||
Días de operación
|
||||
</label>
|
||||
<div className="grid grid-cols-4 gap-2 sm:grid-cols-7">
|
||||
{['L', 'M', 'X', 'J', 'V', 'S', 'D'].map((day, index) => (
|
||||
<label key={day} className="flex items-center justify-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultChecked={index < 6} // Monday to Saturday checked by default
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-10 h-10 bg-gray-200 peer-checked:bg-primary-500 peer-checked:text-white rounded-lg flex items-center justify-center font-medium text-sm cursor-pointer transition-colors">
|
||||
{day}
|
||||
</div>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Integración con POS</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="p-4 bg-gray-50 rounded-lg">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div>
|
||||
<h4 className="font-medium text-gray-900">Sistema POS Conectado</h4>
|
||||
<p className="text-sm text-gray-600">Sincroniza ventas automáticamente</p>
|
||||
</div>
|
||||
<span className="px-2 py-1 bg-red-100 text-red-800 rounded text-xs">
|
||||
Desconectado
|
||||
</span>
|
||||
</div>
|
||||
<button className="w-full px-4 py-2 bg-primary-500 text-white rounded-lg hover:bg-primary-600 transition-colors">
|
||||
Conectar Sistema POS
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderNotificationsTab = () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Canales de Notificación</h3>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
||||
<div className="flex items-center">
|
||||
<Mail className="h-5 w-5 text-gray-600 mr-3" />
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Notificaciones por Email</div>
|
||||
<div className="text-sm text-gray-500">Recibe alertas y reportes por correo</div>
|
||||
</div>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationSettings.emailNotifications}
|
||||
onChange={(e) => setNotificationSettings(prev => ({
|
||||
...prev,
|
||||
emailNotifications: 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 rounded-full peer 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 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
||||
<div className="flex items-center">
|
||||
<Smartphone className="h-5 w-5 text-gray-600 mr-3" />
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Notificaciones SMS</div>
|
||||
<div className="text-sm text-gray-500">Alertas urgentes por mensaje de texto</div>
|
||||
</div>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationSettings.smsNotifications}
|
||||
onChange={(e) => setNotificationSettings(prev => ({
|
||||
...prev,
|
||||
smsNotifications: 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 rounded-full peer 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 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Tipos de Notificación</h3>
|
||||
<div className="space-y-3">
|
||||
{[
|
||||
{ key: 'dailyReports', label: 'Reportes Diarios', desc: 'Resumen diario de ventas y predicciones' },
|
||||
{ key: 'weeklyReports', label: 'Reportes Semanales', desc: 'Análisis semanal de rendimiento' },
|
||||
{ key: 'forecastAlerts', label: 'Alertas de Predicción', desc: 'Cambios significativos en demanda' },
|
||||
{ key: 'stockAlerts', label: 'Alertas de Stock', desc: 'Inventario bajo o próximos vencimientos' },
|
||||
{ key: 'orderReminders', label: 'Recordatorios de Pedidos', desc: 'Próximas entregas y fechas límite' }
|
||||
].map((item) => (
|
||||
<div key={item.key} className="flex items-center justify-between p-3 border border-gray-200 rounded-lg">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">{item.label}</div>
|
||||
<div className="text-sm text-gray-500">{item.desc}</div>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notificationSettings[item.key as keyof NotificationSettings] as boolean}
|
||||
onChange={(e) => setNotificationSettings(prev => ({
|
||||
...prev,
|
||||
[item.key]: 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 rounded-full peer 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 peer-checked:bg-primary-600"></div>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderSecurityTab = () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Cambiar Contraseña</h3>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Contraseña actual
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Nueva contraseña
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Confirmar nueva contraseña
|
||||
</label>
|
||||
<input
|
||||
type="password"
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button className="w-full sm:w-auto px-6 py-3 bg-primary-500 text-white rounded-xl hover:bg-primary-600 transition-colors">
|
||||
Actualizar Contraseña
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t pt-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Sesiones Activas</h3>
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between p-4 border border-gray-200 rounded-lg">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Navegador actual</div>
|
||||
<div className="text-sm text-gray-500">Chrome en Windows • Madrid, España</div>
|
||||
<div className="text-xs text-green-600 mt-1">Sesión actual</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between p-4 border border-gray-200 rounded-lg">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Mobile App</div>
|
||||
<div className="text-sm text-gray-500">iPhone • Hace 2 días</div>
|
||||
</div>
|
||||
<button className="text-red-600 hover:text-red-700 text-sm">
|
||||
Cerrar sesión
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t pt-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4 text-red-600">Zona Peligrosa</h3>
|
||||
<div className="bg-red-50 border border-red-200 rounded-lg p-4">
|
||||
<h4 className="font-medium text-red-900 mb-2">Eliminar Cuenta</h4>
|
||||
<p className="text-red-800 text-sm mb-4">
|
||||
Esta acción eliminará permanentemente tu cuenta y todos los datos asociados.
|
||||
No se puede deshacer.
|
||||
</p>
|
||||
<button className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm">
|
||||
Eliminar Cuenta
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderPreferencesTab = () => (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Configuración Regional</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<Globe className="inline h-4 w-4 mr-1" />
|
||||
Idioma
|
||||
</label>
|
||||
<select
|
||||
value={userSettings.language}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, language: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
>
|
||||
<option value="es">Español</option>
|
||||
<option value="en">English</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<Clock className="inline h-4 w-4 mr-1" />
|
||||
Zona horaria
|
||||
</label>
|
||||
<select
|
||||
value={userSettings.timezone}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, timezone: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
>
|
||||
<option value="Europe/Madrid">Europa/Madrid (CET)</option>
|
||||
<option value="Europe/London">Europa/Londres (GMT)</option>
|
||||
<option value="America/New_York">América/Nueva York (EST)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||||
<DollarSign className="inline h-4 w-4 mr-1" />
|
||||
Moneda
|
||||
</label>
|
||||
<select
|
||||
value={userSettings.currency}
|
||||
onChange={(e) => setUserSettings(prev => ({ ...prev, currency: e.target.value }))}
|
||||
className="w-full px-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500"
|
||||
>
|
||||
<option value="EUR">Euro (€)</option>
|
||||
<option value="USD">Dólar americano ($)</option>
|
||||
<option value="GBP">Libra esterlina (£)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Exportar Datos</h3>
|
||||
<div className="space-y-3">
|
||||
<button className="w-full p-4 border border-gray-300 rounded-lg hover:border-primary-500 hover:bg-primary-50 transition-all text-left">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Exportar todas las predicciones</div>
|
||||
<div className="text-sm text-gray-500">Descargar historial completo en CSV</div>
|
||||
</div>
|
||||
<ChevronRight className="h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button className="w-full p-4 border border-gray-300 rounded-lg hover:border-primary-500 hover:bg-primary-50 transition-all text-left">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Exportar datos de ventas</div>
|
||||
<div className="text-sm text-gray-500">Historial de ventas y análisis</div>
|
||||
</div>
|
||||
<ChevronRight className="h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<button className="w-full p-4 border border-gray-300 rounded-lg hover:border-primary-500 hover:bg-primary-50 transition-all text-left">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<div className="font-medium text-gray-900">Exportar configuración</div>
|
||||
<div className="text-sm text-gray-500">Respaldo de toda la configuración</div>
|
||||
</div>
|
||||
<ChevronRight className="h-5 w-5 text-gray-400" />
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const renderTabContent = () => {
|
||||
switch (activeTab) {
|
||||
case 'profile':
|
||||
return renderProfileTab();
|
||||
case 'notifications':
|
||||
return renderNotificationsTab();
|
||||
case 'security':
|
||||
return renderSecurityTab();
|
||||
case 'preferences':
|
||||
return renderPreferencesTab();
|
||||
default:
|
||||
return renderProfileTab();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-6xl mx-auto">
|
||||
{/* Header */}
|
||||
<div className="mb-8">
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">Configuración</h1>
|
||||
<p className="text-gray-600">
|
||||
Administra tu cuenta y personaliza tu experiencia en PanIA
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
|
||||
{/* Sidebar Navigation */}
|
||||
<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-4 py-3 text-sm font-medium rounded-lg transition-all ${
|
||||
activeTab === tab.id
|
||||
? 'bg-primary-100 text-primary-700 shadow-soft'
|
||||
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100'
|
||||
}`}
|
||||
>
|
||||
<Icon className="h-5 w-5 mr-3" />
|
||||
{tab.label}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
||||
{/* Logout Button */}
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full flex items-center px-4 py-3 text-sm font-medium text-red-600 hover:text-red-700 hover:bg-red-50 rounded-lg transition-all mt-6"
|
||||
>
|
||||
<LogOut className="h-5 w-5 mr-3" />
|
||||
Cerrar Sesión
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="lg:col-span-3">
|
||||
<div className="bg-white rounded-xl shadow-soft p-6">
|
||||
{renderTabContent()}
|
||||
|
||||
{/* Save Button */}
|
||||
{(activeTab === 'profile' || activeTab === 'notifications' || activeTab === 'preferences') && (
|
||||
<div className="mt-8 pt-6 border-t border-gray-200">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm text-gray-600 mb-4 sm:mb-0">
|
||||
Los cambios se guardarán automáticamente
|
||||
</p>
|
||||
<button
|
||||
onClick={handleSaveSettings}
|
||||
disabled={isLoading}
|
||||
className="inline-flex items-center px-6 py-3 bg-primary-500 text-white rounded-xl hover:bg-primary-600 transition-all hover:shadow-lg disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
|
||||
Guardando...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Save className="h-4 w-4 mr-2" />
|
||||
Guardar Cambios
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsPage;
|
||||
Reference in New Issue
Block a user