Refactor: Integrate Stripe and complete configuration
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Settings, Cog, Database, Wifi, Shield, Server, Key, Users, Activity, RefreshCw, TestTube, Edit, Save, X } from 'lucide-react';
|
||||
import { Settings, Cog, Database, Wifi, Shield, Server, Key, Users, Activity, RefreshCw, TestTube, Edit, Save, X, CreditCard, Eye, EyeOff } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
@@ -16,18 +16,23 @@ interface ConfigTabProps {
|
||||
const ConfigTab: React.FC<ConfigTabProps> = ({ isSuperAdmin }) => {
|
||||
const [editingApi, setEditingApi] = useState<string | null>(null);
|
||||
const [editingParam, setEditingParam] = useState<string | null>(null);
|
||||
const [editingPayment, setEditingPayment] = useState<string | null>(null);
|
||||
const [showSecrets, setShowSecrets] = useState<Record<string, boolean>>({});
|
||||
const {
|
||||
apiConfigs,
|
||||
systemParameters,
|
||||
integrations,
|
||||
securityConfig,
|
||||
auditLogs,
|
||||
paymentConfigs,
|
||||
loading,
|
||||
updateApiConfig,
|
||||
testApiConnection,
|
||||
updateSystemParameter,
|
||||
syncIntegration,
|
||||
updateSecurityConfig,
|
||||
updatePaymentConfig,
|
||||
testPaymentConnection,
|
||||
} = useSystemConfig();
|
||||
|
||||
if (!isSuperAdmin) {
|
||||
@@ -56,16 +61,39 @@ const ConfigTab: React.FC<ConfigTabProps> = ({ isSuperAdmin }) => {
|
||||
}
|
||||
};
|
||||
|
||||
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">
|
||||
<h2 className="text-xl font-semibold text-gray-900">Configuración del Sistema</h2>
|
||||
|
||||
<Tabs defaultValue="apis" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-5">
|
||||
<TabsList className="grid w-full grid-cols-6">
|
||||
<TabsTrigger value="apis" className="flex items-center gap-2">
|
||||
<Server className="w-4 h-4" />
|
||||
APIs
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="payments" className="flex items-center gap-2">
|
||||
<CreditCard className="w-4 h-4" />
|
||||
Pagos
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="parameters" className="flex items-center gap-2">
|
||||
<Settings className="w-4 h-4" />
|
||||
Parámetros
|
||||
@@ -160,6 +188,220 @@ const ConfigTab: React.FC<ConfigTabProps> = ({ isSuperAdmin }) => {
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="payments" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<CreditCard className="w-5 h-5" />
|
||||
Configuración de Medios de Pago
|
||||
</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-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>
|
||||
{editingPayment === config.id + '-publishableKey' ? (
|
||||
<div className="flex gap-2 mt-1">
|
||||
<Input
|
||||
defaultValue={config.credentials.publishableKey || ''}
|
||||
placeholder="pk_test_..."
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handlePaymentEdit(config.id, 'publishableKey', e.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button size="sm" onClick={() => setEditingPayment(null)}>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-sm text-gray-600 truncate">
|
||||
{config.credentials.publishableKey || 'No configurado'}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setEditingPayment(config.id + '-publishableKey')}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">Secret Key:</label>
|
||||
{editingPayment === config.id + '-secretKey' ? (
|
||||
<div className="flex gap-2 mt-1">
|
||||
<Input
|
||||
type={showSecrets[config.id + '-secretKey'] ? 'text' : 'password'}
|
||||
defaultValue={config.credentials.secretKey || ''}
|
||||
placeholder="sk_test_..."
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handlePaymentEdit(config.id, 'secretKey', e.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => toggleSecretVisibility(config.id + '-secretKey')}
|
||||
>
|
||||
{showSecrets[config.id + '-secretKey'] ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => setEditingPayment(null)}>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-sm text-gray-600">
|
||||
{config.credentials.secretKey ? '••••••••••••' : 'No configurado'}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setEditingPayment(config.id + '-secretKey')}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="text-sm font-medium">Webhook Secret:</label>
|
||||
{editingPayment === config.id + '-webhookSecret' ? (
|
||||
<div className="flex gap-2 mt-1">
|
||||
<Input
|
||||
type={showSecrets[config.id + '-webhookSecret'] ? 'text' : 'password'}
|
||||
defaultValue={config.credentials.webhookSecret || ''}
|
||||
placeholder="whsec_..."
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handlePaymentEdit(config.id, 'webhookSecret', e.currentTarget.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => toggleSecretVisibility(config.id + '-webhookSecret')}
|
||||
>
|
||||
{showSecrets[config.id + '-webhookSecret'] ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />}
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => setEditingPayment(null)}>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
<span className="text-sm text-gray-600">
|
||||
{config.credentials.webhookSecret ? '••••••••••••' : 'No configurado'}
|
||||
</span>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setEditingPayment(config.id + '-webhookSecret')}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Switch
|
||||
checked={config.testMode}
|
||||
onCheckedChange={(checked) =>
|
||||
handlePaymentEdit(config.id, 'testMode', checked)
|
||||
}
|
||||
/>
|
||||
<span className="text-sm font-medium">Modo Test</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{config.provider === 'paypal' && (
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="text-sm font-medium">Client ID:</label>
|
||||
<Input
|
||||
defaultValue={config.credentials.clientId || ''}
|
||||
placeholder="Client ID de PayPal"
|
||||
onBlur={(e) => handlePaymentEdit(config.id, 'clientId', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium">Client Secret:</label>
|
||||
<Input
|
||||
type="password"
|
||||
defaultValue={config.credentials.clientSecret || ''}
|
||||
placeholder="Client Secret de PayPal"
|
||||
onBlur={(e) => handlePaymentEdit(config.id, 'clientSecret', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{config.lastTested && (
|
||||
<p className="text-xs text-gray-500">
|
||||
Última prueba: {new Date(config.lastTested).toLocaleString()}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="parameters" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
|
||||
Reference in New Issue
Block a user