Créer des tableaux de bord en direct avec Airtable et React

Publié: 2022-03-11

Qu'une entreprise soit une grande entreprise ou une start-up en herbe, la collecte de données auprès des utilisateurs et des clients, ainsi que la création de rapports ou la visualisation de ces données sont essentielles pour l'entreprise.

J'ai récemment travaillé avec une startup de télémédecine basée au Brésil. Sa mission est de fournir des soins et un suivi à distance en mettant les patients en relation avec des professionnels de la santé et des coachs de santé. Le besoin essentiel était de créer une interface permettant aux coachs et aux professionnels de la santé de consulter facilement les informations d'un patient et les mesures les plus importantes liées à sa situation particulière : un tableau de bord.

Entrez Typeform et Airtable.

Forme de caractères

Typeform est l'un des outils de collecte de données incontournables qui permet des expériences Web réactives pour les utilisateurs répondant à une enquête. Il est également livré avec plusieurs fonctionnalités qui rendent les sondages plus intelligents, en particulier lorsqu'ils sont combinés :

  • Sauts logiques
  • Champs cachés

Les enquêtes peuvent être partagées via des URL qui peuvent être prédéfinies avec des valeurs pour les champs masqués, qui peuvent ensuite être utilisées pour implémenter des sauts logiques et modifier le comportement de l'enquête pour l'utilisateur avec le lien.

Utilisations de la table à air

Airtable est un hybride feuille de calcul-base de données et une plate-forme cloud collaborative. L'accent mis sur la fonctionnalité pointer-cliquer signifie que les utilisateurs non techniques peuvent le configurer sans codage. Airtable a une multitude de cas d'utilisation dans n'importe quelle entreprise ou projet.

Vous pouvez utiliser une Airtable Base pour :

  • CRM (Gestion de la Relation Client)
  • SIRH (Système d'Information Ressources Humaines)
  • Gestion de projet
  • Planification du contenu
  • Planification d'événements
  • Commentaires des utilisateurs

Il existe de nombreux autres cas d'utilisation potentiels. Vous pouvez explorer les études de cas Airtable ici.

Si vous n'êtes pas familier avec Airtable, le modèle de données conceptuel se décompose comme suit :

  • Espace de travail - composé de bases
  • Base - Composé de Tables
  • Tableau - Composé de champs (colonnes) et de lignes
  • Vue - Une perspective sur les données du tableau avec des filtres facultatifs et des champs réduits
  • Champ - Une colonne d'une table avec un type de champ ; voir ici pour plus d'informations sur les types de champs

En plus de fournir une base de données hébergée dans le cloud avec des fonctionnalités de feuille de calcul familières, voici quelques-unes des raisons pour lesquelles la plate-forme est si puissante :

Représentation d'utilisateurs techniques et non techniques travaillant avec Airtable.

Pour les utilisateurs non techniques, Airtable fournit :

  • Une interface frontale facile à utiliser
  • Automatisations pouvant être créées avec une configuration pointer-cliquer pour envoyer des e-mails, traiter des lignes de données, planifier des rendez-vous dans des calendriers, etc.
  • Plusieurs types de vues qui permettent aux équipes de collaborer sur la même base et les mêmes tables
  • Applications Airtable pouvant être installées à partir du marché pour suralimenter une base

Pour les développeurs, Airtable fournit :

  • Une API back-end bien documentée
  • Un environnement de script qui permet aux développeurs d'automatiser les actions au sein d'une Base
  • Des automatisations qui peuvent également déclencher des scripts développés personnalisés qui s'exécutent dans l'environnement Airtable, étendant les capacités des automatisations

Vous pouvez en savoir plus sur Airtable ici.

Mise en route : Typeform vers Airtable

Les enquêtes Typeform étaient déjà configurées par le client, et l'étape suivante consistait à planifier comment ces données atterriraient dans Airtable, puis seraient transformées en tableau de bord. De nombreuses questions doivent être prises en compte lors de la création de tableaux de bord au-dessus de n'importe quelle base de données : comment devons-nous structurer les données ? Quelles données devront être traitées avant la visualisation ? Doit-on synchroniser la Base avec Google Sheets et utiliser Google Data Studio ? Doit-on exporter et trouver un autre outil tiers ?

Heureusement pour les développeurs, non seulement Airtable fournit des automatisations et des scripts pour gérer les étapes de traitement des données, mais il a également permis de créer des applications et des interfaces personnalisées au-dessus d'une base Airtable avec des applications Airtable.

Applications personnalisées dans Airtable

Les applications personnalisées dans Airtable existent depuis la sortie du SDK Airtable Blocks au début de 2018 et ont récemment été renommées en applications. La sortie de Blocks était énorme dans la mesure où elle signifiait que les créateurs avaient désormais la possibilité de développer, comme le dit Airtable, "un kit Lego infiniment recombinable".

Plus récemment, avec le passage aux applications, Airtable Marketplace a également permis de partager publiquement des applications.

Les applications Airtable fournissent aux entreprises un kit Lego recombinable à l'infini qu'elles peuvent adapter à leurs besoins.

Afin de créer une application personnalisée dans Airtable, un développeur JavaScript doit savoir utiliser React, l'une des bibliothèques JavaScript les plus populaires pour la création d'interfaces utilisateur. Airtable fournit une bibliothèque de composants de composants et de crochets React fonctionnels, qui sont d'une grande aide pour créer rapidement une interface utilisateur cohérente et déterminer comment vous allez gérer l'état au sein de l'application et de ses composants.

Consultez l'article de démarrage d'Airtable pour plus d'informations et Airtable sur GitHub pour des exemples d'applications.

Exigences du tableau de bord Airtable

Après avoir examiné les maquettes de tableau de bord avec l'équipe client, les types de données à utiliser étaient clairs. Nous aurions besoin d'une série de composants de tableau de bord qui s'afficheraient sous forme de texte sur le tableau de bord et de graphiques de différentes mesures qui pourraient être suivies au fil du temps.

Les entraîneurs et les professionnels de la santé devaient pouvoir créer un tableau de bord personnalisé pour chaque patient. Nous avions donc besoin d'un moyen flexible d'ajouter et de supprimer des graphiques. D'autres données statiques relatives à chaque patient seraient affichées quel que soit le patient sélectionné.

Dans ce cas, les sections du tableau de bord se résumaient à :

  • Informations générales - Nom du patient, e-mail, numéro de téléphone, préférence de contact, date de naissance, âge
  • Objectifs - Objectifs que le patient a basés sur les résultats de l'enquête
  • Quelques statistiques - IMC, taille et poids
  • Utilisation de médicaments - Liste de tous les médicaments sur ordonnance déjà utilisés par un patient
  • Antécédents familiaux de conditions - Utile pour diagnostiquer certaines conditions
  • Graphiques - Une section où l'utilisateur du tableau de bord Airtable peut ajouter un graphique et configurer la métrique qu'il visualisera au fil du temps

Image montrant une maquette de tableau de bord Airtable.

Une façon d'aborder toutes les sections à l'exception des graphiques serait de coder en dur toutes les colonnes pour les objectifs, l'utilisation de médicaments et les antécédents familiaux dans le tableau de bord. Cependant, cela ne permettrait pas à l'équipe client d'ajouter de nouvelles questions à une enquête Typeform ni d'ajouter une nouvelle colonne à une table Airtable pour présenter ces données sur le tableau de bord sans qu'un développeur ne mette à jour l'application personnalisée.

Une solution plus élégante et extensible à ce défi consistait à trouver un moyen de baliser les colonnes comme pertinentes pour une section de tableau de bord particulière et de récupérer ces colonnes à l'aide des métadonnées exposées par Airtable lors de l'utilisation des modèles Table et Field.

Ceci a été réalisé en utilisant les descriptions de champ comme emplacement pour marquer une colonne du tableau comme pertinente pour une section de tableau de bord à afficher à l'utilisateur. Ensuite, nous avons pu nous assurer que seuls ceux qui avaient le rôle de créateur (les administrateurs) pour la base avaient la possibilité de modifier ces descriptions de champ pour modifier ce qui apparaît sur le tableau de bord. Pour illustrer cette solution, nous nous concentrerons principalement sur les éléments des informations générales et sur la manière de présenter les graphiques.

Création d'un système #TAG#

Compte tenu des sections du tableau de bord, il était logique de créer des balises réutilisables pour certaines sections et des balises spécifiques pour certaines colonnes. Pour les éléments tels que le nom du patient, l'e-mail et le numéro de téléphone, #NAME# , #EMAIL# et #PHONE# ont été ajoutés à la description de chaque champ, respectivement. Cela permettrait à ces informations d'être récupérées via les métadonnées de la table comme ceci :

 const name = table ? table.fields.filter(field => field.description?.includes("#NAME#"))

Pour les zones du tableau de bord qui auraient besoin de puiser dans de nombreuses colonnes balisées, nous aurions les balises suivantes pour chaque section du tableau de bord :

  • OBJ - Objectifs
  • FAM - Histoire familiale
  • MED - Utilisation des médicaments
  • CAN - Antécédents familiaux spécifiques au cancer
  • CHART - Toute colonne qui devrait être source pour ajouter des graphiques ; doit être une quantité

De plus, il était important de séparer le nom d'une colonne dans un tableau de l'étiquette qu'elle recevrait sur le tableau de bord, de sorte que tout ce qui recevait un #TAG# aurait également la possibilité de recevoir deux balises #LABEL# dans sa description de champ . Une description de champ ressemblerait à ceci :

Capture d'écran montrant l'utilisation de balises dans une description de champ.

Si les balises #LABEL# sont manquantes, nous afficherons le nom de la colonne de la table.

Nous pouvons analyser l'ensemble d'étiquettes dans la description avec une fonction simple comme celle-ci après avoir récupéré le champ avec l'exemple de code précédent :

 // 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}; }

Avec ce système #TAG# , nous réalisons trois choses principales :

  • Les noms de colonne (champs) du tableau peuvent être modifiés à volonté.
  • Les étiquettes des données du tableau de bord peuvent être distinctes des noms de colonne.
  • Les sections du tableau de bord pour les objectifs, l'utilisation des médicaments, les antécédents familiaux et les graphiques peuvent être mises à jour par l'équipe du client sans toucher à une ligne de code.

État persistant dans Airtable

Dans React, nous utilisons l'état et le transmettons aux composants en tant qu'accessoires afin de restituer ce composant si son état change. Normalement, cela est lié à un appel API qui alimente un composant de tableau de bord, mais dans Airtable, nous avons déjà toutes les données et devons simplement filtrer ce que nous affichons en fonction du patient que nous visualisons. De plus, si nous utilisons l'état, il ne conservera pas les données après une actualisation dans le tableau de bord lui-même.

Alors, comment pouvons-nous conserver une valeur après l'actualisation pour garder un tableau de bord filtré ? Heureusement, Airtable fournit un hook pour cela appelé useGlobalConfig dans lequel il maintient un magasin clé-valeur pour une installation d'application sur un tableau de bord. Nous devons simplement implémenter la logique de récupération des valeurs de ce magasin clé-valeur lorsque l'application se charge pour alimenter nos composants de tableau de bord.

Ce qui est encore plus utile dans l'utilisation du crochet useGlobalConfig , c'est que lorsque ses valeurs sont définies, le composant de tableau de bord et ses composants enfants sont restitués, vous pouvez donc utiliser Global Config comme vous utiliseriez une variable d'état dans une implémentation typique de React.

Présentation des graphiques

Airtable fournit des exemples de graphiques avec son application Simple Chart, qui utilise React Charts, un wrapper React sur Chart.js (chart-ception).

Dans l'application Simple Chart, nous avons un graphique pour l'ensemble de l'application, mais dans notre application Dashboard, nous avons besoin de la possibilité pour l'utilisateur d'ajouter et de supprimer ses propres graphiques de son propre tableau de bord. De plus, en discutant avec l'équipe client, il semble que certaines mesures seraient mieux visualisées sur le même graphique (comme les lectures pour la pression artérielle diastolique et systolique).

Avec cela, nous avons les éléments suivants à aborder :

  • État persistant pour le graphique de chaque utilisateur (ou encore mieux en utilisant Global Config)
  • Autoriser plusieurs métriques par graphique

C'est là que la puissance de Global Config est utile, car nous pouvons utiliser le magasin clé-valeur pour conserver les métriques sélectionnées et tout ce qui concerne notre liste de graphiques. Lorsque nous configurons un graphique dans l'interface utilisateur, le composant graphique lui-même sera restitué en raison des mises à jour de la configuration globale. Pour la section graphique du tableau de bord, voici un aperçu des composants de référence, en se concentrant sur le tableau de bord charts.js et single chart.js.

La table transmise à chaque graphique est ce qui est utilisé pour ses métadonnées afin de trouver les champs, alors que les enregistrements transmis ont déjà été filtrés par le patient sélectionné au niveau du composant de tableau de bord de niveau supérieur qui importe dashboard_charts/index.js .

Notez que les champs répertoriés en tant qu'options dans la liste déroulante d'un graphique sont extraits à l'aide de la balise #CHART# que nous avons mentionnée précédemment, avec cette ligne dans un crochet 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]); ...

Le code ci-dessus montre comment la fonction setLabel référencée précédemment est utilisée avec le #TAG# pour ajouter tout élément fourni dans les balises #LABEL# et l'afficher pour l'option dans la liste déroulante des champs.

Notre composant graphique tire parti des capacités multi-axes fournies par Chart.js, qui sont présentées avec React Charts. Nous l'avons simplement étendu via l'interface utilisateur avec la possibilité pour l'utilisateur d'ajouter un jeu de données et un type de graphique (ligne ou barre).

La clé pour utiliser Global Config, dans ce cas, est de savoir que chaque clé ne peut contenir qu'une chaîne | booléen | nombre | nul | GlobalConfigArray | GlobalConfigObject (voir Référence de valeur de configuration globale).

Nous avons les éléments suivants à maintenir par graphique :

  • chartTitle qui est généré automatiquement et peut être renommé par l'utilisateur
  • tableau de champs dans lequel chaque élément a :
    • champ en tant que fieldId de Airtable
    • chartOption sur une seule ligne | barre comme l'indiquent les documents Chart.js
    • color comme la couleur Airtable de colorUtils
    • hex comme code hexadécimal relatif à la couleur de l'Airtable

Pour gérer cela, j'ai trouvé plus pratique de chaîner ces données en tant qu'objet au lieu de définir les clés et les valeurs de Global Config tout en bas. Voir l'exemple ci-dessous (globalConfig.json dans l'essentiel), qui inclut des valeurs de configuration globale pour filtrer les enregistrements par le patient et certaines variables associées utilisées pour prendre en charge un composant de filtrage typeahead (grâce à 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]\"}]" }

Remarque : Toutes les données contenues ci-dessus, ainsi que les données incluses dans les animations ci-dessous, ne sont pas des données patient réelles.

Voici un aperçu du résultat final :

Affichage animé de l'interface utilisateur du tableau de bord Airtable.

Qu'en est-il du Typeahead ?

Afin de filtrer par patient, nous avions besoin d'un moyen de sélectionner un patient, puis de filtrer les enregistrements en fonction de ce patient. Dans cette section, nous examinons comment cela a été réalisé.

Pour le typeahead, react-bootstrap-typeahead était un choix facile, car les seules étapes restantes consistaient à préparer les options pour le typeahead, en le mélangeant avec une entrée Airtable pour le style et le chargement du bootstrap, et quelques autres styles pour notre menu. Déposer des composants de vos bibliothèques de composants préférées dans une application Airtable n'est pas aussi simple que dans le développement Web typique de React ; cependant, il n'y a que quelques étapes supplémentaires pour que tout ressemble à ce que vous attendez.

Voici le résultat final :

GIF animé présentant la fonctionnalité de filtrage par patient.

Pour rendre l'entrée Airtable et garder tous nos styles cohérents, react-bootstrap-typeahead est livré avec un accessoire renderInput . En savoir plus sur la façon de modifier le rendu du composant ici.

Pour les styles d'amorçage et pour remplacer nos éléments de menu, les deux utilitaires suivants ont été utilisés à partir d'Airtable :

  • loadCSSFromString
  • loadCSSFromURLAsync

Voir frontend.js dans l'essentiel pour un extrait de l'implémentation typeahead.

Cette ligne a été utilisée pour charger le bootstrap globalement :

 // frontend/index.js loadCSSFromURLAsync('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css');

Vous remarquerez une logique supplémentaire pour des choses comme la gestion des changements de style lors du survol ou le restylage des liens ( <a></a> ) pour obtenir l'apparence familière du bootstrap. Cela inclut également la gestion de la définition des valeurs de configuration globale pour la frappe et le filtrage des enregistrements afin que si un utilisateur quitte son tableau de bord, actualise sa page ou souhaite partager ce tableau de bord avec d'autres, l'application conserve le patient sélectionné dans le tableau de bord. App. Cela permet également aux utilisateurs d'installer plusieurs copies de cette même application côte à côte dans le même tableau de bord Airtable avec différents patients sélectionnés ou avec différents graphiques.

Gardez à l'esprit qu'un tableau de bord dans Airtable est également disponible pour tous les utilisateurs de la Base, de sorte que ces installations d'applications personnalisées sur un tableau de bord seront filtrées sur les mêmes patients et dossiers, quels que soient les utilisateurs qui consultent le tableau de bord en même temps.

Récapitulons ce que nous avons couvert jusqu'à présent :

  1. Airtable permet aux utilisateurs non techniques et aux utilisateurs techniques de collaborer dans Airtable.
  2. Typeform est livré avec une intégration Airtable qui permet aux utilisateurs non techniques de mapper les résultats Typeform à Airtable.
  3. Les applications Airtable offrent un moyen puissant de suralimenter sa base Airtable, qu'il s'agisse de sélectionner sur le marché ou de créer une application personnalisée.
  4. Les développeurs peuvent étendre Airtable rapidement de presque toutes les manières imaginables avec ces applications. Notre exemple ci-dessus n'a pris que trois semaines pour être conçu et mis en œuvre (avec l'aide considérable des bibliothèques existantes, bien sûr).
  5. Un système #TAG# peut être utilisé pour modifier le tableau de bord sans nécessiter de modifications de code par les développeurs. Il y a des cas d'utilisation meilleurs et pires pour cela. Assurez-vous de limiter les autorisations au rôle Creator si vous utilisez cette stratégie.
  6. L'utilisation de Global Config permet aux développeurs de conserver les données dans une installation d'application. Mélangez cela dans votre stratégie de gestion d'état pour amorcer les données de vos composants.
  7. Ne vous attendez pas à faire glisser et déposer des composants d'autres bibliothèques et projets directement dans votre application Airtable. Les styles peuvent être chargés à l'aide des loadCSSFromString et loadCSSFromURLAsync fournis par Airtable.

Pérennité

Utiliser un middleware plus sophistiqué

Avec Typeform et Airtable, il est facile et économique de configurer le mappage des questions aux colonnes.

Cependant, il y a un gros inconvénient : si vous avez une enquête de plus de 100 questions mappées sur Airtable et que vous devez modifier un mappage, vous devez supprimer tout le mappage et recommencer. Ce n'est clairement pas l'idéal, mais pour une intégration libre, on peut s'en accommoder.

D'autres options seraient d'avoir une intégration Zapier (ou similaire) pour gérer les données entre Typeform et Airtable. Ensuite, vous pouvez modifier le mappage de n'importe quelle question à n'importe quelle colonne sans repartir de zéro. Cela aurait également ses propres considérations de coût à prendre en compte.

Espérons que certaines des leçons apprises et communiquées ici aideront d'autres personnes qui cherchent à créer des solutions avec Airtable.

Enfin, vous pouvez consulter l'essentiel avec les fichiers abordés dans cet article.