{"spec_id":"area-cumulative-flow","library":"muix","language":"javascript","code":"// anyplot.ai\n// area-cumulative-flow: Cumulative Flow Diagram for Workflow Analytics\n// Library: muix 7.29.1 | JavaScript 22.23.2\n// Quality: 88/100 | Created: 2026-08-19\nimport { LineChart } from \"@mui/x-charts/LineChart\";\nimport { Box, Typography } from \"@mui/material\";\n\nconst t = window.ANYPLOT_TOKENS;\n\n// --- Data (in-memory, deterministic) ----------------------------------------\n// A support-ticket board with four workflow stages, earliest to latest:\n// New -> Triaged -> In Progress -> Resolved. Each stage's cumulative count is\n// simulated as a queue: every day it can move at most its daily capacity of\n// tickets out of the backlog handed to it by the previous stage. That keeps\n// every curve monotonically non-decreasing and guarantees an earlier stage's\n// cumulative count never dips below a later stage's — the CFD invariant.\nconst NUM_DAYS = 70;\nconst START_DATE = new Date(2026, 3, 1);\n\nconst dates = Array.from({ length: NUM_DAYS }, (_, day) => {\n  const d = new Date(START_DATE);\n  d.setDate(d.getDate() + day);\n  return d;\n});\n\n// Daily new-ticket arrivals: a steady baseline plus a late-quarter surge.\nconst arrivalsPerDay = Array.from(\n  { length: NUM_DAYS },\n  (_, day) => 9 + Math.round(2 * Math.sin(day / 8)) + (day >= 45 ? 4 : 0)\n);\n\n// Per-stage daily processing capacity. Development capacity tightens for\n// three weeks, then a staffing boost drains the backlog — the bottleneck\n// this chart tells the story of.\nconst triageCapacityForDay = () => 14;\nconst devCapacityForDay = (day) => (day < 20 ? 9 : day < 42 ? 4 : 12);\nconst releaseCapacityForDay = () => 8;\n\nconst advanceQueue = (previousCumulative, capacityForDay) => {\n  const cumulative = [];\n  let processedTotal = 0;\n  for (let day = 0; day < previousCumulative.length; day += 1) {\n    const waiting = previousCumulative[day] - processedTotal;\n    processedTotal += Math.min(capacityForDay(day), waiting);\n    cumulative.push(processedTotal);\n  }\n  return cumulative;\n};\n\nconst newCumulative = [];\narrivalsPerDay.reduce((total, arrivals, day) => {\n  const next = total + arrivals;\n  newCumulative[day] = next;\n  return next;\n}, 0);\n\nconst triagedCumulative = advanceQueue(newCumulative, triageCapacityForDay);\nconst inProgressCumulative = advanceQueue(triagedCumulative, devCapacityForDay);\nconst resolvedCumulative = advanceQueue(inProgressCumulative, releaseCapacityForDay);\n\n// Band widths (work-in-progress per stage) fed to the stacked areas.\nconst wipNew = newCumulative.map((v, i) => v - triagedCumulative[i]);\nconst wipTriaged = triagedCumulative.map((v, i) => v - inProgressCumulative[i]);\nconst wipInProgress = inProgressCumulative.map((v, i) => v - resolvedCumulative[i]);\nconst wipResolved = resolvedCumulative;\n\nconst TITLE = \"area-cumulative-flow · javascript · muix · anyplot.ai\";\n\n// --- Chart (default-exported component — the harness mounts it) ------------\nexport default function Chart() {\n  const size = window.ANYPLOT_SIZE;\n  const padding = { top: 28, right: 40, bottom: 24, left: 40 };\n  const titleBlockHeight = 56;\n  // MUI X's y-axis `label` prop offsets itself from a hardcoded `tickFontSize`\n  // guess rather than the tick labels' real measured width, so with a 3-digit\n  // axis it collides with the tick numbers. A hand-rotated label in its own\n  // flex column sidesteps that and gives predictable, collision-free spacing.\n  const yLabelWidth = 32;\n  const chartWidth = size.width - padding.left - padding.right - yLabelWidth;\n  const chartHeight = size.height - padding.top - padding.bottom - titleBlockHeight;\n\n  return (\n    <Box\n      sx={{\n        width: size.width,\n        height: size.height,\n        boxSizing: \"border-box\",\n        padding: `${padding.top}px ${padding.right}px ${padding.bottom}px ${padding.left}px`,\n        display: \"flex\",\n        flexDirection: \"column\",\n      }}\n    >\n      <Typography sx={{ fontSize: 22, fontWeight: 600, color: \"text.primary\", mb: \"20px\", lineHeight: 1 }}>\n        {TITLE}\n      </Typography>\n      <Box sx={{ display: \"flex\", flexDirection: \"row\", height: chartHeight }}>\n        <Box sx={{ width: yLabelWidth, display: \"flex\", alignItems: \"center\", justifyContent: \"center\" }}>\n          <Typography\n            sx={{\n              fontSize: 16,\n              color: \"text.secondary\",\n              whiteSpace: \"nowrap\",\n              transform: \"rotate(-90deg)\",\n            }}\n          >\n            Cumulative ticket count\n          </Typography>\n        </Box>\n        <LineChart\n          width={chartWidth}\n          height={chartHeight}\n          skipAnimation\n          series={[\n            {\n              id: \"new\",\n              label: \"New\",\n              data: wipNew,\n              stack: \"flow\",\n              stackOrder: \"reverse\",\n              area: true,\n              showMark: false,\n              color: t.palette[0],\n              valueFormatter: (v) => `${v} tickets waiting`,\n            },\n            {\n              id: \"triaged\",\n              label: \"Triaged\",\n              data: wipTriaged,\n              stack: \"flow\",\n              stackOrder: \"reverse\",\n              area: true,\n              showMark: false,\n              color: t.palette[1],\n              valueFormatter: (v) => `${v} tickets waiting`,\n            },\n            {\n              id: \"in-progress\",\n              label: \"In Progress\",\n              data: wipInProgress,\n              stack: \"flow\",\n              stackOrder: \"reverse\",\n              area: true,\n              showMark: false,\n              color: t.palette[2],\n              valueFormatter: (v) => `${v} tickets waiting`,\n            },\n            {\n              id: \"resolved\",\n              label: \"Resolved\",\n              data: wipResolved,\n              stack: \"flow\",\n              stackOrder: \"reverse\",\n              area: true,\n              showMark: false,\n              color: t.palette[3],\n              valueFormatter: (v) => `${v} tickets`,\n            },\n          ]}\n          xAxis={[\n            {\n              data: dates,\n              scaleType: \"time\",\n              label: \"Date\",\n              valueFormatter: (date) => date.toLocaleDateString(\"en-US\", { month: \"short\", day: \"numeric\" }),\n              tickLabelStyle: { fontSize: 14 },\n              labelStyle: { fontSize: 16 },\n            },\n          ]}\n          yAxis={[\n            {\n              valueFormatter: (value) => value.toLocaleString(\"en-US\"),\n              tickLabelStyle: { fontSize: 14 },\n            },\n          ]}\n          grid={{ horizontal: true }}\n          slotProps={{\n            legend: {\n              direction: \"row\",\n              labelStyle: { fontSize: 14 },\n              itemMarkWidth: 18,\n              itemMarkHeight: 10,\n              markGap: 8,\n            },\n          }}\n          sx={{\n            \"& .MuiLineElement-root\": { strokeWidth: 2.5 },\n            \"& .MuiAreaElement-root\": { fillOpacity: 0.85 },\n            \"& .MuiChartsGrid-line\": { strokeDasharray: \"4 3\" },\n          }}\n        />\n      </Box>\n    </Box>\n  );\n}\n"}