@memberjunction/ng-dashboards 5.10.1 → 5.12.0

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 (231) hide show
  1. package/dist/AI/components/agents/agent-configuration.component.d.ts +34 -2
  2. package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
  3. package/dist/AI/components/agents/agent-configuration.component.js +586 -223
  4. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  5. package/dist/AI/components/agents/agent-editor.component.js +2 -2
  6. package/dist/AI/components/agents/agent-filter-panel.component.d.ts +8 -0
  7. package/dist/AI/components/agents/agent-filter-panel.component.d.ts.map +1 -1
  8. package/dist/AI/components/agents/agent-filter-panel.component.js +85 -52
  9. package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -1
  10. package/dist/AI/components/charts/performance-heatmap.component.d.ts +1 -0
  11. package/dist/AI/components/charts/performance-heatmap.component.d.ts.map +1 -1
  12. package/dist/AI/components/charts/performance-heatmap.component.js +27 -5
  13. package/dist/AI/components/charts/performance-heatmap.component.js.map +1 -1
  14. package/dist/AI/components/charts/time-series-chart.component.d.ts +5 -0
  15. package/dist/AI/components/charts/time-series-chart.component.d.ts.map +1 -1
  16. package/dist/AI/components/charts/time-series-chart.component.js +23 -8
  17. package/dist/AI/components/charts/time-series-chart.component.js.map +1 -1
  18. package/dist/AI/components/execution-monitoring.component.js +2 -2
  19. package/dist/AI/components/execution-monitoring.component.js.map +1 -1
  20. package/dist/AI/components/models/model-management.component.js +2 -2
  21. package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +2 -2
  22. package/dist/AI/components/prompts/prompt-filter-panel.component.js +2 -2
  23. package/dist/AI/components/prompts/prompt-management.component.js +3 -3
  24. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  25. package/dist/AI/components/prompts/prompt-version-control.component.js +2 -2
  26. package/dist/AI/components/requests/agent-requests-resource.component.d.ts +83 -0
  27. package/dist/AI/components/requests/agent-requests-resource.component.d.ts.map +1 -0
  28. package/dist/AI/components/requests/agent-requests-resource.component.js +547 -0
  29. package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -0
  30. package/dist/AI/components/system/system-config-filter-panel.component.js +2 -2
  31. package/dist/AI/components/system/system-configuration.component.js +2 -2
  32. package/dist/AI/components/widgets/kpi-card.component.js +7 -7
  33. package/dist/AI/components/widgets/kpi-card.component.js.map +1 -1
  34. package/dist/AI/components/widgets/live-execution-widget.component.d.ts.map +1 -1
  35. package/dist/AI/components/widgets/live-execution-widget.component.js +6 -6
  36. package/dist/AI/components/widgets/live-execution-widget.component.js.map +1 -1
  37. package/dist/AI/index.d.ts +1 -0
  38. package/dist/AI/index.d.ts.map +1 -1
  39. package/dist/AI/index.js +2 -0
  40. package/dist/AI/index.js.map +1 -1
  41. package/dist/APIKeys/api-applications-panel.component.js +3 -3
  42. package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
  43. package/dist/APIKeys/api-key-create-dialog.component.js +3 -3
  44. package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
  45. package/dist/APIKeys/api-key-edit-panel.component.js +1 -1
  46. package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
  47. package/dist/APIKeys/api-key-list.component.js +3 -3
  48. package/dist/APIKeys/api-key-list.component.js.map +1 -1
  49. package/dist/APIKeys/api-keys-resource.component.js +1 -1
  50. package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
  51. package/dist/APIKeys/api-scopes-panel.component.js +2 -2
  52. package/dist/APIKeys/api-usage-panel.component.js +2 -2
  53. package/dist/Actions/components/actions-overview.component.js +2 -2
  54. package/dist/Actions/components/execution-monitoring.component.js +2 -2
  55. package/dist/Actions/components/explorer/action-breadcrumb.component.js +2 -2
  56. package/dist/Actions/components/explorer/action-card.component.js +2 -2
  57. package/dist/Actions/components/explorer/action-explorer.component.js +2 -2
  58. package/dist/Actions/components/explorer/action-list-item.component.js +2 -2
  59. package/dist/Actions/components/explorer/action-toolbar.component.js +2 -2
  60. package/dist/Actions/components/explorer/action-tree-panel.component.js +2 -2
  61. package/dist/Actions/components/explorer/new-action-panel.component.js +2 -2
  62. package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -1
  63. package/dist/Actions/components/explorer/new-category-panel.component.js +2 -2
  64. package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -1
  65. package/dist/Communication/communication-dashboard.component.js +2 -2
  66. package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
  67. package/dist/Communication/communication-logs-resource.component.js +3 -3
  68. package/dist/Communication/communication-logs-resource.component.js.map +1 -1
  69. package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
  70. package/dist/Communication/communication-monitor-resource.component.js +5 -5
  71. package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
  72. package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
  73. package/dist/Communication/communication-providers-resource.component.js +3 -3
  74. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  75. package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
  76. package/dist/Communication/communication-runs-resource.component.js +3 -3
  77. package/dist/Communication/communication-runs-resource.component.js.map +1 -1
  78. package/dist/Communication/communication-templates-resource.component.js +2 -2
  79. package/dist/Communication/communication-templates-resource.component.js.map +1 -1
  80. package/dist/ComponentStudio/component-studio-dashboard.component.js +2 -2
  81. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +2 -2
  82. package/dist/ComponentStudio/components/artifact-load-dialog.component.js +2 -2
  83. package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +2 -2
  84. package/dist/ComponentStudio/components/browser/component-browser.component.js +2 -2
  85. package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +2 -2
  86. package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -1
  87. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +2 -2
  88. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -1
  89. package/dist/ComponentStudio/components/editors/requirements-editor.component.js +2 -2
  90. package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -1
  91. package/dist/ComponentStudio/components/editors/spec-editor.component.js +2 -2
  92. package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -1
  93. package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +2 -2
  94. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +2 -2
  95. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -1
  96. package/dist/ComponentStudio/components/text-import-dialog.component.js +2 -2
  97. package/dist/ComponentStudio/components/text-import-dialog.component.js.map +1 -1
  98. package/dist/ComponentStudio/components/workspace/component-preview.component.js +2 -2
  99. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +2 -2
  100. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -1
  101. package/dist/Credentials/components/credentials-audit-resource.component.js +9 -9
  102. package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
  103. package/dist/Credentials/components/credentials-categories-resource.component.d.ts.map +1 -1
  104. package/dist/Credentials/components/credentials-categories-resource.component.js +11 -3
  105. package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
  106. package/dist/Credentials/components/credentials-list-resource.component.js +2 -2
  107. package/dist/Credentials/components/credentials-overview-resource.component.d.ts.map +1 -1
  108. package/dist/Credentials/components/credentials-overview-resource.component.js +12 -11
  109. package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
  110. package/dist/Credentials/components/credentials-types-resource.component.js +9 -9
  111. package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
  112. package/dist/Credentials/credentials-dashboard.component.js +2 -2
  113. package/dist/DashboardBrowser/dashboard-browser-resource.component.js +2 -2
  114. package/dist/DashboardBrowser/dashboard-share-dialog.component.js +2 -2
  115. package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js +2 -2
  116. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +2 -2
  117. package/dist/DataExplorer/components/view-selector/view-selector.component.js +2 -2
  118. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +6 -2
  119. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  120. package/dist/DataExplorer/data-explorer-dashboard.component.js +26 -8
  121. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  122. package/dist/Home/home-dashboard.component.js +2 -2
  123. package/dist/Integration/components/activity/activity.component.d.ts +1 -1
  124. package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
  125. package/dist/Integration/components/activity/activity.component.js +5 -5
  126. package/dist/Integration/components/activity/activity.component.js.map +1 -1
  127. package/dist/Integration/components/connections/connections.component.d.ts +31 -2
  128. package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
  129. package/dist/Integration/components/connections/connections.component.js +753 -412
  130. package/dist/Integration/components/connections/connections.component.js.map +1 -1
  131. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +3 -3
  132. package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
  133. package/dist/Integration/components/overview/overview.component.d.ts +0 -1
  134. package/dist/Integration/components/overview/overview.component.d.ts.map +1 -1
  135. package/dist/Integration/components/overview/overview.component.js +3 -6
  136. package/dist/Integration/components/overview/overview.component.js.map +1 -1
  137. package/dist/Integration/components/pipelines/pipelines.component.js +3 -3
  138. package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -1
  139. package/dist/Integration/components/schedules/schedules.component.d.ts +20 -0
  140. package/dist/Integration/components/schedules/schedules.component.d.ts.map +1 -1
  141. package/dist/Integration/components/schedules/schedules.component.js +97 -5
  142. package/dist/Integration/components/schedules/schedules.component.js.map +1 -1
  143. package/dist/Integration/components/visual-editor/visual-editor.component.js +2 -2
  144. package/dist/Integration/components/widgets/integration-card.component.d.ts.map +1 -1
  145. package/dist/Integration/components/widgets/integration-card.component.js +5 -1
  146. package/dist/Integration/components/widgets/integration-card.component.js.map +1 -1
  147. package/dist/Integration/components/widgets/run-history-panel.component.js +2 -2
  148. package/dist/Integration/components/widgets/run-history-panel.component.js.map +1 -1
  149. package/dist/Integration/integration.module.d.ts +2 -1
  150. package/dist/Integration/integration.module.d.ts.map +1 -1
  151. package/dist/Integration/integration.module.js +7 -3
  152. package/dist/Integration/integration.module.js.map +1 -1
  153. package/dist/Integration/services/integration-data.service.d.ts +27 -2
  154. package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
  155. package/dist/Integration/services/integration-data.service.js +107 -4
  156. package/dist/Integration/services/integration-data.service.js.map +1 -1
  157. package/dist/Lists/components/lists-browse-resource.component.js +2 -2
  158. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  159. package/dist/Lists/components/lists-categories-resource.component.js +2 -2
  160. package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
  161. package/dist/Lists/components/lists-my-lists-resource.component.js +2 -2
  162. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  163. package/dist/Lists/components/lists-operations-resource.component.js +2 -2
  164. package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
  165. package/dist/Lists/components/venn-diagram/venn-diagram.component.js +3 -3
  166. package/dist/Lists/components/venn-diagram/venn-diagram.component.js.map +1 -1
  167. package/dist/MCP/components/mcp-connection-dialog.component.js +2 -2
  168. package/dist/MCP/components/mcp-log-detail-panel.component.js +2 -2
  169. package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -1
  170. package/dist/MCP/components/mcp-server-dialog.component.js +2 -2
  171. package/dist/MCP/components/mcp-test-tool-dialog.component.js +2 -2
  172. package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -1
  173. package/dist/MCP/mcp-dashboard.component.js +2 -2
  174. package/dist/MCP/mcp-filter-panel.component.js +2 -2
  175. package/dist/QueryBrowser/query-browser-resource.component.d.ts +55 -1
  176. package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
  177. package/dist/QueryBrowser/query-browser-resource.component.js +664 -199
  178. package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
  179. package/dist/Scheduling/components/index.d.ts +0 -1
  180. package/dist/Scheduling/components/index.d.ts.map +1 -1
  181. package/dist/Scheduling/components/index.js +0 -1
  182. package/dist/Scheduling/components/index.js.map +1 -1
  183. package/dist/Scheduling/components/scheduling-activity.component.js +2 -2
  184. package/dist/Scheduling/components/scheduling-jobs.component.d.ts +6 -9
  185. package/dist/Scheduling/components/scheduling-jobs.component.d.ts.map +1 -1
  186. package/dist/Scheduling/components/scheduling-jobs.component.js +118 -110
  187. package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
  188. package/dist/Scheduling/components/scheduling-overview.component.js +3 -3
  189. package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
  190. package/dist/Scheduling/scheduling-dashboard.component.js +2 -2
  191. package/dist/SystemDiagnostics/system-diagnostics.component.js +4 -4
  192. package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
  193. package/dist/Testing/components/testing-analytics.component.js +2 -2
  194. package/dist/Testing/components/testing-analytics.component.js.map +1 -1
  195. package/dist/Testing/components/testing-dashboard-tab.component.js +4 -4
  196. package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -1
  197. package/dist/Testing/components/testing-explorer.component.js +2 -2
  198. package/dist/Testing/components/testing-explorer.component.js.map +1 -1
  199. package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
  200. package/dist/Testing/components/testing-review.component.js +5 -5
  201. package/dist/Testing/components/testing-review.component.js.map +1 -1
  202. package/dist/Testing/components/testing-runs.component.js +2 -2
  203. package/dist/Testing/components/testing-runs.component.js.map +1 -1
  204. package/dist/Testing/components/widgets/oracle-breakdown-table.component.js +2 -2
  205. package/dist/Testing/components/widgets/oracle-breakdown-table.component.js.map +1 -1
  206. package/dist/Testing/components/widgets/suite-tree.component.js +4 -4
  207. package/dist/Testing/components/widgets/suite-tree.component.js.map +1 -1
  208. package/dist/Testing/components/widgets/test-run-detail-panel.component.js +2 -2
  209. package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
  210. package/dist/Testing/testing-dashboard.component.js +2 -2
  211. package/dist/VersionHistory/components/diff-resource.component.js +2 -2
  212. package/dist/VersionHistory/components/graph-resource.component.js +2 -2
  213. package/dist/VersionHistory/components/labels-resource.component.js +3 -3
  214. package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
  215. package/dist/VersionHistory/components/restore-resource.component.js +3 -3
  216. package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
  217. package/dist/__tests__/integration-data-service.test.js +1 -0
  218. package/dist/__tests__/integration-data-service.test.js.map +1 -1
  219. package/dist/module.d.ts +52 -49
  220. package/dist/module.d.ts.map +1 -1
  221. package/dist/module.js +25 -6
  222. package/dist/module.js.map +1 -1
  223. package/dist/public-api.d.ts +1 -1
  224. package/dist/public-api.d.ts.map +1 -1
  225. package/dist/public-api.js +1 -1
  226. package/dist/public-api.js.map +1 -1
  227. package/package.json +42 -40
  228. package/dist/Scheduling/components/job-slideout.component.d.ts +0 -45
  229. package/dist/Scheduling/components/job-slideout.component.d.ts.map +0 -1
  230. package/dist/Scheduling/components/job-slideout.component.js +0 -459
  231. package/dist/Scheduling/components/job-slideout.component.js.map +0 -1
@@ -883,7 +883,7 @@ export class TestingAnalyticsComponent {
883
883
  i0.ɵɵconditional((tmp_8_0 = i0.ɵɵpipeBind1(68, 23, ctx.ScoreDistribution$)) ? 67 : -1, tmp_8_0);
884
884
  i0.ɵɵadvance(8);
885
885
  i0.ɵɵconditional(ctx.IsLoadingVersions ? 75 : ctx.VersionRows.length === 0 ? 76 : 77);
886
- } }, dependencies: [i2.AsyncPipe, i2.DecimalPipe, i2.DatePipe], styles: ["\n\n \n\n \n\n .testing-analytics[_ngcontent-%COMP%] {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n \n\n \n\n \n\n .page-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 12px;\n background: #fff;\n padding: 20px 24px;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n margin-bottom: 24px;\n }\n .header-left[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .header-left[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: #2196f3; }\n .header-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n }\n\n \n\n .time-range-pills[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: #f0f0f0;\n border-radius: 8px;\n padding: 3px;\n }\n .pill[_ngcontent-%COMP%] {\n padding: 6px 14px;\n border: none;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 600;\n color: #666;\n background: transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .pill[_ngcontent-%COMP%]:hover { background: #e0e0e0; }\n .pill.active[_ngcontent-%COMP%] {\n background: #2196f3;\n color: #fff;\n box-shadow: 0 1px 4px rgba(33,150,243,0.3);\n }\n\n .refresh-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid #ddd;\n border-radius: 8px;\n background: #fff;\n color: #555;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .refresh-btn[_ngcontent-%COMP%]:hover { background: #f5f5f5; border-color: #bbb; }\n\n \n\n \n\n \n\n .section-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 15px;\n font-weight: 600;\n color: #444;\n margin-bottom: 14px;\n }\n .section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: #2196f3; }\n .section-subtitle[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 400;\n color: #888;\n margin-left: 4px;\n }\n\n \n\n \n\n \n\n .card[_ngcontent-%COMP%] {\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n padding: 20px;\n transition: transform 0.2s ease, box-shadow 0.2s ease;\n }\n .card[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n }\n .card[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 14px 0;\n font-size: 13px;\n font-weight: 600;\n color: #555;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .card[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: #2196f3; font-size: 14px; }\n\n \n\n \n\n \n\n .trend-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 28px;\n }\n\n \n\n .bar-chart[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .bar-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 60px 1fr 52px;\n align-items: center;\n gap: 8px;\n }\n .bar-label[_ngcontent-%COMP%] {\n font-size: 10px;\n color: #888;\n font-weight: 500;\n text-align: right;\n white-space: nowrap;\n }\n .bar-track[_ngcontent-%COMP%] {\n height: 14px;\n background: #f0f0f0;\n border-radius: 7px;\n overflow: hidden;\n position: relative;\n }\n .bar-track.stacked[_ngcontent-%COMP%] {\n display: flex;\n }\n .bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 7px;\n transition: width 0.4s ease;\n }\n .bar-value[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: #555;\n text-align: right;\n }\n\n \n\n .pass-rate-bar[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, #66bb6a, #43a047);\n }\n .cost-bar[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, #ffa726, #f57c00);\n }\n .score-excellent[_ngcontent-%COMP%] { background: linear-gradient(90deg, #66bb6a, #43a047); }\n .score-good[_ngcontent-%COMP%] { background: linear-gradient(90deg, #42a5f5, #1e88e5); }\n .score-fair[_ngcontent-%COMP%] { background: linear-gradient(90deg, #ffa726, #f57c00); }\n .score-poor[_ngcontent-%COMP%] { background: linear-gradient(90deg, #ef5350, #e53935); }\n\n \n\n .text-excellent[_ngcontent-%COMP%] { color: #43a047; }\n .text-good[_ngcontent-%COMP%] { color: #1e88e5; }\n .text-fair[_ngcontent-%COMP%] { color: #f57c00; }\n .text-poor[_ngcontent-%COMP%] { color: #e53935; }\n .text-pass-good[_ngcontent-%COMP%] { color: #43a047; }\n .text-pass-warn[_ngcontent-%COMP%] { color: #f57c00; }\n .text-pass-bad[_ngcontent-%COMP%] { color: #e53935; }\n\n \n\n .bar-segment[_ngcontent-%COMP%] { height: 100%; transition: width 0.4s ease; }\n .passed-seg[_ngcontent-%COMP%] { background: #66bb6a; }\n .failed-seg[_ngcontent-%COMP%] { background: #ef5350; }\n .skipped-seg[_ngcontent-%COMP%] { background: #bdbdbd; }\n\n .legend-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n font-size: 10px;\n color: #888;\n }\n .legend-dot[_ngcontent-%COMP%] {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n display: inline-block;\n margin-left: 8px;\n }\n .legend-dot[_ngcontent-%COMP%]:first-child { margin-left: 0; }\n .legend-dot.passed-seg[_ngcontent-%COMP%] { background: #66bb6a; }\n .legend-dot.failed-seg[_ngcontent-%COMP%] { background: #ef5350; }\n .legend-dot.skipped-seg[_ngcontent-%COMP%] { background: #bdbdbd; }\n\n .trend-direction[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] { color: #43a047; }\n .trend-down[_ngcontent-%COMP%] { color: #e53935; }\n .trend-neutral[_ngcontent-%COMP%] { color: #9e9e9e; }\n\n \n\n \n\n \n\n .insights-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n margin-bottom: 28px;\n }\n\n .insight-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .insight-row[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover { background: #f8f9fa; }\n .insight-name[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .badge-fail[_ngcontent-%COMP%] {\n background: #ffebee;\n color: #e53935;\n font-size: 11px;\n font-weight: 700;\n padding: 2px 8px;\n border-radius: 10px;\n }\n .insight-primary[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: #1e88e5;\n }\n .insight-secondary[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n color: #999;\n }\n\n \n\n \n\n \n\n .distribution-card[_ngcontent-%COMP%] {\n margin-bottom: 28px;\n }\n .distribution-bars[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .dist-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 110px 1fr 40px 50px;\n align-items: center;\n gap: 10px;\n }\n .dist-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: #555;\n }\n .dist-track[_ngcontent-%COMP%] {\n height: 22px;\n background: #f0f0f0;\n border-radius: 6px;\n overflow: hidden;\n }\n .dist-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 6px;\n transition: width 0.4s ease;\n }\n .dist-count[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: #333;\n text-align: right;\n }\n .dist-pct[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: #888;\n text-align: right;\n }\n\n \n\n \n\n \n\n .version-card[_ngcontent-%COMP%] {\n margin-bottom: 28px;\n }\n .version-table-wrap[_ngcontent-%COMP%] {\n overflow-x: auto;\n }\n .version-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n .version-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n font-size: 11px;\n font-weight: 600;\n color: #888;\n text-transform: uppercase;\n padding: 10px 12px;\n border-bottom: 2px solid #eee;\n }\n .version-table[_ngcontent-%COMP%] th.num[_ngcontent-%COMP%], \n .version-table[_ngcontent-%COMP%] td.num[_ngcontent-%COMP%] { text-align: right; }\n .version-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 10px 12px;\n border-bottom: 1px solid #f0f0f0;\n color: #333;\n vertical-align: middle;\n }\n .version-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover { background: #f8f9fa; }\n .version-label[_ngcontent-%COMP%] {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n font-size: 12px;\n color: #1e88e5;\n }\n .version-agent[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: #999;\n margin-top: 2px;\n }\n\n .delta[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 9px; }\n .delta-up[_ngcontent-%COMP%] { background: #e8f5e9; color: #43a047; }\n .delta-down[_ngcontent-%COMP%] { background: #ffebee; color: #e53935; }\n .delta-neutral[_ngcontent-%COMP%] { background: #f5f5f5; color: #9e9e9e; }\n\n \n\n \n\n \n\n .empty-mini[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 32px 16px;\n color: #bbb;\n gap: 8px;\n }\n .empty-mini[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 28px; opacity: 0.5; }\n .empty-mini[_ngcontent-%COMP%] span[_ngcontent-%COMP%] { font-size: 12px; }\n\n \n\n \n\n \n\n @media (max-width: 1100px) {\n .trend-grid[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n .insights-grid[_ngcontent-%COMP%] { grid-template-columns: 1fr 1fr; }\n }\n @media (max-width: 720px) {\n .page-header[_ngcontent-%COMP%] { flex-direction: column; align-items: flex-start; }\n .insights-grid[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n .dist-row[_ngcontent-%COMP%] { grid-template-columns: 80px 1fr 32px 42px; }\n }"], changeDetection: 0 });
886
+ } }, dependencies: [i2.AsyncPipe, i2.DecimalPipe, i2.DatePipe], styles: ["\n\n \n\n \n\n .testing-analytics[_ngcontent-%COMP%] {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n }\n\n \n\n \n\n \n\n .page-header[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n .header-actions[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n }\n\n \n\n .time-range-pills[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n padding: 3px;\n }\n .pill[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover { background: var(--mj-border-default); }\n .pill.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n box-shadow: var(--mj-shadow-sm);\n }\n\n .refresh-btn[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-card); border-color: var(--mj-border-strong); }\n\n \n\n \n\n \n\n .section-title[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n .section-subtitle[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 400;\n color: var(--mj-text-disabled);\n margin-left: 4px;\n }\n\n \n\n \n\n \n\n .card[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: var(--mj-shadow-md);\n }\n .card[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); font-size: 14px; }\n\n \n\n \n\n \n\n .trend-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 16px;\n margin-bottom: 28px;\n }\n\n \n\n .bar-chart[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n .bar-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 60px 1fr 52px;\n align-items: center;\n gap: 8px;\n }\n .bar-label[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n display: flex;\n }\n .bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 7px;\n transition: width 0.4s ease;\n }\n .bar-value[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-align: right;\n }\n\n \n\n .pass-rate-bar[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n }\n .cost-bar[_ngcontent-%COMP%] {\n background: var(--mj-status-warning);\n }\n .score-excellent[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n .score-good[_ngcontent-%COMP%] { background: var(--mj-brand-primary); }\n .score-fair[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n .score-poor[_ngcontent-%COMP%] { background: var(--mj-status-error); }\n\n \n\n .text-excellent[_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .text-good[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n .text-fair[_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .text-poor[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n .text-pass-good[_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .text-pass-warn[_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .text-pass-bad[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n\n \n\n .bar-segment[_ngcontent-%COMP%] { height: 100%; transition: width 0.4s ease; }\n .passed-seg[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n .failed-seg[_ngcontent-%COMP%] { background: var(--mj-status-error); }\n .skipped-seg[_ngcontent-%COMP%] { background: var(--mj-text-disabled); }\n\n .legend-row[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n display: inline-block;\n margin-left: 8px;\n }\n .legend-dot[_ngcontent-%COMP%]:first-child { margin-left: 0; }\n .legend-dot.passed-seg[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n .legend-dot.failed-seg[_ngcontent-%COMP%] { background: var(--mj-status-error); }\n .legend-dot.skipped-seg[_ngcontent-%COMP%] { background: var(--mj-text-disabled); }\n\n .trend-direction[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .trend-down[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n .trend-neutral[_ngcontent-%COMP%] { color: var(--mj-text-disabled); }\n\n \n\n \n\n \n\n .insights-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n margin-bottom: 28px;\n }\n\n .insight-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n .insight-row[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-card); }\n .insight-name[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-brand-primary);\n }\n .insight-secondary[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 500;\n color: var(--mj-text-disabled);\n }\n\n \n\n \n\n \n\n .distribution-card[_ngcontent-%COMP%] {\n margin-bottom: 28px;\n }\n .distribution-bars[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n .dist-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 110px 1fr 40px 50px;\n align-items: center;\n gap: 10px;\n }\n .dist-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n }\n .dist-track[_ngcontent-%COMP%] {\n height: 22px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n overflow: hidden;\n }\n .dist-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 6px;\n transition: width 0.4s ease;\n }\n .dist-count[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-primary);\n text-align: right;\n }\n .dist-pct[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-align: right;\n }\n\n \n\n \n\n \n\n .version-card[_ngcontent-%COMP%] {\n margin-bottom: 28px;\n }\n .version-table-wrap[_ngcontent-%COMP%] {\n overflow-x: auto;\n }\n .version-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n .version-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] th.num[_ngcontent-%COMP%], \n .version-table[_ngcontent-%COMP%] td.num[_ngcontent-%COMP%] { text-align: right; }\n .version-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-card); }\n .version-label[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 2px;\n }\n\n .delta[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 9px; }\n .delta-up[_ngcontent-%COMP%] { background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface)); color: var(--mj-status-success); }\n .delta-down[_ngcontent-%COMP%] { background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface)); color: var(--mj-status-error); }\n .delta-neutral[_ngcontent-%COMP%] { background: var(--mj-bg-surface-card); color: var(--mj-text-disabled); }\n\n \n\n \n\n \n\n .empty-mini[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { font-size: 28px; opacity: 0.5; }\n .empty-mini[_ngcontent-%COMP%] span[_ngcontent-%COMP%] { font-size: 12px; }\n\n \n\n \n\n \n\n @media (max-width: 1100px) {\n .trend-grid[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n .insights-grid[_ngcontent-%COMP%] { grid-template-columns: 1fr 1fr; }\n }\n @media (max-width: 720px) {\n .page-header[_ngcontent-%COMP%] { flex-direction: column; align-items: flex-start; }\n .insights-grid[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n .dist-row[_ngcontent-%COMP%] { grid-template-columns: 80px 1fr 32px 42px; }\n }"], changeDetection: 0 });
887
887
  }
888
888
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingAnalyticsComponent, [{
889
889
  type: Component,
@@ -1190,7 +1190,7 @@ export class TestingAnalyticsComponent {
1190
1190
  }
1191
1191
  </div>
1192
1192
  </div>
1193
- `, styles: ["\n /* ------------------------------------------------------------------ */\n /* Layout & page */\n /* ------------------------------------------------------------------ */\n .testing-analytics {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\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: #fff;\n padding: 20px 24px;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n margin-bottom: 24px;\n }\n .header-left h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .header-left h2 i { color: #2196f3; }\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: #f0f0f0;\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: #666;\n background: transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .pill:hover { background: #e0e0e0; }\n .pill.active {\n background: #2196f3;\n color: #fff;\n box-shadow: 0 1px 4px rgba(33,150,243,0.3);\n }\n\n .refresh-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid #ddd;\n border-radius: 8px;\n background: #fff;\n color: #555;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .refresh-btn:hover { background: #f5f5f5; border-color: #bbb; }\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: #444;\n margin-bottom: 14px;\n }\n .section-title i { color: #2196f3; }\n .section-subtitle {\n font-size: 12px;\n font-weight: 400;\n color: #888;\n margin-left: 4px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Cards */\n /* ------------------------------------------------------------------ */\n .card {\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\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: 0 4px 16px rgba(0,0,0,0.12);\n }\n .card h4 {\n margin: 0 0 14px 0;\n font-size: 13px;\n font-weight: 600;\n color: #555;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .card h4 i { color: #2196f3; 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: #888;\n font-weight: 500;\n text-align: right;\n white-space: nowrap;\n }\n .bar-track {\n height: 14px;\n background: #f0f0f0;\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: #555;\n text-align: right;\n }\n\n /* colour classes for bars */\n .pass-rate-bar {\n background: linear-gradient(90deg, #66bb6a, #43a047);\n }\n .cost-bar {\n background: linear-gradient(90deg, #ffa726, #f57c00);\n }\n .score-excellent { background: linear-gradient(90deg, #66bb6a, #43a047); }\n .score-good { background: linear-gradient(90deg, #42a5f5, #1e88e5); }\n .score-fair { background: linear-gradient(90deg, #ffa726, #f57c00); }\n .score-poor { background: linear-gradient(90deg, #ef5350, #e53935); }\n\n /* text colour classes */\n .text-excellent { color: #43a047; }\n .text-good { color: #1e88e5; }\n .text-fair { color: #f57c00; }\n .text-poor { color: #e53935; }\n .text-pass-good { color: #43a047; }\n .text-pass-warn { color: #f57c00; }\n .text-pass-bad { color: #e53935; }\n\n /* stacked segments */\n .bar-segment { height: 100%; transition: width 0.4s ease; }\n .passed-seg { background: #66bb6a; }\n .failed-seg { background: #ef5350; }\n .skipped-seg { background: #bdbdbd; }\n\n .legend-row {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n font-size: 10px;\n color: #888;\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: #66bb6a; }\n .legend-dot.failed-seg { background: #ef5350; }\n .legend-dot.skipped-seg { background: #bdbdbd; }\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: #43a047; }\n .trend-down { color: #e53935; }\n .trend-neutral { color: #9e9e9e; }\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: #f8f9fa; }\n .insight-name {\n font-size: 12px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .badge-fail {\n background: #ffebee;\n color: #e53935;\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: #1e88e5;\n }\n .insight-secondary {\n font-size: 11px;\n font-weight: 500;\n color: #999;\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: #555;\n }\n .dist-track {\n height: 22px;\n background: #f0f0f0;\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: #333;\n text-align: right;\n }\n .dist-pct {\n font-size: 11px;\n font-weight: 600;\n color: #888;\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: #888;\n text-transform: uppercase;\n padding: 10px 12px;\n border-bottom: 2px solid #eee;\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 #f0f0f0;\n color: #333;\n vertical-align: middle;\n }\n .version-table tbody tr:hover { background: #f8f9fa; }\n .version-label {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n font-size: 12px;\n color: #1e88e5;\n }\n .version-agent {\n display: block;\n font-size: 11px;\n color: #999;\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: #e8f5e9; color: #43a047; }\n .delta-down { background: #ffebee; color: #e53935; }\n .delta-neutral { background: #f5f5f5; color: #9e9e9e; }\n\n /* ------------------------------------------------------------------ */\n /* Empty / mini empty states */\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: #bbb;\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 "] }]
1193
+ `, 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 /* Empty / mini empty states */\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 "] }]
1194
1194
  }], () => [{ type: i1.TestingInstrumentationService }, { type: i0.ChangeDetectorRef }], { initialState: [{
1195
1195
  type: Input
1196
1196
  }], stateChange: [{
@@ -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 <div class=\"testing-analytics\">\n\n <!-- ===== 1. Page Header ===== -->\n <div class=\"page-header\">\n <div class=\"header-left\">\n <h2>\n <i class=\"fa-solid fa-chart-bar\"></i>\n Testing Analytics\n </h2>\n </div>\n <div class=\"header-actions\">\n <div class=\"time-range-pills\">\n @for (range of TimeRanges; track range.days) {\n <button\n class=\"pill\"\n [class.active]=\"SelectedDays === range.days\"\n (click)=\"OnTimeRangeSelect(range.days)\">\n {{ range.label }}\n </button>\n }\n </div>\n <button class=\"refresh-btn\" (click)=\"Refresh()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n Refresh\n </button>\n </div>\n </div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-chart-area\"></i><span>No trend data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-chart-bar\"></i><span>No volume data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-star\"></i><span>No score data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-wallet\"></i><span>No cost data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-circle-check\"></i><span>No failing tests</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-bolt\"></i><span>No duration data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-dollar-sign\"></i><span>No cost data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-chart-simple\"></i><span>No score data available</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-code-branch\"></i><span>No version data available</span></div>\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 `,\n styles: [`\n /* ------------------------------------------------------------------ */\n /* Layout & page */\n /* ------------------------------------------------------------------ */\n .testing-analytics {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\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: #fff;\n padding: 20px 24px;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n margin-bottom: 24px;\n }\n .header-left h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .header-left h2 i { color: #2196f3; }\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: #f0f0f0;\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: #666;\n background: transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .pill:hover { background: #e0e0e0; }\n .pill.active {\n background: #2196f3;\n color: #fff;\n box-shadow: 0 1px 4px rgba(33,150,243,0.3);\n }\n\n .refresh-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid #ddd;\n border-radius: 8px;\n background: #fff;\n color: #555;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n .refresh-btn:hover { background: #f5f5f5; border-color: #bbb; }\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: #444;\n margin-bottom: 14px;\n }\n .section-title i { color: #2196f3; }\n .section-subtitle {\n font-size: 12px;\n font-weight: 400;\n color: #888;\n margin-left: 4px;\n }\n\n /* ------------------------------------------------------------------ */\n /* Cards */\n /* ------------------------------------------------------------------ */\n .card {\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\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: 0 4px 16px rgba(0,0,0,0.12);\n }\n .card h4 {\n margin: 0 0 14px 0;\n font-size: 13px;\n font-weight: 600;\n color: #555;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .card h4 i { color: #2196f3; 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: #888;\n font-weight: 500;\n text-align: right;\n white-space: nowrap;\n }\n .bar-track {\n height: 14px;\n background: #f0f0f0;\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: #555;\n text-align: right;\n }\n\n /* colour classes for bars */\n .pass-rate-bar {\n background: linear-gradient(90deg, #66bb6a, #43a047);\n }\n .cost-bar {\n background: linear-gradient(90deg, #ffa726, #f57c00);\n }\n .score-excellent { background: linear-gradient(90deg, #66bb6a, #43a047); }\n .score-good { background: linear-gradient(90deg, #42a5f5, #1e88e5); }\n .score-fair { background: linear-gradient(90deg, #ffa726, #f57c00); }\n .score-poor { background: linear-gradient(90deg, #ef5350, #e53935); }\n\n /* text colour classes */\n .text-excellent { color: #43a047; }\n .text-good { color: #1e88e5; }\n .text-fair { color: #f57c00; }\n .text-poor { color: #e53935; }\n .text-pass-good { color: #43a047; }\n .text-pass-warn { color: #f57c00; }\n .text-pass-bad { color: #e53935; }\n\n /* stacked segments */\n .bar-segment { height: 100%; transition: width 0.4s ease; }\n .passed-seg { background: #66bb6a; }\n .failed-seg { background: #ef5350; }\n .skipped-seg { background: #bdbdbd; }\n\n .legend-row {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n font-size: 10px;\n color: #888;\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: #66bb6a; }\n .legend-dot.failed-seg { background: #ef5350; }\n .legend-dot.skipped-seg { background: #bdbdbd; }\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: #43a047; }\n .trend-down { color: #e53935; }\n .trend-neutral { color: #9e9e9e; }\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: #f8f9fa; }\n .insight-name {\n font-size: 12px;\n font-weight: 500;\n color: #333;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .badge-fail {\n background: #ffebee;\n color: #e53935;\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: #1e88e5;\n }\n .insight-secondary {\n font-size: 11px;\n font-weight: 500;\n color: #999;\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: #555;\n }\n .dist-track {\n height: 22px;\n background: #f0f0f0;\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: #333;\n text-align: right;\n }\n .dist-pct {\n font-size: 11px;\n font-weight: 600;\n color: #888;\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: #888;\n text-transform: uppercase;\n padding: 10px 12px;\n border-bottom: 2px solid #eee;\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 #f0f0f0;\n color: #333;\n vertical-align: middle;\n }\n .version-table tbody tr:hover { background: #f8f9fa; }\n .version-label {\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-weight: 600;\n font-size: 12px;\n color: #1e88e5;\n }\n .version-agent {\n display: block;\n font-size: 11px;\n color: #999;\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: #e8f5e9; color: #43a047; }\n .delta-down { background: #ffebee; color: #e53935; }\n .delta-neutral { background: #f5f5f5; color: #9e9e9e; }\n\n /* ------------------------------------------------------------------ */\n /* Empty / mini empty states */\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: #bbb;\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 @Output() stateChange = new EventEmitter<Record<string, unknown>>();\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 // ------- 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 // ===================================================================\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 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.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 });\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,EAAa,GAAG,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;;;;;;;;;;IA2D/C,kCAG0C;IAAxC,gNAAS,uCAA6B,KAAC;IACvC,YACF;IAAA,iBAAS;;;;IAHP,+DAA4C;IAE5C,cACF;IADE,+CACF;;;IAsBA,+BAAwB;IAAA,wBAAsC;IAAA,4BAAM;IAAA,6BAAa;IAAO,AAAP,iBAAO,EAAM;;;IAKxF,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,4HAAoB,sGAES,sGAEpB;;;IAJT,iEAMC;;;IAnBL,+BAAuB;IACrB,yJAQC;IACH,iBAAM;IACN,+BAA6B;IAC3B,8GAA+C;;IASjD,iBAAM;;;;;IApBJ,cAQC;IARD,wBAQC;IAGD,eAQC;IARD,oGAQC;;;IArBH,AAFF,2GAA2B,0EAElB;;IAFT,0CAyBC;;;IASC,+BAAwB;IAAA,uBAAqC;IAAA,4BAAM;IAAA,8BAAc;IAAO,AAAP,iBAAO,EAAM;;;IAKxF,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,6DAAmC;IAErB,eAAyC;IAAzC,+DAAyC;IACzC,cAAyC;IAAzC,+DAAyC;IACxC,cAA0C;IAA1C,gEAA0C;IAEzD,eAAiB;IAAjB,oCAAiB;;;IAT/C,+BAAuB;IACrB,yJAUC;IACH,iBAAM;IACN,+BAAwB;IACtB,2BAA2C;IAAC,wBAC5C;IAAA,2BAA2C;IAAC,wBAC5C;IAAA,2BAA4C;IAAC,yBAC/C;IAAA,iBAAM;;;IAhBJ,cAUC;IAVD,wBAUC;;;IAZH,AAFF,2GAA2B,2EAElB;;IAFT,0CAqBC;;;IASC,+BAAwB;IAAA,wBAAgC;IAAA,4BAAM;IAAA,6BAAa;IAAO,AAAP,iBAAO,EAAM;;;IAKlF,AADF,+BAAqB,eACK;IAAA,YAAmC;IAAA,iBAAO;IAClE,+BAAuB;IACrB,0BAA2G;IAC7G,iBAAM;IACN,gCAAiE;IAAA,YAA8C;;IACjH,AADiH,iBAAO,EAClH;;;;IALoB,eAAmC;IAAnC,6DAAmC;IAEnC,eAAuC;IAAvC,sDAAuC;IAAC,qDAAsC;IAE9E,cAAwC;IAAxC,uDAAwC;IAAC,cAA8C;IAA9C,sFAA8C;;;IAPrH,+BAAuB;IACrB,0JAQC;IACH,iBAAM;;;IATJ,cAQC;IARD,yBAQC;;;IAVH,AAFF,2GAA2B,qFAElB;;IAFT,0CAcC;;;IASC,+BAAwB;IAAA,wBAAkC;IAAA,4BAAM;IAAA,4BAAY;IAAO,AAAP,iBAAO,EAAM;;;IAKnF,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,yJAQC;IACH,iBAAM;;;IATJ,cAQC;IARD,yBAQC;;;IAVH,AAFF,2GAA2B,qFAElB;;IAFT,0CAcC;;;IAiBC,+BAAwB;IAAA,wBAAwC;IAAA,4BAAM;IAAA,gCAAgB;IAAO,AAAP,iBAAO,EAAM;;;IAK7F,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,0HAMC;IACH,iBAAM;;;IAPJ,cAMC;IAND,wBAMC;;;IARH,AAFF,2GAA0B,qFAEjB;;IAFT,0CAYC;;;IASC,+BAAwB;IAAA,wBAAgC;IAAA,4BAAM;IAAA,gCAAgB;IAAO,AAAP,iBAAO,EAAM;;;IAKrF,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,0HAMC;IACH,iBAAM;;;IAPJ,cAMC;IAND,wBAMC;;;IARH,AAFF,2GAA0B,qFAEjB;;IAFT,0CAYC;;;IASC,+BAAwB;IAAA,wBAAuC;IAAA,4BAAM;IAAA,4BAAY;IAAO,AAAP,iBAAO,EAAM;;;IAKxF,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,2HAMC;IACH,iBAAM;;;IAPJ,cAMC;IAND,wBAMC;;;IARH,AAFF,2GAA0B,qFAEjB;;IAFT,0CAYC;;;IAaD,+BAAwB;IAAA,wBAAwC;IAAA,4BAAM;IAAA,uCAAuB;IAAO,AAAP,iBAAO,EAAM;;;IAKpG,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,4HASC;IACH,iBAAM;;;IAVJ,cASC;IATD,0BASC;;;IAXH,AAFF,2GAA4B,qFAEnB;;IAFT,0CAeC;;;IAYD,+BAAwB;IAAA,wBAA2C;IAAA,4BAAM;IAAA,mCAAmB;IAAO,AAAP,iBAAO,EAAM;;;IAEzG,+BAAwB;IAAA,wBAAuC;IAAA,4BAAM;IAAA,yCAAyB;IAAO,AAAP,iBAAO,EAAM;;;IA2BzF,wBAAoC;;;IAEpC,wBAAsC;;;IAJ1C,gCAAwJ;IAGpJ,AAFF,+HAA6B,yGAEO;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,+HAAyB,yGAEO;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,sHAAkC;IAUpC,iBAAK;IAEH,AADF,+BAAgB,YACR;IAAA,aAAsC;;IAAA,iBAAO;IACnD,sHAA8B;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,iHAmCC;IAGP,AADE,AADE,iBAAQ,EACF,EACJ;;;IAtCA,gBAmCC;IAnCD,iCAmCC;;AAhTjB,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAiuB9E,MAAM,OAAO,yBAAyB;IA6B1B;IACA;IA5BV,mCAAmC;IAC1B,YAAY,GAAmC,IAAI,CAAC;IACnD,WAAW,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEpE,+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;IAE1B,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;IAC5B,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,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,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;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;QACxB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAC/B,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;SAChC,CAAC,CAAC;IACL,CAAC;mHA3QU,yBAAyB;6DAAzB,yBAAyB;YArtB5B,AADF,AADF,AAHF,8BAA+B,aAGJ,aACE,SACnB;YACF,uBAAqC;YACrC,mCACF;YACF,AADE,iBAAK,EACD;YAEJ,AADF,8BAA4B,aACI;YAC5B,+FAOC;YACH,iBAAM;YACN,kCAAgD;YAApB,uGAAS,aAAS,IAAC;YAC7C,wBAAyC;YACzC,0BACF;YAEJ,AADE,AADE,iBAAS,EACL,EACF;YAGN,+BAA2B;YACzB,yBAAsC;YACtC,iCACF;YAAA,iBAAM;YAKF,AADF,AAHF,gCAAwB,eAGO,UACvB;YAAA,yBAAwC;YAAC,iCAAe;YAAA,iBAAK;YACjE,mFAAyC;;YA4B3C,iBAAM;YAIJ,AADF,gCAA6B,UACvB;YAAA,yBAAuC;YAAC,4BAAU;YAAA,iBAAK;YAC3D,mFAAyC;;YAwB3C,iBAAM;YAIJ,AADF,gCAA6B,UACvB;YAAA,yBAA4C;YAAC,6BAAW;YAAA,iBAAK;YACjE,mFAAyC;;YAiB3C,iBAAM;YAIJ,AADF,gCAA6B,UACvB;YAAA,yBAAuC;YAAC,4BAAU;YAAA,iBAAK;YAC3D,mFAAyC;;YAkB7C,AADE,iBAAM,EACF;YAGN,+BAA2B;YACzB,yBAAkD;YAClD,2BACF;YAAA,iBAAM;YAKF,AADF,AAHF,gCAA2B,eAGM,UACzB;YAAA,yBAAgD;YAAC,mCAAiB;YAAA,iBAAK;YAC3E,mFAA0C;;YAe5C,iBAAM;YAIJ,AADF,gCAA+B,UACzB;YAAA,yBAAiC;YAAC,+BAAa;YAAA,iBAAK;YACxD,mFAAuC;;YAezC,iBAAM;YAIJ,AADF,gCAA+B,UACzB;YAAA,yBAAiC;YAAC,sCAAoB;YAAA,iBAAK;YAC/D,mFAA6C;;YAgBjD,AADE,iBAAM,EACF;YAGN,+BAA2B;YACzB,yBAAqC;YACrC,qCACF;YAAA,iBAAM;YACN,gCAAoC;YAClC,mFAA8C;;YAkBhD,iBAAM;YAGN,+BAA2B;YACzB,yBAAwC;YACxC,qCACA;YAAA,iCAA+B;YAAA,wDAAuC;YACxE,AADwE,iBAAO,EACzE;YACN,gCAA+B;YAK3B,AAFA,AAFF,8FAAyB,wEAEc,yEAE9B;YAsDb,AADE,iBAAM,EACF;;;;;;;;;;YAjSE,eAOC;YAPD,6BAOC;YAmBH,gBA2BC;YA3BD,0FA2BC;YAMD,eAuBC;YAvBD,2FAuBC;YAMD,eAgBC;YAhBD,2FAgBC;YAMD,eAgBC;YAhBD,2FAgBC;YAcD,gBAcC;YAdD,6FAcC;YAMD,eAcC;YAdD,0FAcC;YAMD,eAcC;YAdD,gGAcC;YAUH,eAiBC;YAjBD,+FAiBC;YAUD,eAwDC;YAxDD,qFAwDC;;;iFA+aI,yBAAyB;cA/tBrC,SAAS;6BACI,KAAK,YACP,uBAAuB,mBAChB,uBAAuB,CAAC,MAAM,YACrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+ST;;kBA+aA,KAAK;;kBACL,MAAM;;kFAJI,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';\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 <div class=\"testing-analytics\">\n\n <!-- ===== 1. Page Header ===== -->\n <div class=\"page-header\">\n <div class=\"header-left\">\n <h2>\n <i class=\"fa-solid fa-chart-bar\"></i>\n Testing Analytics\n </h2>\n </div>\n <div class=\"header-actions\">\n <div class=\"time-range-pills\">\n @for (range of TimeRanges; track range.days) {\n <button\n class=\"pill\"\n [class.active]=\"SelectedDays === range.days\"\n (click)=\"OnTimeRangeSelect(range.days)\">\n {{ range.label }}\n </button>\n }\n </div>\n <button class=\"refresh-btn\" (click)=\"Refresh()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n Refresh\n </button>\n </div>\n </div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-chart-area\"></i><span>No trend data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-chart-bar\"></i><span>No volume data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-star\"></i><span>No score data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-wallet\"></i><span>No cost data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-circle-check\"></i><span>No failing tests</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-bolt\"></i><span>No duration data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-dollar-sign\"></i><span>No cost data</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-chart-simple\"></i><span>No score data available</span></div>\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 <div class=\"empty-mini\"><i class=\"fa-solid fa-code-branch\"></i><span>No version data available</span></div>\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 `,\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 /* Empty / mini empty states */\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 @Output() stateChange = new EventEmitter<Record<string, unknown>>();\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 // ------- 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 // ===================================================================\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 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.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 });\n }\n}\n"]}
@@ -475,11 +475,11 @@ export class TestingDashboardTabComponent {
475
475
  }
476
476
  }
477
477
  static ɵfac = function TestingDashboardTabComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TestingDashboardTabComponent)(i0.ɵɵdirectiveInject(i1.TestingInstrumentationService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
478
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingDashboardTabComponent, selectors: [["app-testing-dashboard-tab"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, standalone: false, decls: 2, vars: 1, consts: [[1, "full-page-loading"], [1, "dashboard-container"], ["text", "Loading Testing Dashboard..."], [1, "page-header"], [1, "page-title"], [1, "fa-solid", "fa-gauge-high"], [1, "refresh-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], [1, "kpi-row"], [3, "data"], [1, "live-activity-card"], [1, "section-header"], [1, "running-tests-list"], [1, "lower-grid"], [1, "card", "recent-runs-card"], [1, "fa-solid", "fa-history"], [1, "runs-list"], [1, "run-row"], [1, "empty-state"], [1, "right-column"], [1, "card", "suite-health-card"], [1, "fa-solid", "fa-heartbeat"], [1, "suite-health-list"], [1, "suite-health-row"], [1, "card", "alerts-card"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "alerts-list"], [1, "alert-row"], [1, "live-dot"], [1, "fa-solid", "fa-circle-pause", "live-idle-icon"], [1, "running-test-item"], [1, "running-test-name"], [1, "running-test-meta"], [1, "running-elapsed"], [1, "running-progress-bar"], [1, "running-progress-fill"], [1, "run-row", 3, "click"], [1, "run-row-left"], [1, "run-row-name"], [1, "run-row-time"], [1, "run-row-right"], [3, "status"], [3, "score", "showBar"], [1, "run-row-duration"], [3, "cost"], [1, "suite-health-info"], [1, "suite-health-name"], [1, "suite-health-count"], [1, "suite-health-bar-wrapper"], [1, "suite-health-bar"], [1, "suite-health-bar-fill"], [1, "suite-health-pct"], [1, "alert-row", 3, "click"], [1, "fa-solid"], [1, "alert-info"], [1, "alert-name"], [1, "alert-reason"], [1, "fa-solid", "fa-check-circle", 2, "color", "#22c55e", "margin-right", "6px"]], template: function TestingDashboardTabComponent_Template(rf, ctx) { if (rf & 1) {
478
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingDashboardTabComponent, selectors: [["app-testing-dashboard-tab"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, standalone: false, decls: 2, vars: 1, consts: [[1, "full-page-loading"], [1, "dashboard-container"], ["text", "Loading Testing Dashboard..."], [1, "page-header"], [1, "page-title"], [1, "fa-solid", "fa-gauge-high"], [1, "refresh-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], [1, "kpi-row"], [3, "data"], [1, "live-activity-card"], [1, "section-header"], [1, "running-tests-list"], [1, "lower-grid"], [1, "card", "recent-runs-card"], [1, "fa-solid", "fa-history"], [1, "runs-list"], [1, "run-row"], [1, "empty-state"], [1, "right-column"], [1, "card", "suite-health-card"], [1, "fa-solid", "fa-heartbeat"], [1, "suite-health-list"], [1, "suite-health-row"], [1, "card", "alerts-card"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "alerts-list"], [1, "alert-row"], [1, "live-dot"], [1, "fa-solid", "fa-circle-pause", "live-idle-icon"], [1, "running-test-item"], [1, "running-test-name"], [1, "running-test-meta"], [1, "running-elapsed"], [1, "running-progress-bar"], [1, "running-progress-fill"], [1, "run-row", 3, "click"], [1, "run-row-left"], [1, "run-row-name"], [1, "run-row-time"], [1, "run-row-right"], [3, "status"], [3, "score", "showBar"], [1, "run-row-duration"], [3, "cost"], [1, "suite-health-info"], [1, "suite-health-name"], [1, "suite-health-count"], [1, "suite-health-bar-wrapper"], [1, "suite-health-bar"], [1, "suite-health-bar-fill"], [1, "suite-health-pct"], [1, "alert-row", 3, "click"], [1, "fa-solid"], [1, "alert-info"], [1, "alert-name"], [1, "alert-reason"], [1, "fa-solid", "fa-check-circle", 2, "color", "var(--mj-status-success)", "margin-right", "6px"]], template: function TestingDashboardTabComponent_Template(rf, ctx) { if (rf & 1) {
479
479
  i0.ɵɵconditionalCreate(0, TestingDashboardTabComponent_Conditional_0_Template, 2, 0, "div", 0)(1, TestingDashboardTabComponent_Conditional_1_Template, 45, 8, "div", 1);
480
480
  } if (rf & 2) {
481
481
  i0.ɵɵconditional(ctx.IsLoading ? 0 : 1);
482
- } }, dependencies: [i2.TestStatusBadgeComponent, i2.ScoreIndicatorComponent, i2.CostDisplayComponent, i3.LoadingComponent, i4.KPICardComponent, i5.DecimalPipe], styles: ["\n\n .full-page-loading[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: #f8f9fa;\n }\n\n .dashboard-container[_ngcontent-%COMP%] {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n \n\n .page-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n\n .page-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: #1e293b;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .page-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: #6366f1; }\n\n .refresh-btn[_ngcontent-%COMP%] {\n background: #6366f1;\n color: white;\n border: none;\n padding: 8px 18px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.2s ease;\n }\n\n .refresh-btn[_ngcontent-%COMP%]:hover:not(:disabled) { background: #4f46e5; }\n .refresh-btn[_ngcontent-%COMP%]:disabled { opacity: 0.6; cursor: not-allowed; }\n .refresh-btn[_ngcontent-%COMP%] i.spinning[_ngcontent-%COMP%] { animation: _ngcontent-%COMP%_spin 1s linear infinite; }\n\n @keyframes _ngcontent-%COMP%_spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n \n\n .kpi-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n \n\n .card[_ngcontent-%COMP%] {\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n overflow: hidden;\n }\n\n .section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-bottom: 1px solid #f1f5f9;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #334155;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: #6366f1; font-size: 13px; }\n\n \n\n .live-activity-card[_ngcontent-%COMP%] {\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n margin-bottom: 24px;\n overflow: hidden;\n }\n\n .live-dot[_ngcontent-%COMP%] {\n width: 10px;\n height: 10px;\n background: #22c55e;\n border-radius: 50%;\n display: inline-block;\n animation: _ngcontent-%COMP%_pulse-dot 1.5s ease-in-out infinite;\n }\n\n @keyframes _ngcontent-%COMP%_pulse-dot {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.5); }\n 50% { box-shadow: 0 0 0 6px rgba(34,197,94,0); }\n }\n\n .live-idle-icon[_ngcontent-%COMP%] { color: #9ca3af; font-size: 14px; }\n\n .running-tests-list[_ngcontent-%COMP%] {\n padding: 0 20px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .running-test-item[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n background: #f0fdf4;\n border-radius: 8px;\n border: 1px solid #bbf7d0;\n }\n\n .running-test-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #166534;\n }\n\n .running-test-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .running-elapsed[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #15803d;\n font-weight: 500;\n }\n\n .running-progress-bar[_ngcontent-%COMP%] {\n width: 60px;\n height: 4px;\n background: #dcfce7;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .running-progress-fill[_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, #22c55e, #86efac);\n animation: _ngcontent-%COMP%_progress-slide 1.2s ease-in-out infinite;\n border-radius: 2px;\n }\n\n @keyframes _ngcontent-%COMP%_progress-slide {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(100%); }\n }\n\n \n\n .lower-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n }\n\n .right-column[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n \n\n .runs-list[_ngcontent-%COMP%] {\n max-height: 520px;\n overflow-y: auto;\n }\n\n .run-row[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 20px;\n border-bottom: 1px solid #f8fafc;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .run-row[_ngcontent-%COMP%]:hover { background: #f8fafc; }\n\n .run-row-left[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .run-row-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .run-row-time[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #94a3b8;\n }\n\n .run-row-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .run-row-duration[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #64748b;\n min-width: 44px;\n text-align: right;\n }\n\n \n\n .suite-health-list[_ngcontent-%COMP%] {\n padding: 8px 0;\n max-height: 260px;\n overflow-y: auto;\n }\n\n .suite-health-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 20px;\n }\n\n .suite-health-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .suite-health-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .suite-health-count[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #94a3b8;\n }\n\n .suite-health-bar-wrapper[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n\n .suite-health-bar[_ngcontent-%COMP%] {\n width: 80px;\n height: 6px;\n background: #f1f5f9;\n border-radius: 3px;\n overflow: hidden;\n }\n\n .suite-health-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .suite-health-pct[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n min-width: 36px;\n text-align: right;\n }\n\n \n\n .alerts-list[_ngcontent-%COMP%] {\n max-height: 220px;\n overflow-y: auto;\n }\n\n .alert-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 20px;\n border-bottom: 1px solid #f8fafc;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .alert-row[_ngcontent-%COMP%]:hover { background: #fef2f2; }\n\n .alert-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .alert-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .alert-reason[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #94a3b8;\n }\n\n \n\n .empty-state[_ngcontent-%COMP%] {\n padding: 32px 20px;\n text-align: center;\n color: #94a3b8;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n \n\n @media (max-width: 900px) {\n .lower-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 640px) {\n .dashboard-container[_ngcontent-%COMP%] { padding: 16px; }\n .kpi-row[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n }"], changeDetection: 0 });
482
+ } }, dependencies: [i2.TestStatusBadgeComponent, i2.ScoreIndicatorComponent, i2.CostDisplayComponent, i3.LoadingComponent, i4.KPICardComponent, i5.DecimalPipe], styles: ["\n\n .full-page-loading[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: var(--mj-bg-surface-card);\n }\n\n .dashboard-container[_ngcontent-%COMP%] {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n }\n\n \n\n .page-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n\n .page-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .page-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n\n .refresh-btn[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n padding: 8px 18px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.2s ease;\n }\n\n .refresh-btn[_ngcontent-%COMP%]:hover:not(:disabled) { background: var(--mj-brand-primary-hover); }\n .refresh-btn[_ngcontent-%COMP%]:disabled { opacity: 0.6; cursor: not-allowed; }\n .refresh-btn[_ngcontent-%COMP%] i.spinning[_ngcontent-%COMP%] { animation: _ngcontent-%COMP%_spin 1s linear infinite; }\n\n @keyframes _ngcontent-%COMP%_spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n \n\n .kpi-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n \n\n .card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .section-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); font-size: 13px; }\n\n \n\n .live-activity-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n margin-bottom: 24px;\n overflow: hidden;\n }\n\n .live-dot[_ngcontent-%COMP%] {\n width: 10px;\n height: 10px;\n background: var(--mj-status-success);\n border-radius: 50%;\n display: inline-block;\n animation: _ngcontent-%COMP%_pulse-dot 1.5s ease-in-out infinite;\n }\n\n @keyframes _ngcontent-%COMP%_pulse-dot {\n 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mj-status-success) 50%, transparent); }\n 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--mj-status-success) 0%, transparent); }\n }\n\n .live-idle-icon[_ngcontent-%COMP%] { color: var(--mj-text-disabled); font-size: 14px; }\n\n .running-tests-list[_ngcontent-%COMP%] {\n padding: 0 20px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .running-test-item[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-success) 25%, var(--mj-bg-surface));\n }\n\n .running-test-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-status-success);\n }\n\n .running-test-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .running-elapsed[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-status-success);\n font-weight: 500;\n }\n\n .running-progress-bar[_ngcontent-%COMP%] {\n width: 60px;\n height: 4px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 2px;\n overflow: hidden;\n }\n\n .running-progress-fill[_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--mj-status-success);\n animation: _ngcontent-%COMP%_progress-slide 1.2s ease-in-out infinite;\n border-radius: 2px;\n }\n\n @keyframes _ngcontent-%COMP%_progress-slide {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(100%); }\n }\n\n \n\n .lower-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n }\n\n .right-column[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n \n\n .runs-list[_ngcontent-%COMP%] {\n max-height: 520px;\n overflow-y: auto;\n }\n\n .run-row[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-card);\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .run-row[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-card); }\n\n .run-row-left[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .run-row-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .run-row-time[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n .run-row-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .run-row-duration[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n min-width: 44px;\n text-align: right;\n }\n\n \n\n .suite-health-list[_ngcontent-%COMP%] {\n padding: 8px 0;\n max-height: 260px;\n overflow-y: auto;\n }\n\n .suite-health-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 20px;\n }\n\n .suite-health-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .suite-health-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .suite-health-count[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n .suite-health-bar-wrapper[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n\n .suite-health-bar[_ngcontent-%COMP%] {\n width: 80px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .suite-health-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .suite-health-pct[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n min-width: 36px;\n text-align: right;\n }\n\n \n\n .alerts-list[_ngcontent-%COMP%] {\n max-height: 220px;\n overflow-y: auto;\n }\n\n .alert-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-card);\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .alert-row[_ngcontent-%COMP%]:hover { background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface)); }\n\n .alert-info[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .alert-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .alert-reason[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n \n\n .empty-state[_ngcontent-%COMP%] {\n padding: 32px 20px;\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n \n\n @media (max-width: 900px) {\n .lower-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 640px) {\n .dashboard-container[_ngcontent-%COMP%] { padding: 16px; }\n .kpi-row[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n }"], changeDetection: 0 });
483
483
  }
484
484
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingDashboardTabComponent, [{
485
485
  type: Component,
@@ -628,7 +628,7 @@ export class TestingDashboardTabComponent {
628
628
  </div>
629
629
  } @empty {
630
630
  <div class="empty-state">
631
- <i class="fa-solid fa-check-circle" style="color: #22c55e; margin-right: 6px;"></i>
631
+ <i class="fa-solid fa-check-circle" style="color: var(--mj-status-success); margin-right: 6px;"></i>
632
632
  No alerts - all tests healthy
633
633
  </div>
634
634
  }
@@ -639,7 +639,7 @@ export class TestingDashboardTabComponent {
639
639
  </div>
640
640
  </div>
641
641
  }
642
- `, styles: ["\n /* ===== Layout ===== */\n .full-page-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: #f8f9fa;\n }\n\n .dashboard-container {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n /* ===== Page Header ===== */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n\n .page-title {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: #1e293b;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .page-title i { color: #6366f1; }\n\n .refresh-btn {\n background: #6366f1;\n color: white;\n border: none;\n padding: 8px 18px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.2s ease;\n }\n\n .refresh-btn:hover:not(:disabled) { background: #4f46e5; }\n .refresh-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n .refresh-btn i.spinning { animation: spin 1s linear infinite; }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n /* ===== KPI Row ===== */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n /* ===== Card Base ===== */\n .card {\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n overflow: hidden;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-bottom: 1px solid #f1f5f9;\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #334155;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header h3 i { color: #6366f1; font-size: 13px; }\n\n /* ===== Live Activity ===== */\n .live-activity-card {\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n margin-bottom: 24px;\n overflow: hidden;\n }\n\n .live-dot {\n width: 10px;\n height: 10px;\n background: #22c55e;\n border-radius: 50%;\n display: inline-block;\n animation: pulse-dot 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse-dot {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.5); }\n 50% { box-shadow: 0 0 0 6px rgba(34,197,94,0); }\n }\n\n .live-idle-icon { color: #9ca3af; font-size: 14px; }\n\n .running-tests-list {\n padding: 0 20px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .running-test-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n background: #f0fdf4;\n border-radius: 8px;\n border: 1px solid #bbf7d0;\n }\n\n .running-test-name {\n font-size: 13px;\n font-weight: 500;\n color: #166534;\n }\n\n .running-test-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .running-elapsed {\n font-size: 12px;\n color: #15803d;\n font-weight: 500;\n }\n\n .running-progress-bar {\n width: 60px;\n height: 4px;\n background: #dcfce7;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .running-progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, #22c55e, #86efac);\n animation: progress-slide 1.2s ease-in-out infinite;\n border-radius: 2px;\n }\n\n @keyframes progress-slide {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(100%); }\n }\n\n /* ===== Lower Grid ===== */\n .lower-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n }\n\n .right-column {\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n /* ===== Recent Runs ===== */\n .runs-list {\n max-height: 520px;\n overflow-y: auto;\n }\n\n .run-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 20px;\n border-bottom: 1px solid #f8fafc;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .run-row:hover { background: #f8fafc; }\n\n .run-row-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .run-row-name {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .run-row-time {\n font-size: 11px;\n color: #94a3b8;\n }\n\n .run-row-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .run-row-duration {\n font-size: 11px;\n color: #64748b;\n min-width: 44px;\n text-align: right;\n }\n\n /* ===== Suite Health ===== */\n .suite-health-list {\n padding: 8px 0;\n max-height: 260px;\n overflow-y: auto;\n }\n\n .suite-health-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 20px;\n }\n\n .suite-health-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .suite-health-name {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .suite-health-count {\n font-size: 11px;\n color: #94a3b8;\n }\n\n .suite-health-bar-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n\n .suite-health-bar {\n width: 80px;\n height: 6px;\n background: #f1f5f9;\n border-radius: 3px;\n overflow: hidden;\n }\n\n .suite-health-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .suite-health-pct {\n font-size: 12px;\n font-weight: 600;\n min-width: 36px;\n text-align: right;\n }\n\n /* ===== Alerts ===== */\n .alerts-list {\n max-height: 220px;\n overflow-y: auto;\n }\n\n .alert-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 20px;\n border-bottom: 1px solid #f8fafc;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .alert-row:hover { background: #fef2f2; }\n\n .alert-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .alert-name {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .alert-reason {\n font-size: 11px;\n color: #94a3b8;\n }\n\n /* ===== Empty State ===== */\n .empty-state {\n padding: 32px 20px;\n text-align: center;\n color: #94a3b8;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n /* ===== Responsive ===== */\n @media (max-width: 900px) {\n .lower-grid {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 640px) {\n .dashboard-container { padding: 16px; }\n .kpi-row { grid-template-columns: 1fr; }\n }\n "] }]
642
+ `, styles: ["\n /* ===== Layout ===== */\n .full-page-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: var(--mj-bg-surface-card);\n }\n\n .dashboard-container {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n }\n\n /* ===== Page Header ===== */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n\n .page-title {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .page-title i { color: var(--mj-brand-primary); }\n\n .refresh-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n padding: 8px 18px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.2s ease;\n }\n\n .refresh-btn:hover:not(:disabled) { background: var(--mj-brand-primary-hover); }\n .refresh-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n .refresh-btn i.spinning { animation: spin 1s linear infinite; }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n /* ===== KPI Row ===== */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n /* ===== Card Base ===== */\n .card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header h3 i { color: var(--mj-brand-primary); font-size: 13px; }\n\n /* ===== Live Activity ===== */\n .live-activity-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n margin-bottom: 24px;\n overflow: hidden;\n }\n\n .live-dot {\n width: 10px;\n height: 10px;\n background: var(--mj-status-success);\n border-radius: 50%;\n display: inline-block;\n animation: pulse-dot 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse-dot {\n 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mj-status-success) 50%, transparent); }\n 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--mj-status-success) 0%, transparent); }\n }\n\n .live-idle-icon { color: var(--mj-text-disabled); font-size: 14px; }\n\n .running-tests-list {\n padding: 0 20px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .running-test-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-success) 25%, var(--mj-bg-surface));\n }\n\n .running-test-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-status-success);\n }\n\n .running-test-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .running-elapsed {\n font-size: 12px;\n color: var(--mj-status-success);\n font-weight: 500;\n }\n\n .running-progress-bar {\n width: 60px;\n height: 4px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 2px;\n overflow: hidden;\n }\n\n .running-progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--mj-status-success);\n animation: progress-slide 1.2s ease-in-out infinite;\n border-radius: 2px;\n }\n\n @keyframes progress-slide {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(100%); }\n }\n\n /* ===== Lower Grid ===== */\n .lower-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n }\n\n .right-column {\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n /* ===== Recent Runs ===== */\n .runs-list {\n max-height: 520px;\n overflow-y: auto;\n }\n\n .run-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-card);\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .run-row:hover { background: var(--mj-bg-surface-card); }\n\n .run-row-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .run-row-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .run-row-time {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n .run-row-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .run-row-duration {\n font-size: 11px;\n color: var(--mj-text-muted);\n min-width: 44px;\n text-align: right;\n }\n\n /* ===== Suite Health ===== */\n .suite-health-list {\n padding: 8px 0;\n max-height: 260px;\n overflow-y: auto;\n }\n\n .suite-health-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 20px;\n }\n\n .suite-health-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .suite-health-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .suite-health-count {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n .suite-health-bar-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n\n .suite-health-bar {\n width: 80px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .suite-health-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .suite-health-pct {\n font-size: 12px;\n font-weight: 600;\n min-width: 36px;\n text-align: right;\n }\n\n /* ===== Alerts ===== */\n .alerts-list {\n max-height: 220px;\n overflow-y: auto;\n }\n\n .alert-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-card);\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .alert-row:hover { background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface)); }\n\n .alert-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .alert-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .alert-reason {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n /* ===== Empty State ===== */\n .empty-state {\n padding: 32px 20px;\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n /* ===== Responsive ===== */\n @media (max-width: 900px) {\n .lower-grid {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 640px) {\n .dashboard-container { padding: 16px; }\n .kpi-row { grid-template-columns: 1fr; }\n }\n "] }]
643
643
  }], () => [{ type: i1.TestingInstrumentationService }, { type: i0.ChangeDetectorRef }], { initialState: [{
644
644
  type: Input
645
645
  }], stateChange: [{