{"spec_id":"dashboard-metrics-tiles","library":"echarts","language":"javascript","code":"// anyplot.ai\n// dashboard-metrics-tiles: Real-Time Dashboard Tiles\n// Library: echarts 6.1.0 | JavaScript 22.23.2\n// Quality: 90/100 | Created: 2026-09-02\n\nconst t = window.ANYPLOT_TOKENS;\nconst size = window.ANYPLOT_SIZE;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// Tiny LCG so sparklines look like real telemetry without a network fetch.\nlet seed = 42;\nfunction nextRandom() {\n  seed = (seed * 1664525 + 1013904223) % 4294967296;\n  return seed / 4294967296;\n}\n\nfunction makeHistory(start, end, points, noise) {\n  const values = [];\n  for (let i = 0; i < points; i += 1) {\n    const progress = i / (points - 1);\n    const base = start + (end - start) * progress;\n    const jitter = (nextRandom() - 0.5) * 2 * noise;\n    values.push(Math.max(0, base + jitter));\n  }\n  values[values.length - 1] = end;\n  return values;\n}\n\n// status: \"good\" | \"warning\" | \"critical\" — overall health of the metric.\n// higherIsBetter: whether a rising value is the favorable direction.\nconst metrics = [\n  {\n    name: \"CPU Usage\",\n    value: 45,\n    unit: \"%\",\n    changePercent: -5,\n    status: \"good\",\n    higherIsBetter: false,\n    history: makeHistory(52, 45, 20, 3),\n  },\n  {\n    name: \"Memory Usage\",\n    value: 72,\n    unit: \"%\",\n    changePercent: 8,\n    status: \"warning\",\n    higherIsBetter: false,\n    history: makeHistory(64, 72, 20, 2.5),\n  },\n  {\n    name: \"Response Time\",\n    value: 120,\n    unit: \"ms\",\n    changePercent: -15,\n    status: \"good\",\n    higherIsBetter: false,\n    history: makeHistory(142, 120, 20, 6),\n  },\n  {\n    name: \"Error Rate\",\n    value: 2.3,\n    unit: \"%\",\n    changePercent: 12,\n    status: \"critical\",\n    higherIsBetter: false,\n    history: makeHistory(2.0, 2.3, 20, 0.15),\n  },\n  {\n    name: \"Active Users\",\n    value: 8540,\n    unit: \"\",\n    changePercent: 6,\n    status: \"good\",\n    higherIsBetter: true,\n    history: makeHistory(8020, 8540, 20, 120),\n  },\n  {\n    name: \"Requests / sec\",\n    value: 1240,\n    unit: \"\",\n    changePercent: -3,\n    status: \"warning\",\n    higherIsBetter: true,\n    history: makeHistory(1280, 1240, 20, 25),\n  },\n];\n\nconst STATUS_COLOR = {\n  good: t.palette[0], // #009E73 brand green\n  warning: t.amber, // #DDCC77\n  critical: t.palette[4], // #AE3030 matte red\n};\n\nfunction formatValue(m) {\n  const rounded = Number.isInteger(m.value) ? m.value : Math.round(m.value * 10) / 10;\n  const grouped = rounded.toLocaleString(\"en-US\");\n  return `${grouped}${m.unit}`;\n}\n\nfunction isFavorable(m) {\n  return m.higherIsBetter ? m.changePercent > 0 : m.changePercent < 0;\n}\n\n// --- Layout -------------------------------------------------------------\nconst cols = 3;\nconst rows = 2;\nconst outerX = size.width * 0.025;\nconst outerTop = size.height * 0.135;\nconst outerBottom = size.height * 0.04;\nconst gutter = size.width * 0.018;\n\nconst gridAreaWidth = size.width - 2 * outerX;\nconst gridAreaHeight = size.height - outerTop - outerBottom;\nconst tileWidth = (gridAreaWidth - (cols - 1) * gutter) / cols;\nconst tileHeight = (gridAreaHeight - (rows - 1) * gutter) / rows;\n\nconst grids = [];\nconst xAxes = [];\nconst yAxes = [];\nconst series = [];\nconst graphics = [];\n\nmetrics.forEach((m, i) => {\n  const col = i % cols;\n  const row = Math.floor(i / cols);\n  const tileX = outerX + col * (tileWidth + gutter);\n  const tileY = outerTop + row * (tileHeight + gutter);\n  const statusColor = STATUS_COLOR[m.status];\n  const changeColor = isFavorable(m) ? t.palette[0] : t.palette[4];\n  const arrow = m.changePercent >= 0 ? \"▲\" : \"▼\";\n\n  // Card background\n  graphics.push({\n    type: \"rect\",\n    left: tileX,\n    top: tileY,\n    shape: { width: tileWidth, height: tileHeight, r: 14 },\n    style: { fill: t.elevatedBg, stroke: t.grid, lineWidth: 1.5 },\n    z: 1,\n  });\n\n  // Status dot\n  graphics.push({\n    type: \"circle\",\n    left: tileX + tileWidth - 30,\n    top: tileY + 26,\n    shape: { r: 8 },\n    style: { fill: statusColor },\n    z: 2,\n  });\n\n  // Metric label\n  graphics.push({\n    type: \"text\",\n    left: tileX + 26,\n    top: tileY + 20,\n    style: {\n      text: m.name,\n      fill: t.inkSoft,\n      font: \"600 17px sans-serif\",\n    },\n    z: 2,\n  });\n\n  // Big value\n  graphics.push({\n    type: \"text\",\n    left: tileX + 26,\n    top: tileY + 46,\n    style: {\n      text: formatValue(m),\n      fill: t.ink,\n      font: \"700 46px sans-serif\",\n    },\n    z: 2,\n  });\n\n  // Change indicator\n  graphics.push({\n    type: \"text\",\n    left: tileX + 26,\n    top: tileY + 106,\n    style: {\n      text: `${arrow} ${Math.abs(m.changePercent)}%`,\n      fill: changeColor,\n      font: \"600 20px sans-serif\",\n    },\n    z: 2,\n  });\n\n  // Sparkline grid (fills the remaining tile space below the change indicator)\n  const sparkTop = tileY + 148;\n  const sparkHeight = tileHeight - 148 - 22;\n  grids.push({\n    left: tileX + 24,\n    top: sparkTop,\n    width: tileWidth - 48,\n    height: sparkHeight,\n  });\n  xAxes.push({\n    type: \"category\",\n    gridIndex: i,\n    show: false,\n    boundaryGap: false,\n    data: m.history.map((_, idx) => idx),\n  });\n  yAxes.push({\n    type: \"value\",\n    gridIndex: i,\n    show: false,\n    min: (val) => val.min - (val.max - val.min) * 0.15,\n    max: (val) => val.max + (val.max - val.min) * 0.15,\n  });\n  series.push({\n    type: \"line\",\n    gridIndex: i,\n    xAxisIndex: i,\n    yAxisIndex: i,\n    data: m.history,\n    showSymbol: false,\n    smooth: true,\n    lineStyle: { color: statusColor, width: 3 },\n    areaStyle: { color: statusColor, opacity: 0.12 },\n  });\n});\n\n// --- Init ---------------------------------------------------------------\nconst chart = echarts.init(document.getElementById(\"container\"));\n\n// --- Option ---------------------------------------------------------------\nchart.setOption({\n  animation: false,\n  backgroundColor: \"transparent\",\n  title: {\n    text: \"dashboard-metrics-tiles · javascript · echarts · anyplot.ai\",\n    left: \"center\",\n    top: 22,\n    textStyle: { color: t.ink, fontSize: 22 },\n  },\n  grid: grids,\n  xAxis: xAxes,\n  yAxis: yAxes,\n  series,\n  graphic: graphics,\n});\n"}