> ## Documentation Index
> Fetch the complete documentation index at: https://omniloy.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> Integración de SofIA SDK con React

Esta guía muestra cómo integrar SofIA SDK en aplicaciones React usando el componente `<Omniscribe>`.

## Configuración inicial

### 1. Instalación

```bash theme={null}
npm install @omniloy/sofia-sdk
```

### 2. Importación en su aplicación

```typescript theme={null}
import { Omniscribe, LanguageCode } from '@omniloy/sofia-sdk/react';
import '@omniloy/sofia-sdk/react/index.css';
```

## Componente React básico

### Implementación con hooks

```tsx theme={null}
import { useCallback, useState } from 'react';
import { Omniscribe, LanguageCode } from '@omniloy/sofia-sdk/react';
import '@omniloy/sofia-sdk/react/index.css';

const SofiaComponent = () => {
  const [isOpen, setIsOpen] = useState(true);
  const [lastReport, setLastReport] = useState<unknown>(null);
  const [getLastReportFn, setGetLastReportFn] = useState<(() => Promise<unknown>) | null>(null);

  const handleReport = useCallback((report: unknown) => {
    console.log('Reporte recibido:', report);
    setLastReport(report);
  }, []);

  const handleSetGetLastReport = useCallback((fn: () => Promise<unknown>) => {
    setGetLastReportFn(() => fn);
  }, []);

  const template = {
    title: 'clinical_notes',
    type: 'object',
    properties: {
      diagnosis: {
        type: 'string',
        description: 'Diagnóstico principal',
        source: 'ICD10',
      },
      treatment_plan: {
        type: 'string',
        description: 'Plan de tratamiento discutido durante la consulta',
      },
    },
  };

  return (
    <div>
      <Omniscribe
        apikey="your-api-key"
        userid="user_12345"
        patientid="patient_67890"
        templateid="clinical-notes-v1"
        template={template}
        isopen={isOpen}
        setIsOpen={setIsOpen}
        handleReport={handleReport}
        setGetLastReport={handleSetGetLastReport}
        language={LanguageCode.es}
      />

      <button onClick={() => setIsOpen((prev) => !prev)}>
        {isOpen ? 'Cerrar SofIA' : 'Abrir SofIA'}
      </button>

      {lastReport !== null && (
        <pre>{JSON.stringify(lastReport, null, 2)}</pre>
      )}
    </div>
  );
};

export default SofiaComponent;
```

## Props

### Requeridas

| Prop        | Tipo   | Descripción                                                                        |
| ----------- | ------ | ---------------------------------------------------------------------------------- |
| `apikey`    | string | Clave de autenticación de Omniloy                                                  |
| `userid`    | string | Identificador del usuario en el sistema EHR/HIS (id interno subrogado, nunca PII)  |
| `patientid` | string | Identificador del paciente en el sistema EHR/HIS (id interno subrogado, nunca PII) |

### Generación de reportes (requeridas en conjunto)

| Prop         | Tipo   | Descripción                                                                 |
| ------------ | ------ | --------------------------------------------------------------------------- |
| `template`   | object | Objeto de template para la generación de reportes                           |
| `templateid` | string | Identificador del template (ambos `template` y `templateid` son necesarios) |

### Opcionales

| Prop          | Tipo         | Descripción                                                                                                   |
| ------------- | ------------ | ------------------------------------------------------------------------------------------------------------- |
| `baseurl`     | string       | Endpoint de la API de SofIA. Opcional para las claves más nuevas (autorresuelto); requerido en caso contrario |
| `isopen`      | boolean      | Mostrar/ocultar componente                                                                                    |
| `language`    | LanguageCode | Idioma de la interfaz (e.g., `LanguageCode.es`)                                                               |
| `debug`       | boolean      | Habilitar logging de depuración                                                                               |
| `patientdata` | object       | Información del paciente (ver estructura más abajo)                                                           |

### Callbacks

| Prop               | Firma                                                    | Descripción                                      |
| ------------------ | -------------------------------------------------------- | ------------------------------------------------ |
| `handleReport`     | `(report: unknown) => void`                              | Recibe reportes generados                        |
| `setIsOpen`        | `(value: boolean \| (prev: boolean) => boolean) => void` | Controla visibilidad del widget                  |
| `setGetLastReport` | `(fn: () => Promise<unknown>) => void`                   | Expone función async para obtener último reporte |

## Obtener el último reporte

El callback `setGetLastReport` recibe una función async que puede almacenar y llamar después:

```tsx theme={null}
const [getLastReportFn, setGetLastReportFn] = useState<(() => Promise<unknown>) | null>(null);

const handleSetGetLastReport = useCallback((fn: () => Promise<unknown>) => {
  setGetLastReportFn(() => fn);
}, []);

const fetchLastReport = async () => {
  if (getLastReportFn) {
    const report = await getLastReportFn();
    console.log('Último reporte:', report);
  }
};

// En JSX:
<Omniscribe
  setGetLastReport={handleSetGetLastReport}
  // ...otras props
/>

<button onClick={fetchLastReport} disabled={!getLastReportFn}>
  Obtener último reporte
</button>
```

<Tip>
  Pase `debug={true}` al componente `<Omniscribe>` para habilitar logging detallado en consola. Esto es útil durante el desarrollo para rastrear eventos del ciclo de vida del SDK, estado de conexión y validación de configuración.

  ```tsx theme={null}
  <Omniscribe debug={true} ... />
  ```
</Tip>

## Actualización de datos del paciente

Use el estado de React para actualizar dinámicamente el contexto del paciente. El SDK se reinicializa automáticamente cuando `patientid` o `userid` cambian.

```tsx theme={null}
const [patientId, setPatientId] = useState('patient_67890');
const [patientData, setPatientData] = useState({
  fullName: 'Jane Doe',
  birthDate: '15-03-1985',
  phone: '+1 555-987-6543',
  address: '456 Oak Ave, Example City, USA',
  extraData: {
    medical_practice: 'Internal Medicine',
    allergies: 'penicillin',
  },
});

// En JSX:
<Omniscribe
  patientid={patientId}
  patientdata={patientData}
  // ...otras props
/>

// Para cambiar de paciente:
function switchPatient(newId: string, newData: typeof patientData) {
  setPatientId(newId);
  setPatientData(newData);
}
```

### Estructura de datos del paciente

```typescript theme={null}
type TPatientData = {
  fullName: string | undefined;
  birthDate: string | undefined;
  phone: string | undefined;
  address: string | undefined;
  extraData: Record<string, unknown> | undefined;
  signedConsent?: { signed: boolean; date: string };
};
```

#### Campos de `extraData`

| Campo                     | Descripción                                                                                                   |
| ------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `medical_practice`        | Especialidad del médico (e.g., `"Cardiology"`, `"Pediatrics"`). Ayuda a SofIA a contextualizar las respuestas |
| `patient_medical_notes`   | Notas de consultas previas. Incluya campos `url` para que SofIA pueda citar la fuente                         |
| *(campos personalizados)* | Cualquier dato adicional relevante para la consulta (e.g., `allergies`, `medications`)                        |

#### Ejemplo completo

```typescript theme={null}
const patientData = {
  fullName: 'John Doe',
  birthDate: '01/15/1980',
  phone: '+1 555-123-4567',
  address: '123 Main St, Example City, USA',
  extraData: {
    medical_practice: 'Cardiology',
    patient_medical_notes: [
      {
        date: '2024-11-20',
        note: 'Patient presents with chest pain. ECG normal. Prescribed aspirin.',
        url: 'https://ehr.example.com/notes/12345',
      },
      {
        date: '2025-01-10',
        note: 'Follow-up visit. Chest pain resolved. Blood pressure 130/85.',
      },
    ],
    allergies: 'pollen, penicillin',
    medications: 'metformin, insulin, aspirin',
  },
  signedConsent: {
    signed: true,
    date: '2025-03-01',
  },
};
```

Consulta [Datos del paciente](/sofia/es/sdk/patient-data) para la referencia completa de la estructura de datos.

## Gestión de estado con context

Para aplicaciones que necesitan compartir el estado de SofIA entre múltiples componentes:

```tsx theme={null}
import { createContext, useContext, useState, useCallback, type ReactNode } from 'react';

interface SofiaContextType {
  currentPatient: string | null;
  setCurrentPatient: (patientId: string) => void;
  reports: unknown[];
  addReport: (report: unknown) => void;
}

const SofiaContext = createContext<SofiaContextType | undefined>(undefined);

export const SofiaProvider = ({ children }: { children: ReactNode }) => {
  const [currentPatient, setCurrentPatient] = useState<string | null>(null);
  const [reports, setReports] = useState<unknown[]>([]);

  const addReport = useCallback((report: unknown) => {
    setReports((prev) => [...prev, report]);
  }, []);

  return (
    <SofiaContext.Provider value={{ currentPatient, setCurrentPatient, reports, addReport }}>
      {children}
    </SofiaContext.Provider>
  );
};

export const useSofiaContext = () => {
  const context = useContext(SofiaContext);
  if (!context) throw new Error('useSofiaContext must be used within SofiaProvider');
  return context;
};
```

## Ejemplo completo

Para ver una implementación completa con React y TypeScript, consulte nuestro repositorio de ejemplos:

**[Ver ejemplo completo de React](https://github.com/Omniloy/sofia-sdk-examples/tree/main/examples/react)**

El ejemplo incluye:

* Configuración completa con Vite + React 19 + TypeScript
* Consola de desarrollo con controles en tiempo real
* Editores de template y datos del paciente con validación JSON
* Cambio dinámico de User ID, Patient ID y Template ID

## Mejores prácticas

### Rendimiento

* Use `useCallback` para callbacks estables (`handleReport`, `setGetLastReport`)
* Implemente lazy loading para el componente en rutas no críticas

### Estado

* Pase `setIsOpen` directamente — el SDK controla la visibilidad
* Cambiar `userid` o `patientid` remonta el SDK automáticamente

### Error boundaries

```tsx theme={null}
import { Component, type ReactNode, type ErrorInfo } from 'react';

class SofiaErrorBoundary extends Component<
  { children: ReactNode },
  { hasError: boolean }
> {
  constructor(props: { children: ReactNode }) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    console.error('Error in SofIA SDK:', error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return <div>SofIA encountered an error. Check the browser console for details.</div>;
    }
    return this.props.children;
  }
}
```

Para la lista completa de mensajes de error y sus pasos de resolución, consulta la [Referencia de errores](/sofia/es/sdk/error-reference).

<Warning>
  Nunca exponga su `apikey` en el código del cliente en producción. Use un proxy en el backend para inyectar la API key del lado del servidor. Consulte la [guía de instalación](/sofia/es/sdk/installation#production-security) para más detalles.
</Warning>

## Próximos pasos

1. [Integración con JavaScript](/sofia/es/sdk/vanilla)
2. [Integración con Angular](/sofia/es/sdk/angular)
3. [Referencia de propiedades requeridas](/sofia/es/sdk/required-properties)
4. [Referencia de propiedades opcionales](/sofia/es/sdk/optional-properties)
