Refactor admin panel menu structure
This commit is contained in:
67
src/App.tsx
67
src/App.tsx
@@ -35,14 +35,20 @@ import Invoices from "./pages/dashboard/Invoices";
|
||||
import InvoiceDetail from "./pages/dashboard/InvoiceDetail";
|
||||
import HotelManagement from "./pages/dashboard/HotelManagement";
|
||||
import RestaurantPOS from "./pages/dashboard/RestaurantPOS";
|
||||
import Personalization from "./pages/dashboard/Personalization";
|
||||
import Security from "./pages/dashboard/Security";
|
||||
import VehicleManagement from "./pages/dashboard/VehicleManagement";
|
||||
import Sustainability from "./pages/dashboard/Sustainability";
|
||||
import Establishments from "./pages/dashboard/Establishments";
|
||||
import Analytics from "./pages/dashboard/Analytics";
|
||||
import SearchPlaces from "./pages/dashboard/SearchPlaces";
|
||||
import SearchEstablishments from "./pages/dashboard/SearchEstablishments";
|
||||
// Config pages
|
||||
import APIs from "./pages/dashboard/config/APIs";
|
||||
import Payments from "./pages/dashboard/config/Payments";
|
||||
import Parameters from "./pages/dashboard/config/Parameters";
|
||||
import Integrations from "./pages/dashboard/config/Integrations";
|
||||
import Audit from "./pages/dashboard/config/Audit";
|
||||
import SecurityCenter from "./pages/dashboard/config/SecurityCenter";
|
||||
import PersonalizationPage from "./pages/dashboard/config/PersonalizationPage";
|
||||
// Commerce pages (for retail stores)
|
||||
import CommerceStore from "./pages/dashboard/commerce/Store";
|
||||
import CommercePOS from "./pages/dashboard/commerce/POSTerminal";
|
||||
@@ -507,6 +513,63 @@ const AppRouter = () => (
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
{/* Config Routes */}
|
||||
<Route path="/dashboard/config/apis" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<APIs />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
<Route path="/dashboard/config/payments" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<Payments />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
<Route path="/dashboard/config/parameters" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<Parameters />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
<Route path="/dashboard/config/integrations" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<Integrations />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
<Route path="/dashboard/config/audit" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<Audit />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
<Route path="/dashboard/config/security" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<SecurityCenter />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
<Route path="/dashboard/config/personalization" element={
|
||||
<ProtectedRoute>
|
||||
<DashboardLayout>
|
||||
<PersonalizationPage />
|
||||
</DashboardLayout>
|
||||
</ProtectedRoute>
|
||||
} />
|
||||
|
||||
{/* Catch-all route */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
</Routes>
|
||||
|
||||
@@ -56,7 +56,8 @@ import {
|
||||
Radio,
|
||||
Sparkles,
|
||||
Leaf,
|
||||
Store
|
||||
Store,
|
||||
Server
|
||||
} from 'lucide-react';
|
||||
|
||||
const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
@@ -115,7 +116,20 @@ const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
||||
},
|
||||
{ icon: AlertTriangle, label: 'Emergencias', path: '/dashboard/admin?tab=emergency' },
|
||||
{ icon: MessageSquare, label: 'Soporte', path: '/dashboard/admin?tab=support' },
|
||||
{ icon: Settings, label: 'Configuración', path: '/dashboard/admin?tab=config' },
|
||||
{
|
||||
icon: Settings,
|
||||
label: 'Configuración',
|
||||
path: '/dashboard/config',
|
||||
subItems: [
|
||||
{ icon: Server, label: 'APIs', path: '/dashboard/config/apis' },
|
||||
{ icon: CreditCard, label: 'Pagos', path: '/dashboard/config/payments' },
|
||||
{ icon: Settings, label: 'Parámetros', path: '/dashboard/config/parameters' },
|
||||
{ icon: Radio, label: 'Integraciones', path: '/dashboard/config/integrations' },
|
||||
{ icon: FileText, label: 'Auditoría', path: '/dashboard/config/audit' },
|
||||
{ icon: Shield, label: 'Security Center', path: '/dashboard/config/security' },
|
||||
{ icon: Brain, label: 'Personalización', path: '/dashboard/config/personalization' }
|
||||
]
|
||||
},
|
||||
{ icon: BarChart3, label: 'Analytics', path: '/dashboard/analytics' },
|
||||
{ icon: Search, label: 'Buscar Lugares', path: '/dashboard/search-places' },
|
||||
{ icon: Store, label: 'Buscar Comercios', path: '/dashboard/search-establishments' }
|
||||
|
||||
109
src/pages/dashboard/config/APIs.tsx
Normal file
109
src/pages/dashboard/config/APIs.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Server, TestTube, Edit, X, RefreshCw } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSystemConfig } from '@/hooks/useSystemConfig';
|
||||
|
||||
const APIs = () => {
|
||||
const [editingApi, setEditingApi] = useState<string | null>(null);
|
||||
const {
|
||||
apiConfigs,
|
||||
loading,
|
||||
updateApiConfig,
|
||||
testApiConnection,
|
||||
} = useSystemConfig();
|
||||
|
||||
const handleApiEdit = (id: string, field: string, value: string) => {
|
||||
const config = apiConfigs.find(c => c.id === id);
|
||||
if (config) {
|
||||
updateApiConfig({ ...config, [field]: value });
|
||||
setEditingApi(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Configuración de APIs</h2>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Server className="w-5 h-5" />
|
||||
APIs Externas
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center p-8">
|
||||
<RefreshCw className="w-6 h-6 animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{apiConfigs.map((config) => (
|
||||
<div key={config.id} className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h4 className="font-medium">{config.name}</h4>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant={config.status === 'active' ? 'default' : 'secondary'}>
|
||||
{config.status}
|
||||
</Badge>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => testApiConnection(config.id)}
|
||||
>
|
||||
<TestTube className="w-4 h-4 mr-1" />
|
||||
Probar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<label className="font-medium">Endpoint:</label>
|
||||
{editingApi === config.id + '-endpoint' ? (
|
||||
<div className="flex gap-2 mt-1">
|
||||
<Input
|
||||
defaultValue={config.endpoint}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleApiEdit(config.id, 'endpoint', e.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" onClick={() => setEditingApi(null)}>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-gray-600">{config.endpoint}</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setEditingApi(config.id + '-endpoint')}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<label className="font-medium">Timeout:</label>
|
||||
<span className="text-gray-600 ml-2">{config.timeout}ms</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default APIs;
|
||||
15
src/pages/dashboard/config/Audit.tsx
Normal file
15
src/pages/dashboard/config/Audit.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import AuditLogs from '@/components/security/AuditLogs';
|
||||
|
||||
const Audit = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Registro de Auditoría</h2>
|
||||
</div>
|
||||
<AuditLogs />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Audit;
|
||||
88
src/pages/dashboard/config/Integrations.tsx
Normal file
88
src/pages/dashboard/config/Integrations.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import { Wifi, RefreshCw, CheckCircle, XCircle, Clock } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSystemConfig } from '@/hooks/useSystemConfig';
|
||||
|
||||
const Integrations = () => {
|
||||
const {
|
||||
integrations,
|
||||
loading,
|
||||
syncIntegration,
|
||||
} = useSystemConfig();
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
switch (status) {
|
||||
case 'connected':
|
||||
return <CheckCircle className="w-5 h-5 text-green-500" />;
|
||||
case 'error':
|
||||
return <XCircle className="w-5 h-5 text-red-500" />;
|
||||
default:
|
||||
return <Clock className="w-5 h-5 text-yellow-500" />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Integraciones</h2>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Wifi className="w-5 h-5" />
|
||||
Servicios Externos
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center p-8">
|
||||
<RefreshCw className="w-6 h-6 animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{integrations.map((integration) => (
|
||||
<div key={integration.id} className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
{getStatusIcon(integration.status)}
|
||||
<div>
|
||||
<h4 className="font-medium">{integration.name}</h4>
|
||||
<p className="text-sm text-gray-600">{integration.type}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge
|
||||
variant={integration.status === 'connected' ? 'default' :
|
||||
integration.status === 'error' ? 'destructive' : 'secondary'}
|
||||
>
|
||||
{integration.status}
|
||||
</Badge>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => syncIntegration(integration.id)}
|
||||
>
|
||||
<RefreshCw className="w-4 h-4 mr-1" />
|
||||
Sincronizar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{integration.lastSync && (
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
Última sincronización: {new Date(integration.lastSync).toLocaleString()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Integrations;
|
||||
95
src/pages/dashboard/config/Parameters.tsx
Normal file
95
src/pages/dashboard/config/Parameters.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Settings, Edit, X, RefreshCw } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSystemConfig } from '@/hooks/useSystemConfig';
|
||||
|
||||
const Parameters = () => {
|
||||
const [editingParam, setEditingParam] = useState<string | null>(null);
|
||||
const {
|
||||
systemParameters,
|
||||
loading,
|
||||
updateSystemParameter,
|
||||
} = useSystemConfig();
|
||||
|
||||
const handleParamEdit = (id: string, value: string) => {
|
||||
const param = systemParameters.find(p => p.id === id);
|
||||
if (param) {
|
||||
updateSystemParameter({ ...param, value });
|
||||
setEditingParam(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Parámetros del Sistema</h2>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Settings className="w-5 h-5" />
|
||||
Configuración Global
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center p-8">
|
||||
<RefreshCw className="w-6 h-6 animate-spin" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{systemParameters.map((param) => (
|
||||
<div key={param.id} className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div>
|
||||
<h4 className="font-medium">{param.key}</h4>
|
||||
<p className="text-sm text-gray-600">{param.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
{editingParam === param.id ? (
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
defaultValue={param.value}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleParamEdit(param.id, e.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" onClick={() => setEditingParam(null)}>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-gray-900 font-mono bg-gray-50 px-3 py-1 rounded">
|
||||
{param.value}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setEditingParam(param.id)}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-2 text-xs text-gray-500">
|
||||
Tipo: {param.type}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Parameters;
|
||||
133
src/pages/dashboard/config/Payments.tsx
Normal file
133
src/pages/dashboard/config/Payments.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import React, { useState } from 'react';
|
||||
import { CreditCard, TestTube, Edit, X, RefreshCw, Eye, EyeOff } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { useSystemConfig } from '@/hooks/useSystemConfig';
|
||||
|
||||
const Payments = () => {
|
||||
const [editingPayment, setEditingPayment] = useState<string | null>(null);
|
||||
const [showSecrets, setShowSecrets] = useState<Record<string, boolean>>({});
|
||||
const {
|
||||
paymentConfigs,
|
||||
loading,
|
||||
updatePaymentConfig,
|
||||
testPaymentConnection,
|
||||
} = useSystemConfig();
|
||||
|
||||
const handlePaymentEdit = (id: string, field: string, value: string | boolean) => {
|
||||
const config = paymentConfigs.find(c => c.id === id);
|
||||
if (config) {
|
||||
if (field === 'enabled' || field === 'testMode') {
|
||||
updatePaymentConfig({ ...config, [field]: value });
|
||||
} else {
|
||||
updatePaymentConfig({
|
||||
...config,
|
||||
credentials: { ...config.credentials, [field]: value as string }
|
||||
});
|
||||
}
|
||||
setEditingPayment(null);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleSecretVisibility = (key: string) => {
|
||||
setShowSecrets(prev => ({ ...prev, [key]: !prev[key] }));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Configuración de Medios de Pago</h2>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<CreditCard className="w-5 h-5" />
|
||||
Proveedores de Pago
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center p-8">
|
||||
<RefreshCw className="w-6 h-6 animate-spin" />
|
||||
</div>
|
||||
) : paymentConfigs.length === 0 ? (
|
||||
<div className="text-center p-8 bg-gray-50 rounded-lg">
|
||||
<CreditCard className="w-12 h-12 text-gray-400 mx-auto mb-4" />
|
||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
||||
No hay configuraciones de pago
|
||||
</h3>
|
||||
<p className="text-gray-600">
|
||||
Configure los proveedores de pago para comenzar a procesar transacciones
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
{paymentConfigs.map((config) => (
|
||||
<div key={config.id} className="border rounded-lg p-6">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h4 className="text-lg font-semibold">{config.name}</h4>
|
||||
<p className="text-sm text-gray-600">{config.provider.toUpperCase()}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant={config.status === 'active' ? 'default' : 'secondary'}>
|
||||
{config.status}
|
||||
</Badge>
|
||||
<Badge variant={config.testMode ? 'outline' : 'default'}>
|
||||
{config.testMode ? 'Test' : 'Producción'}
|
||||
</Badge>
|
||||
<Switch
|
||||
checked={config.enabled}
|
||||
onCheckedChange={(checked) =>
|
||||
handlePaymentEdit(config.id, 'enabled', checked)
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => testPaymentConnection(config.id)}
|
||||
>
|
||||
<TestTube className="w-4 h-4 mr-1" />
|
||||
Probar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{config.provider === 'stripe' && (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium">Publishable Key:</label>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-sm text-gray-600 truncate">
|
||||
{config.credentials.publishableKey || 'No configurado'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">Secret Key:</label>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-sm text-gray-600">
|
||||
{config.credentials.secretKey ? '••••••••••••' : 'No configurado'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Payments;
|
||||
24
src/pages/dashboard/config/PersonalizationPage.tsx
Normal file
24
src/pages/dashboard/config/PersonalizationPage.tsx
Normal file
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import AIRecommendations from '@/components/personalization/AIRecommendations';
|
||||
import UserPreferences from '@/components/personalization/UserPreferences';
|
||||
import BehaviorAnalytics from '@/components/personalization/BehaviorAnalytics';
|
||||
import SegmentManagement from '@/components/personalization/SegmentManagement';
|
||||
|
||||
const PersonalizationPage = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Personalización</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<AIRecommendations />
|
||||
<UserPreferences />
|
||||
<BehaviorAnalytics />
|
||||
<SegmentManagement />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PersonalizationPage;
|
||||
22
src/pages/dashboard/config/SecurityCenter.tsx
Normal file
22
src/pages/dashboard/config/SecurityCenter.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import React from 'react';
|
||||
import ThreatDetection from '@/components/security/ThreatDetection';
|
||||
import AccessControl from '@/components/security/AccessControl';
|
||||
import ComplianceMonitor from '@/components/security/ComplianceMonitor';
|
||||
|
||||
const SecurityCenter = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Centro de Seguridad</h2>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6">
|
||||
<ThreatDetection />
|
||||
<AccessControl />
|
||||
<ComplianceMonitor />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SecurityCenter;
|
||||
Reference in New Issue
Block a user