@memberjunction/ng-dashboards 6.2.0-edge.1 → 6.2.0-edge.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -1
  2. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +3 -1
  3. package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -1
  4. package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -1
  5. package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1 -2
  6. package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -1
  7. package/dist/AI/components/rubric-drift-series.d.ts +32 -0
  8. package/dist/AI/components/rubric-drift-series.d.ts.map +1 -0
  9. package/dist/AI/components/rubric-drift-series.js +49 -0
  10. package/dist/AI/components/rubric-drift-series.js.map +1 -0
  11. package/dist/AI/components/rubric-drift.component.d.ts +42 -0
  12. package/dist/AI/components/rubric-drift.component.d.ts.map +1 -0
  13. package/dist/AI/components/rubric-drift.component.js +197 -0
  14. package/dist/AI/components/rubric-drift.component.js.map +1 -0
  15. package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
  16. package/dist/AI/components/tags/tags-resource.component.js +4 -3
  17. package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
  18. package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
  19. package/dist/AI/components/vectors/vector-management-resource.component.js +5 -2
  20. package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
  21. package/dist/Integration/components/connections/connections.component.d.ts +5 -0
  22. package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
  23. package/dist/Integration/components/connections/connections.component.js +151 -136
  24. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  25. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.d.ts.map +1 -1
  26. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +3 -2
  27. package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
  28. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts +11 -0
  29. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.d.ts.map +1 -1
  30. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js +17 -1
  31. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipeline.engine.js.map +1 -1
  32. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js +52 -45
  33. package/dist/KnowledgeHub/components/feature-pipelines/feature-pipelines-resource.component.js.map +1 -1
  34. package/dist/QueryBrowser/query-browser-resource.component.d.ts +11 -0
  35. package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
  36. package/dist/QueryBrowser/query-browser-resource.component.js +47 -10
  37. package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
  38. package/dist/Rubrics/catalog-grid.d.ts +4 -0
  39. package/dist/Rubrics/catalog-grid.d.ts.map +1 -0
  40. package/dist/Rubrics/catalog-grid.js +22 -0
  41. package/dist/Rubrics/catalog-grid.js.map +1 -0
  42. package/dist/Rubrics/rubrics-dashboard.component.d.ts +31 -0
  43. package/dist/Rubrics/rubrics-dashboard.component.d.ts.map +1 -0
  44. package/dist/Rubrics/rubrics-dashboard.component.js +285 -0
  45. package/dist/Rubrics/rubrics-dashboard.component.js.map +1 -0
  46. package/dist/Rubrics/rubrics-resource.component.d.ts +13 -0
  47. package/dist/Rubrics/rubrics-resource.component.d.ts.map +1 -0
  48. package/dist/Rubrics/rubrics-resource.component.js +42 -0
  49. package/dist/Rubrics/rubrics-resource.component.js.map +1 -0
  50. package/dist/Rubrics/scales-dashboard.component.d.ts +34 -0
  51. package/dist/Rubrics/scales-dashboard.component.d.ts.map +1 -0
  52. package/dist/Rubrics/scales-dashboard.component.js +204 -0
  53. package/dist/Rubrics/scales-dashboard.component.js.map +1 -0
  54. package/dist/Rubrics/scales-resource.component.d.ts +13 -0
  55. package/dist/Rubrics/scales-resource.component.d.ts.map +1 -0
  56. package/dist/Rubrics/scales-resource.component.js +42 -0
  57. package/dist/Rubrics/scales-resource.component.js.map +1 -0
  58. package/dist/Testing/components/testing-analytics.component.d.ts +15 -0
  59. package/dist/Testing/components/testing-analytics.component.d.ts.map +1 -1
  60. package/dist/Testing/components/testing-analytics.component.js +465 -296
  61. package/dist/Testing/components/testing-analytics.component.js.map +1 -1
  62. package/dist/Testing/components/testing-review.component.d.ts +6 -0
  63. package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
  64. package/dist/Testing/components/testing-review.component.js +289 -173
  65. package/dist/Testing/components/testing-review.component.js.map +1 -1
  66. package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts +8 -1
  67. package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts.map +1 -1
  68. package/dist/Testing/components/widgets/test-run-detail-panel.component.js +68 -43
  69. package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
  70. package/dist/module.d.ts +23 -22
  71. package/dist/module.d.ts.map +1 -1
  72. package/dist/module.js +7 -0
  73. package/dist/module.js.map +1 -1
  74. package/dist/public-api.d.ts +1 -0
  75. package/dist/public-api.d.ts.map +1 -1
  76. package/dist/public-api.js +1 -0
  77. package/dist/public-api.js.map +1 -1
  78. package/dist/rubrics-dashboards.module.d.ts +17 -0
  79. package/dist/rubrics-dashboards.module.d.ts.map +1 -0
  80. package/dist/rubrics-dashboards.module.js +76 -0
  81. package/dist/rubrics-dashboards.module.js.map +1 -0
  82. package/package.json +70 -64
@@ -1 +1 @@
1
- {"version":3,"file":"testing-analytics.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-analytics.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { Observable, Subject, BehaviorSubject } from 'rxjs';\nimport { takeUntil, map, shareReplay } from 'rxjs/operators';\nimport {\n TestingInstrumentationService,\n TestTrendData,\n TestAnalytics,\n TestRunSummary,\n VersionMetrics\n} from '../services/testing-instrumentation.service';\n\n// ---------------------------------------------------------------------------\n// Local interfaces\n// ---------------------------------------------------------------------------\n\ninterface TimeRangeOption {\n label: string;\n days: number;\n}\n\ninterface ScoreDistributionBucket {\n label: string;\n color: string;\n count: number;\n percentage: number;\n}\n\ninterface VersionRow {\n version: string;\n gitCommitShort: string;\n agentVersion: string;\n date: Date;\n totalTests: number;\n passRate: number;\n totalCost: number;\n passRateDelta: number | null;\n costDelta: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n@Component({\n standalone: false,\n selector: 'app-testing-analytics',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (HideToolbar) {\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n } @else {\n <mj-page-layout>\n <mj-page-header\n Title=\"Testing Analytics\"\n Icon=\"fa-solid fa-chart-bar\"\n Subtitle=\"Test trends, pass rates, and version metrics\">\n <div actions>\n @for (range of TimeRanges; track range.days) {\n <mj-filter-chip\n [Label]=\"range.label\"\n [Active]=\"SelectedDays === range.days\"\n (Clicked)=\"OnTimeRangeSelect(range.days)\">\n </mj-filter-chip>\n }\n <mj-refresh-button (Clicked)=\"Refresh()\"></mj-refresh-button>\n </div>\n </mj-page-header>\n <mj-page-body>\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </mj-page-body>\n </mj-page-layout>\n }\n\n <ng-template #content>\n <div class=\"testing-analytics\">\n\n <!-- ===== 2. Trend Overview (2-column CSS charts) ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-chart-line\"></i>\n Trend Overview\n </div>\n <div class=\"trend-grid\">\n\n <!-- Pass Rate Trend -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-check-double\"></i> Pass Rate Trend</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-chart-area\" Title=\"No trend data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track\">\n <div class=\"bar-fill pass-rate-bar\" [style.width.%]=\"PassRate(t)\"></div>\n </div>\n <span class=\"bar-value\" [class]=\"PassRateClass(t)\">{{ PassRate(t) | number:'1.0-0' }}%</span>\n </div>\n }\n </div>\n <div class=\"trend-direction\">\n @if (PassRateTrendDirection$ | async; as dir) {\n @if (dir === 'up') {\n <i class=\"fa-solid fa-arrow-trend-up trend-up\"></i> <span class=\"trend-up\">Improving</span>\n } @else if (dir === 'down') {\n <i class=\"fa-solid fa-arrow-trend-down trend-down\"></i> <span class=\"trend-down\">Declining</span>\n } @else {\n <i class=\"fa-solid fa-minus trend-neutral\"></i> <span class=\"trend-neutral\">Stable</span>\n }\n }\n </div>\n }\n }\n </div>\n\n <!-- Run Volume -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-layer-group\"></i> Run Volume</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-chart-bar\" Title=\"No volume data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track stacked\">\n <div class=\"bar-segment passed-seg\" [style.width.%]=\"SegmentPct(t, 'passed')\"></div>\n <div class=\"bar-segment failed-seg\" [style.width.%]=\"SegmentPct(t, 'failed')\"></div>\n <div class=\"bar-segment skipped-seg\" [style.width.%]=\"SegmentPct(t, 'skipped')\"></div>\n </div>\n <span class=\"bar-value\">{{ t.totalRuns }}</span>\n </div>\n }\n </div>\n <div class=\"legend-row\">\n <span class=\"legend-dot passed-seg\"></span> Passed\n <span class=\"legend-dot failed-seg\"></span> Failed\n <span class=\"legend-dot skipped-seg\"></span> Skipped\n </div>\n }\n }\n </div>\n\n <!-- Score Trend -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-star-half-stroke\"></i> Score Trend</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-star\" Title=\"No score data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track\">\n <div class=\"bar-fill\" [class]=\"ScoreBarClass(t.averageScore)\" [style.width.%]=\"t.averageScore * 100\"></div>\n </div>\n <span class=\"bar-value\" [class]=\"ScoreTextClass(t.averageScore)\">{{ (t.averageScore * 100) | number:'1.0-0' }}%</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Cost Trend -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-dollar-sign\"></i> Cost Trend</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-wallet\" Title=\"No cost data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track\">\n <div class=\"bar-fill cost-bar\" [style.width.%]=\"CostBarPct(t, trends)\"></div>\n </div>\n <span class=\"bar-value\">\\${{ t.totalCost | number:'1.2-2' }}</span>\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n\n <!-- ===== 3. Insights Section ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-magnifying-glass-chart\"></i>\n Insights\n </div>\n <div class=\"insights-grid\">\n\n <!-- Top Failing Tests -->\n <div class=\"card insight-card\">\n <h4><i class=\"fa-solid fa-triangle-exclamation\"></i> Top Failing Tests</h4>\n @if (TopFailingTests$ | async; as tests) {\n @if (tests.length === 0) {\n <mj-empty-state Variant=\"success\" Size=\"compact\" Title=\"No failing tests\" />\n } @else {\n <div class=\"insight-list\">\n @for (t of tests; track t.testName) {\n <div class=\"insight-row\">\n <span class=\"insight-name\" [title]=\"t.testName\">{{ t.testName }}</span>\n <span class=\"badge-fail\">{{ t.failureCount }}</span>\n <span class=\"insight-secondary\">{{ t.failureRate | number:'1.0-1' }}%</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Slowest Tests -->\n <div class=\"card insight-card\">\n <h4><i class=\"fa-solid fa-clock\"></i> Slowest Tests</h4>\n @if (SlowestTests$ | async; as tests) {\n @if (tests.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-bolt\" Title=\"No duration data\" />\n } @else {\n <div class=\"insight-list\">\n @for (t of tests; track t.testName) {\n <div class=\"insight-row\">\n <span class=\"insight-name\" [title]=\"t.testName\">{{ t.testName }}</span>\n <span class=\"insight-primary\">{{ FormatDuration(t.avgDuration) }}</span>\n <span class=\"insight-secondary\">max {{ FormatDuration(t.maxDuration) }}</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Most Expensive Tests -->\n <div class=\"card insight-card\">\n <h4><i class=\"fa-solid fa-coins\"></i> Most Expensive Tests</h4>\n @if (MostExpensiveTests$ | async; as tests) {\n @if (tests.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-dollar-sign\" Title=\"No cost data\" />\n } @else {\n <div class=\"insight-list\">\n @for (t of tests; track t.testName) {\n <div class=\"insight-row\">\n <span class=\"insight-name\" [title]=\"t.testName\">{{ t.testName }}</span>\n <span class=\"insight-primary\">\\${{ t.totalCost | number:'1.2-2' }}</span>\n <span class=\"insight-secondary\">\\${{ t.avgCost | number:'1.4-4' }}/run</span>\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n\n <!-- ===== 4. Score Distribution ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-chart-pie\"></i>\n Score Distribution\n </div>\n <div class=\"card distribution-card\">\n @if (ScoreDistribution$ | async; as buckets) {\n @if (buckets.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-chart-simple\" Title=\"No score data available\" />\n } @else {\n <div class=\"distribution-bars\">\n @for (b of buckets; track b.label) {\n <div class=\"dist-row\">\n <span class=\"dist-label\">{{ b.label }}</span>\n <div class=\"dist-track\">\n <div class=\"dist-fill\" [style.width.%]=\"b.percentage\" [style.background]=\"b.color\"></div>\n </div>\n <span class=\"dist-count\">{{ b.count }}</span>\n <span class=\"dist-pct\">{{ b.percentage | number:'1.0-1' }}%</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- ===== 5. Version Comparison ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-code-compare\"></i>\n Version Comparison\n <span class=\"section-subtitle\">Compare metrics across different builds</span>\n </div>\n <div class=\"card version-card\">\n @if (IsLoadingVersions) {\n <div class=\"empty-mini\"><i class=\"fa-solid fa-spinner fa-spin\"></i><span>Loading versions...</span></div>\n } @else if (VersionRows.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-code-branch\" Title=\"No version data available\" />\n } @else {\n <div class=\"version-table-wrap\">\n <table class=\"version-table\">\n <thead>\n <tr>\n <th>Version</th>\n <th>Date</th>\n <th class=\"num\">Tests</th>\n <th class=\"num\">Pass Rate</th>\n <th class=\"num\">Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (row of VersionRows; track row.version) {\n <tr>\n <td>\n <span class=\"version-label\">{{ row.gitCommitShort }}</span>\n <span class=\"version-agent\">{{ row.agentVersion }}</span>\n </td>\n <td>{{ row.date | date:'mediumDate' }}</td>\n <td class=\"num\">{{ row.totalTests }}</td>\n <td class=\"num\">\n <span>{{ row.passRate | number:'1.1-1' }}%</span>\n @if (row.passRateDelta !== null) {\n <span class=\"delta\" [class.delta-up]=\"row.passRateDelta > 0\" [class.delta-down]=\"row.passRateDelta < 0\" [class.delta-neutral]=\"row.passRateDelta === 0\">\n @if (row.passRateDelta > 0) {\n <i class=\"fa-solid fa-arrow-up\"></i>\n } @else if (row.passRateDelta < 0) {\n <i class=\"fa-solid fa-arrow-down\"></i>\n }\n {{ AbsNum(row.passRateDelta) | number:'1.1-1' }}%\n </span>\n }\n </td>\n <td class=\"num\">\n <span>\\${{ row.totalCost | number:'1.2-2' }}</span>\n @if (row.costDelta !== null) {\n <span class=\"delta\" [class.delta-up]=\"row.costDelta < 0\" [class.delta-down]=\"row.costDelta > 0\" [class.delta-neutral]=\"row.costDelta === 0\">\n @if (row.costDelta > 0) {\n <i class=\"fa-solid fa-arrow-up\"></i>\n } @else if (row.costDelta < 0) {\n <i class=\"fa-solid fa-arrow-down\"></i>\n }\n {{ AbsNum(row.costDelta) | number:'1.1-1' }}%\n </span>\n }\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </div>\n </ng-template>\n `,\n styles: [`\n /* ------------------------------------------------------------------ */\n /* Layout & page */\n /* ------------------------------------------------------------------ */\n .testing-analytics {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n }\n\n /* ------------------------------------------------------------------ */\n /* Page header */\n /* ------------------------------------------------------------------ */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 12px;\n background: var(--mj-bg-surface);\n padding: 20px 24px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n margin-bottom: 24px;\n }\n .header-left h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .header-left h2 i { color: var(--mj-brand-primary); }\n .header-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n }\n\n /* time-range pills */\n .time-range-pills {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n }\n .pill {\n padding: 6px 14px;\n border: none;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n background: transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .pill:hover { background: var(--mj-border-default); }\n .pill.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .refresh-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .refresh-btn:hover { background: var(--mj-bg-surface-card); border-color: var(--mj-border-strong); }\n\n /* ------------------------------------------------------------------ */\n /* Section titles */\n /* ------------------------------------------------------------------ */\n .section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 14px;\n }\n .section-title i { color: var(--mj-brand-primary); }\n .section-subtitle {\n font-size: 12px;\n font-weight: 400;\n color: var(--mj-text-disabled);\n margin-left: 4px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Cards */\n /* ------------------------------------------------------------------ */\n .card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n padding: 20px;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n .card:hover {\n transform: translateY(-2px);\n box-shadow: var(--mj-shadow-md);\n }\n .card h4 {\n margin: 0 0 14px 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .card h4 i { color: var(--mj-brand-primary); font-size: 14px; }\n\n /* ------------------------------------------------------------------ */\n /* Trend grid (2 columns) */\n /* ------------------------------------------------------------------ */\n .trend-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 28px;\n }\n\n /* CSS bar charts */\n .bar-chart {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .bar-row {\n display: grid;\n grid-template-columns: 60px 1fr 52px;\n align-items: center;\n gap: 8px;\n }\n .bar-label {\n font-size: 10px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n text-align: right;\n white-space: nowrap;\n }\n .bar-track {\n height: 14px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 7px;\n overflow: hidden;\n position: relative;\n }\n .bar-track.stacked {\n display: flex;\n }\n .bar-fill {\n height: 100%;\n border-radius: 7px;\n transition: width 0.4s ease;\n }\n .bar-value {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-align: right;\n }\n\n /* colour classes for bars */\n .pass-rate-bar {\n background: var(--mj-status-success);\n }\n .cost-bar {\n background: var(--mj-status-warning);\n }\n .score-excellent { background: var(--mj-status-success); }\n .score-good { background: var(--mj-brand-primary); }\n .score-fair { background: var(--mj-status-warning); }\n .score-poor { background: var(--mj-status-error); }\n\n /* text colour classes */\n .text-excellent { color: var(--mj-status-success); }\n .text-good { color: var(--mj-brand-primary); }\n .text-fair { color: var(--mj-status-warning); }\n .text-poor { color: var(--mj-status-error); }\n .text-pass-good { color: var(--mj-status-success); }\n .text-pass-warn { color: var(--mj-status-warning); }\n .text-pass-bad { color: var(--mj-status-error); }\n\n /* stacked segments */\n .bar-segment { height: 100%; transition: width 0.4s ease; }\n .passed-seg { background: var(--mj-status-success); }\n .failed-seg { background: var(--mj-status-error); }\n .skipped-seg { background: var(--mj-text-disabled); }\n\n .legend-row {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n font-size: 10px;\n color: var(--mj-text-disabled);\n }\n .legend-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n display: inline-block;\n margin-left: 8px;\n }\n .legend-dot:first-child { margin-left: 0; }\n .legend-dot.passed-seg { background: var(--mj-status-success); }\n .legend-dot.failed-seg { background: var(--mj-status-error); }\n .legend-dot.skipped-seg { background: var(--mj-text-disabled); }\n\n .trend-direction {\n margin-top: 10px;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .trend-up { color: var(--mj-status-success); }\n .trend-down { color: var(--mj-status-error); }\n .trend-neutral { color: var(--mj-text-disabled); }\n\n /* ------------------------------------------------------------------ */\n /* Insights grid (3 columns) */\n /* ------------------------------------------------------------------ */\n .insights-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n margin-bottom: 28px;\n }\n\n .insight-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .insight-row {\n display: grid;\n grid-template-columns: 1fr auto auto;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border-radius: 6px;\n transition: background 0.15s ease;\n }\n .insight-row:hover { background: var(--mj-bg-surface-card); }\n .insight-name {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .badge-fail {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n font-size: 11px;\n font-weight: 700;\n padding: 2px 8px;\n border-radius: 10px;\n }\n .insight-primary {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-brand-primary);\n }\n .insight-secondary {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n }\n\n /* ------------------------------------------------------------------ */\n /* Score Distribution */\n /* ------------------------------------------------------------------ */\n .distribution-card {\n margin-bottom: 28px;\n }\n .distribution-bars {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .dist-row {\n display: grid;\n grid-template-columns: 110px 1fr 40px 50px;\n align-items: center;\n gap: 10px;\n }\n .dist-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n }\n .dist-track {\n height: 22px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n overflow: hidden;\n }\n .dist-fill {\n height: 100%;\n border-radius: 6px;\n transition: width 0.4s ease;\n }\n .dist-count {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-primary);\n text-align: right;\n }\n .dist-pct {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-align: right;\n }\n\n /* ------------------------------------------------------------------ */\n /* Version Comparison */\n /* ------------------------------------------------------------------ */\n .version-card {\n margin-bottom: 28px;\n }\n .version-table-wrap {\n overflow-x: auto;\n }\n .version-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n .version-table th {\n text-align: left;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n padding: 10px 12px;\n border-bottom: 2px solid var(--mj-border-default);\n }\n .version-table th.num,\n .version-table td.num { text-align: right; }\n .version-table td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n vertical-align: middle;\n }\n .version-table tbody tr:hover { background: var(--mj-bg-surface-card); }\n .version-label {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n font-size: 12px;\n color: var(--mj-brand-primary);\n }\n .version-agent {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 2px;\n }\n\n .delta {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-size: 11px;\n font-weight: 600;\n margin-left: 6px;\n padding: 1px 6px;\n border-radius: 4px;\n }\n .delta i { font-size: 9px; }\n .delta-up { background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface)); color: var(--mj-status-success); }\n .delta-down { background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface)); color: var(--mj-status-error); }\n .delta-neutral { background: var(--mj-bg-surface-card); color: var(--mj-text-disabled); }\n\n /* ------------------------------------------------------------------ */\n /* Loading placeholder (data-empty states now use <mj-empty-state>) */\n /* ------------------------------------------------------------------ */\n .empty-mini {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 32px 16px;\n color: var(--mj-border-strong);\n gap: 8px;\n }\n .empty-mini i { font-size: 28px; opacity: 0.5; }\n .empty-mini span { font-size: 12px; }\n\n /* ------------------------------------------------------------------ */\n /* Responsive */\n /* ------------------------------------------------------------------ */\n @media (max-width: 1100px) {\n .trend-grid { grid-template-columns: 1fr; }\n .insights-grid { grid-template-columns: 1fr 1fr; }\n }\n @media (max-width: 720px) {\n .page-header { flex-direction: column; align-items: flex-start; }\n .insights-grid { grid-template-columns: 1fr; }\n .dist-row { grid-template-columns: 80px 1fr 32px 42px; }\n }\n `]\n})\nexport class TestingAnalyticsComponent implements OnInit, OnDestroy {\n\n // ------- Inputs / Outputs -------\n @Input() InitialState: Record<string, unknown> | null = null;\n\n /** @deprecated Use {@link InitialState}. */\n @Input() set initialState(value: Record<string, unknown> | null) {\n this.InitialState = value;\n }\n /** @deprecated Use {@link InitialState}. */\n get initialState(): Record<string, unknown> | null {\n return this.InitialState;\n }\n /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */\n @Input() HideToolbar = false;\n @Output() StateChange = new EventEmitter<Record<string, unknown>>();\n\n /**\n * @deprecated Use {@link StateChange}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (stateChange) keeps working. Must stay AFTER StateChange: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() stateChange = this.StateChange;\n\n // ------- Public state -------\n TimeRanges: TimeRangeOption[] = [\n { label: '7 Days', days: 7 },\n { label: '30 Days', days: 30 },\n { label: '90 Days', days: 90 }\n ];\n SelectedDays = 30;\n VersionRows: VersionRow[] = [];\n IsLoadingVersions = false;\n\n // Cached breakdown name lists for the dashboard's agent context.\n private topFailingNames: string[] = [];\n private slowestNames: string[] = [];\n private mostExpensiveNames: string[] = [];\n\n // ------- Observables -------\n DisplayTrends$!: Observable<TestTrendData[]>;\n PassRateTrendDirection$!: Observable<'up' | 'down' | 'stable'>;\n TopFailingTests$!: Observable<TestAnalytics['topFailingTests']>;\n SlowestTests$!: Observable<TestAnalytics['slowestTests']>;\n MostExpensiveTests$!: Observable<TestAnalytics['mostExpensiveTests']>;\n ScoreDistribution$!: Observable<ScoreDistributionBucket[]>;\n\n // ------- Private -------\n private destroy$ = new Subject<void>();\n private maxTrendRuns = 0; // cached for cost bar scaling\n\n constructor(\n private instrumentationService: TestingInstrumentationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n // ===================================================================\n // Lifecycle\n // ===================================================================\n\n ngOnInit(): void {\n this.restoreState();\n this.setupObservables();\n this.loadVersionMetrics();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ===================================================================\n // Public methods (template-bound)\n // ===================================================================\n\n OnTimeRangeSelect(days: number): void {\n this.SelectedDays = days;\n const end = new Date();\n const start = new Date(end.getTime() - days * 24 * 60 * 60 * 1000);\n this.instrumentationService.setDateRange(start, end);\n this.loadVersionMetrics();\n this.emitState();\n }\n\n Refresh(): void {\n this.instrumentationService.refresh();\n this.loadVersionMetrics();\n }\n\n // -- Trend helpers (called from template) --\n\n PassRate(t: TestTrendData): number {\n return t.totalRuns === 0 ? 0 : (t.passed / t.totalRuns) * 100;\n }\n\n PassRateClass(t: TestTrendData): string {\n const rate = this.PassRate(t);\n if (rate >= 80) return 'text-pass-good';\n if (rate >= 60) return 'text-pass-warn';\n return 'text-pass-bad';\n }\n\n SegmentPct(t: TestTrendData, segment: 'passed' | 'failed' | 'skipped'): number {\n if (t.totalRuns === 0) return 0;\n const value = segment === 'passed' ? t.passed : segment === 'failed' ? t.failed : t.skipped;\n return (value / t.totalRuns) * 100;\n }\n\n ScoreBarClass(score: number): string {\n if (score >= 0.9) return 'bar-fill score-excellent';\n if (score >= 0.7) return 'bar-fill score-good';\n if (score >= 0.5) return 'bar-fill score-fair';\n return 'bar-fill score-poor';\n }\n\n ScoreTextClass(score: number): string {\n if (score >= 0.9) return 'text-excellent';\n if (score >= 0.7) return 'text-good';\n if (score >= 0.5) return 'text-fair';\n return 'text-poor';\n }\n\n CostBarPct(t: TestTrendData, all: TestTrendData[]): number {\n const maxCost = Math.max(...all.map(x => x.totalCost), 0.01);\n return (t.totalCost / maxCost) * 100;\n }\n\n FormatBucketDate(d: Date): string {\n const dt = d instanceof Date ? d : new Date(d);\n return `${dt.getMonth() + 1}/${dt.getDate()}`;\n }\n\n FormatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`;\n const s = Math.floor(ms / 1000);\n const m = Math.floor(s / 60);\n if (m > 0) return `${m}m ${s % 60}s`;\n return `${s}s`;\n }\n\n AbsNum(n: number): number {\n return Math.abs(n);\n }\n\n /** trackBy for trend @for loops */\n TrackTrend(index: number, t: TestTrendData): number {\n return index;\n }\n\n /** @deprecated Use {@link TrackTrend}. */\n trackTrend(index: number, t: TestTrendData): number {\n return this.TrackTrend(index, t);\n }\n\n // ===================================================================\n // Private helpers\n // ===================================================================\n\n private restoreState(): void {\n if (this.InitialState != null) {\n if (typeof this.InitialState['selectedDays'] === 'number') {\n this.SelectedDays = this.InitialState['selectedDays'] as number;\n }\n }\n // Apply initial date range\n const end = new Date();\n const start = new Date(end.getTime() - this.SelectedDays * 24 * 60 * 60 * 1000);\n this.instrumentationService.setDateRange(start, end);\n }\n\n private setupObservables(): void {\n // Limit trends to last 10 buckets for readability\n this.DisplayTrends$ = this.instrumentationService.trends$.pipe(\n map(trends => trends.slice(-10)),\n shareReplay(1)\n );\n\n this.PassRateTrendDirection$ = this.DisplayTrends$.pipe(\n map(trends => this.calculateTrendDirection(trends)),\n shareReplay(1)\n );\n\n const analytics$ = this.instrumentationService.analytics$;\n\n this.TopFailingTests$ = analytics$.pipe(\n map(a => a.topFailingTests.slice(0, 5)),\n shareReplay(1)\n );\n\n this.SlowestTests$ = analytics$.pipe(\n map(a => a.slowestTests.slice(0, 5)),\n shareReplay(1)\n );\n\n this.MostExpensiveTests$ = analytics$.pipe(\n map(a => a.mostExpensiveTests.slice(0, 5)),\n shareReplay(1)\n );\n\n // Cache the breakdown name lists so the dashboard's agent context can publish\n // them (top failing / slowest / most expensive tests).\n analytics$.pipe(takeUntil(this.destroy$)).subscribe(a => {\n this.topFailingNames = a.topFailingTests.slice(0, 5).map(t => t.testName);\n this.slowestNames = a.slowestTests.slice(0, 5).map(t => t.testName);\n this.mostExpensiveNames = a.mostExpensiveTests.slice(0, 5).map(t => t.testName);\n this.emitState();\n });\n\n this.ScoreDistribution$ = this.instrumentationService.testRuns$.pipe(\n map(runs => this.buildScoreDistribution(runs)),\n shareReplay(1)\n );\n }\n\n private calculateTrendDirection(trends: TestTrendData[]): 'up' | 'down' | 'stable' {\n if (trends.length < 2) return 'stable';\n const recentHalf = trends.slice(Math.floor(trends.length / 2));\n const olderHalf = trends.slice(0, Math.floor(trends.length / 2));\n\n const avgRecent = this.averagePassRate(recentHalf);\n const avgOlder = this.averagePassRate(olderHalf);\n const diff = avgRecent - avgOlder;\n\n if (diff > 2) return 'up';\n if (diff < -2) return 'down';\n return 'stable';\n }\n\n private averagePassRate(trends: TestTrendData[]): number {\n const withRuns = trends.filter(t => t.totalRuns > 0);\n if (withRuns.length === 0) return 0;\n return withRuns.reduce((s, t) => s + (t.passed / t.totalRuns) * 100, 0) / withRuns.length;\n }\n\n private buildScoreDistribution(runs: TestRunSummary[]): ScoreDistributionBucket[] {\n const defs: Array<{ label: string; color: string; min: number; max: number }> = [\n { label: 'Excellent (>=0.9)', color: '#43a047', min: 0.9, max: 1.01 },\n { label: 'Good (0.7-0.89)', color: '#1e88e5', min: 0.7, max: 0.9 },\n { label: 'Fair (0.5-0.69)', color: '#f57c00', min: 0.5, max: 0.7 },\n { label: 'Poor (0.3-0.49)', color: '#e64a19', min: 0.3, max: 0.5 },\n { label: 'Fail (<0.3)', color: '#e53935', min: 0, max: 0.3 }\n ];\n\n const total = runs.length || 1;\n\n return defs.map(d => {\n const count = runs.filter(r => r.score >= d.min && r.score < d.max).length;\n return {\n label: d.label,\n color: d.color,\n count,\n percentage: (count / total) * 100\n };\n });\n }\n\n private async loadVersionMetrics(): Promise<void> {\n this.IsLoadingVersions = true;\n this.cdr.markForCheck();\n\n try {\n const metrics = await this.instrumentationService.getVersionMetrics();\n this.VersionRows = this.buildVersionRows(metrics);\n } catch {\n this.VersionRows = [];\n } finally {\n this.IsLoadingVersions = false;\n this.emitState();\n this.cdr.markForCheck();\n }\n }\n\n private buildVersionRows(metrics: VersionMetrics[]): VersionRow[] {\n // metrics are already sorted newest-first by the service\n return metrics.map((m, i) => {\n const prev = i < metrics.length - 1 ? metrics[i + 1] : null;\n\n let passRateDelta: number | null = null;\n let costDelta: number | null = null;\n\n if (prev) {\n passRateDelta = m.passRate - prev.passRate;\n costDelta = prev.totalCost !== 0\n ? ((m.totalCost - prev.totalCost) / prev.totalCost) * 100\n : null;\n }\n\n return {\n version: m.version,\n gitCommitShort: m.gitCommit.length > 7 ? m.gitCommit.substring(0, 7) : m.gitCommit,\n agentVersion: m.agentVersion,\n date: m.runDate,\n totalTests: m.totalTests,\n passRate: m.passRate,\n totalCost: m.totalCost,\n passRateDelta,\n costDelta\n };\n });\n }\n\n private emitState(): void {\n this.StateChange.emit({\n selectedDays: this.SelectedDays,\n // Breakdown name lists + version count for the dashboard's agent context.\n topFailingTests: this.topFailingNames,\n slowestTests: this.slowestNames,\n mostExpensiveTests: this.mostExpensiveNames,\n versionCount: this.VersionRows.length\n });\n }\n}\n"]}
1
+ {"version":3,"file":"testing-analytics.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-analytics.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,KAAK,EAAE,MAAM,EAAE,YAAY,EAAqB,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACtI,OAAO,EAAc,OAAO,EAAmB,MAAM,MAAM,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAQ7D,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;;;;;;;;;;;IAwClG,wBAAyD;;;IAAzD,0GAA0C;;;;IAA3B,6CAAyB;;;;IAShC,yCAG4C;IAA1C,2OAAW,uCAA6B,KAAC;IAC3C,iBAAiB;;;;IAFf,AADA,sCAAqB,iDACiB;;;IAQ5C,wBAAyD;;;;IAZzD,AAJF,AADF,sCAAgB,wBAI4C,aAC3C;IACX,qHAMC;IACD,4CAAyC;IAAtB,4MAAW,gBAAS,KAAC;IAE5C,AADE,AAD2C,iBAAoB,EACzD,EACS;IACjB,oCAAc;IACZ,0GAA0C;IAE9C,AADE,iBAAe,EACA;;;;IAbX,eAMC;IAND,gCAMC;IAKY,eAAyB;IAAzB,6CAAyB;;;IAQ1C,4BAAgB;IAAA,YAA0B;IAAA,iBAAI;;;IAA9B,cAA0B;IAA1B,iDAA0B;;;;IAWpC,kCAA4F;IAA/B,+QAAS,8BAAoB,KAAC;IAAC,wBAAQ;IAAA,iBAAS;;;IAHjH,yBAAG;IACD,YACA;IAAA,iIAAmB;IAGrB,iBAAI;;;IAJF,cACA;IADA,wEACA;IAAA,cAEC;IAFD,yCAEC;;;IAIH,yBAAG;IAAA,6BAAa;IAAA,4BAAM;IAAA,YAAc;IAAA,iBAAO;IAAC,YAAiB;IAAA,iBAAI;;;IAA3C,eAAc;IAAd,iCAAc;IAAQ,cAAiB;IAAjB,+CAAiB;;;IAb/D,8BAA2B;IACzB,wBAA0C;IAC1C,+BACF;IAAA,iBAAM;IACN,wIAOC;IACD,yHAEC;;;IAVD,eAOC;IAPD,iCAOC;IACD,eAEC;IAFD,kCAEC;;;IAeK,qCAAqF;;;IAK/E,AADF,+BAAqB,eACK;IAAA,YAAmC;IAAA,iBAAO;IAClE,+BAAuB;IACrB,0BAAwE;IAC1E,iBAAM;IACN,gCAAmD;IAAA,YAAmC;;IACxF,AADwF,iBAAO,EACzF;;;;IALoB,eAAmC;IAAnC,6DAAmC;IAErB,eAA6B;IAA7B,mDAA6B;IAE3C,cAA0B;IAA1B,yCAA0B;IAAC,cAAmC;IAAnC,oFAAmC;;;IAOtF,wBAAmD;IAAC,gCAAuB;IAAA,yBAAS;IAAA,iBAAO;;;IAE3F,wBAAuD;IAAC,gCAAyB;IAAA,yBAAS;IAAA,iBAAO;;;IAEjG,wBAA+C;IAAC,gCAA4B;IAAA,sBAAM;IAAA,iBAAO;;;IADzF,AAFA,AAFF,0IAAoB,oHAES,oHAEpB;;;IAJT,mEAMC;;;IAnBL,+BAAuB;IACrB,uKAQC;IACH,iBAAM;IACN,+BAA6B;IAC3B,4HAA+C;;IASjD,iBAAM;;;;;IApBJ,cAQC;IARD,yBAQC;IAGD,eAQC;IARD,oGAQC;;;IArBH,AAFF,oIAA2B,wFAElB;;IAFT,0CAyBC;;;IASC,qCAAqF;;;IAK/E,AADF,+BAAqB,eACK;IAAA,YAAmC;IAAA,iBAAO;IAClE,+BAA+B;IAG7B,AADA,AADA,0BAAoF,cACA,cACE;IACxF,iBAAM;IACN,gCAAwB;IAAA,YAAiB;IAC3C,AAD2C,iBAAO,EAC5C;;;;IAPoB,eAAmC;IAAnC,8DAAmC;IAErB,eAAyC;IAAzC,gEAAyC;IACzC,cAAyC;IAAzC,gEAAyC;IACxC,cAA0C;IAA1C,iEAA0C;IAEzD,eAAiB;IAAjB,qCAAiB;;;IAT/C,+BAAuB;IACrB,uKAUC;IACH,iBAAM;IACN,+BAAwB;IACtB,2BAA2C;IAAC,wBAC5C;IAAA,2BAA2C;IAAC,wBAC5C;IAAA,2BAA4C;IAAC,yBAC/C;IAAA,iBAAM;;;IAhBJ,cAUC;IAVD,yBAUC;;;IAZH,AAFF,oIAA2B,yFAElB;;IAFT,0CAqBC;;;IASC,qCAA+E;;;IAKzE,AADF,+BAAqB,eACK;IAAA,YAAmC;IAAA,iBAAO;IAClE,+BAAuB;IACrB,0BAA2G;IAC7G,iBAAM;IACN,gCAAiE;IAAA,YAA8C;;IACjH,AADiH,iBAAO,EAClH;;;;IALoB,eAAmC;IAAnC,8DAAmC;IAEnC,eAAuC;IAAvC,uDAAuC;IAAC,sDAAsC;IAE9E,cAAwC;IAAxC,wDAAwC;IAAC,cAA8C;IAA9C,uFAA8C;;;IAPrH,+BAAuB;IACrB,wKAQC;IACH,iBAAM;;;IATJ,cAQC;IARD,yBAQC;;;IAVH,AAFF,oIAA2B,mGAElB;;IAFT,0CAcC;;;IASC,qCAAgF;;;IAK1E,AADF,+BAAqB,eACK;IAAA,YAAmC;IAAA,iBAAO;IAClE,+BAAuB;IACrB,0BAA6E;IAC/E,iBAAM;IACN,gCAAwB;IAAA,YAAoC;;IAC9D,AAD8D,iBAAO,EAC/D;;;;;IALoB,eAAmC;IAAnC,8DAAmC;IAE1B,eAAuC;IAAvC,kEAAuC;IAEhD,eAAoC;IAApC,0EAAoC;;;IAPlE,+BAAuB;IACrB,uKAQC;IACH,iBAAM;;;IATJ,cAQC;IARD,yBAQC;;;IAVH,AAFF,oIAA2B,mGAElB;;IAFT,0CAcC;;;IAiBC,qCAA4E;;;IAKtE,AADF,+BAAyB,eACyB;IAAA,YAAgB;IAAA,iBAAO;IACvE,gCAAyB;IAAA,YAAoB;IAAA,iBAAO;IACpD,gCAAgC;IAAA,YAAqC;;IACvE,AADuE,iBAAO,EACxE;;;IAHuB,cAAoB;IAApB,sCAAoB;IAAC,cAAgB;IAAhB,oCAAgB;IACvC,eAAoB;IAApB,wCAAoB;IACb,eAAqC;IAArC,gFAAqC;;;IAL3E,+BAA0B;IACxB,wIAMC;IACH,iBAAM;;;IAPJ,cAMC;IAND,wBAMC;;;IARH,AAFF,oIAA0B,mGAEjB;;IAFT,0CAYC;;;IASC,qCAAkF;;;IAK5E,AADF,+BAAyB,eACyB;IAAA,YAAgB;IAAA,iBAAO;IACvE,gCAA8B;IAAA,YAAmC;IAAA,iBAAO;IACxE,gCAAgC;IAAA,YAAuC;IACzE,AADyE,iBAAO,EAC1E;;;;IAHuB,cAAoB;IAApB,sCAAoB;IAAC,cAAgB;IAAhB,oCAAgB;IAClC,eAAmC;IAAnC,8DAAmC;IACjC,eAAuC;IAAvC,uEAAuC;;;IAL7E,+BAA0B;IACxB,wIAMC;IACH,iBAAM;;;IAPJ,cAMC;IAND,wBAMC;;;IARH,AAFF,oIAA0B,mGAEjB;;IAFT,0CAYC;;;IASC,qCAAqF;;;IAK/E,AADF,+BAAyB,eACyB;IAAA,YAAgB;IAAA,iBAAO;IACvE,gCAA8B;IAAA,YAAoC;;IAAA,iBAAO;IACzE,gCAAgC;IAAA,YAAsC;;IACxE,AADwE,iBAAO,EACzE;;;IAHuB,cAAoB;IAApB,sCAAoB;IAAC,cAAgB;IAAhB,oCAAgB;IAClC,eAAoC;IAApC,0EAAoC;IAClC,eAAsC;IAAtC,gFAAsC;;;IAL5E,+BAA0B;IACxB,yIAMC;IACH,iBAAM;;;IAPJ,cAMC;IAND,wBAMC;;;IARH,AAFF,oIAA0B,mGAEjB;;IAFT,0CAYC;;;IAaD,qCAAiG;;;IAK3F,AADF,+BAAsB,eACK;IAAA,YAAa;IAAA,iBAAO;IAC7C,+BAAwB;IACtB,0BAAyF;IAC3F,iBAAM;IACN,gCAAyB;IAAA,YAAa;IAAA,iBAAO;IAC7C,gCAAuB;IAAA,YAAoC;;IAC7D,AAD6D,iBAAO,EAC9D;;;IANqB,eAAa;IAAb,iCAAa;IAEb,eAA8B;IAAC,AAA/B,8CAA8B,2BAA6B;IAE3D,eAAa;IAAb,iCAAa;IACf,eAAoC;IAApC,+EAAoC;;;IARjE,+BAA+B;IAC7B,0IASC;IACH,iBAAM;;;IAVJ,cASC;IATD,0BASC;;;IAXH,AAFF,oIAA4B,mGAEnB;;IAFT,0CAeC;;;IAYD,+BAAwB;IAAA,wBAA2C;IAAA,4BAAM;IAAA,mCAAmB;IAAO,AAAP,iBAAO,EAAM;;;IAEzG,4BAAgB;IAAA,YAAyB;IAAA,iBAAI;;;IAA7B,cAAyB;IAAzB,gDAAyB;;;IAEzC,qCAAkG;;;IA2BhF,wBAAoC;;;IAEpC,wBAAsC;;;IAJ1C,gCAAwJ;IAGpJ,AAFF,6IAA6B,uHAEO;IAGpC,YACF;;IAAA,iBAAO;;;;IAPiG,AAA3C,AAAzC,qDAAwC,yCAA2C,8CAAgD;IACrJ,cAIC;IAJD,oFAIC;IACD,eACF;IADE,qGACF;;;IAQI,wBAAoC;;;IAEpC,wBAAsC;;;IAJ1C,gCAA4I;IAGxI,AAFF,6IAAyB,uHAEO;IAGhC,YACF;;IAAA,iBAAO;;;;IAPyF,AAAvC,AAArC,iDAAoC,qCAAuC,0CAA4C;IACzI,cAIC;IAJD,4EAIC;IACD,eACF;IADE,iGACF;;;IA5BF,AADF,AADF,0BAAI,SACE,eAC0B;IAAA,YAAwB;IAAA,iBAAO;IAC3D,gCAA4B;IAAA,YAAsB;IACpD,AADoD,iBAAO,EACtD;IACL,0BAAI;IAAA,YAAkC;;IAAA,iBAAK;IAC3C,8BAAgB;IAAA,aAAoB;IAAA,iBAAK;IAEvC,AADF,+BAAgB,YACR;IAAA,aAAoC;;IAAA,iBAAO;IACjD,oIAAkC;IAUpC,iBAAK;IAEH,AADF,+BAAgB,YACR;IAAA,aAAsC;;IAAA,iBAAO;IACnD,oIAA8B;IAWlC,AADE,iBAAK,EACF;;;IA/B2B,eAAwB;IAAxB,4CAAwB;IACxB,eAAsB;IAAtB,0CAAsB;IAEhD,eAAkC;IAAlC,sEAAkC;IACtB,eAAoB;IAApB,wCAAoB;IAE5B,eAAoC;IAApC,iFAAoC;IAC1C,eASC;IATD,0DASC;IAGK,eAAsC;IAAtC,8EAAsC;IAC5C,eASC;IATD,sDASC;;;IAxCL,AADF,AADF,AADF,AADF,+BAAgC,gBACD,YACpB,SACD,SACE;IAAA,uBAAO;IAAA,iBAAK;IAChB,0BAAI;IAAA,oBAAI;IAAA,iBAAK;IACb,8BAAgB;IAAA,qBAAK;IAAA,iBAAK;IAC1B,+BAAgB;IAAA,0BAAS;IAAA,iBAAK;IAC9B,+BAAgB;IAAA,qBAAI;IAExB,AADE,AADsB,iBAAK,EACtB,EACC;IACR,8BAAO;IACL,+HAmCC;IAGP,AADE,AADE,iBAAQ,EACF,EACJ;;;IAtCA,gBAmCC;IAnCD,iCAmCC;;;IAnSb,8BAA+B;IAC7B,uGAA4B;IAG5B,+FAAiD;IAmBjD,8BAA2B;IACzB,wBAAsC;IACtC,gCACF;IAAA,iBAAM;IAKF,AADF,AAHF,+BAAwB,cAGO,SACvB;IAAA,wBAAwC;IAAC,iCAAe;IAAA,iBAAK;IACjE,iGAAyC;;IA4B3C,iBAAM;IAIJ,AADF,gCAA6B,UACvB;IAAA,yBAAuC;IAAC,4BAAU;IAAA,iBAAK;IAC3D,iGAAyC;;IAwB3C,iBAAM;IAIJ,AADF,gCAA6B,UACvB;IAAA,yBAA4C;IAAC,6BAAW;IAAA,iBAAK;IACjE,iGAAyC;;IAiB3C,iBAAM;IAIJ,AADF,gCAA6B,UACvB;IAAA,yBAAuC;IAAC,4BAAU;IAAA,iBAAK;IAC3D,iGAAyC;;IAkB7C,AADE,iBAAM,EACF;IAGN,+BAA2B;IACzB,yBAAkD;IAClD,2BACF;IAAA,iBAAM;IAKF,AADF,AAHF,gCAA2B,eAGM,UACzB;IAAA,yBAAgD;IAAC,mCAAiB;IAAA,iBAAK;IAC3E,iGAA0C;;IAe5C,iBAAM;IAIJ,AADF,gCAA+B,UACzB;IAAA,yBAAiC;IAAC,+BAAa;IAAA,iBAAK;IACxD,iGAAuC;;IAezC,iBAAM;IAIJ,AADF,gCAA+B,UACzB;IAAA,yBAAiC;IAAC,sCAAoB;IAAA,iBAAK;IAC/D,iGAA6C;;IAgBjD,AADE,iBAAM,EACF;IAGN,+BAA2B;IACzB,yBAAqC;IACrC,qCACF;IAAA,iBAAM;IACN,gCAAoC;IAClC,iGAA8C;;IAkBhD,iBAAM;IAGN,+BAA2B;IACzB,yBAAwC;IACxC,qCACA;IAAA,iCAA+B;IAAA,wDAAuC;IACxE,AADwE,iBAAO,EACzE;IACN,gCAA+B;IAO3B,AAFA,AAFA,AAFF,4GAAyB,mFAES,iGAEK,uFAE9B;IAsDb,AADE,iBAAM,EACF;;;;;;;;;;;IAxSJ,cAEC;IAFD,sDAEC;IACD,cAgBC;IAhBD,kFAgBC;IAYG,eA2BC;IA3BD,8FA2BC;IAMD,eAuBC;IAvBD,8FAuBC;IAMD,eAgBC;IAhBD,8FAgBC;IAMD,eAgBC;IAhBD,8FAgBC;IAcD,gBAcC;IAdD,gGAcC;IAMD,eAcC;IAdD,6FAcC;IAMD,eAcC;IAdD,qGAcC;IAUH,eAiBC;IAjBD,oGAiBC;IAUD,eA0DC;IA1DD,6HA0DC;;AA1UT,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAwvB9E,MAAM,OAAO,yBAAyB;IA0D1B;IACA;IAzDV,mCAAmC;IAC1B,YAAY,GAAmC,IAAI,CAAC;IAE7D,4CAA4C;IAC5C,IAAa,YAAY,CAAC,KAAqC;QAC7D,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IACD,4CAA4C;IAC5C,IAAI,YAAY;QACd,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IACD,8FAA8F;IACrF,WAAW,GAAG,KAAK,CAAC;IACnB,WAAW,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEpE;;;;;;OAMG;IACO,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC;IAEzC,+BAA+B;IAC/B,UAAU,GAAsB;QAC9B,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE;QAC5B,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE;QAC9B,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,EAAE;KAC/B,CAAC;IACF,YAAY,GAAG,EAAE,CAAC;IAClB,WAAW,GAAiB,EAAE,CAAC;IAC/B,iBAAiB,GAAG,KAAK,CAAC;IAC1B,WAAW,GAAmD,EAAE,CAAC;IACjE,YAAY,GAAmD,EAAE,CAAC;IAClE,oBAAoB,GAAG,EAAE,CAAC;IAC1B,mBAAmB,GAAG,EAAE,CAAC;IAEzB,iEAAiE;IACzD,eAAe,GAAa,EAAE,CAAC;IAC/B,YAAY,GAAa,EAAE,CAAC;IAC5B,kBAAkB,GAAa,EAAE,CAAC;IAE1C,8BAA8B;IAC9B,cAAc,CAA+B;IAC7C,uBAAuB,CAAwC;IAC/D,gBAAgB,CAAgD;IAChE,aAAa,CAA6C;IAC1D,mBAAmB,CAAmD;IACtE,kBAAkB,CAAyC;IAE3D,0BAA0B;IAClB,QAAQ,GAAG,IAAI,OAAO,EAAQ,CAAC;IAC/B,YAAY,GAAG,CAAC,CAAC,CAAC,8BAA8B;IAExD,YACU,sBAAqD,EACrD,GAAsB;QADtB,2BAAsB,GAAtB,sBAAsB,CAA+B;QACrD,QAAG,GAAH,GAAG,CAAmB;IAC7B,CAAC;IAEJ,sEAAsE;IACtE,YAAY;IACZ,sEAAsE;IAEtE,QAAQ;QACN,IAAI,CAAC,YAAY,EAAE,CAAC;QACpB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAClC,CAAC;IAED,sHAAsH;IACtH,KAAK,CAAC,mBAAmB;QACvB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,IAAI,CAAC,sBAAsB,CAAC,QAAQ,CAAC;YACtD,IAAI,CAAC,QAAQ;gBAAE,OAAO;YACtB,MAAM,IAAI,GAAG,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC;YACpD,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;gBACrC,UAAU,EAAE,wBAAwB;gBACpC,WAAW,EAAE,sDAAsD;gBACnE,OAAO,EAAE,kBAAkB;gBAC3B,UAAU,EAAE,QAAQ;gBACpB,OAAO,EAAE,GAAG;aACb,CAAC,CAAC;YACH,IAAI,CAAC,WAAW,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,WAAW,CAAC,YAAY,IAAI,6BAA6B,CAAC,CAAC;YACrG,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAAE,CAAC,WAAW,CAAC,OAAO,IAAI,EAAE,CAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACzG,EAAE,EAAE,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC;gBAC5D,KAAK,EAAE,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC;gBACvE,KAAK,EAAE,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC;aACxE,CAAC,CAAC,CAAC,CAAC;YACL,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;gBAChC,UAAU,EAAE,8BAA8B;gBAC1C,WAAW,EAAE,gEAAgE;gBAC7E,UAAU,EAAE,QAAQ;gBACpB,OAAO,EAAE,IAAI;aACd,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,YAAY,IAAI,+BAA+B,CAAC,CAAC;YAC7F,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;gBAClC,UAAU,EAAE,qBAAqB;gBACjC,UAAU,EAAE,QAAQ;gBACpB,OAAO,EAAE,GAAG;aACb,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,CAAC,OAAO;gBAAE,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,YAAY,IAAI,iCAAiC,CAAC,CAAC;YACnG,MAAM,cAAc,GAAG,IAAI,GAAG,CAAE,CAAC,WAAW,CAAC,OAAO,IAAI,EAAE,CAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;YAC7H,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAE,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;YACvL,IAAI,CAAC,YAAY,GAAG,qBAAqB,CAAE,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAA+B,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;gBACxG,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC;gBACtE,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7E,OAAO;oBACL,GAAG,EAAE,iBAAiB,CAAC,GAAG,CAAC,CAAC,GAAG;oBAC/B,eAAe,EAAE,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC;oBACjF,UAAU,EAAE,GAAG,CAAC,UAAU,KAAK,IAAI,IAAI,GAAG,CAAC,UAAU,KAAK,CAAC;oBAC3D,aAAa,EAAE,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI;iBACzD,CAAC;YACJ,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YACtC,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC;YAC/B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;YACvB,IAAI,CAAC,oBAAoB,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,kCAAkC,CAAC;YACxG,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,eAAe,EAAE,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,WAAW;QACT,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAED,sEAAsE;IACtE,kCAAkC;IAClC,sEAAsE;IAEtE,iBAAiB,CAAC,IAAY;QAC5B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QACnE,IAAI,CAAC,sBAAsB,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACrD,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAC1B,IAAI,CAAC,SAAS,EAAE,CAAC;IACnB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,CAAC;QACtC,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAED,6CAA6C;IAE7C,QAAQ,CAAC,CAAgB;QACvB,OAAO,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;IAChE,CAAC;IAED,aAAa,CAAC,CAAgB;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,IAAI,IAAI,EAAE;YAAE,OAAO,gBAAgB,CAAC;QACxC,IAAI,IAAI,IAAI,EAAE;YAAE,OAAO,gBAAgB,CAAC;QACxC,OAAO,eAAe,CAAC;IACzB,CAAC;IAED,UAAU,CAAC,CAAgB,EAAE,OAAwC;QACnE,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QAC5F,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;IACrC,CAAC;IAED,aAAa,CAAC,KAAa;QACzB,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,0BAA0B,CAAC;QACpD,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,qBAAqB,CAAC;QAC/C,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,qBAAqB,CAAC;QAC/C,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,gBAAgB,CAAC;QAC1C,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,WAAW,CAAC;QACrC,IAAI,KAAK,IAAI,GAAG;YAAE,OAAO,WAAW,CAAC;QACrC,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,UAAU,CAAC,CAAgB,EAAE,GAAoB;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;QAC7D,OAAO,CAAC,CAAC,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,GAAG,CAAC;IACvC,CAAC;IAED,gBAAgB,CAAC,CAAO;QACtB,MAAM,EAAE,GAAG,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO,GAAG,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC;IAChD,CAAC;IAED,cAAc,CAAC,EAAU;QACvB,IAAI,EAAE,GAAG,IAAI;YAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC;QAC5C,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;QAChC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC;QACrC,OAAO,GAAG,CAAC,GAAG,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,CAAS;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;IAED,mCAAmC;IACnC,UAAU,CAAC,KAAa,EAAE,CAAgB;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,0CAA0C;IAC1C,UAAU,CAAC,KAAa,EAAE,CAAgB;QACxC,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACnC,CAAC;IAED,sEAAsE;IACtE,kBAAkB;IAClB,sEAAsE;IAE9D,YAAY;QAClB,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,EAAE,CAAC;YAC9B,IAAI,OAAO,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC1D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,cAAc,CAAW,CAAC;YAClE,CAAC;QACH,CAAC;QACD,2BAA2B;QAC3B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;QAChF,IAAI,CAAC,sBAAsB,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACvD,CAAC;IAEO,gBAAgB;QACtB,kDAAkD;QAClD,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,IAAI,CAC5D,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAChC,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;QAEF,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CACrD,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC,EACnD,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;QAEF,MAAM,UAAU,GAAG,IAAI,CAAC,sBAAsB,CAAC,UAAU,CAAC;QAE1D,IAAI,CAAC,gBAAgB,GAAG,UAAU,CAAC,IAAI,CACrC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACvC,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;QAEF,IAAI,CAAC,aAAa,GAAG,UAAU,CAAC,IAAI,CAClC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EACpC,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;QAEF,IAAI,CAAC,mBAAmB,GAAG,UAAU,CAAC,IAAI,CACxC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAC1C,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;QAEF,8EAA8E;QAC9E,uDAAuD;QACvD,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE;YACtD,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC1E,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACpE,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAChF,IAAI,CAAC,SAAS,EAAE,CAAC;QACnB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,sBAAsB,CAAC,SAAS,CAAC,IAAI,CAClE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC,EAC9C,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;IACJ,CAAC;IAEO,uBAAuB,CAAC,MAAuB;QACrD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,QAAQ,CAAC;QACvC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAC/D,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;QAEjE,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QACnD,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QACjD,MAAM,IAAI,GAAG,SAAS,GAAG,QAAQ,CAAC;QAElC,IAAI,IAAI,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC1B,IAAI,IAAI,GAAG,CAAC,CAAC;YAAE,OAAO,MAAM,CAAC;QAC7B,OAAO,QAAQ,CAAC;IAClB,CAAC;IAEO,eAAe,CAAC,MAAuB;QAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QACpC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC5F,CAAC;IAEO,sBAAsB,CAAC,IAAsB;QACnD,MAAM,IAAI,GAAsE;YAC9E,EAAE,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE;YACrE,EAAE,KAAK,EAAE,iBAAiB,EAAI,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;YACpE,EAAE,KAAK,EAAE,iBAAiB,EAAI,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;YACpE,EAAE,KAAK,EAAE,iBAAiB,EAAI,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE;YACpE,EAAE,KAAK,EAAE,aAAa,EAAQ,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAI,GAAG,EAAE,GAAG,EAAE;SACrE,CAAC;QAEF,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;QAE/B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YAClB,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;YAC3E,OAAO;gBACL,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,KAAK,EAAE,CAAC,CAAC,KAAK;gBACd,KAAK;gBACL,UAAU,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,GAAG;aAClC,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,kBAAkB;QAC9B,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,sBAAsB,CAAC,iBAAiB,EAAE,CAAC;YACtE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,mBAAmB,GAAG,EAAE,CAAC;QAChC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,mBAAmB,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,iCAAiC,CAAC;QACxG,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAC/B,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC1B,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,OAAyB;QAChD,yDAAyD;QACzD,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAC1B,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAE5D,IAAI,aAAa,GAAkB,IAAI,CAAC;YACxC,IAAI,SAAS,GAAkB,IAAI,CAAC;YAEpC,IAAI,IAAI,EAAE,CAAC;gBACT,aAAa,GAAG,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;gBAC3C,SAAS,GAAG,IAAI,CAAC,SAAS,KAAK,CAAC;oBAC9B,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,GAAG;oBACzD,CAAC,CAAC,IAAI,CAAC;YACX,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,cAAc,EAAE,CAAC,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;gBAClF,YAAY,EAAE,CAAC,CAAC,YAAY;gBAC5B,IAAI,EAAE,CAAC,CAAC,OAAO;gBACf,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,QAAQ,EAAE,CAAC,CAAC,QAAQ;gBACpB,SAAS,EAAE,CAAC,CAAC,SAAS;gBACtB,aAAa;gBACb,SAAS;aACV,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,SAAS;QACf,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;YACpB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,0EAA0E;YAC1E,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;YAC3C,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,MAAM;SACtC,CAAC,CAAC;IACL,CAAC;mHA1XU,yBAAyB;6DAAzB,yBAAyB;YA/uBhC,AAFF,iGAAmB,6EAEV;YAuBT,6HAAsB;;YAzBtB,yCAuBC;;;iFA0tBQ,yBAAyB;cAtvBrC,SAAS;6BACI,KAAK,YACP,uBAAuB,mBAChB,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsUT;;kBA+aA,KAAK;;kBAGL,KAAK;;kBAQL,KAAK;;kBACL,MAAM;;kBASN,MAAM;;kFAxBI,yBAAyB","sourcesContent":["import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { Observable, Subject, BehaviorSubject } from 'rxjs';\nimport { takeUntil, map, shareReplay } from 'rxjs/operators';\nimport {\n TestingInstrumentationService,\n TestTrendData,\n TestAnalytics,\n TestRunSummary,\n VersionMetrics\n} from '../services/testing-instrumentation.service';\nimport { CompositeKey, RunView } from '@memberjunction/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { CriterionFailureRates, CriterionIdentity, RubricScoreTrend } from '@memberjunction/ng-testing';\n\n// ---------------------------------------------------------------------------\n// Local interfaces\n// ---------------------------------------------------------------------------\n\ninterface TimeRangeOption {\n label: string;\n days: number;\n}\n\ninterface ScoreDistributionBucket {\n label: string;\n color: string;\n count: number;\n percentage: number;\n}\n\ninterface VersionRow {\n version: string;\n gitCommitShort: string;\n agentVersion: string;\n date: Date;\n totalTests: number;\n passRate: number;\n totalCost: number;\n passRateDelta: number | null;\n costDelta: number | null;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\n@Component({\n standalone: false,\n selector: 'app-testing-analytics',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (HideToolbar) {\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n } @else {\n <mj-page-layout>\n <mj-page-header\n Title=\"Testing Analytics\"\n Icon=\"fa-solid fa-chart-bar\"\n Subtitle=\"Test trends, pass rates, and version metrics\">\n <div actions>\n @for (range of TimeRanges; track range.days) {\n <mj-filter-chip\n [Label]=\"range.label\"\n [Active]=\"SelectedDays === range.days\"\n (Clicked)=\"OnTimeRangeSelect(range.days)\">\n </mj-filter-chip>\n }\n <mj-refresh-button (Clicked)=\"Refresh()\"></mj-refresh-button>\n </div>\n </mj-page-header>\n <mj-page-body>\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </mj-page-body>\n </mj-page-layout>\n }\n\n <ng-template #content>\n <div class=\"testing-analytics\">\n @if (RubricAnalyticsError) {\n <p role=\"alert\">{{ RubricAnalyticsError }}</p>\n }\n @if (RubricTrend.length || FailureRates.length) {\n <div class=\"section-title\">\n <i class=\"fa-solid fa-scale-balanced\"></i>\n Rubric scores\n </div>\n @for (point of RubricTrend; track $index) {\n <p>\n {{ point.at }} — {{ point.score }}\n @if (point.runId) {\n <button type=\"button\" mjButton variant=\"secondary\" size=\"sm\" (click)=\"OpenRun(point.runId)\">Open run</button>\n }\n </p>\n }\n @for (rate of FailureRates; track rate.key) {\n <p>CriterionKey <code>{{ rate.key }}</code> — {{ rate.rate }}</p>\n }\n }\n\n <!-- ===== 2. Trend Overview (2-column CSS charts) ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-chart-line\"></i>\n Trend Overview\n </div>\n <div class=\"trend-grid\">\n\n <!-- Pass Rate Trend -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-check-double\"></i> Pass Rate Trend</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-chart-area\" Title=\"No trend data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track\">\n <div class=\"bar-fill pass-rate-bar\" [style.width.%]=\"PassRate(t)\"></div>\n </div>\n <span class=\"bar-value\" [class]=\"PassRateClass(t)\">{{ PassRate(t) | number:'1.0-0' }}%</span>\n </div>\n }\n </div>\n <div class=\"trend-direction\">\n @if (PassRateTrendDirection$ | async; as dir) {\n @if (dir === 'up') {\n <i class=\"fa-solid fa-arrow-trend-up trend-up\"></i> <span class=\"trend-up\">Improving</span>\n } @else if (dir === 'down') {\n <i class=\"fa-solid fa-arrow-trend-down trend-down\"></i> <span class=\"trend-down\">Declining</span>\n } @else {\n <i class=\"fa-solid fa-minus trend-neutral\"></i> <span class=\"trend-neutral\">Stable</span>\n }\n }\n </div>\n }\n }\n </div>\n\n <!-- Run Volume -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-layer-group\"></i> Run Volume</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-chart-bar\" Title=\"No volume data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track stacked\">\n <div class=\"bar-segment passed-seg\" [style.width.%]=\"SegmentPct(t, 'passed')\"></div>\n <div class=\"bar-segment failed-seg\" [style.width.%]=\"SegmentPct(t, 'failed')\"></div>\n <div class=\"bar-segment skipped-seg\" [style.width.%]=\"SegmentPct(t, 'skipped')\"></div>\n </div>\n <span class=\"bar-value\">{{ t.totalRuns }}</span>\n </div>\n }\n </div>\n <div class=\"legend-row\">\n <span class=\"legend-dot passed-seg\"></span> Passed\n <span class=\"legend-dot failed-seg\"></span> Failed\n <span class=\"legend-dot skipped-seg\"></span> Skipped\n </div>\n }\n }\n </div>\n\n <!-- Score Trend -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-star-half-stroke\"></i> Score Trend</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-star\" Title=\"No score data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track\">\n <div class=\"bar-fill\" [class]=\"ScoreBarClass(t.averageScore)\" [style.width.%]=\"t.averageScore * 100\"></div>\n </div>\n <span class=\"bar-value\" [class]=\"ScoreTextClass(t.averageScore)\">{{ (t.averageScore * 100) | number:'1.0-0' }}%</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Cost Trend -->\n <div class=\"card trend-card\">\n <h4><i class=\"fa-solid fa-dollar-sign\"></i> Cost Trend</h4>\n @if (DisplayTrends$ | async; as trends) {\n @if (trends.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-wallet\" Title=\"No cost data\" />\n } @else {\n <div class=\"bar-chart\">\n @for (t of trends; track trackTrend($index, t)) {\n <div class=\"bar-row\">\n <span class=\"bar-label\">{{ FormatBucketDate(t.timestamp) }}</span>\n <div class=\"bar-track\">\n <div class=\"bar-fill cost-bar\" [style.width.%]=\"CostBarPct(t, trends)\"></div>\n </div>\n <span class=\"bar-value\">\\${{ t.totalCost | number:'1.2-2' }}</span>\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n\n <!-- ===== 3. Insights Section ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-magnifying-glass-chart\"></i>\n Insights\n </div>\n <div class=\"insights-grid\">\n\n <!-- Top Failing Tests -->\n <div class=\"card insight-card\">\n <h4><i class=\"fa-solid fa-triangle-exclamation\"></i> Top Failing Tests</h4>\n @if (TopFailingTests$ | async; as tests) {\n @if (tests.length === 0) {\n <mj-empty-state Variant=\"success\" Size=\"compact\" Title=\"No failing tests\" />\n } @else {\n <div class=\"insight-list\">\n @for (t of tests; track t.testName) {\n <div class=\"insight-row\">\n <span class=\"insight-name\" [title]=\"t.testName\">{{ t.testName }}</span>\n <span class=\"badge-fail\">{{ t.failureCount }}</span>\n <span class=\"insight-secondary\">{{ t.failureRate | number:'1.0-1' }}%</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Slowest Tests -->\n <div class=\"card insight-card\">\n <h4><i class=\"fa-solid fa-clock\"></i> Slowest Tests</h4>\n @if (SlowestTests$ | async; as tests) {\n @if (tests.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-bolt\" Title=\"No duration data\" />\n } @else {\n <div class=\"insight-list\">\n @for (t of tests; track t.testName) {\n <div class=\"insight-row\">\n <span class=\"insight-name\" [title]=\"t.testName\">{{ t.testName }}</span>\n <span class=\"insight-primary\">{{ FormatDuration(t.avgDuration) }}</span>\n <span class=\"insight-secondary\">max {{ FormatDuration(t.maxDuration) }}</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Most Expensive Tests -->\n <div class=\"card insight-card\">\n <h4><i class=\"fa-solid fa-coins\"></i> Most Expensive Tests</h4>\n @if (MostExpensiveTests$ | async; as tests) {\n @if (tests.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-dollar-sign\" Title=\"No cost data\" />\n } @else {\n <div class=\"insight-list\">\n @for (t of tests; track t.testName) {\n <div class=\"insight-row\">\n <span class=\"insight-name\" [title]=\"t.testName\">{{ t.testName }}</span>\n <span class=\"insight-primary\">\\${{ t.totalCost | number:'1.2-2' }}</span>\n <span class=\"insight-secondary\">\\${{ t.avgCost | number:'1.4-4' }}/run</span>\n </div>\n }\n </div>\n }\n }\n </div>\n </div>\n\n <!-- ===== 4. Score Distribution ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-chart-pie\"></i>\n Score Distribution\n </div>\n <div class=\"card distribution-card\">\n @if (ScoreDistribution$ | async; as buckets) {\n @if (buckets.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-chart-simple\" Title=\"No score data available\" />\n } @else {\n <div class=\"distribution-bars\">\n @for (b of buckets; track b.label) {\n <div class=\"dist-row\">\n <span class=\"dist-label\">{{ b.label }}</span>\n <div class=\"dist-track\">\n <div class=\"dist-fill\" [style.width.%]=\"b.percentage\" [style.background]=\"b.color\"></div>\n </div>\n <span class=\"dist-count\">{{ b.count }}</span>\n <span class=\"dist-pct\">{{ b.percentage | number:'1.0-1' }}%</span>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- ===== 5. Version Comparison ===== -->\n <div class=\"section-title\">\n <i class=\"fa-solid fa-code-compare\"></i>\n Version Comparison\n <span class=\"section-subtitle\">Compare metrics across different builds</span>\n </div>\n <div class=\"card version-card\">\n @if (IsLoadingVersions) {\n <div class=\"empty-mini\"><i class=\"fa-solid fa-spinner fa-spin\"></i><span>Loading versions...</span></div>\n } @else if (VersionMetricsError) {\n <p role=\"alert\">{{ VersionMetricsError }}</p>\n } @else if (VersionRows.length === 0) {\n <mj-empty-state Size=\"compact\" Icon=\"fa-solid fa-code-branch\" Title=\"No version data available\" />\n } @else {\n <div class=\"version-table-wrap\">\n <table class=\"version-table\">\n <thead>\n <tr>\n <th>Version</th>\n <th>Date</th>\n <th class=\"num\">Tests</th>\n <th class=\"num\">Pass Rate</th>\n <th class=\"num\">Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (row of VersionRows; track row.version) {\n <tr>\n <td>\n <span class=\"version-label\">{{ row.gitCommitShort }}</span>\n <span class=\"version-agent\">{{ row.agentVersion }}</span>\n </td>\n <td>{{ row.date | date:'mediumDate' }}</td>\n <td class=\"num\">{{ row.totalTests }}</td>\n <td class=\"num\">\n <span>{{ row.passRate | number:'1.1-1' }}%</span>\n @if (row.passRateDelta !== null) {\n <span class=\"delta\" [class.delta-up]=\"row.passRateDelta > 0\" [class.delta-down]=\"row.passRateDelta < 0\" [class.delta-neutral]=\"row.passRateDelta === 0\">\n @if (row.passRateDelta > 0) {\n <i class=\"fa-solid fa-arrow-up\"></i>\n } @else if (row.passRateDelta < 0) {\n <i class=\"fa-solid fa-arrow-down\"></i>\n }\n {{ AbsNum(row.passRateDelta) | number:'1.1-1' }}%\n </span>\n }\n </td>\n <td class=\"num\">\n <span>\\${{ row.totalCost | number:'1.2-2' }}</span>\n @if (row.costDelta !== null) {\n <span class=\"delta\" [class.delta-up]=\"row.costDelta < 0\" [class.delta-down]=\"row.costDelta > 0\" [class.delta-neutral]=\"row.costDelta === 0\">\n @if (row.costDelta > 0) {\n <i class=\"fa-solid fa-arrow-up\"></i>\n } @else if (row.costDelta < 0) {\n <i class=\"fa-solid fa-arrow-down\"></i>\n }\n {{ AbsNum(row.costDelta) | number:'1.1-1' }}%\n </span>\n }\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n </div>\n </div>\n </ng-template>\n `,\n styles: [`\n /* ------------------------------------------------------------------ */\n /* Layout & page */\n /* ------------------------------------------------------------------ */\n .testing-analytics {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n }\n\n /* ------------------------------------------------------------------ */\n /* Page header */\n /* ------------------------------------------------------------------ */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 12px;\n background: var(--mj-bg-surface);\n padding: 20px 24px;\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n margin-bottom: 24px;\n }\n .header-left h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .header-left h2 i { color: var(--mj-brand-primary); }\n .header-actions {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n }\n\n /* time-range pills */\n .time-range-pills {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n }\n .pill {\n padding: 6px 14px;\n border: none;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n background: transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .pill:hover { background: var(--mj-border-default); }\n .pill.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .refresh-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .refresh-btn:hover { background: var(--mj-bg-surface-card); border-color: var(--mj-border-strong); }\n\n /* ------------------------------------------------------------------ */\n /* Section titles */\n /* ------------------------------------------------------------------ */\n .section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 14px;\n }\n .section-title i { color: var(--mj-brand-primary); }\n .section-subtitle {\n font-size: 12px;\n font-weight: 400;\n color: var(--mj-text-disabled);\n margin-left: 4px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Cards */\n /* ------------------------------------------------------------------ */\n .card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n padding: 20px;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n .card:hover {\n transform: translateY(-2px);\n box-shadow: var(--mj-shadow-md);\n }\n .card h4 {\n margin: 0 0 14px 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .card h4 i { color: var(--mj-brand-primary); font-size: 14px; }\n\n /* ------------------------------------------------------------------ */\n /* Trend grid (2 columns) */\n /* ------------------------------------------------------------------ */\n .trend-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 28px;\n }\n\n /* CSS bar charts */\n .bar-chart {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .bar-row {\n display: grid;\n grid-template-columns: 60px 1fr 52px;\n align-items: center;\n gap: 8px;\n }\n .bar-label {\n font-size: 10px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n text-align: right;\n white-space: nowrap;\n }\n .bar-track {\n height: 14px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 7px;\n overflow: hidden;\n position: relative;\n }\n .bar-track.stacked {\n display: flex;\n }\n .bar-fill {\n height: 100%;\n border-radius: 7px;\n transition: width 0.4s ease;\n }\n .bar-value {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-align: right;\n }\n\n /* colour classes for bars */\n .pass-rate-bar {\n background: var(--mj-status-success);\n }\n .cost-bar {\n background: var(--mj-status-warning);\n }\n .score-excellent { background: var(--mj-status-success); }\n .score-good { background: var(--mj-brand-primary); }\n .score-fair { background: var(--mj-status-warning); }\n .score-poor { background: var(--mj-status-error); }\n\n /* text colour classes */\n .text-excellent { color: var(--mj-status-success); }\n .text-good { color: var(--mj-brand-primary); }\n .text-fair { color: var(--mj-status-warning); }\n .text-poor { color: var(--mj-status-error); }\n .text-pass-good { color: var(--mj-status-success); }\n .text-pass-warn { color: var(--mj-status-warning); }\n .text-pass-bad { color: var(--mj-status-error); }\n\n /* stacked segments */\n .bar-segment { height: 100%; transition: width 0.4s ease; }\n .passed-seg { background: var(--mj-status-success); }\n .failed-seg { background: var(--mj-status-error); }\n .skipped-seg { background: var(--mj-text-disabled); }\n\n .legend-row {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n font-size: 10px;\n color: var(--mj-text-disabled);\n }\n .legend-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n display: inline-block;\n margin-left: 8px;\n }\n .legend-dot:first-child { margin-left: 0; }\n .legend-dot.passed-seg { background: var(--mj-status-success); }\n .legend-dot.failed-seg { background: var(--mj-status-error); }\n .legend-dot.skipped-seg { background: var(--mj-text-disabled); }\n\n .trend-direction {\n margin-top: 10px;\n font-size: 12px;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .trend-up { color: var(--mj-status-success); }\n .trend-down { color: var(--mj-status-error); }\n .trend-neutral { color: var(--mj-text-disabled); }\n\n /* ------------------------------------------------------------------ */\n /* Insights grid (3 columns) */\n /* ------------------------------------------------------------------ */\n .insights-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n margin-bottom: 28px;\n }\n\n .insight-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .insight-row {\n display: grid;\n grid-template-columns: 1fr auto auto;\n align-items: center;\n gap: 10px;\n padding: 8px 10px;\n border-radius: 6px;\n transition: background 0.15s ease;\n }\n .insight-row:hover { background: var(--mj-bg-surface-card); }\n .insight-name {\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .badge-fail {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n font-size: 11px;\n font-weight: 700;\n padding: 2px 8px;\n border-radius: 10px;\n }\n .insight-primary {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-brand-primary);\n }\n .insight-secondary {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n }\n\n /* ------------------------------------------------------------------ */\n /* Score Distribution */\n /* ------------------------------------------------------------------ */\n .distribution-card {\n margin-bottom: 28px;\n }\n .distribution-bars {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .dist-row {\n display: grid;\n grid-template-columns: 110px 1fr 40px 50px;\n align-items: center;\n gap: 10px;\n }\n .dist-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n }\n .dist-track {\n height: 22px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n overflow: hidden;\n }\n .dist-fill {\n height: 100%;\n border-radius: 6px;\n transition: width 0.4s ease;\n }\n .dist-count {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-primary);\n text-align: right;\n }\n .dist-pct {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-align: right;\n }\n\n /* ------------------------------------------------------------------ */\n /* Version Comparison */\n /* ------------------------------------------------------------------ */\n .version-card {\n margin-bottom: 28px;\n }\n .version-table-wrap {\n overflow-x: auto;\n }\n .version-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n .version-table th {\n text-align: left;\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n padding: 10px 12px;\n border-bottom: 2px solid var(--mj-border-default);\n }\n .version-table th.num,\n .version-table td.num { text-align: right; }\n .version-table td {\n padding: 10px 12px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n vertical-align: middle;\n }\n .version-table tbody tr:hover { background: var(--mj-bg-surface-card); }\n .version-label {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n font-size: 12px;\n color: var(--mj-brand-primary);\n }\n .version-agent {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 2px;\n }\n\n .delta {\n display: inline-flex;\n align-items: center;\n gap: 3px;\n font-size: 11px;\n font-weight: 600;\n margin-left: 6px;\n padding: 1px 6px;\n border-radius: 4px;\n }\n .delta i { font-size: 9px; }\n .delta-up { background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface)); color: var(--mj-status-success); }\n .delta-down { background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface)); color: var(--mj-status-error); }\n .delta-neutral { background: var(--mj-bg-surface-card); color: var(--mj-text-disabled); }\n\n /* ------------------------------------------------------------------ */\n /* Loading placeholder (data-empty states now use <mj-empty-state>) */\n /* ------------------------------------------------------------------ */\n .empty-mini {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 32px 16px;\n color: var(--mj-border-strong);\n gap: 8px;\n }\n .empty-mini i { font-size: 28px; opacity: 0.5; }\n .empty-mini span { font-size: 12px; }\n\n /* ------------------------------------------------------------------ */\n /* Responsive */\n /* ------------------------------------------------------------------ */\n @media (max-width: 1100px) {\n .trend-grid { grid-template-columns: 1fr; }\n .insights-grid { grid-template-columns: 1fr 1fr; }\n }\n @media (max-width: 720px) {\n .page-header { flex-direction: column; align-items: flex-start; }\n .insights-grid { grid-template-columns: 1fr; }\n .dist-row { grid-template-columns: 80px 1fr 32px 42px; }\n }\n `]\n})\nexport class TestingAnalyticsComponent implements OnInit, OnDestroy {\n\n // ------- Inputs / Outputs -------\n @Input() InitialState: Record<string, unknown> | null = null;\n\n /** @deprecated Use {@link InitialState}. */\n @Input() set initialState(value: Record<string, unknown> | null) {\n this.InitialState = value;\n }\n /** @deprecated Use {@link InitialState}. */\n get initialState(): Record<string, unknown> | null {\n return this.InitialState;\n }\n /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */\n @Input() HideToolbar = false;\n @Output() StateChange = new EventEmitter<Record<string, unknown>>();\n\n /**\n * @deprecated Use {@link StateChange}.\n *\n * The same emitter under the old binding name, so a template still binding\n * (stateChange) keeps working. Must stay AFTER StateChange: class fields\n * initialise in order, and the other way round this captures undefined.\n */\n @Output() stateChange = this.StateChange;\n\n // ------- Public state -------\n TimeRanges: TimeRangeOption[] = [\n { label: '7 Days', days: 7 },\n { label: '30 Days', days: 30 },\n { label: '90 Days', days: 90 }\n ];\n SelectedDays = 30;\n VersionRows: VersionRow[] = [];\n IsLoadingVersions = false;\n RubricTrend: { at: string; score: number; runId: string }[] = [];\n FailureRates: { key: string; rate: number; count: number }[] = [];\n RubricAnalyticsError = '';\n VersionMetricsError = '';\n\n // Cached breakdown name lists for the dashboard's agent context.\n private topFailingNames: string[] = [];\n private slowestNames: string[] = [];\n private mostExpensiveNames: string[] = [];\n\n // ------- Observables -------\n DisplayTrends$!: Observable<TestTrendData[]>;\n PassRateTrendDirection$!: Observable<'up' | 'down' | 'stable'>;\n TopFailingTests$!: Observable<TestAnalytics['topFailingTests']>;\n SlowestTests$!: Observable<TestAnalytics['slowestTests']>;\n MostExpensiveTests$!: Observable<TestAnalytics['mostExpensiveTests']>;\n ScoreDistribution$!: Observable<ScoreDistributionBucket[]>;\n\n // ------- Private -------\n private destroy$ = new Subject<void>();\n private maxTrendRuns = 0; // cached for cost bar scaling\n\n constructor(\n private instrumentationService: TestingInstrumentationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n // ===================================================================\n // Lifecycle\n // ===================================================================\n\n ngOnInit(): void {\n this.restoreState();\n this.setupObservables();\n this.loadVersionMetrics();\n void this.LoadRubricAnalytics();\n }\n\n /** Submitted evaluation scores over time, and per-criterion failure rates. Suite-run scores are not rubric scores. */\n async LoadRubricAnalytics(): Promise<void> {\n try {\n const provider = this.instrumentationService.Provider;\n if (!provider) return;\n const view = RunView.FromMetadataProvider(provider);\n const evaluations = await view.RunView({\n EntityName: 'MJ: Rubric Evaluations',\n ExtraFilter: `Status = 'Submitted' AND NormalizedScore IS NOT NULL`,\n OrderBy: 'SubmittedAt DESC',\n ResultType: 'simple',\n MaxRows: 500,\n });\n if (!evaluations.Success) throw new Error(evaluations.ErrorMessage || 'Could not load evaluations.');\n this.RubricTrend = RubricScoreTrend(((evaluations.Results ?? []) as Record<string, unknown>[]).map(row => ({\n at: row.SubmittedAt == null ? null : String(row.SubmittedAt),\n score: row.NormalizedScore == null ? null : Number(row.NormalizedScore),\n runId: row.SubjectRecordID == null ? null : String(row.SubjectRecordID),\n })));\n const scores = await view.RunView({\n EntityName: 'MJ: Rubric Evaluation Scores',\n ExtraFilter: `EvaluationStatus = 'Submitted' AND NormalizedScore IS NOT NULL`,\n ResultType: 'simple',\n MaxRows: 1000,\n });\n if (!scores.Success) throw new Error(scores.ErrorMessage || 'Could not load rubric scores.');\n const versions = await view.RunView({\n EntityName: 'MJ: Rubric Versions',\n ResultType: 'simple',\n MaxRows: 500,\n });\n if (!versions.Success) throw new Error(versions.ErrorMessage || 'Could not load rubric versions.');\n const evaluationById = new Map(((evaluations.Results ?? []) as Record<string, unknown>[]).map(row => [String(row.ID), row]));\n const thresholdByVersion = new Map(((versions.Results ?? []) as Record<string, unknown>[]).map(row => [String(row.ID), row.PassThreshold == null ? null : Number(row.PassThreshold)]));\n this.FailureRates = CriterionFailureRates(((scores.Results ?? []) as Record<string, unknown>[]).map(row => {\n const evaluation = evaluationById.get(String(row.EvaluationID ?? ''));\n const versionId = evaluation ? String(evaluation.RubricVersionID ?? '') : '';\n return {\n key: CriterionIdentity(row).key,\n normalizedScore: row.NormalizedScore == null ? null : Number(row.NormalizedScore),\n gateFailed: row.GateFailed === true || row.GateFailed === 1,\n passThreshold: thresholdByVersion.get(versionId) ?? null,\n };\n }).filter(row => row.key.length > 0));\n this.RubricAnalyticsError = '';\n this.cdr.markForCheck();\n } catch (error) {\n this.RubricTrend = [];\n this.FailureRates = [];\n this.RubricAnalyticsError = error instanceof Error ? error.message : 'Could not load rubric analytics.';\n this.cdr.markForCheck();\n }\n }\n\n OpenRun(runId: string): void {\n if (!runId) return;\n SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(runId));\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ===================================================================\n // Public methods (template-bound)\n // ===================================================================\n\n OnTimeRangeSelect(days: number): void {\n this.SelectedDays = days;\n const end = new Date();\n const start = new Date(end.getTime() - days * 24 * 60 * 60 * 1000);\n this.instrumentationService.setDateRange(start, end);\n this.loadVersionMetrics();\n this.emitState();\n }\n\n Refresh(): void {\n this.instrumentationService.refresh();\n this.loadVersionMetrics();\n }\n\n // -- Trend helpers (called from template) --\n\n PassRate(t: TestTrendData): number {\n return t.totalRuns === 0 ? 0 : (t.passed / t.totalRuns) * 100;\n }\n\n PassRateClass(t: TestTrendData): string {\n const rate = this.PassRate(t);\n if (rate >= 80) return 'text-pass-good';\n if (rate >= 60) return 'text-pass-warn';\n return 'text-pass-bad';\n }\n\n SegmentPct(t: TestTrendData, segment: 'passed' | 'failed' | 'skipped'): number {\n if (t.totalRuns === 0) return 0;\n const value = segment === 'passed' ? t.passed : segment === 'failed' ? t.failed : t.skipped;\n return (value / t.totalRuns) * 100;\n }\n\n ScoreBarClass(score: number): string {\n if (score >= 0.9) return 'bar-fill score-excellent';\n if (score >= 0.7) return 'bar-fill score-good';\n if (score >= 0.5) return 'bar-fill score-fair';\n return 'bar-fill score-poor';\n }\n\n ScoreTextClass(score: number): string {\n if (score >= 0.9) return 'text-excellent';\n if (score >= 0.7) return 'text-good';\n if (score >= 0.5) return 'text-fair';\n return 'text-poor';\n }\n\n CostBarPct(t: TestTrendData, all: TestTrendData[]): number {\n const maxCost = Math.max(...all.map(x => x.totalCost), 0.01);\n return (t.totalCost / maxCost) * 100;\n }\n\n FormatBucketDate(d: Date): string {\n const dt = d instanceof Date ? d : new Date(d);\n return `${dt.getMonth() + 1}/${dt.getDate()}`;\n }\n\n FormatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`;\n const s = Math.floor(ms / 1000);\n const m = Math.floor(s / 60);\n if (m > 0) return `${m}m ${s % 60}s`;\n return `${s}s`;\n }\n\n AbsNum(n: number): number {\n return Math.abs(n);\n }\n\n /** trackBy for trend @for loops */\n TrackTrend(index: number, t: TestTrendData): number {\n return index;\n }\n\n /** @deprecated Use {@link TrackTrend}. */\n trackTrend(index: number, t: TestTrendData): number {\n return this.TrackTrend(index, t);\n }\n\n // ===================================================================\n // Private helpers\n // ===================================================================\n\n private restoreState(): void {\n if (this.InitialState != null) {\n if (typeof this.InitialState['selectedDays'] === 'number') {\n this.SelectedDays = this.InitialState['selectedDays'] as number;\n }\n }\n // Apply initial date range\n const end = new Date();\n const start = new Date(end.getTime() - this.SelectedDays * 24 * 60 * 60 * 1000);\n this.instrumentationService.setDateRange(start, end);\n }\n\n private setupObservables(): void {\n // Limit trends to last 10 buckets for readability\n this.DisplayTrends$ = this.instrumentationService.trends$.pipe(\n map(trends => trends.slice(-10)),\n shareReplay(1)\n );\n\n this.PassRateTrendDirection$ = this.DisplayTrends$.pipe(\n map(trends => this.calculateTrendDirection(trends)),\n shareReplay(1)\n );\n\n const analytics$ = this.instrumentationService.analytics$;\n\n this.TopFailingTests$ = analytics$.pipe(\n map(a => a.topFailingTests.slice(0, 5)),\n shareReplay(1)\n );\n\n this.SlowestTests$ = analytics$.pipe(\n map(a => a.slowestTests.slice(0, 5)),\n shareReplay(1)\n );\n\n this.MostExpensiveTests$ = analytics$.pipe(\n map(a => a.mostExpensiveTests.slice(0, 5)),\n shareReplay(1)\n );\n\n // Cache the breakdown name lists so the dashboard's agent context can publish\n // them (top failing / slowest / most expensive tests).\n analytics$.pipe(takeUntil(this.destroy$)).subscribe(a => {\n this.topFailingNames = a.topFailingTests.slice(0, 5).map(t => t.testName);\n this.slowestNames = a.slowestTests.slice(0, 5).map(t => t.testName);\n this.mostExpensiveNames = a.mostExpensiveTests.slice(0, 5).map(t => t.testName);\n this.emitState();\n });\n\n this.ScoreDistribution$ = this.instrumentationService.testRuns$.pipe(\n map(runs => this.buildScoreDistribution(runs)),\n shareReplay(1)\n );\n }\n\n private calculateTrendDirection(trends: TestTrendData[]): 'up' | 'down' | 'stable' {\n if (trends.length < 2) return 'stable';\n const recentHalf = trends.slice(Math.floor(trends.length / 2));\n const olderHalf = trends.slice(0, Math.floor(trends.length / 2));\n\n const avgRecent = this.averagePassRate(recentHalf);\n const avgOlder = this.averagePassRate(olderHalf);\n const diff = avgRecent - avgOlder;\n\n if (diff > 2) return 'up';\n if (diff < -2) return 'down';\n return 'stable';\n }\n\n private averagePassRate(trends: TestTrendData[]): number {\n const withRuns = trends.filter(t => t.totalRuns > 0);\n if (withRuns.length === 0) return 0;\n return withRuns.reduce((s, t) => s + (t.passed / t.totalRuns) * 100, 0) / withRuns.length;\n }\n\n private buildScoreDistribution(runs: TestRunSummary[]): ScoreDistributionBucket[] {\n const defs: Array<{ label: string; color: string; min: number; max: number }> = [\n { label: 'Excellent (>=0.9)', color: '#43a047', min: 0.9, max: 1.01 },\n { label: 'Good (0.7-0.89)', color: '#1e88e5', min: 0.7, max: 0.9 },\n { label: 'Fair (0.5-0.69)', color: '#f57c00', min: 0.5, max: 0.7 },\n { label: 'Poor (0.3-0.49)', color: '#e64a19', min: 0.3, max: 0.5 },\n { label: 'Fail (<0.3)', color: '#e53935', min: 0, max: 0.3 }\n ];\n\n const total = runs.length || 1;\n\n return defs.map(d => {\n const count = runs.filter(r => r.score >= d.min && r.score < d.max).length;\n return {\n label: d.label,\n color: d.color,\n count,\n percentage: (count / total) * 100\n };\n });\n }\n\n private async loadVersionMetrics(): Promise<void> {\n this.IsLoadingVersions = true;\n this.cdr.markForCheck();\n\n try {\n const metrics = await this.instrumentationService.getVersionMetrics();\n this.VersionRows = this.buildVersionRows(metrics);\n this.VersionMetricsError = '';\n } catch (error) {\n this.VersionRows = [];\n this.VersionMetricsError = error instanceof Error ? error.message : 'Could not load version metrics.';\n } finally {\n this.IsLoadingVersions = false;\n this.emitState();\n this.cdr.markForCheck();\n }\n }\n\n private buildVersionRows(metrics: VersionMetrics[]): VersionRow[] {\n // metrics are already sorted newest-first by the service\n return metrics.map((m, i) => {\n const prev = i < metrics.length - 1 ? metrics[i + 1] : null;\n\n let passRateDelta: number | null = null;\n let costDelta: number | null = null;\n\n if (prev) {\n passRateDelta = m.passRate - prev.passRate;\n costDelta = prev.totalCost !== 0\n ? ((m.totalCost - prev.totalCost) / prev.totalCost) * 100\n : null;\n }\n\n return {\n version: m.version,\n gitCommitShort: m.gitCommit.length > 7 ? m.gitCommit.substring(0, 7) : m.gitCommit,\n agentVersion: m.agentVersion,\n date: m.runDate,\n totalTests: m.totalTests,\n passRate: m.passRate,\n totalCost: m.totalCost,\n passRateDelta,\n costDelta\n };\n });\n }\n\n private emitState(): void {\n this.StateChange.emit({\n selectedDays: this.SelectedDays,\n // Breakdown name lists + version count for the dashboard's agent context.\n topFailingTests: this.topFailingNames,\n slowestTests: this.slowestNames,\n mostExpensiveTests: this.mostExpensiveNames,\n versionCount: this.VersionRows.length\n });\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { OnInit, OnDestroy, EventEmitter, ChangeDetectorRef } from '@angular/core';
2
2
  import { TestingInstrumentationService, FeedbackPending, TestRunWithFeedbackSummary, EvaluationSummaryMetrics } from '../services/testing-instrumentation.service';
3
+ import { type DisagreementItem } from '@memberjunction/ng-testing';
3
4
  import * as i0 from "@angular/core";
4
5
  type ViewMode = 'queue' | 'history';
5
6
  type HistorySort = 'date' | 'rating' | 'test-name';
@@ -41,9 +42,14 @@ export declare class TestingReviewComponent implements OnInit, OnDestroy {
41
42
  FilteredHistoryItems: TestRunWithFeedbackSummary[];
42
43
  Metrics: EvaluationSummaryMetrics | null;
43
44
  PendingCount: number;
45
+ Disagreement: DisagreementItem[];
46
+ DisagreementError: string;
44
47
  readonly RatingNumbers: number[];
45
48
  constructor(instrumentationService: TestingInstrumentationService, cdr: ChangeDetectorRef);
46
49
  ngOnInit(): void;
50
+ /** Stored cohort means, largest gap first. The view computes the means; this screen does not average a sample. */
51
+ LoadDisagreement(): Promise<void>;
52
+ OpenRun(runId: string): void;
47
53
  ngOnDestroy(): void;
48
54
  get IsFormValid(): boolean;
49
55
  get AgreementRate(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"testing-review.component.d.ts","sourceRoot":"","sources":["../../../src/Testing/components/testing-review.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testing-review.component.d.ts","sourceRoot":"","sources":["../../../src/Testing/components/testing-review.component.ts"],"names":[],"mappings":"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"}