Creación de paneles en vivo con Airtable y React
Publicado: 2022-03-11Ya sea que una empresa sea una gran empresa o una nueva empresa en ciernes, la recopilación de datos de usuarios y clientes, y la presentación de informes o la visualización de esos datos es crucial para el negocio.
Recientemente trabajé con una startup de telemedicina con sede en Brasil. Su misión es brindar atención y monitoreo remotos conectando a los pacientes con profesionales médicos y entrenadores de salud. La necesidad principal era crear una interfaz para que los entrenadores y profesionales de la salud revisaran fácilmente la información de un paciente y las métricas más importantes relacionadas con su situación particular: un tablero.
Introduzca Typeform y Airtable.
tipo de letra
Typeform es una de las herramientas de recopilación de datos de acceso que permite experiencias web receptivas para los usuarios que completan una encuesta. También viene con varias funciones que hacen que las encuestas sean más inteligentes, especialmente cuando se combinan:
- Saltos lógicos
- Campos Ocultos
Las encuestas se pueden compartir a través de URL que se pueden sembrar previamente con valores para los campos ocultos, que luego se pueden usar para implementar saltos lógicos y alterar el comportamiento de la encuesta para el usuario con el enlace.
Usos de la mesa de aire
Airtable es un híbrido de hoja de cálculo y base de datos y una plataforma colaborativa en la nube. Su enfoque en la funcionalidad de apuntar y hacer clic significa que los usuarios no técnicos pueden configurarlo sin codificación. Airtable tiene multitud de casos de uso en cualquier negocio o proyecto.
Puede utilizar una base Airtable para:
- CRM (Gestión de la relación con el cliente)
- HRIS (Sistema de Información de Recursos Humanos)
- Gestión de proyectos
- Planificación de contenido
- Evento planeado
- Comentarios del usuario
Hay muchos más casos de uso potenciales. Puede explorar los estudios de casos de Airtable aquí.
Si no está familiarizado con Airtable, el modelo de datos conceptual se desglosa así:
- Espacio de trabajo - Compuesto por bases
- Base - Compuesto por Mesas
- Tabla - Compuesta de Campos (columnas) y filas
- Ver : una perspectiva de los datos de la tabla con filtros opcionales y campos reducidos
- Campo : una columna de una tabla con un tipo de campo; consulte aquí para obtener más información sobre los tipos de campo
Además de proporcionar una base de datos alojada en la nube con características familiares de hoja de cálculo, estas son algunas de las razones por las que la plataforma es tan poderosa:
Para usuarios no técnicos, Airtable ofrece:
- Una interfaz frontal fácil de usar
- Automatizaciones que se pueden crear con configuración de apuntar y hacer clic para enviar correos electrónicos, procesar filas de datos, programar citas en calendarios y más
- Múltiples tipos de vistas que permiten a los equipos colaborar en la misma Base y tablas
- Aplicaciones Airtable que se pueden instalar desde el mercado para potenciar una Base
Para los desarrolladores, Airtable ofrece:
- Una API de back-end bien documentada
- Un entorno de secuencias de comandos que permite a los desarrolladores automatizar acciones dentro de una Base
- Automatizaciones que también pueden desencadenar scripts desarrollados a medida que se ejecutan dentro del entorno de Airtable, lo que amplía las capacidades de las automatizaciones
Puede obtener más información sobre Airtable aquí.
Primeros pasos: Typeform a Airtable
El cliente ya configuró las encuestas de Typeform, y el siguiente paso fue planificar cómo llegarían esos datos a Airtable y luego se convertirían en un tablero. Hay muchas preguntas a tener en cuenta al crear paneles sobre cualquier base de datos: ¿Cómo debemos estructurar los datos? ¿Qué datos deberán procesarse antes de la visualización? ¿Deberíamos sincronizar la Base con Google Sheets y usar Google Data Studio? ¿Deberíamos exportar y buscar otra herramienta de terceros?
Afortunadamente para los desarrolladores, Airtable no solo proporciona automatizaciones y secuencias de comandos para manejar los pasos de procesamiento de datos, sino que también ha hecho posible crear aplicaciones e interfaces personalizadas sobre una Airtable Base con Airtable Apps.
Aplicaciones personalizadas en Airtable
Las aplicaciones personalizadas en Airtable han existido desde que se lanzó Airtable Blocks SDK a principios de 2018, y recientemente se les cambió el nombre a Aplicaciones. El lanzamiento de Blocks fue enorme, ya que significó que los creadores ahora tenían la capacidad de desarrollar, como dice Airtable, "un kit de Lego infinitamente recombinable".
Más recientemente, con el cambio a las aplicaciones, Airtable Marketplace también hizo posible compartir aplicaciones públicamente.
Airtable Apps proporciona a las empresas un kit de Lego infinitamente recombinable que pueden adaptar a sus necesidades.
Para crear una aplicación personalizada en Airtable, un desarrollador de JavaScript debe saber cómo usar React, una de las bibliotecas de JavaScript más populares para crear interfaces de usuario. Airtable proporciona una biblioteca de componentes de componentes y ganchos funcionales de React, que son de gran ayuda para crear rápidamente una interfaz de usuario consistente y determinar cómo administrará el estado dentro de la aplicación y sus componentes.
Consulte el artículo de introducción de Airtable para obtener más información y Airtable en GitHub para ver ejemplos de aplicaciones.
Requisitos del panel de Airtable
Después de revisar las maquetas del tablero con el equipo del cliente, quedaron claros los tipos de datos que se usarían. Necesitaríamos una serie de componentes del tablero que se mostrarían como texto en el tablero y gráficos de diferentes métricas que podrían rastrearse a lo largo del tiempo.
Los entrenadores y los profesionales médicos necesitaban poder crear un tablero personalizado para cada paciente, por lo que necesitábamos una forma flexible de agregar y eliminar gráficos. Se mostrarían otros datos estáticos relativos a cada paciente sin importar el paciente seleccionado.
En este caso, las secciones del tablero se redujeron a:
- Información general : nombre del paciente, correo electrónico, número de teléfono, preferencia de contacto, fecha de nacimiento, edad
- Objetivos : metas que tiene el paciente en función de los resultados de la encuesta
- Algunas estadísticas : IMC, altura y peso
- Uso de medicamentos : lista de todos los medicamentos recetados que ya usa un paciente
- Historial familiar de condiciones : útil para diagnosticar ciertas condiciones
- Gráficos : una sección en la que el usuario del panel de control de Airtable podría agregar un gráfico y configurar qué métrica visualizaría a lo largo del tiempo
Una forma de abordar todas las secciones excepto los gráficos sería codificar todas las columnas para objetivos, uso de medicamentos e historial familiar en el tablero. Sin embargo, eso no permitiría que el equipo del cliente agregue nuevas preguntas a una encuesta de Typeform ni agregue una nueva columna a una tabla de Airtable para presentar esos datos en el tablero sin que un desarrollador actualice la aplicación personalizada.
Una solución más elegante y extensible para este desafío fue encontrar una manera de etiquetar las columnas como relevantes para una sección del tablero en particular y recuperar esas columnas usando los metadatos que expone Airtable cuando se usan los modelos de tabla y campo.
Esto se logró utilizando las descripciones de campo como un lugar para etiquetar una columna de la tabla como relevante para una sección del tablero que se mostrará al usuario. Luego, pudimos asegurarnos de que solo aquellos con el rol de Creador (los administradores) para la Base tuvieran la capacidad de modificar estas Descripciones de campo para alterar lo que aparece en el tablero. Para ilustrar esta solución, nos centraremos principalmente en los elementos de Información general y en cómo presentar los gráficos.
Creación de un sistema #TAG#
Dadas las secciones del tablero, tenía sentido crear etiquetas reutilizables para algunas secciones y etiquetas específicas para ciertas columnas. Para elementos como el nombre del paciente, el correo electrónico y el número de teléfono, se agregaron #NAME# , #EMAIL# y #PHONE# a la descripción de cada campo, respectivamente. Eso permitiría recuperar esa información a través de los metadatos de la tabla de esta manera:
const name = table ? table.fields.filter(field => field.description?.includes("#NAME#"))Para las áreas del tablero que necesitarían dibujar desde muchas columnas etiquetadas, tendríamos las siguientes etiquetas para cada sección del tablero:
- OBJ - Objetivos
- FAM - Historia familiar
- MED - Uso de medicamentos
- CAN - Antecedentes familiares específicos de cáncer
- GRÁFICO: cualquier columna que se debe obtener para agregar gráficos; debe ser una cantidad
Además, era importante separar el nombre de una columna en una tabla de la etiqueta que recibiría en el tablero, de modo que todo lo que recibiera una #TAG# también tendría la capacidad de recibir dos etiquetas #LABEL# en su Descripción de campo. . Una descripción de campo se vería así:
En caso de que falten las etiquetas #LABEL# , mostraremos el nombre de la columna de la tabla.
Podemos analizar el conjunto de etiquetas en la descripción con una función simple como esta después de recuperar el campo con el ejemplo de código anterior:
// utils.js export const setLabel = (field, labelTag = "#LABEL#") => { const labelTags = (field.description?.match(new RegExp(labelTag, "g")) || []).length; let label; if (labelTags === 2) label = field.description?.split(`${labelTag}`)[1]; if (!label || label?.trim() === '') label = field.name; return {...field, label, name: field.name, description: field.description}; } Con este sistema #TAG# conseguimos tres cosas principales:
- Los nombres de las columnas (campos) en la tabla se pueden cambiar según se desee.
- Las etiquetas de los datos en el tablero pueden ser distintas de los nombres de las columnas.
- El equipo del cliente puede actualizar las secciones del tablero para Objetivos, Uso de medicamentos, Historial familiar y Gráficos sin tocar una línea de código.
Estado persistente en Airtable
En React, usamos el estado y lo pasamos a los componentes como accesorios para volver a renderizar ese componente si su estado cambia. Normalmente, esto está vinculado a una llamada API que alimenta un componente del tablero, pero en Airtable ya tenemos todos los datos y simplemente necesitamos filtrar lo que mostramos según el paciente que estamos viendo. Además, si usamos el estado, no persistirán los datos más allá de una actualización en el tablero mismo.
Entonces, ¿cómo podemos conservar un valor más allá de la actualización para mantener filtrado un tablero? Afortunadamente, Airtable proporciona un gancho para esto llamado useGlobalConfig en el que mantiene un almacén de clave-valor para la instalación de una aplicación en un tablero. Simplemente necesitamos implementar la lógica de recuperar valores de este almacén de valores clave cuando la aplicación se carga para alimentar los componentes de nuestro tablero.
Lo que es aún más útil sobre el uso del gancho useGlobalConfig es que cuando se establecen sus valores, el componente del tablero y sus componentes secundarios se vuelven a renderizar, por lo que puede usar la configuración global como si usara una variable de estado en una implementación típica de React.
Introducción a los gráficos
Airtable proporciona ejemplos de gráficos con su aplicación Simple Chart, que utiliza React Charts, un contenedor de React en Chart.js (chart-ception).

En la aplicación Simple Chart, tenemos un gráfico para toda la aplicación, pero en nuestra aplicación Dashboard, necesitamos que el usuario pueda agregar y eliminar sus propios gráficos de su propio tablero. Además, en conversaciones con el equipo del cliente, parece que ciertas métricas se verían mejor en el mismo gráfico (como lecturas de presión arterial diastólica y sistólica).
Con esto tenemos los siguientes elementos a abordar:
- Estado persistente para el gráfico de cada usuario (o incluso mejor usando Global Config)
- Permitir múltiples métricas por gráfico
Aquí es donde el poder de Global Config es útil, ya que podemos usar el almacén de clave-valor para mantener las métricas seleccionadas y cualquier otra cosa sobre nuestra lista de gráficos. A medida que configuramos un gráfico en la interfaz de usuario, el componente del gráfico en sí se volverá a representar debido a las actualizaciones de la configuración global. Para la sección de gráficos del tablero, aquí hay una idea general con los componentes como referencia, centrándose en tablero charts.js y single chart.js.
La tabla que se pasa a cada gráfico es lo que se usa para sus metadatos para encontrar los campos, mientras que los registros pasados ya han sido filtrados por el paciente seleccionado en el componente del tablero de nivel superior que importa dashboard_charts/index.js .
Tenga en cuenta que los campos enumerados como opciones en el menú desplegable de un gráfico se extraen mediante la etiqueta #CHART# que mencionamos antes, con esta línea en un useEffect :
// single_chart/index.js … useEffect(() => { (async () => { ... if (table) { const tempFieldOptions = table.fields.filter(field => field.description?.includes('#CHART#')).map(field => { return { ...setLabel(field), value: field.id } }); setFieldSelectOptions([...tempFieldOptions]); } })(); }, [table, records, fields]); ... El código anterior muestra cómo la función setLabel la que se hace referencia anteriormente se usa con #TAG# para agregar cualquier cosa proporcionada en las etiquetas #LABEL# y mostrarlo para la opción en el campo desplegable.
Nuestro componente de gráficos aprovecha las capacidades de varios ejes proporcionadas por Chart.js, que se muestra con React Charts. Simplemente lo ampliamos a través de la interfaz de usuario con la capacidad del usuario de agregar un conjunto de datos y un tipo de gráfico (línea o barra).
La clave para usar Global Config, en este caso, es saber que cada tecla solo puede contener una cadena | booleano | número | nulo | matriz de configuración global | GlobalConfigObject (consulte la referencia del valor de configuración global).
Tenemos los siguientes artículos para mantener por gráfico:
- chartTitle que se genera automáticamente y el usuario puede cambiarle el nombre
- matriz de campos en la que cada elemento tiene:
- campo como fieldId de Airtable
- chartOption como una línea | barra como indican los documentos de Chart.js
- color como el color Airtable de colorUtils
- hex como el código hexadecimal relacionado con el color Airtable
Para manejar esto, me pareció más conveniente clasificar estos datos como un objeto en lugar de establecer claves y valores de configuración global hasta el final. Vea el ejemplo a continuación (globalConfig.json en esencia), que incluye valores de configuración global para filtrar registros por paciente y algunas variables relacionadas que se utilizan para admitir un componente de filtrado de escritura anticipada (gracias a react-bootstrap-typeahead):
{ "xCharts": { "chart-1605425876029": "{\"fields\":[{\"field\":\"fldxLfpjdmYeDOhXT\",\"chartOption\":\"line\",\"color\":\"blueBright\",\"hex\":\"#2d7ff9\"},{\"field\":\"fldqwG8iFazZD5CLH\",\"chartOption\":\"line\",\"color\":\"blueLight1\",\"hex\":\"#9cc7ff\"}],\"chartTitle\":\"Grafico criado em 11/15/2020, 2:37:56 AM\"}", "chart-1605425876288": "{\"fields\":[{\"field\":\"fldGJZIdRlq3V3cKu\",\"chartOption\":\"line\",\"color\":\"blue\",\"hex\":\"#1283da\"}],\"chartTitle\":\"Grafico criado em 11/15/2020, 2:37:56 AM\"}", "chart-1605425876615": "{\"fields\":[{\"field\":\"fld1AnNcfvXm8DiNs\",\"chartOption\":\"line\",\"color\":\"blueLight1\",\"hex\":\"#9cc7ff\"},{\"field\":\"fldryX5N6vUYWbdzy\",\"chartOption\":\"line\",\"color\":\"blueDark1\",\"hex\":\"#2750ae\"}],\"chartTitle\":\"Grafico criado em 11/15/2020, 2:37:56 AM\"}", "chart-1605425994036": "{\"fields\":[{\"field\":\"fld9ak8Ja6DPweMdJ\",\"chartOption\":\"line\",\"color\":\"blueLight2\",\"hex\":\"#cfdfff\"},{\"field\":\"fldxVgXdZSECMVEj6\",\"chartOption\":\"line\",\"color\":\"blue\",\"hex\":\"#1283da\"}],\"chartTitle\":\"Grafico criado em 11/15/2020, 2:39:54 AM\"}", "chart-1605430015978": "{\"fields\":[{\"field\":\"fldwdMJkmEGFFSqMy\",\"chartOption\":\"line\",\"color\":\"blue\",\"hex\":\"#1283da\"},{\"field\":\"fldqwG8iFazZD5CLH\",\"chartOption\":\"line\",\"color\":\"blueLight1\",\"hex\":\"#9cc7ff\"}],\"chartTitle\":\"New Chart\"}", "chart-1605430916029": "{\"fields\":[{\"field\":\"fldCuf3I2V027YAWL\",\"chartOption\":\"line\",\"color\":\"blueLight1\",\"hex\":\"#9cc7ff\"},{\"field\":\"fldBJjtRkWUTuUf60\",\"chartOption\":\"line\",\"color\":\"blueDark1\",\"hex\":\"#2750ae\"}],\"chartTitle\":\"Grafico criado em 11/15/2020, 4:01:56 AM\"}", "chart-1605431704374": "{\"fields\":[{\"field\":\"fld7oBtl3iiHNHqoJ\",\"chartOption\":\"line\",\"color\":\"blue\",\"hex\":\"#1283da\"}],\"chartTitle\":\"Grafico criado em 11/15/2020, 4:15:04 AM\"}" }, "xPatientEmail": "[email protected]", "xTypeaheadValue": "Elle Gold ([email protected])", "xSelectedValue": "[{\"label\":\"Elle Gold ([email protected])\",\"id\":\"[email protected]\",\"name\":\"Elle Gold\",\"email\":\"[email protected]\"}]" }Nota: Todos los datos contenidos anteriormente y los datos incluidos en las animaciones a continuación no son datos de pacientes reales.
He aquí un vistazo al resultado final:
¿Qué pasa con la escritura anticipada?
Para filtrar por paciente, necesitábamos una forma de seleccionar un paciente y luego filtrar los registros en función de este paciente. En esta sección, revisamos cómo se logró esto.
Para el tipeo anticipado, react-bootstrap-typeahead fue una elección fácil, ya que los únicos pasos que quedaban eran preparar las opciones para el tipeo anticipado, mezclarlo con una entrada de Airtable para diseñar y cargar bootstrap, y algunos otros estilos para nuestro menú. Colocar componentes de sus bibliotecas de componentes favoritas en una aplicación Airtable no es tan sencillo como en el desarrollo web típico de React; sin embargo, solo hay unos pocos pasos adicionales para que todo se vea de la manera esperada.
Aquí esta el resultado final:
Para renderizar la entrada de Airtable y mantener todos nuestros estilos consistentes, react-bootstrap-typeahead viene con un accesorio renderInput. Vea más sobre cómo modificar la representación del componente aquí.
Para los estilos de arranque y para anular los elementos de nuestro menú, se usaron las siguientes dos herramientas de Airtable:
- cargarCSSFromString
- cargarCSSFromURLAsync
Consulte frontend.js en la esencia para obtener un extracto de la implementación de escritura anticipada.
Esta línea se usó para cargar bootstrap globalmente:
// frontend/index.js loadCSSFromURLAsync('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'); Notará algo de lógica adicional para cosas como el manejo de cambios de estilo al pasar el mouse por encima o el restablecimiento del estilo de los enlaces ( <a></a> ) para obtener la apariencia familiar de arranque. Esto también incluye el manejo de la configuración de los valores de configuración global para la escritura anticipada y el filtrado de registros, de modo que si un usuario sale de su tablero, actualiza su página o desea compartir este tablero con otros, la aplicación mantiene al paciente seleccionado en el tablero. aplicación. Esto también permite a los usuarios instalar varias copias de esta misma aplicación una al lado de la otra en el mismo Airtable Dashboard con diferentes pacientes seleccionados o con diferentes gráficos.
Tenga en cuenta que un tablero en Airtable también está disponible para todos los usuarios de Base, por lo que estas instalaciones de aplicaciones personalizadas en un tablero se filtrarán a los mismos pacientes y gráficos, sin importar qué usuarios estén mirando el tablero al mismo tiempo.
Recapitulemos lo que hemos cubierto hasta ahora:
- Airtable permite tanto a usuarios no técnicos como a usuarios técnicos colaborar en Airtable.
- Typeform viene con una integración de Airtable que permite a los usuarios no técnicos asignar los resultados de Typeform a Airtable.
- Airtable Apps proporciona una forma poderosa de potenciar su Airtable Base, ya sea seleccionando del mercado o creando una aplicación personalizada.
- Los desarrolladores pueden extender Airtable rápidamente en casi cualquier forma imaginable con estas aplicaciones. Nuestro ejemplo anterior tomó solo tres semanas para diseñar e implementar (con una enorme ayuda de las bibliotecas existentes, por supuesto).
- Se puede usar un sistema
#TAG#para modificar el tablero sin que los desarrolladores hagan cambios en el código. Hay mejores y peores casos de uso para esto. Asegúrese de limitar los permisos al rol de Creador si usa esta estrategia. - El uso de Global Config permite a los desarrolladores conservar los datos dentro de la instalación de una aplicación. Combine esto con su estrategia de administración de estado para generar datos para sus componentes.
- No espere arrastrar y soltar componentes de otras bibliotecas y proyectos directamente en su aplicación Airtable. Los estilos se pueden cargar con las
loadCSSFromStringyloadCSSFromURLAsyncproporcionadas por Airtable.
A prueba de futuro
Use un middleware más sofisticado
Con Typeform y Airtable, es fácil y rentable configurar el mapeo de preguntas a columnas.
Sin embargo, hay un gran inconveniente: si tiene una encuesta de más de 100 preguntas asignadas a Airtable y necesita modificar una asignación, debe eliminar toda la asignación y comenzar de nuevo. Claramente, esto no es lo ideal, pero para una integración libre, podemos lidiar con esto.
Otras opciones serían tener una integración de Zapier (o similar) que administre los datos entre Typeform y Airtable. Luego, podría modificar el mapeo de cualquier pregunta a cualquier columna sin comenzar desde cero. Esto también tendría sus propias consideraciones de costos a tener en cuenta.
Con suerte, algunas de las lecciones aprendidas y comunicadas aquí ayudarán a otros que buscan crear soluciones con Airtable.
Finalmente, puede consultar la esencia con los archivos discutidos en este artículo.
