Display Funnel Charts

Contents

Overview

This article introduces how to display a funnel chart in a Kintone App using Apache ECharts (External link) .

  • A funnel chart visually represents how quantities decrease at each stage of a process in the shape of a funnel.
  • It shows how items move through a process, so it can be used to analyze drop-off at each stage.

Sample Image

This article uses a hiring App named "Applicant Tracking" as the sample. The funnel chart shows how many candidates are progressing through each stage.

In this example, the funnel chart shows a sharp drop-off from Applications to Document Screening. The Document Screening stage may be the bottleneck. The process for this stage may need to be reviewed.

Prepare the App

Create an App (External link) named "Applicant Tracking" with the following fields and settings.

Field Type Field Name Field Code Options
Drop-down Hiring Stage hiring_stage Set the following options:
  • Offer Acceptance
  • Offer
  • Final Interview
  • Third Interview
  • Second Interview
  • First Interview
  • Document Screening
  • Applications
Number Candidates candidates

The form should look like the below screenshot.

After creating the App, add a few records inside, like the following.

Hiring Stage Candidates
Offer Acceptance 4
Offer 11
Final Interview 21
Third Interview 35
Second Interview 68
First Interview 105
Document Screening 150
Applications 452

JavaScript Settings

This customization has two JavaScript settings that need to be set. The first is for loading the ECharts library. The second is for creating the chart, based on the Kintone data.

Set the Library

This example uses Apache ECharts v6.1.0. Set the following URL into the App's JavaScript and CSS Customization settings (External link) .

Set the JavaScript Code

Type the following code into a text editor and save it as a JavaScript file. Upload it to the App's JavaScript and CSS Customization settings.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
(() => {
  'use strict';

  // Kintone Part
  const STAGE_FIELD = 'hiring_stage';
  const CANDIDATES_FIELD = 'candidates';

  // Get the stage names and candidate counts from the records in the event object
  const getFunnelData = (records) => {
    return {
      labels: records.map((record) => record[STAGE_FIELD].value),
      data: records.map((record) => Number(record[CANDIDATES_FIELD].value)),
    };
  };

  // Funnel Chart Part
  const CHART_ID = 'hiring-funnel-chart';

  const renderFunnelChart = (header, labels, data) => {
    // Create an element for ECharts to draw the chart
    const chartElement = document.createElement('div');
    chartElement.id = CHART_ID;
    chartElement.style.cssText = 'max-width:800px;height:420px';
    header.appendChild(chartElement);

    const chart = echarts.init(chartElement);
    chart.setOption({
      series: [{
        type: 'funnel',
        label: {
          show: true,
          // Show the stage name and count on each segment
          position: 'inside',
          formatter: '{b}\n{c}',
        },
        data: labels.map((label, index) => ({
          name: label,
          value: data[index],
        })),
      }],
    });
  };

  // Display the chart on the Record List page
  kintone.events.on('app.record.index.show', (event) => {
    const header = kintone.app.getHeaderSpaceElement();
    if (!header || document.getElementById(CHART_ID)) return event;
    const { labels, data } = getFunnelData(event.records);
    renderFunnelChart(header, labels, data);
    return event;
  });
})();
caution
Caution

Caution: The order in which JavaScript and CSS are uploaded to an App matters. In this example, upload the ECharts library before the customization file. You can change the order of uploads by clicking and dragging on the arrows for each item on the Upload JavaScript / CSS page.

Click Save, and then on Update App. A funnel chart should be displayed above the list of records.

Code Explanation

This section explains the sample code above.

Get records from the event object

When the app.record.index.show event runs, the records displayed on the Record List page are stored in the event object. The records argument stores the array of records within the event object. getFunnelData() receives those records.

map() is used to extract data from the Hiring Stage and Candidates fields. The data are then transformed into two separate arrays, labels and data.

1
2
3
4
5
6
7
8
9
  const STAGE_FIELD = 'hiring_stage';
  const CANDIDATES_FIELD = 'candidates';

  const getFunnelData = (records) => {
    return {
      labels: records.map((record) => record[STAGE_FIELD].value),
      data: records.map((record) => Number(record[CANDIDATES_FIELD].value)),
    };
  };

Draw the Funnel Chart

When drawing the funnel chart, the name property defines the text label (the Hiring Stage), and the value parameter defines the number label (the Candidate count). ECharts places the largest segment (the largest count) at the top. position: 'inside' places the stage name and count inside each segment.

For more details on ECharts options, refer to the ECharts documentation (External link) .

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  // Funnel Chart Part
  const CHART_ID = 'hiring-funnel-chart';
  
  const renderFunnelChart = (header, labels, data) => {
    // Create an element for ECharts to draw the chart
    const chartElement = document.createElement('div');
    chartElement.id = CHART_ID;
    chartElement.style.cssText = 'max-width:800px;height:420px';
    header.appendChild(chartElement);

    const chart = echarts.init(chartElement);
    chart.setOption({
      series: [{
        type: 'funnel',
        label: {
          show: true,
          // Show the stage name and count on each segment
          position: 'inside',
          formatter: '{b}\n{c}',
        },
        data: labels.map((label, index) => ({
          name: label,
          value: data[index],
        })),
      }],
    });
  };

Prevent chart duplication bug

The app.record.index.show event runs when the Record List page is displayed. Actions such as applying a filter cause the list to be redisplayed and trigger this event again.

In this case, a duplication bug can occur, resulting in multiple charts being displayed above the list of records. To prevent this, the code checks for existing graph elements in the header.

If no duplicate chart exists, getFunnelData() is called with event.records, the records stored in the event object.

1
2
3
4
5
6
7
  kintone.events.on('app.record.index.show', (event) => {
    const header = kintone.app.getHeaderSpaceElement();
    if (!header || document.getElementById(CHART_ID)) return event;
    const { labels, data } = getFunnelData(event.records);
    renderFunnelChart(header, labels, data);
    return event;
  });

Reference