Charts

The Dashboard page continues with a donut chart that visualizes some of the same facts reported using InfoCard. We define it as a Statuses component whose width and title are defined by its containing Dashboard component. Here we'll use it standalone.

<App>
  <Statuses />
</App>

Statuses wraps EChart in a SimpleDonutChart component that accepts familiar props: data, dataKey, and nameKey. Internally it builds the ECharts option object.

  • data: The ubiquitous attribute that refers to data that may be defined literally or, as in this case, via an API call backed by a database query.
  • dataKey: The object key that holds data.
  • nameKey: The object key whose value is the data label.
<Component name="SimpleDonutChart">
  <EChart
    height="{$props.height || '100%'}"
    option="{
      {
        tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
        legend: { 
          orient: $props.layout === 'vertical' ? 'vertical' : 'horizontal', 
          bottom: 0 
        },
        series: [{
          type: 'pie',
          radius: ['40%', '70%'],
          label: { show: true, formatter: '{b}' },
          data: ($props.data || []).map(function(item) {
            return { 
              name: item[$props.nameKey || 'name'], 
              value: item[$props.dataKey || 'value'] 
            }
          })
        }]
      }
    }" />
</Component>
<Component name="Statuses">
  <DataSource 
    id="dashboardStats" 
    url="/resources/files/dashboard-stats.json" />
  <Card title="Statuses" height="400px" width="{$props.width}">
    <SimpleDonutChart
      data="{
        [
          {
            name: 'sent',
            value: dashboardStats.value[0].sent_invoices
          },
          {
            name: 'draft',
            value: dashboardStats.value[0].draft_invoices
          },
          {
            name: 'paid',
            value: dashboardStats.value[0].paid_invoices
          },
        ]
      }"
      dataKey="value"
      nameKey="name"
    />
  </Card>
</Component>
<App>
  <Statuses />
</App>

Statuses wraps EChart in a SimpleDonutChart component that accepts familiar props: data, dataKey, and nameKey. Internally it builds the ECharts option object.

  • data: The ubiquitous attribute that refers to data that may be defined literally or, as in this case, via an API call backed by a database query.
  • dataKey: The object key that holds data.
  • nameKey: The object key whose value is the data label.
<Component name="SimpleDonutChart">
  <EChart
    height="{$props.height || '100%'}"
    option="{
      {
        tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
        legend: { 
          orient: $props.layout === 'vertical' ? 'vertical' : 'horizontal', 
          bottom: 0 
        },
        series: [{
          type: 'pie',
          radius: ['40%', '70%'],
          label: { show: true, formatter: '{b}' },
          data: ($props.data || []).map(function(item) {
            return { 
              name: item[$props.nameKey || 'name'], 
              value: item[$props.dataKey || 'value'] 
            }
          })
        }]
      }
    }" />
</Component>
<Component name="Statuses">
  <DataSource 
    id="dashboardStats" 
    url="/resources/files/dashboard-stats.json" />
  <Card title="Statuses" height="400px" width="{$props.width}">
    <SimpleDonutChart
      data="{
        [
          {
            name: 'sent',
            value: dashboardStats.value[0].sent_invoices
          },
          {
            name: 'draft',
            value: dashboardStats.value[0].draft_invoices
          },
          {
            name: 'paid',
            value: dashboardStats.value[0].paid_invoices
          },
        ]
      }"
      dataKey="value"
      nameKey="name"
    />
  </Card>
</Component>

Multiseries charts

The SimpleDonutChart works with a single series of data and uses dataKey. A SimpleBarChart wrapper can display multiple series denoted by yKeys. We see that in the MonthlyStatus chart.

<App>
  <MonthlyStatus />
</App>

<Component name="SimpleBarChart">
  <EChart
    height="{$props.height || '100%'}"
    option="{
      {
        tooltip: { trigger: 'axis' },
        legend: $props.showLegend ? {} : undefined,
        grid: { containLabel: true },
        xAxis: $props.orientation === 'horizontal'
          ? { 
              type: 'category', 
              data: ($props.data || []).map(function(d) { return d[$props.xKey] }), 
              axisLabel: { formatter: $props.tickFormatterX } 
            }
          : { 
              type: 'value', 
              axisLabel: { formatter: $props.tickFormatterY } 
            },
        yAxis: $props.orientation === 'horizontal'
          ? { 
              type: 'value', 
              axisLabel: { formatter: $props.tickFormatterY } 
            }
          : { 
              type: 'category', 
              data: ($props.data || []).map(function(d) { return d[$props.xKey] }), 
              axisLabel: { formatter: $props.tickFormatterX } 
            },
        series: ($props.yKeys || []).map(function(k) {
          return {
            type: 'bar',
            name: k,
            stack: ($props.stacked === 'true' || $props.stacked === true) 
              ? 'total' : undefined,
            data: ($props.data || []).map(function(d) { return d[k] })
          }
        })
      }
    }" />
</Component>

<Component name="MonthlyStatus">
  <DataSource
      id="monthlyStatus"
      url="/resources/files/monthly-status.json"
      method="GET" />
  <VStack width="{$props.width}">
      <H1>{$props.title}</H1>
      <Card height="400px">
      <SimpleBarChart
        orientation="horizontal"
        data="{monthlyStatus.value}"
        xKey="month"
        yKeys="{['paid_revenue', 'sent_revenue']}"
        stacked="true"
        showLegend="true"
        tickFormatterX="{(value) => window.formatMonth(value)}"
      />
      </Card>
  </VStack>
</Component>
<App>
  <MonthlyStatus />
</App>

<Component name="SimpleBarChart">
  <EChart
    height="{$props.height || '100%'}"
    option="{
      {
        tooltip: { trigger: 'axis' },
        legend: $props.showLegend ? {} : undefined,
        grid: { containLabel: true },
        xAxis: $props.orientation === 'horizontal'
          ? { 
              type: 'category', 
              data: ($props.data || []).map(function(d) { return d[$props.xKey] }), 
              axisLabel: { formatter: $props.tickFormatterX } 
            }
          : { 
              type: 'value', 
              axisLabel: { formatter: $props.tickFormatterY } 
            },
        yAxis: $props.orientation === 'horizontal'
          ? { 
              type: 'value', 
              axisLabel: { formatter: $props.tickFormatterY } 
            }
          : { 
              type: 'category', 
              data: ($props.data || []).map(function(d) { return d[$props.xKey] }), 
              axisLabel: { formatter: $props.tickFormatterX } 
            },
        series: ($props.yKeys || []).map(function(k) {
          return {
            type: 'bar',
            name: k,
            stack: ($props.stacked === 'true' || $props.stacked === true) 
              ? 'total' : undefined,
            data: ($props.data || []).map(function(d) { return d[k] })
          }
        })
      }
    }" />
</Component>

<Component name="MonthlyStatus">
  <DataSource
      id="monthlyStatus"
      url="/resources/files/monthly-status.json"
      method="GET" />
  <VStack width="{$props.width}">
      <H1>{$props.title}</H1>
      <Card height="400px">
      <SimpleBarChart
        orientation="horizontal"
        data="{monthlyStatus.value}"
        xKey="month"
        yKeys="{['paid_revenue', 'sent_revenue']}"
        stacked="true"
        showLegend="true"
        tickFormatterX="{(value) => window.formatMonth(value)}"
      />
      </Card>
  </VStack>
</Component>