Skip to content

Apache ECharts — Dashboard Analytics Research

Project: Forma3D.Connect
Version: 1.0
Date: February 13, 2026
Status: Implemented → ADR-050


Executive Summary

This document evaluates Apache ECharts as the charting library for building an analytics dashboard in Forma3D.Connect. The current dashboard shows only basic stat cards and lists — no visual charts or trend analysis. Adding pie/donut charts for orders, print jobs, and shipments (grouped by status) along with revenue breakdowns and trend lines would give operators immediate visual insight into their 3D print farm operations.

Recommendation

Adopt Apache ECharts via echarts-for-react as the charting library. It offers the richest pie chart variants (donut, rose, nested, sunburst), excellent TypeScript support, and professional label formatting — all of which align with the analytics requirements below. The bundle size trade-off is acceptable when using on-demand imports (~150 KB gzipped vs ~320 KB full).


Table of Contents

  1. Current State
  2. Library Comparison
  3. ECharts Technical Assessment
  4. Proposed Dashboard Metrics
  5. Chart Specifications
  6. Mockups
  7. Implementation Plan
  8. Backend API Requirements
  9. Risks & Mitigations
  10. Decision

1. Current State

The current dashboard (apps/web/src/pages/dashboard.tsx) provides:

Element Content
System Health Card Status indicator, database connection, uptime, version
Stat Cards (×4) Pending Orders, Processing, Active Print Jobs, Needs Attention
Recent Orders List Last 5 orders with status badges
Active Print Jobs List Last 5 active jobs with printer name
Welcome Card Static branding message

What's missing:

  • No visual charts or graphs
  • No time-based filtering (today / week / month / all-time)
  • No revenue or monetary aggregations
  • No trend analysis (is the business growing?)
  • No print success/failure rate visualization
  • No shipping pipeline visibility

2. Library Comparison

Criterion ECharts Recharts Chart.js Nivo
Pie/Donut Variants Basic, donut, rose, nested, sunburst, semi-circle Basic, donut Basic, doughnut Basic, donut
TypeScript Native (written in TS) Good Via @types Native
Bundle Size (gz) ~320 KB full / ~150 KB tree-shaken ~160 KB ~11 KB Varies by package
Label Customization Rich text, leader lines, inside/outside Basic Plugin-based Good
Animation Quality Excellent built-in Basic Good Good
Interactive Tooltips Advanced (formatter functions, rich HTML) Component-based Plugin-based Built-in
React 19 Compat Works (peer dep override) Had breaking issues Works via wrapper Works
GitHub Stars ~65.5K ~26.5K ~67K ~14K
npm Downloads/wk ~1.7M ~11.7M ~6.4M ~14K
Dark Theme Built-in dark theme Manual Manual Theme prop
API Style JSON configuration JSX declarative Imperative JSX declarative

Verdict: ECharts wins on chart variety, label richness, and built-in dark theme support — critical for our admin dashboard. The bundle size cost is manageable with on-demand imports. Recharts has broader React adoption but lacks the pie chart variants we need.


3. ECharts Technical Assessment

3.1 Packages Required

echarts           ^5.6.0    # Core library
echarts-for-react ^3.0.6    # React wrapper (3 KB gzipped)

3.2 React 19 Compatibility

echarts-for-react@3.0.6 declares peer dependencies for React ^15 || ^16 || ^17. It works with React 19 without issues — only a peer dependency warning. Fix with pnpm config:

{
  "pnpm": {
    "peerDependencyRules": {
      "allowedVersions": {
        "echarts-for-react>react": "19",
        "echarts-for-react>react-dom": "19"
      }
    }
  }
}

3.3 Tree-Shaking (On-Demand Imports)

Full import of ECharts adds ~320 KB gzipped. Using on-demand imports reduces this to ~150 KB:

// libs/ui/src/charts/echarts-setup.ts
import * as echarts from 'echarts/core';
import { PieChart, BarChart, LineChart, GaugeChart } from 'echarts/charts';
import {
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent,
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';

echarts.use([
  PieChart,
  BarChart,
  LineChart,
  GaugeChart,
  TitleComponent,
  TooltipComponent,
  LegendComponent,
  GridComponent,
  CanvasRenderer,
]);

export { echarts };
// Usage in components
import ReactEChartsCore from 'echarts-for-react/lib/core';
import { echarts } from '@forma3d/ui/charts/echarts-setup';

<ReactEChartsCore echarts={echarts} option={chartOption} />

3.4 Dark Theme Integration

ECharts ships with a built-in dark theme. We can also create a custom Forma3D theme that matches our CSS variables:

echarts.registerTheme('forma3d-dark', {
  backgroundColor: 'transparent',
  textStyle: { color: 'var(--color-text-primary)' },
  // ... custom colors matching our design system
});

4. Proposed Dashboard Metrics

4.1 Order Metrics

Metric Description Chart Type Time Filter
Orders by Status Count + EUR per status (PENDING, PROCESSING, PARTIALLY_COMPLETED, COMPLETED, FAILED, CANCELLED) Donut chart Today / Week / Month / All-time
Revenue by Status EUR breakdown by order status Inner ring of nested donut Same
Order Volume Trend Daily order count over time Line chart Last 7d / 30d / 90d
Average Order Value EUR average per order KPI card Same
Revenue Trend Daily revenue in EUR Bar chart Last 7d / 30d
Top Products by Revenue Revenue per product SKU Horizontal bar or sunburst Month / All-time

4.2 Print Job Metrics

Metric Description Chart Type Time Filter
Print Jobs by Status Count per status (QUEUED, ASSIGNED, PRINTING, COMPLETED, FAILED, CANCELLED) Donut chart Today / Week / Month / All-time
Print Success Rate % of completed vs total finished jobs Gauge chart Same
Average Print Duration Hours per job KPI card Same
Failure Reasons Breakdown of why jobs fail Rose/Nightingale chart Month / All-time
Printer Utilization Jobs per printer Horizontal bar Today / Week
Retry Rate % of jobs requiring retries KPI card Same

4.3 Shipment Metrics

Metric Description Chart Type Time Filter
Shipments by Status Count per status (PENDING, LABEL_CREATED, ANNOUNCED, IN_TRANSIT, DELIVERED, FAILED, CANCELLED) Donut chart Today / Week / Month / All-time
Delivery Rate % of DELIVERED vs total KPI card Same
Avg. Time to Delivery Days from label creation to delivery KPI card Month
Shipping Pipeline Funnel visualization from pending to delivered Funnel chart This week
Failed Shipments Count and reasons Badge + list Same

4.4 Business Overview Metrics

Metric Description Chart Type
Revenue Breakdown Nested: inner = order status, outer = product SKU Nested donut / Sunburst
Fulfillment Pipeline Order → Print → Ship conversion funnel Funnel chart
Order-to-Delivery Time Average time from order creation to shipment delivery KPI card + trend line
Daily Summary Orders received, completed, shipped today Multi-metric KPI row

5. Chart Specifications

5.1 Orders by Status — Donut Chart

The primary chart. Shows the distribution of orders across all 6 statuses for the selected time period.

Data structure:

interface OrderStatusMetric {
  status: OrderStatus;
  count: number;
  percentage: number;
  totalValue: number; // EUR
}

interface OrderStatusChartData {
  period: 'today' | 'week' | 'month' | 'all';
  totalOrders: number;
  totalRevenue: number;
  avgOrderValue: number;
  statuses: OrderStatusMetric[];
}

ECharts config (simplified):

const option: EChartsOption = {
  tooltip: {
    trigger: 'item',
    formatter: ({ data }) =>
      `${data.name}<br/>` +
      `${data.value} orders (${data.percentage}%)<br/>` +
      `€${data.totalValue.toLocaleString()}`,
  },
  legend: { orient: 'vertical', left: 'left' },
  series: [
    {
      type: 'pie',
      radius: ['40%', '70%'], // Donut
      avoidLabelOverlap: true,
      label: {
        formatter: '{b}\n{c} orders ({d}%)\n€{totalValue}',
        position: 'outside',
      },
      labelLine: { show: true },
      data: [
        { value: 12, name: 'Pending', itemStyle: { color: '#9CA3AF' } },
        { value: 24, name: 'Processing', itemStyle: { color: '#3B82F6' } },
        { value: 8, name: 'Partial', itemStyle: { color: '#F59E0B' } },
        { value: 28, name: 'Completed', itemStyle: { color: '#10B981' } },
        { value: 4, name: 'Failed', itemStyle: { color: '#EF4444' } },
        { value: 4, name: 'Cancelled', itemStyle: { color: '#6B7280' } },
      ],
    },
  ],
};

Color mapping (matching existing constants):

Status Color Hex
PENDING Gray #9CA3AF
PROCESSING Blue #3B82F6
PARTIALLY_COMPLETED Amber #F59E0B
COMPLETED Green #10B981
FAILED Red #EF4444
CANCELLED Gray (dim) #6B7280

5.2 Print Jobs by Status — Donut Chart

Same donut pattern, with print-job-specific statuses and colors.

Color mapping:

Status Color Hex
QUEUED Light Blue #60A5FA
ASSIGNED Indigo #818CF8
PRINTING Purple #A855F7
COMPLETED Green #10B981
FAILED Red #EF4444
CANCELLED Gray (dim) #6B7280

Center label: Total jobs count + average duration per job.

5.3 Shipments by Status — Donut Chart

Seven statuses representing the full shipping pipeline.

Color mapping:

Status Color Hex
PENDING Gray #9CA3AF
LABEL_CREATED Amber #F59E0B
ANNOUNCED Light Blue #60A5FA
IN_TRANSIT Indigo #818CF8
DELIVERED Green #10B981
FAILED Red #EF4444
CANCELLED Gray (dim) #6B7280

Center label: Total shipments + delivered count.

5.4 Revenue Breakdown — Nested Donut / Sunburst

A nested chart with two rings:

  • Inner ring: Revenue by order status (Completed, Processing, Pending)
  • Outer ring: Revenue by product SKU within each status

This leverages ECharts' unique nested pie capability — not available in Recharts or Chart.js.

5.5 Print Success Rate — Gauge Chart

A semi-circle gauge showing the success rate percentage (COMPLETED / (COMPLETED + FAILED)) with:

  • Green zone: 90–100%
  • Yellow zone: 75–90%
  • Red zone: < 75%

5.6 Order Volume Trend — Line Chart

A time-series line chart showing daily order count. Supports hover tooltip with date and count.

5.7 Revenue Trend — Bar Chart

A bar chart showing daily/weekly revenue in EUR. Each bar is colored by the dominant order status for that day.


6. Mockups

6.1 Full Dashboard Layout

The proposed dashboard layout with stat cards, three donut charts, revenue bar chart, and order trend line:

Dashboard Full Layout

Layout structure:

  • Row 1: 4 enhanced KPI stat cards (replacing existing simple stat cards with trend indicators like "+3 from yesterday")
  • Row 2: 3 donut charts side-by-side (Orders | Print Jobs | Shipments) with a single shared period dropdown (Today / Last Week / Last Month / All Time)
  • Row 3: Revenue bar chart — "This Week" (left) + Order trend line — "Last 30 Days" (right)
  • Row 4: Existing Recent Orders + Active Print Jobs lists (moved down, untouched)

6.2 Orders by Status — Donut Chart

Donut chart with leader-line labels showing count, percentage, and EUR amount per status. Center displays total orders and revenue.

Orders Pie Chart

Features:

  • Time filter tabs: Today | This Week | This Month | All Time
  • Center summary: total order count + total EUR
  • Leader-line labels: status name, count, percentage, EUR
  • Tooltip on hover with detailed breakdown
  • Clickable slices to navigate to filtered order list

6.3 Print Jobs by Status — Donut Chart

Similar donut with print job-specific colors. Center shows total jobs and average duration.

Print Jobs Pie Chart

Features:

  • Time filter tabs
  • Center summary: total jobs + average duration
  • Side KPI cards: Total Printers, Active Printers, Error Rate
  • Clickable slices for filtered job views

6.4 Shipments by Status — Donut Chart

Seven-segment donut reflecting the full shipping lifecycle from pending to delivered.

Shipments Pie Chart

Features:

  • Time filter tabs
  • Center summary: total shipments + delivered count
  • Color gradient from gray (pending) through blue (in-transit) to green (delivered)
  • Visual pipeline feeling — cold-to-warm progression

6.5 Advanced Charts — Revenue Breakdown & Print Success Rate

Nested sunburst chart for revenue analysis and gauge chart for print success rate:

Advanced Charts

Revenue Breakdown (left):

  • Inner ring: revenue by order status
  • Outer ring: revenue by product SKU
  • ECharts-exclusive feature (nested pie/sunburst)

Print Success Rate (right):

  • Semi-circle gauge: overall success percentage
  • Companion rose chart: failure reason breakdown
  • Color zones: green (90%+), yellow (75–90%), red (<75%)

7. Implementation Plan

Phase 1: Foundation (1–2 days)

  1. Install dependencies: echarts, echarts-for-react
  2. Configure pnpm peer dependency overrides for React 19
  3. Create shared chart setup module in libs/ui with on-demand imports
  4. Register custom Forma3D dark theme
  5. Create reusable <ChartCard> wrapper component with time filter tabs

Phase 2: Core Donut Charts (2–3 days)

  1. Build <OrderStatusChart> component
  2. Build <PrintJobStatusChart> component
  3. Build <ShipmentStatusChart> component
  4. Create TanStack Query hooks for each: useOrderStatusMetrics(), usePrintJobMetrics(), useShipmentMetrics()
  5. Add time-period selector (today/week/month/all) with query parameter support
  6. Integrate into dashboard page (Row 2)

Phase 3: Trend & Revenue Charts (2–3 days)

  1. Build <OrderTrendChart> (line chart)
  2. Build <RevenueTrendChart> (bar chart)
  3. Build <RevenueBreakdownChart> (nested donut)
  4. Create corresponding backend endpoints and query hooks
  5. Integrate into dashboard page (Row 3)

Phase 4: Advanced Charts (1–2 days)

  1. Build <PrintSuccessGauge> component
  2. Build <FailureReasonsChart> (rose/nightingale)
  3. Add to a dedicated analytics sub-section or expandable panel

Phase 5: Polish (1 day)

  1. Responsive behavior (stack charts on mobile)
  2. Loading states and empty states for each chart
  3. Click-to-filter navigation (click a pie slice to go to filtered list)
  4. Accessibility: chart descriptions for screen readers
  5. Performance testing: verify bundle size impact

Total estimate: 7–11 days


8. Backend API Requirements

New endpoints needed in apps/api:

GET /api/v1/analytics/orders

// Query params
interface OrderAnalyticsQuery {
  period: 'today' | 'week' | 'month' | 'all';
}

// Response
interface OrderAnalyticsResponse {
  period: string;
  totalOrders: number;
  totalRevenue: number; // EUR
  avgOrderValue: number; // EUR
  statuses: Array<{
    status: OrderStatus;
    count: number;
    percentage: number;
    totalValue: number; // EUR
  }>;
}

GET /api/v1/analytics/print-jobs

interface PrintJobAnalyticsResponse {
  period: string;
  totalJobs: number;
  avgDurationSeconds: number;
  successRate: number; // 0–100
  retryRate: number; // 0–100
  statuses: Array<{
    status: PrintJobStatus;
    count: number;
    percentage: number;
  }>;
}

GET /api/v1/analytics/shipments

interface ShipmentAnalyticsResponse {
  period: string;
  totalShipments: number;
  deliveryRate: number; // 0–100
  avgDeliveryDays: number;
  statuses: Array<{
    status: ShipmentStatus;
    count: number;
    percentage: number;
  }>;
}

GET /api/v1/analytics/trends

interface TrendsQuery {
  metric: 'orders' | 'revenue';
  days: 7 | 30 | 90;
}

interface TrendsResponse {
  metric: string;
  dataPoints: Array<{
    date: string; // ISO date
    value: number;
  }>;
}

GET /api/v1/analytics/revenue-breakdown

interface RevenueBreakdownResponse {
  totalRevenue: number;
  byStatus: Array<{
    status: OrderStatus;
    revenue: number;
    products: Array<{
      sku: string;
      name: string;
      revenue: number;
    }>;
  }>;
}

All endpoints are scoped to the authenticated tenant (tenantId from JWT).


9. Risks & Mitigations

Risk Impact Probability Mitigation
Bundle size increase Slower initial load Medium On-demand imports (~150 KB gz vs ~320 KB full); lazy-load analytics page
React 19 peer dep warning Build noise High pnpm peerDependencyRules override; monitor for echarts-for-react v4
Performance with large datasets Slow rendering Low ECharts handles 10M+ data points; our dataset is small (thousands)
echarts-for-react maintenance Stale wrapper Medium Wrapper is thin (3 KB); easy to fork or replace with direct useRef integration
Accessibility Screen reader gaps Medium Use ECharts aria option for auto-generated descriptions; add role="img" and aria-label
Mobile responsiveness Charts too small Medium Stack vertically on mobile; use responsive option based on container width
Backend query performance Slow analytics endpoints Medium Add database indexes on status + createdAt; consider materialized views for all-time aggregations

10. Decision

Why ECharts over alternatives:

  1. Richest pie chart variety — donut, rose, nested, sunburst — all needed for our use cases
  2. Professional label formatting — leader lines with multi-line rich text labels showing count + percentage + EUR
  3. Built-in dark theme — matches our existing dark-mode admin dashboard
  4. Excellent TypeScript support — native type definitions for all chart options
  5. Interactive features — click handlers on slices for drill-down navigation to filtered views
  6. Single library — covers all chart types (pie, bar, line, gauge, sunburst, funnel) without mixing libraries
  7. Active ecosystem — 65K+ GitHub stars, ~1.7M weekly npm downloads, Apache Foundation backing

Trade-offs accepted:

  • Larger bundle than Chart.js (~150 KB gz with tree-shaking vs ~11 KB)
  • JSON config API style instead of JSX components (acceptable — config objects are easily typed and composable)
  • Peer dependency override needed for React 19 (non-blocking)

Packages to Install

pnpm add echarts echarts-for-react

Priority Order

  1. P0 — Orders by Status donut (most valuable — immediate visibility into order pipeline)
  2. P0 — Print Jobs by Status donut (core operational metric for print farm management)
  3. P1 — Shipments by Status donut (shipping pipeline visibility)
  4. P1 — Revenue trend bar chart (business health indicator)
  5. P2 — Order volume trend line (growth tracking)
  6. P2 — Revenue breakdown sunburst (product-level analysis)
  7. P3 — Print success gauge (operational quality metric)
  8. P3 — Failure reasons rose chart (root cause analysis)

References