用Airtable和React创建实时仪表板

558 阅读19分钟

网络前端

12分钟阅读

用Airtable和React创建实时仪表板

Dylan Golow

Dylan是一名全栈工程师,在电子商务、医疗保健和专业服务等多个行业拥有丰富的经验。

分享

无论一个公司是大型企业还是刚刚起步的初创公司,从用户和客户那里收集数据,并对这些数据进行报告或可视化,对业务来说都是至关重要的。

我最近与一家位于巴西的远程医疗初创公司合作。它的任务是通过将病人与医疗专家和健康教练联系起来,提供远程护理和监测。其核心需求是为教练和健康专家创建一个界面,以方便审查病人的信息和与他们的具体情况有关的最重要的指标:一个仪表板。

输入Typeform和Airtable。

Typeform

Typeform是常用的数据收集工具之一,为完成调查的用户提供响应式的网络体验。它还具有几个功能,使调查更加智能,特别是在组合时。

  • 逻辑跳转
  • 隐藏字段

调查表可以通过URL共享,这些URL可以预先加入隐藏字段的值,然后可以用来实现逻辑跳跃,并通过链接为用户改变调查的行为。

Airtable的用途

Airtable是一个电子表格与数据库的混合体,也是一个协作性的云平台。它对点和点击功能的关注意味着非技术用户可以不通过编码来配置它。Airtable在任何企业或项目中都有大量的使用案例。

你可以将Airtable基地用于。

  • CRM(客户关系管理)
  • HRIS(人力资源信息系统)
  • 项目管理
  • 内容规划
  • 活动策划
  • 用户反馈

还有很多潜在的使用案例。你可以在这里探索Airtable的案例研究。

如果你不熟悉Airtable,其概念性的数据模型是这样分解的。

  • 工作区--由基数组成
  • 基地--由表组成
  • --由字段(列)和行组成
  • 视图--表数据的一个视角,有可选的过滤器和减少的字段
  • 字段--表的一个列,有一个字段类型;关于字段类型的更多信息,请看这里

除了提供一个具有熟悉的电子表格功能的云托管数据库外,以下是该平台如此强大的一些原因。

对于非技术用户,Airtable提供了。

  • 一个易于使用的前端界面
  • 可以通过点选配置创建自动程序,以发送电子邮件、处理数据行、在日历中安排约会等
  • 多种类型的视图,允许团队在同一基地和表格上进行协作
  • 可以从市场上安装Airtable应用程序,以增强基地的功能。

对于开发者,Airtable提供

  • 一个记录良好的后端API
  • 一个脚本环境,允许开发者在基地内自动操作
  • 自动操作也可以触发自定义开发的脚本,在Airtable环境中运行,扩展自动操作的能力

你可以在这里了解更多关于Airtable的信息。

入门。Typeform到Airtable

客户已经配置好了Typeform调查,下一步就是计划如何将这些数据放到Airtable中,然后变成一个仪表盘。在任何数据库的基础上创建仪表盘时,有许多问题需要考虑。我们应该如何构建数据?哪些数据需要在可视化之前进行处理?我们是否应该将基地与谷歌表格同步并使用谷歌数据工作室?我们是否应该导出并找到另一个第三方工具?

对于开发者来说,幸运的是,Airtable不仅提供了自动化和脚本来处理数据处理的步骤,而且它还可以通过Airtable Apps在Airtable Base上建立自定义的应用程序和界面。

Airtable中的自定义应用程序

2018年初Airtable Blocks SDK发布以来,Airtable中的自定义应用程序一直存在,最近重新命名为Apps。Blocks的发布是巨大的,因为它意味着创作者现在有能力开发,正如Airtable所说,"一个无限的可重组的乐高套件"。

最近,随着应用的变化,Airtable市场也使公开分享应用成为可能。

Airtable应用为企业提供了一个可无限重组的乐高套件,他们可以根据自己的需求进行定制。

为了在Airtable中建立一个定制的应用程序,一个JavaScript开发人员必须知道如何使用React,这是一个最流行的用于建立用户界面的JavaScript库。Airtable提供了一个功能性React组件和钩子的组件库,这对快速建立一个一致的用户界面和确定你将如何在应用及其组件中管理状态有很大的帮助。

请查看Airtable的入门文章以了解更多信息,以及GitHub上的Airtable应用程序的例子。

Airtable仪表盘要求

在与客户团队一起审查了仪表盘模拟图后,要使用的数据类型很清楚。我们需要一系列的仪表盘组件,这些组件可以在仪表盘上显示为文本,以及不同指标的图表,可以随着时间的推移进行追踪。

教练和医疗专业人员需要能够为每个病人建立一个定制的仪表板,因此我们需要一个灵活的方式来添加和删除图表。其他与每个病人相关的静态数据将被显示出来,无论选择的是哪位病人。

在这种情况下,仪表盘部分归结为。

  • 一般信息--病人的姓名、电子邮件、电话号码、联系偏好、出生日期、年龄
  • 目标- 患者根据调查结果制定的目标
  • 一些统计数字--BMI、身高和体重
  • 用药情况- 列出病人已经使用的所有处方药
  • 疾病家族史- 有助于诊断某些疾病
  • 图表- Airtable仪表盘用户可以添加一个图表,并配置它在一段时间内可视化的指标。

除图表外,处理所有部分的一种方法是将目标、药物使用和家族史的所有栏目硬编码到仪表板中。然而,这将不允许客户团队在Typeform调查中添加新的问题,也不允许在Airtable表中添加新的列,以便在仪表盘上显示这些数据,而不需要开发人员更新自定义应用程序。

这个挑战的一个更优雅和可扩展的解决方案是找到一种方法来标记与特定仪表盘部分相关的列,并使用Airtable在使用表和字段模型时暴露的元数据来检索这些列。

这是通过使用字段描述来实现的,它可以将表中的列标记为与要显示给用户的仪表盘部分相关。然后,我们可以确保只有那些基地的创建者角色(管理员)才有能力修改这些字段描述,以改变仪表盘上出现的内容。为了说明这个解决方案,我们将主要关注一般信息中的项目以及如何展示图表。

创建一个#TAG#系统

考虑到仪表盘的各个部分,为某些部分制作可重复使用的标签,为某些列制作特定的标签是有意义的。对于像病人姓名、电子邮件和电话号码这样的项目,#NAME##EMAIL##PHONE# ,分别添加到每个字段的描述中。这将允许通过表的元数据来检索这些信息,就像这样。

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

对于需要从许多标签列中获取信息的仪表盘区域,我们将为每个仪表盘部分设置以下标签。

  • OBJ--目标
  • FAM--家庭历史
  • MED - 医学用途
  • CAN - 癌症特有的家族史
  • CHART - 任何应该被用来添加图表的列;必须是一个数量。

此外,重要的是将表中的列名与它在仪表板上收到的标签分开,所以任何收到#TAG# ,也能在其字段描述中收到两个#LABEL# 标签。一个字段描述将看起来像这样。

如果缺少#LABEL# 标签,我们将显示表中的列名。

我们可以在用前面的代码例子检索完字段后,用这样一个简单的函数解析出描述中设置的标签。

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

通过这个#TAG# 系统,我们主要实现了三件事。

  • 表中的列名(字段)可以根据需要改变。
  • 仪表板中的数据标签可以与列名不同。
  • 仪表板上的目标、药品使用情况、家庭历史和图表等部分可以由客户团队更新*,而不需要动用任何一行代码。*

在Airtable中保存状态

在React中,我们使用状态并将其作为道具传递给组件,以便在其状态发生变化时重新渲染该组件。通常情况下,这与推动仪表盘组件的API调用有关,但在Airtable中,我们已经有了所有的数据,只需要根据我们查看的病人过滤我们所显示的内容。此外,如果我们使用状态,它将不会在仪表盘本身的刷新过程中持久化数据。

那么,我们怎样才能将一个值持续到刷新之后,以保持仪表盘的过滤?幸运的是,Airtable为此提供了一个钩子,叫做 使用全球配置的钩子,它为安装在仪表盘上的应用程序维护一个键值存储。我们只需要在应用加载时实现从这个键值存储中检索值的逻辑,为我们的仪表盘组件提供动力。

使用useGlobalConfig 钩子更有用的是,当它的值被设置后,仪表盘组件及其子组件会重新渲染,所以你可以像在典型的React实现中使用状态变量那样使用Global Config。

引入图表

Airtable用它的Simple Chart App提供了图表的例子,它使用了React Charts,一个Chart.js(chart-ception)的React包装器。

在简单图表应用中,我们为整个应用提供了一个图表,但在我们的仪表盘应用中,我们需要用户在自己的仪表盘上添加和删除自己的图表的能力。更重要的是,在与客户团队的讨论中,似乎某些指标在同一个图表上查看会更好(比如舒张压和收缩压的读数)。

有了这个,我们有以下项目需要解决。

  • 为每个用户的图表保存状态(或使用全局配置更好)。
  • 允许每个图表有多个指标

这就是全局配置的力量派上用场的地方,因为我们可以使用键值存储来维护选定的指标和关于我们的图表列表的任何其他东西。当我们在用户界面中配置图表时,图表组件本身会因为全局配置的更新而被重新渲染。对于仪表盘的图表部分,这里有一个包含组件的gist供参考,重点是仪表盘charts.js单图表.js

传递给每个图表的表是用于其元数据以寻找字段的,而传递的记录已经被在导入dashboard_charts/index.js 的顶层仪表板组件中选择的病人过滤了。

请注意,在图表的下拉菜单中作为选项列出的字段是使用我们之前提到的#CHART# 标签拉出的,这一行是在useEffect 钩子中。

// single_chart/index.jsuseEffect(() => {
  (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]);


...

上面的代码显示了前面提到的setLabel 函数是如何与#TAG# 一起使用的,以增加#LABEL# 标签中提供的任何东西,并将其显示为字段下拉中的选项。

我们的图表组件利用了Chart.js提供的多轴功能,这是用React Charts显示的。我们只是通过用户界面对其进行了扩展,用户可以添加一个数据集和一个图表类型(线型或条型)。

在这种情况下,使用全局配置的关键是要知道每个键只能容纳一个字符串|布尔值|数字|空|GlobalConfigArray|GlobalConfigObject(见全局配置值参考)。

我们有以下项目需要维护每个图表。

  • chartTitle,它是自动生成的,可以由用户重新命名
  • 字段数组,其中每个项目都有。
    • 字段是Airtable中的fieldId
    • chartOptionChart.js文档中的单行或单条。
    • color为Airtable的颜色,来自colorUtils。
    • hex作为与Airtable颜色相关的十六进制代码。

为了管理这些,我发现最方便的是将这些数据串联成一个对象,而不是一路设置全局配置的键和值。请看下面的例子(gist中的globalConfig.json),其中包括通过病人过滤记录的Global Config值和一些用于支持typeahead过滤组件的相关变量(感谢react-bootstrap-typeahead)。

{
 "xCharts": {
   "chart-1605425876029": "{\"fields\":[{\"field\":\"fldxLfpjdmYeDOhXT\",\"chartOption\":\"line\",\"color\":\"blueBright\",\"hex\":\"#2d7ff9\"},{\"field\":\"fldqwG8iFazZD5CLH\",\"chartOption\":\"line\",\"color\":\"blueLight1\",\"hex\":\"#9cc7ff\"}],\"chartTitle\":\"Gráfico criado em 11/15/2020, 2:37:56 AM\"}",
   "chart-1605425876288": "{\"fields\":[{\"field\":\"fldGJZIdRlq3V3cKu\",\"chartOption\":\"line\",\"color\":\"blue\",\"hex\":\"#1283da\"}],\"chartTitle\":\"Gráfico 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\":\"Gráfico 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\":\"Gráfico 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\":\"Gráfico criado em 11/15/2020, 4:01:56 AM\"}",
   "chart-1605431704374": "{\"fields\":[{\"field\":\"fld7oBtl3iiHNHqoJ\",\"chartOption\":\"line\",\"color\":\"blue\",\"hex\":\"#1283da\"}],\"chartTitle\":\"Gráfico criado em 11/15/2020, 4:15:04 AM\"}"
 },
 "xPatientEmail": "elle@gmail.com",
 "xTypeaheadValue": "Elle Gold (elle@gmail.com)",
 "xSelectedValue": "[{\"label\":\"Elle Gold (elle@gmail.com)\",\"id\":\"elle@gmail.com\",\"name\":\"Elle Gold\",\"email\":\"elle@gmail.com\"}]"
}

注意。 上面包含的所有数据,以及下面动画中的数据,都不是真实的病人数据。

下面看看最后的结果。

Typeahead的情况如何?

为了按病人过滤,我们需要一种方法来选择一个病人,然后根据这个病人来过滤记录。在这一节中,我们将回顾这一点是如何实现的。

对于typeahead来说,react-bootstrap-typeahead是一个简单的选择,因为剩下的唯一步骤是为typeahead准备选项,将其与Airtable输入混合,用于样式设计和加载bootstrap,以及为我们的菜单准备一些其他样式。从你最喜欢的组件库中把组件放到Airtable应用中,并不像典型的React网页开发那样简单;然而,只需几个额外的步骤就能让一切看起来像你所期望的那样。

下面是最终的结果。

为了渲染Airtable输入并保持所有样式的一致性,react-bootstrap-typeahead附带了一个renderInput道具。在这里可以看到更多关于如何修改组件的渲染的信息。

对于bootstrap样式和覆盖我们的菜单项,我们使用了Airtable的以下两个工具。

参见gist中的frontend.js,了解typeahead实现的节选。

这一行被用来全局加载bootstrap。

// frontend/index.js

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

你会注意到一些新增的逻辑,比如处理悬停时的样式变化或重塑链接(<a></a> )以获得熟悉的bootstrap的外观和感觉。这也包括处理设置全局配置值的typeahead和过滤记录,这样,如果用户离开他们的仪表盘,刷新他们的页面,或者想与其他人分享这个仪表盘,应用程序会在仪表盘应用程序中持续保持所选的病人。这也允许用户在同一个Airtable仪表盘中并排安装这个相同应用的多个副本,选择不同的病人或使用不同的图表。

请记住,Airtable中的仪表盘也可供基地的所有用户使用,因此,无论哪个用户在同一时间看仪表盘,仪表盘上的这些自定义应用安装都会被过滤到相同的病人和图表。

让我们回顾一下到目前为止我们所涉及的内容。

  1. Airtable允许非技术用户和技术用户在Airtable中进行协作。
  2. Typeform带有一个Airtable集成,允许非技术用户将Typeform的结果映射到Airtable。
  3. Airtable应用程序提供了一个强大的方式来增强其Airtable基础,无论是从市场上选择还是建立一个自定义的应用程序。
  4. 开发人员可以通过这些应用以几乎可以想象的任何方式快速扩展Airtable。我们上面的例子只花了三周时间来设计和实现(当然,有现有库的巨大帮助)。
  5. 一个#TAG# 系统可以用来修改仪表盘,而不需要开发人员修改代码。这方面的用例有好有坏。如果使用这种策略,请确保将权限限制在创建者角色上。
  6. 使用全局配置允许开发者在应用安装中持久化数据。将其混入你的状态管理策略中,为你的组件提供种子数据。
  7. 不要指望从其他库和项目中直接拖放组件到你的Airtable App中。样式可以使用Airtable提供的loadCSSFromStringloadCSSFromURLAsync 工具加载。

面向未来

使用一个更复杂的中间件

使用Typeform和Airtable,配置问题与列的映射是很容易的,也很划算。

然而,有一个很大的缺点。如果你有一个超过100个问题的调查映射到Airtable,并且你需要修改一个映射,你必须删除整个映射并重新开始。这显然是不理想的,但对于一个免费的整合,我们可以处理这个问题。

其他的选择是由Zapier(或类似的)集成来管理Typeform和Airtable之间的数据,然后你可以修改任何问题到任何列的映射,而不必从头开始。这也有它自己的成本考虑因素。

希望在这里学到的一些经验和交流能帮助其他想用Airtable建立解决方案的人。

最后,你可以本文中讨论的文件来查看要点

了解基础知识

Airtable是一个关系型数据库吗?

简而言之,是的。数据的组织方式类似于关系型数据库,但不需要同样的脚本知识来建立你自己的Airtable(数据)库。另一个主要区别是Airtable能够容纳的数据量(行/记录的数量)(专业计划中每个基地有50,000条)。

Airtable有什么用?

Airtable可以作为一个数据库和用户界面,在同一数据上进行协作,不需要编码。它结合了所有熟悉的电子表格和类似于关系型数据库的优势。你也可以用自动程序和应用程序来增强Airtable的功能,此外它还有一个API。

Typeform的用途是什么?

Typeform是一个数据收集工具,允许你创建表单并通过URL收集回复。它的优势在于它的用户体验、响应式界面、与其他工具的集成,以及创建逻辑跳跃和隐藏字段的能力,以根据回应或预置数据为用户修改调查体验。

Airtable是免费使用的吗?

是的,有一个免费层级,但为了建立自定义应用程序或使用市场上的应用程序,需要专业订阅。

Typeform是免费的吗?

有一个免费层级可以开始使用,但需要一个付费计划来使用内置的集成(Airtable、Mailchimp)并超越免费计划的限制。

标签

ReactDashboardAirtable

自由职业者? 寻找你的下一份工作。

React.js开发工作

[

查看完整资料

](www.toptal.com/resume/dyla…)

Dylan Golow

开发者

关于作者

Dylan是一名全栈工程师,他热衷于用深思熟虑和有影响力的解决方案将人们与技术联系起来。他在不同行业的企业软件方面有超过5年的经验,包括技术、电子商务和专业服务。迪伦喜欢从零开始创建MVP到全面的解决方案--用户旅程的创建、堆栈的选择、前端和后端开发。他目前的重点是在以太坊区块链上构建去中心化的应用程序。

聘请迪伦

评论

热情_curbed

嗨,Dylan,很好的文章!只是想知道,输入验证是发生在Typeform上,还是有办法让Airtable验证调查的输入并向Typeform发送错误信息?我想,输入需要专门化,并涉及到一个自定义的API调用。

请启用JavaScript以查看由Disqus提供的评论。评论由Disqus提供

世界级的文章,每周交付。

获得伟大的内容

订阅意味着同意我们的隐私政策

谢谢您!
请查看您的收件箱,确认您的邀请。

热门文章

工程图示Chevron后端

使用Express.js路由进行基于承诺的错误处理

工程图示 ChevronWeb前端

企业应用的最佳React状态管理工具

工程图示 ChevronBack-end

使用AWS SSM进行SSH日志和会话管理

工程图ChevronTechnology

明尼苏达大学的Linux禁令引发了对开源的质疑

查看我们的相关人才

React.jsReactWeb 开发人员

自由职业者? 寻找你的下一份工作。

React.js开发工作

聘请作者

[

查看完整资料

](www.toptal.com/resume/dyla…)

Dylan Golow

开发人员

阅读 下一页

工程图示Chevron后端

使用Express.js路由进行基于承诺的错误处理