Skip to main content

Iframe

Implementación de Iframe: Código, Eventos y Manejo de Datos​

Una vez que tengas el token de acceso, debes integrar el iframe en tu aplicación utilizando el siguiente enfoque:

1. HTML - Agregar el iframe​

El iframe maneja dos dimensiones estándar iniciales, dependiendo de la vista que desees mostrar:

Dimensión Mediana (MD): width: 320px, height: 220px. (Vista completa).
Dimensión Pequeña (SM): width: 240px, height: 100px. (Vista minimizada).

El siguiente ejemplo utiliza la dimensión mediana (320x220px) como punto de partida.

<iframe
id="speaknosis-iframe"
src="about:blank"
title="Speaknosis App"
width="320px"
height="220px"
allow="microphone; screen-wake-lock"
style="border: none; border-radius: 8px; overflow: hidden"
></iframe>

2. JavaScript - Configuración, carga y manejo de eventos del iframe​

Una vez que el elemento <iframe> (del Paso 1) existe en tu página, el siguiente paso es configurarlo dinámicamente. El script a continuación se encarga de dos tareas principales:

  1. Cargar el iframe: Construye la URL de Speaknosis (incluyendo el token de autenticación y otros parámetros) y la asigna al atributo src del iframe para iniciar la aplicación.
  2. Manejar el foco: Añade los listeners de eventos blur y focus a la ventana principal de tu sistema.
(async () => {
// --- Configuración de Ambiente (QA o PROD) ---
const portalUrl = "https://qa-portal.speaknosis.com"; // Ambiente de QA
// const portalUrl = "https://portal.speaknosis.com"; // Ambiente de PROD

try {
// --- Cargar iframe ---

// Selecciona el elemento iframe por su ID.
const iframeElement = document.getElementById("speaknosis-iframe");

const response = await login(); // La función login() simula la función que obtiene el token.

const data = {
view: "iframe",
token: response.access_token, // Se obtiene el token de la sentencia de login.
doctorId: "", // Ingrese el ID del doctor que está generando el informe.
appointmentId: "", // Ingrese el ID de la consulta médica a la cual se le está generando el informe.
reportExampleId: "", // (Opcional) Id de plantilla que quieran preseleccionar.
};

// Crea un objeto URLSearchParams para enviar los datos dentro de la URL.
const urlParams = new URLSearchParams(data);

// Construye la URL para el iframe de Speaknosis usando la variable portalUrl.
const iframeUrl = `${portalUrl}/#/pop-up/?${urlParams}`;

// Asigna la URL final al iframe para cargarlo.
iframeElement.src = iframeUrl;

// --- Eventos blur y focus ---

// Manejo del blur para el iframe
window.addEventListener("blur", () => {
const isClickOutsideIframe = document.activeElement !== iframeElement;
if (isClickOutsideIframe && iframeElement.contentWindow) {
iframeElement.contentWindow.postMessage(
{ focusEvent: "WINDOW_BLUR" },
portalUrl, // Se usa la url de ambiente
);
}
});

// Manejo del focus para el iframe
window.addEventListener("focus", () => {
if (iframeElement.contentWindow) {
iframeElement.contentWindow.postMessage(
{ focusEvent: "WINDOW_FOCUS" },
portalUrl, // Se usa la url de ambiente
);
}
});
} catch (error) {
console.error("Error al configurar el iframe:", error);
}
})();

Estos eventos blur y focus son esenciales, ya que notificarán a nuestro sitio web (dentro del iframe) cuando se pierda o recupere el foco de la página web de tu sistema. Esto permite que aparezca una ventana flotante y se controle el estado de la grabación.

Imagen de referencia:

Img-Iframe-Window

3. JavaScript: Recibir evento de redimension desde el iframe (REQUEST_RESIZE)​

Tu aplicación debe escuchar el evento message para capturar la acción REQUEST_RESIZE enviada por el iframe. Este evento se dispara cuando el contenido interno del iframe cambia de tamaño (por ejemplo, al levantarse una alerta durante la grabación) y necesita que tu elemento <iframe> se ajuste a las nuevas dimensiones.

// --- Recibir eventos del iframe ---
// (Este bloque debe ir DENTRO del 'try' del script anterior)

window.addEventListener("message", (event) => {
// Validar origen usando la variable portalUrl
if (event.origin !== portalUrl) {
return; // Ignora mensajes de orígenes desconocidos
}

// Verifica si es un objeto de acción
if (
typeof event.data === "object" &&
event.data !== null &&
event.data.action
) {
// Maneja la acción 'REQUEST_RESIZE'
if (event.data.action === "REQUEST_RESIZE") {
iframeElement.style.width = "320px"; // Ajusta al ancho necesario
iframeElement.style.height = "220px"; // Ajusta a la altura necesaria
return;
}
}
});

Método Alternativo: Autenticación vía postMessage​

Además de enviar el token directamente en la URL, Speaknosis soporta un método alternativo de autenticación mediante postMessage. Este enfoque es útil cuando:

  • Prefieres no exponer el token en la URL.
  • Necesitas enviar datos adicionales junto con el token (como un JSON de configuración).
  • Tu arquitectura requiere generar el token después de cargar el iframe.
Cargar el iframe sin token en la URL​

En este flujo, cargas el iframe sin incluir el token en los parámetros de la URL:

const data = {
view: "iframe",
// token: NO se incluye en la URL
doctorId: "", // Ingrese el ID del doctor que está generando el informe.
appointmentId: "", // Ingrese el ID de la consulta médica.
reportExampleId: "", // (Opcional) Id de plantilla que quieran preseleccionar.
};

// Crea un objeto URLSearchParams para enviar los datos dentro de la URL.
const urlParams = new URLSearchParams(data);

// Construye la URL para el iframe de Speaknosis.
const iframeUrl = `https://qa-portal.speaknosis.com/#/pop-up/?${urlParams}`; // Ambiente de QA
// OR
const iframeUrl = `https://portal.speaknosis.com/#/pop-up/?${urlParams}`; // Ambiente de PROD

// Selecciona el elemento iframe por su ID y asigna la URL.
const iframeElement = document.getElementById("speaknosis-iframe");
iframeElement.src = iframeUrl;

// Guarda los datos pendientes de autenticación para enviar posteriormente
const pendingAuthData = {
token: response.access_token, // Token obtenido de tu sistema de login
jsonReport: {}, // Opcional: estructura JSON para plantillas dinámicas
};
Escuchar y responder a la solicitud de autenticación​

Speaknosis enviará un mensaje de tipo AUTH_REQUEST cuando esté listo para recibir las credenciales. Tu aplicación debe escuchar este evento y responder con un mensaje AUTH_RESPONSE:

useEffect(() => {
const messageHandler = (event) => {
// Escuchar la solicitud de autenticación desde Speaknosis
if (event.data?.type === "AUTH_REQUEST") {
console.log("[App] Solicitud de autenticación recibida desde Speaknosis");

// Verificar que tenemos los datos y la referencia al iframe
if (pendingAuthData && iframeElement.contentWindow) {
const authResponse = {
type: "AUTH_RESPONSE",
token: pendingAuthData.token,
jsonReport: pendingAuthData.jsonReport, // (Opcional) Datos adicionales en formato JSON
};

// Enviar la respuesta al iframe
iframeElement.contentWindow.postMessage(authResponse, "*");
console.log("[App] Respuesta de autenticación enviada");
} else {
console.warn("[App] No hay datos de autenticación disponibles");
}
return;
}

// Continuar manejando otros mensajes del iframe (ej: datos clínicos)
// ...
};

window.addEventListener("message", messageHandler);
return () => window.removeEventListener("message", messageHandler);
}, [pendingAuthData, iframeElement]);

⚠️ Importante: Si la ventana padre no responde dentro de un tiempo límite (aprox. 5 segundos con 1 reintento automático), Speaknosis redirigirá a una vista de error. Asegúrate de que tu listener esté activo antes de que el iframe complete su carga.

Estructura del mensaje AUTH_RESPONSE​
CampoTipoRequeridoDescripción
typestring✅Debe ser "AUTH_RESPONSE"
tokenstring✅Token de autenticación JWT válido
jsonReportobject | nullOpcionalDatos adicionales en formato JSON para configuración avanzada. Para definir correctamente la estructura del JSON, contáctate con el equipo de Speaknosis para que te entreguen la documentación.

Uso y Funcionalidad del Iframe​

Si el iframe recibe correctamente los datos de la consulta (token, doctorId y appointmentId), el componente del informe debería mostrarse dentro del iframe.

Imagen de referencia:

Img-Iframe

Una vez cargado el iframe, podrás comenzar a grabar audio usando el micrófono de tu computador o un micrófono externo desde tu teléfono. Al comenzar la grabación, aparecerán dos nuevos botones: pausar/reanudar grabación ( / ) y detener grabación ().

Imagen de referencia:

Img-Iframe-Recording

Al finalizar la grabación de audio (presionando el botón de detener), aparecerá una ventana emergente donde deberás seleccionar la plantilla a utilizar para generar el informe médico. Luego, deberás hacer clic en el botón "Generar Informe".

💡 Importante: Autorización de ventanas emergentes.
Para que esta ventana se muestre correctamente, es indispensable que las ventanas emergentes estén habilitadas en el navegador. Si están bloqueadas, la ventana no se abrirá y no será posible continuar con el proceso. Recomendamos permitir las ventanas emergentes para este sitio o incluirlo en la lista de excepciones.

📝 Pre-selección de Plantilla (Opcional) Si incluiste el parámetro reportExampleId en la URL inicial, el sistema utilizará ese ID de reporte de ejemplo para pre-seleccionar la plantilla en esta vista automáticamente. El doctor solo tendrá que revisar y confirmar la plantilla antes de hacer clic en 'Generar informe'. Si no se envía este parámetro, el doctor deberá seleccionar manualmente la plantilla del menú desplegable.

Imagen de referencia:

Img-Iframe-Select


Generación del Informe​

Una vez que comienza la generación del informe, una animación indicará que el informe se está generando. Luego, se mostrará el informe médico, el cual podrás editar y aprobar en una ventana flotante o emergente.

Imagen de referencia:

Img-Iframe-Generated

Img-Iframe-Generated

Método para recibir el informe​

Para recibir los informes en tu sistema, debes configurar un endpoint personalizado y seguro. La comunicación se autenticará utilizando el método que tu sistema prefiera (por ejemplo, una API Key, tokens de autorización, etc).

Una vez configurado, nuestro sistema enviará la información del informe a la URL que nos proporciones. Este proceso se activa cuando un informe es aprobado en nuestra plataforma, generando los datos en el formato acordado y enviándolos a tu endpoint.

Estructura Base de la Respuesta​

La carga útil (payload) que recibirás en tu endpoint tendrá la siguiente estructura fundamental:

{
"message": {
"appointmentId": "123123",
"report": { /* ... Contenido del informe ... */ },
"metaData": { /* ... Datos adicionales ... */ }
}
}

Detalle de los Campos​

  • appointmentId (string): Identificador único de la consulta o cita médica.
  • report (object | string): Contiene el informe médico. Su formato es flexible y depende de la configuración acordada.
  • metaData (object): Contiene datos adicionales y estructurados, como catálogos o códigos.

El campo report es dinámico. Puede contener un objeto JSON estructurado o una cadena de texto (string) con el informe en formato MARKDOWN o HTML.

Contenido del Campo "report"​

Opción 1: JSON Estructurado (report como objeto)​

Si necesitas los datos del informe de manera segmentada, el campo report contendrá un objeto JSON.

Ejemplo:

"report": {
"reasonForVisit": "Motivo de consulta: Dolor de garganta persistente.",
"anamnesis": "Paciente refiere dolor y dificultad para tragar desde hace 3 días.",
"physicalExam": "Se observan amígdalas enrojecidas e inflamadas."
}
Opción 2: Markdown (report como string)​

Para una presentación de texto enriquecido simple y legible, el campo report contendrá un objeto MARKDOWN.

Ejemplo:

"report": "## Motivo de consulta\nDolor de garganta persistente.\n\n## Anamnesis\nPaciente refiere dolor y dificultad para tragar desde hace 3 días."
Opción 3: HTML (report como string)​

Si necesitas renderizar el informe directamente en una vista web, el campo report contendrá un objeto HTML.

Ejemplo:

"report": "<h2>Motivo de consulta</h2><p>Dolor de garganta persistente.</p><h2>Anamnesis</h2><p>Paciente refiere dolor y dificultad para tragar desde hace 3 días.</p>"

Contenido del Campo "metaData"​

El campo metaData alberga información complementaria y estructurada que enriquece el informe, como códigos de catálogos previamente configurados.

Ejemplo de metaData con catálogos:

"metaData": {
"catalogs": {
"cie-10": [
{ "key": "J03.9", "code": "J03.9", "description": "Amigdalitis aguda, no especificada" }
],
"vademecum": [
{ "key": "IBU600", "code": "IBUPROFENO 600MG", "subcode": "24COMP" }
]
}
}

Una vez que el Pop-Up envíe los datos a tu endpoint, se cerrará automáticamente, permitiéndote utilizar la información recibida en tu aplicación.