{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "metrics-grid",
  "title": "Metrics Grid",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/metrics-grid/metrics-grid.component.ts",
      "content": "// Six-tile KPI grid. Each tile renders a labeled KPI header (title +\n// big number / percentage / count) above a small chart -- pie or\n// horizontal bar -- that gives the breakdown.\n//\n// Per the registry's primitive-vs-block rule: tile shapes are spelled\n// out inline as siblings. Different tiles use different chart types\n// (donut, full pie, horizontal bar) -- those differences are the whole\n// point of the layout, so they don't hide behind a wrapper.\nimport { Component, ChangeDetectionStrategy } from '@angular/core'\nimport { UiPieChartComponent } from '@/ui/charts/pie-chart/pie-chart.component'\nimport { UiBarChartComponent } from '@/ui/charts/bar-chart/bar-chart.component'\n\n// --- KPI 1: status pie (e.g. successful / partially / error) ---\nconst totalRuns = 770\nconst runsByStatus = [\n  { name: 'Successful', value: 638 },\n  { name: 'Partial', value: 0 },\n  { name: 'Error / Skipped', value: 132 },\n]\n\n// --- KPI 2: disposition split ---\nconst totalProcessed = 638\nconst dispositionSplit = [\n  { name: 'Non-disputable', value: 168 },\n  { name: 'Disputable', value: 470 },\n]\n\n// --- KPI 3: classification breakdown ---\nconst totalClassified = 638\nconst classification = [\n  { name: 'Type A', value: 525 },\n  { name: 'Type B', value: 113 },\n]\n\n// --- KPI 4: top reasons (horizontal bar) ---\nconst skipReasons = [\n  { reason: 'CREDIT_NOTE', count: 11 },\n  { reason: 'CUSTOMS_INSPECT', count: 5 },\n  { reason: 'NEGATIVE_COST', count: 8 },\n]\nconst totalSkipped = skipReasons.reduce((a, r) => a + r.count, 0)\n\n// --- KPI 5: error reasons (horizontal bar) ---\nconst errorReasons = [\n  { reason: 'INVALID_CUTOFF', count: 3 },\n  { reason: 'NO_SHIPMENT_DATA', count: 20 },\n  { reason: 'SELF_BILLING', count: 42 },\n  { reason: 'MISSING_OBLIGATORY_FIELD', count: 2 },\n  { reason: 'MISSING_OBLIGATORY_TIME', count: 4 },\n  { reason: 'UNABLE_TO_CLASSIFY', count: 40 },\n]\nconst totalErrors = errorReasons.reduce((a, r) => a + r.count, 0)\n\n// --- KPI 6: workflow status ---\nconst openShare = 0.7602\nconst workflowStatus = [\n  { name: 'Open', value: 485 },\n  { name: 'Pending', value: 151 },\n  { name: 'Closed', value: 2 },\n]\n\nconst pieOption = {\n  legend: { show: false },\n  series: [\n    {\n      radius: ['0%', '70%'],\n      label: {\n        show: true,\n        formatter: (p: { value: number; percent: number }) => `${p.value}\\n(${p.percent}%)`,\n        fontSize: 11,\n        color: '#fff',\n        fontWeight: 600,\n      },\n      labelLine: { show: false },\n    },\n  ],\n}\n\nconst donutOption = {\n  legend: { show: false },\n  series: [\n    {\n      radius: ['40%', '70%'],\n      label: {\n        show: true,\n        formatter: (p: { value: number; percent: number }) => `${p.value}\\n(${p.percent}%)`,\n        fontSize: 11,\n        color: '#fff',\n        fontWeight: 600,\n      },\n      labelLine: { show: false },\n    },\n  ],\n}\n\nconst horizontalBarOption = {\n  legend: { show: false },\n  xAxis: { type: 'value' as const, axisLabel: { fontSize: 10 } },\n  yAxis: { type: 'category' as const, axisLabel: { fontSize: 10 } },\n  grid: { left: 16, right: 16, top: 8, bottom: 24, containLabel: true },\n  series: [\n    {\n      type: 'bar' as const,\n      barMaxWidth: 18,\n      itemStyle: { borderRadius: [0, 3, 3, 0] },\n    },\n  ],\n}\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-metrics-grid, [ui-metrics-grid]',\n  standalone: true,\n  imports: [UiPieChartComponent, UiBarChartComponent],\n  host: {\n    'data-slot': 'metrics-grid',\n    class: 'block space-y-4',\n  },\n  template: `\n    <h2 class=\"text-primary text-sm font-semibold tracking-tight\">Results Overview</h2>\n\n    <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n      <!-- Tile 1: Runs by status (pie) -->\n      <article class=\"bg-card border-border rounded-lg border p-4\">\n        <h3 class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">Runs by status</h3>\n        <div class=\"text-primary mt-1 mb-2 text-2xl font-semibold\">{{ totalRuns }} runs</div>\n        <div class=\"text-muted-foreground mb-3 flex items-center gap-3 text-xs\">\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-1 size-2 rounded-full\"></span>\n            Successful\n          </span>\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-4 size-2 rounded-full\"></span>\n            Error / Skipped\n          </span>\n        </div>\n        <ui-pie-chart [data]=\"runsByStatus\" [option]=\"pieOption\" height=\"220\" />\n      </article>\n\n      <!-- Tile 2: Disposition split (pie) -->\n      <article class=\"bg-card border-border rounded-lg border p-4\">\n        <h3 class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">Disposition split</h3>\n        <div class=\"text-primary mt-1 mb-2 text-2xl font-semibold\">{{ totalProcessed }} processed</div>\n        <div class=\"text-muted-foreground mb-3 flex items-center gap-3 text-xs\">\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-1 size-2 rounded-full\"></span>\n            Non-disputable\n          </span>\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-3 size-2 rounded-full\"></span>\n            Disputable\n          </span>\n        </div>\n        <ui-pie-chart [data]=\"dispositionSplit\" [option]=\"pieOption\" height=\"220\" />\n      </article>\n\n      <!-- Tile 3: Classification (pie) -->\n      <article class=\"bg-card border-border rounded-lg border p-4\">\n        <h3 class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">Classification</h3>\n        <div class=\"text-primary mt-1 mb-2 text-2xl font-semibold\">{{ totalClassified }} classified</div>\n        <div class=\"text-muted-foreground mb-3 flex items-center gap-3 text-xs\">\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-1 size-2 rounded-full\"></span>\n            Type A\n          </span>\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-3 size-2 rounded-full\"></span>\n            Type B\n          </span>\n        </div>\n        <ui-pie-chart [data]=\"classification\" [option]=\"pieOption\" height=\"220\" />\n      </article>\n\n      <!-- Tile 4: Skip reasons (horizontal bar) -->\n      <article class=\"bg-card border-border rounded-lg border p-4\">\n        <h3 class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">Top skip reasons</h3>\n        <div class=\"text-primary mt-1 mb-2 text-2xl font-semibold\">{{ totalSkipped }} skipped</div>\n        <div class=\"text-muted-foreground mb-3 text-xs\">By trigger</div>\n        <ui-bar-chart [data]=\"skipReasons\" xField=\"reason\" yField=\"count\" [option]=\"horizontalBarOption\" height=\"220\" />\n      </article>\n\n      <!-- Tile 5: Error reasons (horizontal bar) -->\n      <article class=\"bg-card border-border rounded-lg border p-4\">\n        <h3 class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">Top error reasons</h3>\n        <div class=\"text-primary mt-1 mb-2 text-2xl font-semibold\">{{ totalErrors }} errors</div>\n        <div class=\"text-muted-foreground mb-3 text-xs\">By failure mode</div>\n        <ui-bar-chart\n          [data]=\"errorReasons\"\n          xField=\"reason\"\n          yField=\"count\"\n          [option]=\"horizontalBarOption\"\n          height=\"220\"\n        />\n      </article>\n\n      <!-- Tile 6: Workflow status (donut + highlight) -->\n      <article class=\"bg-card border-border rounded-lg border p-4\">\n        <h3 class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\">Workflow status</h3>\n        <div class=\"text-primary mt-1 mb-2 text-2xl font-semibold\">{{ openPercent }}% open</div>\n        <div class=\"text-muted-foreground mb-3 flex items-center gap-3 text-xs\">\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-1 size-2 rounded-full\"></span>\n            Open\n          </span>\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-5 size-2 rounded-full\"></span>\n            Pending\n          </span>\n          <span class=\"inline-flex items-center gap-1.5\">\n            <span class=\"bg-chart-3 size-2 rounded-full\"></span>\n            Closed\n          </span>\n        </div>\n        <!-- React passes donut too; its radius is overridden by donutOption, so the ring comes from the option alone. -->\n        <ui-pie-chart [data]=\"workflowStatus\" [option]=\"donutOption\" height=\"220\" />\n      </article>\n    </div>\n  `,\n})\nexport class UiMetricsGridComponent {\n  protected readonly totalRuns = totalRuns\n  protected readonly runsByStatus = runsByStatus\n  protected readonly totalProcessed = totalProcessed\n  protected readonly dispositionSplit = dispositionSplit\n  protected readonly totalClassified = totalClassified\n  protected readonly classification = classification\n  protected readonly skipReasons = skipReasons\n  protected readonly totalSkipped = totalSkipped\n  protected readonly errorReasons = errorReasons\n  protected readonly totalErrors = totalErrors\n  protected readonly openPercent = (openShare * 100).toFixed(2)\n  protected readonly workflowStatus = workflowStatus\n  protected readonly pieOption = pieOption\n  protected readonly donutOption = donutOption\n  protected readonly horizontalBarOption = horizontalBarOption\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/metrics-grid/metrics-grid.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/metrics-grid/index.ts",
      "content": "export { UiMetricsGridComponent } from './metrics-grid.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/metrics-grid/index.ts"
    }
  ],
  "dependencies": [],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/pie-chart.json",
    "https://uipkge.dev/r/angular/bar-chart.json"
  ],
  "description": "Six-tile KPI status grid. Each tile pairs a stat header (count / percentage) with a small pie or horizontal bar chart that breaks the metric down. Different tiles use different chart types so each KPI reads at a glance.",
  "categories": [
    "analytics",
    "dashboard"
  ]
}