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 InvoiceDetail from "./pages/dashboard/InvoiceDetail";
|
||||||
import HotelManagement from "./pages/dashboard/HotelManagement";
|
import HotelManagement from "./pages/dashboard/HotelManagement";
|
||||||
import RestaurantPOS from "./pages/dashboard/RestaurantPOS";
|
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 VehicleManagement from "./pages/dashboard/VehicleManagement";
|
||||||
import Sustainability from "./pages/dashboard/Sustainability";
|
import Sustainability from "./pages/dashboard/Sustainability";
|
||||||
import Establishments from "./pages/dashboard/Establishments";
|
import Establishments from "./pages/dashboard/Establishments";
|
||||||
import Analytics from "./pages/dashboard/Analytics";
|
import Analytics from "./pages/dashboard/Analytics";
|
||||||
import SearchPlaces from "./pages/dashboard/SearchPlaces";
|
import SearchPlaces from "./pages/dashboard/SearchPlaces";
|
||||||
import SearchEstablishments from "./pages/dashboard/SearchEstablishments";
|
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)
|
// Commerce pages (for retail stores)
|
||||||
import CommerceStore from "./pages/dashboard/commerce/Store";
|
import CommerceStore from "./pages/dashboard/commerce/Store";
|
||||||
import CommercePOS from "./pages/dashboard/commerce/POSTerminal";
|
import CommercePOS from "./pages/dashboard/commerce/POSTerminal";
|
||||||
@@ -507,6 +513,63 @@ const AppRouter = () => (
|
|||||||
</ProtectedRoute>
|
</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 */}
|
{/* Catch-all route */}
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ import {
|
|||||||
Radio,
|
Radio,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Leaf,
|
Leaf,
|
||||||
Store
|
Store,
|
||||||
|
Server
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|
||||||
const DashboardLayout = ({ children }: { children: React.ReactNode }) => {
|
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: AlertTriangle, label: 'Emergencias', path: '/dashboard/admin?tab=emergency' },
|
||||||
{ icon: MessageSquare, label: 'Soporte', path: '/dashboard/admin?tab=support' },
|
{ 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: BarChart3, label: 'Analytics', path: '/dashboard/analytics' },
|
||||||
{ icon: Search, label: 'Buscar Lugares', path: '/dashboard/search-places' },
|
{ icon: Search, label: 'Buscar Lugares', path: '/dashboard/search-places' },
|
||||||
{ icon: Store, label: 'Buscar Comercios', path: '/dashboard/search-establishments' }
|
{ 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