Refactor: Integrate Stripe and complete configuration

This commit is contained in:
gpt-engineer-app[bot]
2025-10-10 22:51:26 +00:00
parent ef888052e2
commit 908b09a1b1
6 changed files with 541 additions and 9 deletions

View File

@@ -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>