Add System Configuration section
This commit is contained in:
@@ -1,5 +1,12 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Settings, Cog, Database, Wifi, Shield } from 'lucide-react';
|
import { Settings, Cog, Database, Wifi, Shield, Server, Key, Users, Activity, RefreshCw, TestTube, Edit, Save, X } 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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
|
import { useSystemConfig } from '@/hooks/useSystemConfig';
|
||||||
|
|
||||||
interface ConfigTabProps {
|
interface ConfigTabProps {
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
@@ -7,6 +14,22 @@ interface ConfigTabProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ConfigTab: React.FC<ConfigTabProps> = ({ isSuperAdmin }) => {
|
const ConfigTab: React.FC<ConfigTabProps> = ({ isSuperAdmin }) => {
|
||||||
|
const [editingApi, setEditingApi] = useState<string | null>(null);
|
||||||
|
const [editingParam, setEditingParam] = useState<string | null>(null);
|
||||||
|
const {
|
||||||
|
apiConfigs,
|
||||||
|
systemParameters,
|
||||||
|
integrations,
|
||||||
|
securityConfig,
|
||||||
|
auditLogs,
|
||||||
|
loading,
|
||||||
|
updateApiConfig,
|
||||||
|
testApiConnection,
|
||||||
|
updateSystemParameter,
|
||||||
|
syncIntegration,
|
||||||
|
updateSecurityConfig,
|
||||||
|
} = useSystemConfig();
|
||||||
|
|
||||||
if (!isSuperAdmin) {
|
if (!isSuperAdmin) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white rounded-lg shadow p-8 text-center">
|
<div className="bg-white rounded-lg shadow p-8 text-center">
|
||||||
@@ -17,30 +40,328 @@ const ConfigTab: React.FC<ConfigTabProps> = ({ isSuperAdmin }) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleApiEdit = (id: string, field: string, value: string) => {
|
||||||
|
const config = apiConfigs.find(c => c.id === id);
|
||||||
|
if (config) {
|
||||||
|
updateApiConfig({ ...config, [field]: value });
|
||||||
|
setEditingApi(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleParamEdit = (id: string, value: string) => {
|
||||||
|
const param = systemParameters.find(p => p.id === id);
|
||||||
|
if (param) {
|
||||||
|
updateSystemParameter({ ...param, value });
|
||||||
|
setEditingParam(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<h2 className="text-xl font-semibold text-gray-900">Configuración del Sistema</h2>
|
<h2 className="text-xl font-semibold text-gray-900">Configuración del Sistema</h2>
|
||||||
|
|
||||||
<div className="bg-white rounded-lg shadow p-8 text-center">
|
<Tabs defaultValue="apis" className="w-full">
|
||||||
<Settings className="w-16 h-16 text-gray-400 mx-auto mb-4" />
|
<TabsList className="grid w-full grid-cols-5">
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-2">
|
<TabsTrigger value="apis" className="flex items-center gap-2">
|
||||||
Configuración del Sistema
|
<Server className="w-4 h-4" />
|
||||||
</h3>
|
APIs
|
||||||
<p className="text-gray-600 mb-4">
|
</TabsTrigger>
|
||||||
Esta sección está en desarrollo y se implementará según las especificaciones del informe.
|
<TabsTrigger value="parameters" className="flex items-center gap-2">
|
||||||
|
<Settings className="w-4 h-4" />
|
||||||
|
Parámetros
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="integrations" className="flex items-center gap-2">
|
||||||
|
<Wifi className="w-4 h-4" />
|
||||||
|
Integraciones
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="security" className="flex items-center gap-2">
|
||||||
|
<Shield className="w-4 h-4" />
|
||||||
|
Seguridad
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="audit" className="flex items-center gap-2">
|
||||||
|
<Activity className="w-4 h-4" />
|
||||||
|
Auditoría
|
||||||
|
</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
|
||||||
|
<TabsContent value="apis" className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Server className="w-5 h-5" />
|
||||||
|
Configuración de APIs
|
||||||
|
</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>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="parameters" className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Settings className="w-5 h-5" />
|
||||||
|
Parámetros del Sistema
|
||||||
|
</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>
|
||||||
|
<Badge variant="outline">{param.category}</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<label className="font-medium text-sm">Valor:</label>
|
||||||
|
{editingParam === param.id ? (
|
||||||
|
<div className="flex gap-2 flex-1">
|
||||||
|
<Input
|
||||||
|
type={param.type === 'number' ? 'number' : 'text'}
|
||||||
|
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 flex-1">
|
||||||
|
<span className="text-gray-600">{param.value}</span>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => setEditingParam(param.id)}
|
||||||
|
>
|
||||||
|
<Edit className="w-4 h-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="integrations" className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Wifi className="w-5 h-5" />
|
||||||
|
Gestión de Integraciones
|
||||||
|
</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 mb-2">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium">{integration.name}</h4>
|
||||||
|
<p className="text-sm text-gray-600">{integration.type}</p>
|
||||||
|
</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 && (
|
||||||
|
<p className="text-xs text-gray-500">
|
||||||
|
Última sincronización: {new Date(integration.lastSync).toLocaleString()}
|
||||||
</p>
|
</p>
|
||||||
<div className="text-sm text-gray-500">
|
)}
|
||||||
Funcionalidades pendientes:
|
</div>
|
||||||
<ul className="mt-2 space-y-1">
|
))}
|
||||||
<li>• Configuración de API</li>
|
</div>
|
||||||
<li>• Parámetros del sistema</li>
|
)}
|
||||||
<li>• Gestión de integrations</li>
|
</CardContent>
|
||||||
<li>• Configuración de seguridad</li>
|
</Card>
|
||||||
<li>• Logs de auditoría</li>
|
</TabsContent>
|
||||||
</ul>
|
|
||||||
|
<TabsContent value="security" className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Shield className="w-5 h-5" />
|
||||||
|
Configuración de Seguridad
|
||||||
|
</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">
|
||||||
|
{securityConfig.map((config) => (
|
||||||
|
<div key={config.id} className="border rounded-lg p-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium">{config.setting}</h4>
|
||||||
|
<p className="text-sm text-gray-600">{config.description}</p>
|
||||||
|
<Badge variant="outline" className="mt-1">{config.category}</Badge>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{typeof config.value === 'boolean' ? (
|
||||||
|
<Switch
|
||||||
|
checked={config.value}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
updateSecurityConfig({ ...config, value: checked })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span className="text-gray-600">{config.value}</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
<TabsContent value="audit" className="space-y-4">
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Activity className="w-5 h-5" />
|
||||||
|
Logs de Auditoría
|
||||||
|
</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-2">
|
||||||
|
{auditLogs.map((log) => (
|
||||||
|
<div key={log.id} className="border rounded-lg p-3">
|
||||||
|
<div className="flex items-center justify-between mb-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Badge variant={log.status === 'success' ? 'default' : 'destructive'}>
|
||||||
|
{log.status}
|
||||||
|
</Badge>
|
||||||
|
<span className="font-medium">{log.action}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
{new Date(log.timestamp).toLocaleString()}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm text-gray-600">
|
||||||
|
<p>Usuario: {log.user}</p>
|
||||||
|
<p>IP: {log.ip}</p>
|
||||||
|
<p>Detalles: {log.details}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
149
src/hooks/useSystemConfig.ts
Normal file
149
src/hooks/useSystemConfig.ts
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
import { configApi, ApiConfig, SystemParameter, Integration, SecurityConfig, AuditLog } from '@/services/configApi';
|
||||||
|
|
||||||
|
export const useSystemConfig = () => {
|
||||||
|
const [apiConfigs, setApiConfigs] = useState<ApiConfig[]>([]);
|
||||||
|
const [systemParameters, setSystemParameters] = useState<SystemParameter[]>([]);
|
||||||
|
const [integrations, setIntegrations] = useState<Integration[]>([]);
|
||||||
|
const [securityConfig, setSecurityConfig] = useState<SecurityConfig[]>([]);
|
||||||
|
const [auditLogs, setAuditLogs] = useState<AuditLog[]>([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
const [apis, parameters, integs, security, logs] = await Promise.all([
|
||||||
|
configApi.getApiConfigs(),
|
||||||
|
configApi.getSystemParameters(),
|
||||||
|
configApi.getIntegrations(),
|
||||||
|
configApi.getSecurityConfig(),
|
||||||
|
configApi.getAuditLogs(1, 20).then(result => result.logs),
|
||||||
|
]);
|
||||||
|
|
||||||
|
setApiConfigs(apis);
|
||||||
|
setSystemParameters(parameters);
|
||||||
|
setIntegrations(integs);
|
||||||
|
setSecurityConfig(security);
|
||||||
|
setAuditLogs(logs);
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : 'Error loading system configuration';
|
||||||
|
setError(message);
|
||||||
|
console.error('Error loading system config:', err);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateApiConfig = async (config: Partial<ApiConfig>) => {
|
||||||
|
try {
|
||||||
|
const updated = await configApi.updateApiConfig(config);
|
||||||
|
setApiConfigs(prev => prev.map(c => c.id === updated.id ? updated : c));
|
||||||
|
toast({
|
||||||
|
title: "Configuración Actualizada",
|
||||||
|
description: "La configuración de API se actualizó correctamente",
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "No se pudo actualizar la configuración de API",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const testApiConnection = async (configId: string) => {
|
||||||
|
try {
|
||||||
|
const success = await configApi.testApiConnection(configId);
|
||||||
|
toast({
|
||||||
|
title: success ? "Conexión Exitosa" : "Conexión Fallida",
|
||||||
|
description: success ? "La API responde correctamente" : "No se pudo conectar a la API",
|
||||||
|
variant: success ? "default" : "destructive",
|
||||||
|
});
|
||||||
|
return success;
|
||||||
|
} catch (err) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "Error al probar la conexión",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSystemParameter = async (parameter: Partial<SystemParameter>) => {
|
||||||
|
try {
|
||||||
|
const updated = await configApi.updateSystemParameter(parameter);
|
||||||
|
setSystemParameters(prev => prev.map(p => p.id === updated.id ? updated : p));
|
||||||
|
toast({
|
||||||
|
title: "Parámetro Actualizado",
|
||||||
|
description: "El parámetro del sistema se actualizó correctamente",
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "No se pudo actualizar el parámetro",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncIntegration = async (integrationId: string) => {
|
||||||
|
try {
|
||||||
|
await configApi.syncIntegration(integrationId);
|
||||||
|
toast({
|
||||||
|
title: "Sincronización Iniciada",
|
||||||
|
description: "La integración se está sincronizando",
|
||||||
|
});
|
||||||
|
// Reload integrations to get updated status
|
||||||
|
const updatedIntegrations = await configApi.getIntegrations();
|
||||||
|
setIntegrations(updatedIntegrations);
|
||||||
|
} catch (err) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "No se pudo sincronizar la integración",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateSecurityConfig = async (config: Partial<SecurityConfig>) => {
|
||||||
|
try {
|
||||||
|
const updated = await configApi.updateSecurityConfig(config);
|
||||||
|
setSecurityConfig(prev => prev.map(c => c.id === updated.id ? updated : c));
|
||||||
|
toast({
|
||||||
|
title: "Configuración de Seguridad Actualizada",
|
||||||
|
description: "Los ajustes de seguridad se actualizaron correctamente",
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: "No se pudo actualizar la configuración de seguridad",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
apiConfigs,
|
||||||
|
systemParameters,
|
||||||
|
integrations,
|
||||||
|
securityConfig,
|
||||||
|
auditLogs,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
refetch: loadData,
|
||||||
|
updateApiConfig,
|
||||||
|
testApiConnection,
|
||||||
|
updateSystemParameter,
|
||||||
|
syncIntegration,
|
||||||
|
updateSecurityConfig,
|
||||||
|
};
|
||||||
|
};
|
||||||
162
src/services/configApi.ts
Normal file
162
src/services/configApi.ts
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
import { API_BASE_URL } from './config';
|
||||||
|
|
||||||
|
export interface ApiConfig {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
endpoint: string;
|
||||||
|
apiKey: string;
|
||||||
|
status: 'active' | 'inactive';
|
||||||
|
timeout: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SystemParameter {
|
||||||
|
id: string;
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
type: 'string' | 'number' | 'boolean' | 'json';
|
||||||
|
description: string;
|
||||||
|
category: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Integration {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
status: 'connected' | 'disconnected' | 'error';
|
||||||
|
config: Record<string, any>;
|
||||||
|
lastSync: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SecurityConfig {
|
||||||
|
id: string;
|
||||||
|
category: string;
|
||||||
|
setting: string;
|
||||||
|
value: boolean | string | number;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuditLog {
|
||||||
|
id: string;
|
||||||
|
action: string;
|
||||||
|
user: string;
|
||||||
|
timestamp: string;
|
||||||
|
details: string;
|
||||||
|
ip: string;
|
||||||
|
status: 'success' | 'failed';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const configApi = {
|
||||||
|
// API Configuration
|
||||||
|
async getApiConfigs(): Promise<ApiConfig[]> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/apis`);
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch API configs');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching API configs:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateApiConfig(config: Partial<ApiConfig>): Promise<ApiConfig> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/apis/${config.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(config),
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to update API config');
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
|
||||||
|
async testApiConnection(configId: string): Promise<boolean> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/apis/${configId}/test`, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
return response.ok;
|
||||||
|
},
|
||||||
|
|
||||||
|
// System Parameters
|
||||||
|
async getSystemParameters(): Promise<SystemParameter[]> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/parameters`);
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch system parameters');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching system parameters:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateSystemParameter(parameter: Partial<SystemParameter>): Promise<SystemParameter> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/parameters/${parameter.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(parameter),
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to update system parameter');
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Integrations
|
||||||
|
async getIntegrations(): Promise<Integration[]> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/integrations`);
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch integrations');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching integrations:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async syncIntegration(integrationId: string): Promise<void> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/integrations/${integrationId}/sync`, {
|
||||||
|
method: 'POST',
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to sync integration');
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateIntegration(integration: Partial<Integration>): Promise<Integration> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/integrations/${integration.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(integration),
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to update integration');
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Security Configuration
|
||||||
|
async getSecurityConfig(): Promise<SecurityConfig[]> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/security`);
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch security config');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching security config:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateSecurityConfig(config: Partial<SecurityConfig>): Promise<SecurityConfig> {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/security/${config.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(config),
|
||||||
|
});
|
||||||
|
if (!response.ok) throw new Error('Failed to update security config');
|
||||||
|
return await response.json();
|
||||||
|
},
|
||||||
|
|
||||||
|
// Audit Logs
|
||||||
|
async getAuditLogs(page = 1, limit = 50): Promise<{ logs: AuditLog[], total: number }> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/config/audit-logs?page=${page}&limit=${limit}`);
|
||||||
|
if (!response.ok) throw new Error('Failed to fetch audit logs');
|
||||||
|
return await response.json();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching audit logs:', error);
|
||||||
|
return { logs: [], total: 0 };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user