> ## 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.

# Angular

> Integración de SofIA SDK con Angular

Esta guía detalla cómo integrar SofIA SDK en aplicaciones Angular, incluyendo la configuración necesaria para el uso de Web Components y la implementación de callbacks.

## Configuración inicial

### 1. Instalación del paquete

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

### 2. Configuración del módulo

Angular requiere configuración específica para soportar Web Components:

```typescript theme={null}
// app.module.ts
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';

// Importar SofIA SDK
import '@omniloy/sofia-sdk';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA] // Requerido para Web Components
})
export class AppModule { }
```

### 3. Configuración en standalone components (Angular 15+)

Para aplicaciones que usan Standalone Components:

```typescript theme={null}
// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import '@omniloy/sofia-sdk';

bootstrapApplication(AppComponent, {
  providers: []
});
```

```typescript theme={null}
// app.component.ts
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@Component({
  selector: 'app-root',
  standalone: true,
  templateUrl: './app.component.html',
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppComponent {
  // Implementación del componente
}
```

## Implementación del componente

### Template HTML

```html theme={null}
<!-- app.component.html -->
<sofia-sdk
  #sofia
  [attr.apikey]="apiKey"
  [attr.userid]="userId"
  [attr.patientid]="patientId"
  [attr.templateid]="templateId"
  [attr.template]="templateJson"
  [attr.isopen]="isOpen ? 'true' : 'false'"
  language="es">
</sofia-sdk>
```

<Info>
  SofIA SDK entrega los datos a tu aplicación mediante **callbacks de propiedad** — asigna `handleReport`, `setIsOpen` y `setGetLastReport` como propiedades del elemento (via `@ViewChild` en `ngAfterViewInit`, como se muestra abajo). El SDK **no** emite eventos DOM `handle-report`/`set-is-open`, por lo que los event bindings de Angular como `(handle-report)` no se dispararán. (El único evento DOM que el SDK emite es `sofia:transcriber-url-changed`.)
</Info>

### Componente TypeScript

```typescript theme={null}
// app.component.ts
import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements AfterViewInit {
  @ViewChild('sofia') sofia!: ElementRef;

  apiKey = 'your-api-key'; // endpoint autorresuelto para las claves más nuevas (baseurl solo si tu clave lo requiere)
  userId = 'user_12345';
  patientId = 'patient_67890';
  templateId = 'clinical-notes-v1';
  isOpen = false;

  // Esquema JSON para configuración clínica
  templateJson = JSON.stringify({
    "$schema": "http://json-schema.org/draft-07/schema#",
    "title": "clinical_notes",
    "type": "object",
    "properties": {
      "diagnosis": {
        "type": "string",
        "description": "Diagnóstico principal"
      }
    },
    "required": ["diagnosis"]
  });

  // Asigna los callbacks como PROPIEDADES del elemento una vez montado el web component
  ngAfterViewInit() {
    const el = this.sofia.nativeElement as any;

    // Se llama cuando se genera un reporte
    el.handleReport = (report: any) => {
      console.log('Reporte recibido:', report);
      // Procesa el reporte generado — por ejemplo, envíalo a tu backend o EHR
    };

    // Se llama cuando cambia el estado de apertura
    el.setIsOpen = (isOpen: boolean) => {
      this.isOpen = isOpen;
      console.log('Estado de apertura:', isOpen);
    };
  }

  // Método para abrir/cerrar programáticamente
  toggleSofIA() {
    this.isOpen = !this.isOpen;
  }
}
```

## Obtener el último reporte

Use el callback `set-get-last-report` para acceder al último reporte generado para la sesión actual de paciente/usuario:

```typescript theme={null}
ngAfterViewInit() {
  const sofiaElement = document.querySelector('sofia-sdk') as any;
  if (sofiaElement) {
    sofiaElement.setGetLastReport = (getLastReportFn: () => Promise<object | undefined>) => {
      this.getLastReport = getLastReportFn;
    };
  }
}

// Después, obtener el último reporte
async fetchLastReport() {
  const report = await this.getLastReport?.();
  if (report) {
    console.log('Último reporte:', report);
  }
}
```

<Tip>
  Añada `debug="true"` al elemento `<sofia-sdk>` 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.

  ```html theme={null}
  <sofia-sdk debug="true" ...></sofia-sdk>
  ```
</Tip>

## Actualización de datos del paciente

Actualice el contexto del paciente dinámicamente cambiando las propiedades vinculadas:

```typescript theme={null}
// Actualizar paciente para nueva consulta
updatePatient(newId: string, data: object) {
  this.patientId = newId;
  this.patientData = JSON.stringify(data);
}
```

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

## Configuración avanzada

### Interfaces TypeScript

Para mejor tipado, puede definir interfaces para los datos:

```typescript theme={null}
// interfaces/sofia.interface.ts
export interface SofiaReport {
  diagnosis: string;
  timestamp: string;
  userId: string;
  patientId: string;
}

export interface PatientData {
  fullName?: string;
  birthDate?: string;
  phone?: string;
  address?: string;
  extraData?: Record<string, unknown>;
}
```

### Servicio Angular

Puede encapsular la lógica de SofIA en un servicio:

```typescript theme={null}
// services/sofia.service.ts
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { SofiaReport } from '../interfaces/sofia.interface';

@Injectable({
  providedIn: 'root'
})
export class SofiaService {
  private reportSubject = new BehaviorSubject<SofiaReport | null>(null);
  private isOpenSubject = new BehaviorSubject<boolean>(false);

  // Observables públicos
  report$: Observable<SofiaReport | null> = this.reportSubject.asObservable();
  isOpen$: Observable<boolean> = this.isOpenSubject.asObservable();

  // Métodos para manejar reportes
  handleReport(report: SofiaReport) {
    this.reportSubject.next(report);
    // Aquí puedes añadir lógica adicional como guardar en localStorage
    // o enviar al servidor
  }

  // Métodos para manejar estado
  setIsOpen(isOpen: boolean) {
    this.isOpenSubject.next(isOpen);
  }

  // Método para generar template dinámicamente
  generateTemplate(specialty: string): string {
    const baseSchema = {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "title": `${specialty} Consultation`,
      "type": "object"
    };

    // Personalizar según especialidad
    switch(specialty) {
      case 'cardiology':
        return JSON.stringify({
          ...baseSchema,
          "properties": {
            "diagnosis": { "type": "string" },
            "heart_rate": { "type": "number" },
            "blood_pressure": { "type": "string" }
          }
        });
      case 'dermatology':
        return JSON.stringify({
          ...baseSchema,
          "properties": {
            "diagnosis": { "type": "string" },
            "lesion_type": { "type": "string" },
            "location": { "type": "string" }
          }
        });
      default:
        return JSON.stringify({
          ...baseSchema,
          "properties": {
            "diagnosis": { "type": "string" },
            "treatment": { "type": "string" }
          }
        });
    }
  }
}
```

### Uso del servicio en componente

```typescript theme={null}
// app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { SofiaService } from './services/sofia.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
  private destroy$ = new Subject<void>();

  userId = 'user_12345';
  patientId = 'patient_67890';
  templateId = 'clinical-notes-v1';
  isOpen = false;
  templateJson = '';

  constructor(private sofiaService: SofiaService) {}

  ngOnInit() {
    // Suscribirse a cambios de estado
    this.sofiaService.isOpen$
      .pipe(takeUntil(this.destroy$))
      .subscribe(isOpen => {
        this.isOpen = isOpen;
      });

    // Suscribirse a reportes
    this.sofiaService.report$
      .pipe(takeUntil(this.destroy$))
      .subscribe(report => {
        if (report) {
          console.log('Nuevo reporte:', report);
          // Procesar reporte
        }
      });

    // Configurar template inicial
    this.templateJson = this.sofiaService.generateTemplate('general');
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  onReport(report: any) {
    this.sofiaService.handleReport(report);
  }

  onSetIsOpen(isOpen: boolean) {
    this.sofiaService.setIsOpen(isOpen);
  }
}
```

## Consideraciones importantes

### Detección de cambios

Angular puede requerir detección manual de cambios en algunos casos:

```typescript theme={null}
import { ChangeDetectorRef } from '@angular/core';

constructor(private cdr: ChangeDetectorRef) {}

onReport(report: any) {
  // Procesar reporte
  this.cdr.detectChanges(); // Forzar detección si es necesario
}
```

### Ciclo de vida de componentes

```typescript theme={null}
ngAfterViewInit() {
  // El Web Component está disponible después de la inicialización de la vista
  const sofiaElement = document.querySelector('sofia-sdk');
  if (sofiaElement) {
    // Configuración adicional si es necesaria
  }
}
```

### Manejo de errores

```typescript theme={null}
onReport(report: any) {
  try {
    // Procesar reporte
    this.processReport(report);
  } catch (error) {
    console.error('Error procesando reporte:', error);
    // Manejar error apropiadamente
  }
}
```

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 React](/sofia/es/sdk/react)
3. [Referencia de propiedades requeridas](/sofia/es/sdk/required-properties)
4. [Referencia de propiedades opcionales](/sofia/es/sdk/optional-properties)
