Files
WhatsAppCentralizado/frontend/src/pages/OdooConfig.tsx
Claude AI 63d4409c00 feat(frontend): add Odoo configuration page
Add OdooConfig page component with form for Odoo connection settings
(URL, database, username, API key) and test connection functionality.
Integrate into main navigation with ApiOutlined icon.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 22:22:12 +00:00

183 lines
4.7 KiB
TypeScript

import { useState, useEffect } from 'react';
import {
Card,
Form,
Input,
Button,
Space,
Typography,
Alert,
Spin,
Tag,
message,
} from 'antd';
import {
LinkOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
ReloadOutlined,
} from '@ant-design/icons';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { apiClient } from '../api/client';
const { Title } = Typography;
interface OdooConfig {
url: string;
database: string;
username: string;
is_connected: boolean;
}
interface OdooConfigUpdate {
url: string;
database: string;
username: string;
api_key?: string;
}
export default function OdooConfig() {
const [form] = Form.useForm();
const queryClient = useQueryClient();
const [testStatus, setTestStatus] = useState<'idle' | 'testing' | 'success' | 'error'>('idle');
const { data: config, isLoading } = useQuery({
queryKey: ['odoo-config'],
queryFn: () => apiClient.get<OdooConfig>('/api/integrations/odoo/config'),
});
useEffect(() => {
if (config) {
form.setFieldsValue({
url: config.url,
database: config.database,
username: config.username,
});
}
}, [config, form]);
const saveMutation = useMutation({
mutationFn: (data: OdooConfigUpdate) =>
apiClient.put('/api/integrations/odoo/config', data),
onSuccess: () => {
message.success('Configuracion guardada');
queryClient.invalidateQueries({ queryKey: ['odoo-config'] });
},
onError: () => {
message.error('Error al guardar');
},
});
const testMutation = useMutation({
mutationFn: () => apiClient.post('/api/integrations/odoo/test', {}),
onSuccess: () => {
setTestStatus('success');
message.success('Conexion exitosa');
queryClient.invalidateQueries({ queryKey: ['odoo-config'] });
},
onError: () => {
setTestStatus('error');
message.error('Error de conexion');
},
});
const handleTest = () => {
setTestStatus('testing');
testMutation.mutate();
};
const handleSave = async () => {
const values = await form.validateFields();
saveMutation.mutate(values);
};
if (isLoading) {
return <Spin />;
}
return (
<div>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 24 }}>
<Title level={4} style={{ margin: 0 }}>Configuracion Odoo</Title>
<Space>
{config?.is_connected ? (
<Tag icon={<CheckCircleOutlined />} color="success">Conectado</Tag>
) : (
<Tag icon={<CloseCircleOutlined />} color="error">Desconectado</Tag>
)}
</Space>
</div>
<Card>
<Form form={form} layout="vertical" style={{ maxWidth: 500 }}>
<Form.Item
name="url"
label="URL de Odoo"
rules={[{ required: true, message: 'Ingrese la URL' }]}
>
<Input
prefix={<LinkOutlined />}
placeholder="https://tu-empresa.odoo.com"
/>
</Form.Item>
<Form.Item
name="database"
label="Base de Datos"
rules={[{ required: true, message: 'Ingrese el nombre de la base de datos' }]}
>
<Input placeholder="nombre_bd" />
</Form.Item>
<Form.Item
name="username"
label="Usuario (Email)"
rules={[{ required: true, message: 'Ingrese el usuario' }]}
>
<Input placeholder="usuario@empresa.com" />
</Form.Item>
<Form.Item
name="api_key"
label="API Key"
extra="Dejar vacio para mantener la actual"
>
<Input.Password placeholder="Nueva API Key (opcional)" />
</Form.Item>
<Alert
message="Como obtener la API Key"
description={
<ol style={{ paddingLeft: 20, margin: 0 }}>
<li>Inicia sesion en Odoo</li>
<li>Ve a Ajustes - Usuarios</li>
<li>Selecciona tu usuario</li>
<li>En la pestana Preferencias, genera una API Key</li>
</ol>
}
type="info"
style={{ marginBottom: 24 }}
/>
<Space>
<Button
type="primary"
onClick={handleSave}
loading={saveMutation.isPending}
>
Guardar
</Button>
<Button
icon={<ReloadOutlined spin={testStatus === 'testing'} />}
onClick={handleTest}
loading={testMutation.isPending}
>
Probar Conexion
</Button>
</Space>
</Form>
</Card>
</div>
);
}