@memberjunction/ng-dashboards 3.3.0 → 3.4.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 (382) hide show
  1. package/dist/AI/components/agents/agent-configuration.component.d.ts +24 -0
  2. package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
  3. package/dist/AI/components/agents/agent-configuration.component.js +198 -111
  4. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
  5. package/dist/AI/components/models/model-management.component.d.ts +23 -0
  6. package/dist/AI/components/models/model-management.component.d.ts.map +1 -1
  7. package/dist/AI/components/models/model-management.component.js +189 -83
  8. package/dist/AI/components/models/model-management.component.js.map +1 -1
  9. package/dist/AI/components/prompts/prompt-management.component.d.ts +23 -0
  10. package/dist/AI/components/prompts/prompt-management.component.d.ts.map +1 -1
  11. package/dist/AI/components/prompts/prompt-management.component.js +135 -30
  12. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
  13. package/dist/APIKeys/api-applications-panel.component.d.ts +5 -3
  14. package/dist/APIKeys/api-applications-panel.component.d.ts.map +1 -1
  15. package/dist/APIKeys/api-applications-panel.component.js +50 -51
  16. package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
  17. package/dist/APIKeys/api-key-create-dialog.component.d.ts +4 -3
  18. package/dist/APIKeys/api-key-create-dialog.component.d.ts.map +1 -1
  19. package/dist/APIKeys/api-key-create-dialog.component.js +119 -94
  20. package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
  21. package/dist/APIKeys/api-key-edit-panel.component.d.ts +1 -1
  22. package/dist/APIKeys/api-key-edit-panel.component.d.ts.map +1 -1
  23. package/dist/APIKeys/api-key-edit-panel.component.js +100 -87
  24. package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
  25. package/dist/APIKeys/api-key-list.component.d.ts +2 -1
  26. package/dist/APIKeys/api-key-list.component.d.ts.map +1 -1
  27. package/dist/APIKeys/api-key-list.component.js +24 -20
  28. package/dist/APIKeys/api-key-list.component.js.map +1 -1
  29. package/dist/APIKeys/api-keys-resource.component.d.ts +6 -4
  30. package/dist/APIKeys/api-keys-resource.component.d.ts.map +1 -1
  31. package/dist/APIKeys/api-keys-resource.component.js +42 -57
  32. package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
  33. package/dist/Actions/components/explorer/action-breadcrumb.component.d.ts +22 -0
  34. package/dist/Actions/components/explorer/action-breadcrumb.component.d.ts.map +1 -0
  35. package/dist/Actions/components/explorer/action-breadcrumb.component.js +139 -0
  36. package/dist/Actions/components/explorer/action-breadcrumb.component.js.map +1 -0
  37. package/dist/Actions/components/explorer/action-card.component.d.ts +39 -0
  38. package/dist/Actions/components/explorer/action-card.component.d.ts.map +1 -0
  39. package/dist/Actions/components/explorer/action-card.component.js +410 -0
  40. package/dist/Actions/components/explorer/action-card.component.js.map +1 -0
  41. package/dist/Actions/components/explorer/action-explorer.component.d.ts +62 -0
  42. package/dist/Actions/components/explorer/action-explorer.component.d.ts.map +1 -0
  43. package/dist/Actions/components/explorer/action-explorer.component.js +527 -0
  44. package/dist/Actions/components/explorer/action-explorer.component.js.map +1 -0
  45. package/dist/Actions/components/explorer/action-list-item.component.d.ts +24 -0
  46. package/dist/Actions/components/explorer/action-list-item.component.d.ts.map +1 -0
  47. package/dist/Actions/components/explorer/action-list-item.component.js +210 -0
  48. package/dist/Actions/components/explorer/action-list-item.component.js.map +1 -0
  49. package/dist/Actions/components/explorer/action-toolbar.component.d.ts +63 -0
  50. package/dist/Actions/components/explorer/action-toolbar.component.d.ts.map +1 -0
  51. package/dist/Actions/components/explorer/action-toolbar.component.js +483 -0
  52. package/dist/Actions/components/explorer/action-toolbar.component.js.map +1 -0
  53. package/dist/Actions/components/explorer/action-tree-panel.component.d.ts +57 -0
  54. package/dist/Actions/components/explorer/action-tree-panel.component.d.ts.map +1 -0
  55. package/dist/Actions/components/explorer/action-tree-panel.component.js +454 -0
  56. package/dist/Actions/components/explorer/action-tree-panel.component.js.map +1 -0
  57. package/dist/Actions/components/explorer/index.d.ts +10 -0
  58. package/dist/Actions/components/explorer/index.d.ts.map +1 -0
  59. package/dist/Actions/components/explorer/index.js +14 -0
  60. package/dist/Actions/components/explorer/index.js.map +1 -0
  61. package/dist/Actions/components/explorer/new-action-panel.component.d.ts +49 -0
  62. package/dist/Actions/components/explorer/new-action-panel.component.d.ts.map +1 -0
  63. package/dist/Actions/components/explorer/new-action-panel.component.js +359 -0
  64. package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -0
  65. package/dist/Actions/components/explorer/new-category-panel.component.d.ts +37 -0
  66. package/dist/Actions/components/explorer/new-category-panel.component.d.ts.map +1 -0
  67. package/dist/Actions/components/explorer/new-category-panel.component.js +282 -0
  68. package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -0
  69. package/dist/Actions/index.d.ts +3 -0
  70. package/dist/Actions/index.d.ts.map +1 -1
  71. package/dist/Actions/index.js +5 -0
  72. package/dist/Actions/index.js.map +1 -1
  73. package/dist/Actions/services/action-explorer-state.service.d.ts +104 -0
  74. package/dist/Actions/services/action-explorer-state.service.d.ts.map +1 -0
  75. package/dist/Actions/services/action-explorer-state.service.js +352 -0
  76. package/dist/Actions/services/action-explorer-state.service.js.map +1 -0
  77. package/dist/Communication/communication-dashboard.component.d.ts +2 -6
  78. package/dist/Communication/communication-dashboard.component.d.ts.map +1 -1
  79. package/dist/Communication/communication-dashboard.component.js +142 -93
  80. package/dist/Communication/communication-dashboard.component.js.map +1 -1
  81. package/dist/Communication/communication-logs-resource.component.d.ts +10 -4
  82. package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
  83. package/dist/Communication/communication-logs-resource.component.js +253 -121
  84. package/dist/Communication/communication-logs-resource.component.js.map +1 -1
  85. package/dist/Communication/communication-monitor-resource.component.d.ts +38 -3
  86. package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
  87. package/dist/Communication/communication-monitor-resource.component.js +431 -157
  88. package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
  89. package/dist/Communication/communication-providers-resource.component.d.ts +16 -5
  90. package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
  91. package/dist/Communication/communication-providers-resource.component.js +229 -118
  92. package/dist/Communication/communication-providers-resource.component.js.map +1 -1
  93. package/dist/Communication/communication-runs-resource.component.d.ts +4 -2
  94. package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
  95. package/dist/Communication/communication-runs-resource.component.js +155 -149
  96. package/dist/Communication/communication-runs-resource.component.js.map +1 -1
  97. package/dist/Communication/communication-templates-resource.component.d.ts +43 -0
  98. package/dist/Communication/communication-templates-resource.component.d.ts.map +1 -0
  99. package/dist/Communication/communication-templates-resource.component.js +371 -0
  100. package/dist/Communication/communication-templates-resource.component.js.map +1 -0
  101. package/dist/ComponentStudio/component-studio-dashboard.component.d.ts +69 -267
  102. package/dist/ComponentStudio/component-studio-dashboard.component.d.ts.map +1 -1
  103. package/dist/ComponentStudio/component-studio-dashboard.component.js +698 -1802
  104. package/dist/ComponentStudio/component-studio-dashboard.component.js.map +1 -1
  105. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts +68 -0
  106. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.d.ts.map +1 -0
  107. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +401 -0
  108. package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js.map +1 -0
  109. package/dist/ComponentStudio/components/browser/component-browser.component.d.ts +44 -0
  110. package/dist/ComponentStudio/components/browser/component-browser.component.d.ts.map +1 -0
  111. package/dist/ComponentStudio/components/browser/component-browser.component.js +636 -0
  112. package/dist/ComponentStudio/components/browser/component-browser.component.js.map +1 -0
  113. package/dist/ComponentStudio/components/editors/code-editor-panel.component.d.ts +35 -0
  114. package/dist/ComponentStudio/components/editors/code-editor-panel.component.d.ts.map +1 -0
  115. package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +380 -0
  116. package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -0
  117. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.d.ts +24 -0
  118. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.d.ts.map +1 -0
  119. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +221 -0
  120. package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -0
  121. package/dist/ComponentStudio/components/editors/requirements-editor.component.d.ts +28 -0
  122. package/dist/ComponentStudio/components/editors/requirements-editor.component.d.ts.map +1 -0
  123. package/dist/ComponentStudio/components/editors/requirements-editor.component.js +263 -0
  124. package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -0
  125. package/dist/ComponentStudio/components/editors/spec-editor.component.d.ts +34 -0
  126. package/dist/ComponentStudio/components/editors/spec-editor.component.d.ts.map +1 -0
  127. package/dist/ComponentStudio/components/editors/spec-editor.component.js +307 -0
  128. package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -0
  129. package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.d.ts +29 -0
  130. package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.d.ts.map +1 -0
  131. package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +159 -0
  132. package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js.map +1 -0
  133. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.d.ts +20 -0
  134. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.d.ts.map +1 -0
  135. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +192 -0
  136. package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -0
  137. package/dist/ComponentStudio/components/workspace/component-preview.component.d.ts +57 -0
  138. package/dist/ComponentStudio/components/workspace/component-preview.component.d.ts.map +1 -0
  139. package/dist/ComponentStudio/components/workspace/component-preview.component.js +342 -0
  140. package/dist/ComponentStudio/components/workspace/component-preview.component.js.map +1 -0
  141. package/dist/ComponentStudio/components/workspace/editor-tabs.component.d.ts +15 -0
  142. package/dist/ComponentStudio/components/workspace/editor-tabs.component.d.ts.map +1 -0
  143. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +144 -0
  144. package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -0
  145. package/dist/ComponentStudio/services/component-studio-state.service.d.ts +203 -0
  146. package/dist/ComponentStudio/services/component-studio-state.service.d.ts.map +1 -0
  147. package/dist/ComponentStudio/services/component-studio-state.service.js +651 -0
  148. package/dist/ComponentStudio/services/component-studio-state.service.js.map +1 -0
  149. package/dist/ComponentStudio/services/component-version.service.d.ts +120 -0
  150. package/dist/ComponentStudio/services/component-version.service.d.ts.map +1 -0
  151. package/dist/ComponentStudio/services/component-version.service.js +394 -0
  152. package/dist/ComponentStudio/services/component-version.service.js.map +1 -0
  153. package/dist/Credentials/components/credentials-categories-resource.component.d.ts +1 -1
  154. package/dist/Credentials/components/credentials-categories-resource.component.d.ts.map +1 -1
  155. package/dist/Credentials/components/credentials-categories-resource.component.js +1 -1
  156. package/dist/Credentials/components/credentials-list-resource.component.d.ts +1 -1
  157. package/dist/Credentials/components/credentials-list-resource.component.d.ts.map +1 -1
  158. package/dist/Credentials/components/credentials-list-resource.component.js +1 -1
  159. package/dist/Credentials/components/credentials-types-resource.component.d.ts +1 -1
  160. package/dist/Credentials/components/credentials-types-resource.component.d.ts.map +1 -1
  161. package/dist/Credentials/components/credentials-types-resource.component.js +1 -1
  162. package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.d.ts +2 -2
  163. package/dist/MCP/components/mcp-connection-dialog.component.d.ts +72 -0
  164. package/dist/MCP/components/mcp-connection-dialog.component.d.ts.map +1 -0
  165. package/dist/MCP/components/mcp-connection-dialog.component.js +529 -0
  166. package/dist/MCP/components/mcp-connection-dialog.component.js.map +1 -0
  167. package/dist/MCP/components/mcp-log-detail-panel.component.d.ts +77 -0
  168. package/dist/MCP/components/mcp-log-detail-panel.component.d.ts.map +1 -0
  169. package/dist/MCP/components/mcp-log-detail-panel.component.js +535 -0
  170. package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -0
  171. package/dist/MCP/components/mcp-server-dialog.component.d.ts +77 -0
  172. package/dist/MCP/components/mcp-server-dialog.component.d.ts.map +1 -0
  173. package/dist/MCP/components/mcp-server-dialog.component.js +437 -0
  174. package/dist/MCP/components/mcp-server-dialog.component.js.map +1 -0
  175. package/dist/MCP/components/mcp-test-tool-dialog.component.d.ts +271 -0
  176. package/dist/MCP/components/mcp-test-tool-dialog.component.d.ts.map +1 -0
  177. package/dist/MCP/components/mcp-test-tool-dialog.component.js +1300 -0
  178. package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -0
  179. package/dist/MCP/index.d.ts +11 -0
  180. package/dist/MCP/index.d.ts.map +1 -0
  181. package/dist/MCP/index.js +15 -0
  182. package/dist/MCP/index.js.map +1 -0
  183. package/dist/MCP/mcp-dashboard.component.d.ts +409 -0
  184. package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -0
  185. package/dist/MCP/mcp-dashboard.component.js +2486 -0
  186. package/dist/MCP/mcp-dashboard.component.js.map +1 -0
  187. package/dist/MCP/mcp-resource.component.d.ts +20 -0
  188. package/dist/MCP/mcp-resource.component.d.ts.map +1 -0
  189. package/dist/MCP/mcp-resource.component.js +55 -0
  190. package/dist/MCP/mcp-resource.component.js.map +1 -0
  191. package/dist/MCP/mcp.module.d.ts +27 -0
  192. package/dist/MCP/mcp.module.d.ts.map +1 -0
  193. package/dist/MCP/mcp.module.js +122 -0
  194. package/dist/MCP/mcp.module.js.map +1 -0
  195. package/dist/MCP/services/mcp-tools.service.d.ts +109 -0
  196. package/dist/MCP/services/mcp-tools.service.d.ts.map +1 -0
  197. package/dist/MCP/services/mcp-tools.service.js +222 -0
  198. package/dist/MCP/services/mcp-tools.service.js.map +1 -0
  199. package/dist/Scheduling/components/index.d.ts +5 -8
  200. package/dist/Scheduling/components/index.d.ts.map +1 -1
  201. package/dist/Scheduling/components/index.js +6 -9
  202. package/dist/Scheduling/components/index.js.map +1 -1
  203. package/dist/Scheduling/components/job-slideout.component.d.ts +45 -0
  204. package/dist/Scheduling/components/job-slideout.component.d.ts.map +1 -0
  205. package/dist/Scheduling/components/job-slideout.component.js +458 -0
  206. package/dist/Scheduling/components/job-slideout.component.js.map +1 -0
  207. package/dist/Scheduling/components/scheduling-activity-resource.component.d.ts +19 -0
  208. package/dist/Scheduling/components/scheduling-activity-resource.component.d.ts.map +1 -0
  209. package/dist/Scheduling/components/scheduling-activity-resource.component.js +51 -0
  210. package/dist/Scheduling/components/scheduling-activity-resource.component.js.map +1 -0
  211. package/dist/Scheduling/components/scheduling-activity.component.d.ts +71 -0
  212. package/dist/Scheduling/components/scheduling-activity.component.d.ts.map +1 -0
  213. package/dist/Scheduling/components/scheduling-activity.component.js +714 -0
  214. package/dist/Scheduling/components/scheduling-activity.component.js.map +1 -0
  215. package/dist/Scheduling/components/scheduling-jobs-resource.component.d.ts +3 -4
  216. package/dist/Scheduling/components/scheduling-jobs-resource.component.d.ts.map +1 -1
  217. package/dist/Scheduling/components/scheduling-jobs-resource.component.js +3 -7
  218. package/dist/Scheduling/components/scheduling-jobs-resource.component.js.map +1 -1
  219. package/dist/Scheduling/components/scheduling-jobs.component.d.ts +52 -34
  220. package/dist/Scheduling/components/scheduling-jobs.component.d.ts.map +1 -1
  221. package/dist/Scheduling/components/scheduling-jobs.component.js +446 -261
  222. package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
  223. package/dist/Scheduling/components/scheduling-overview-resource.component.d.ts +19 -0
  224. package/dist/Scheduling/components/scheduling-overview-resource.component.d.ts.map +1 -0
  225. package/dist/Scheduling/components/scheduling-overview-resource.component.js +51 -0
  226. package/dist/Scheduling/components/scheduling-overview-resource.component.js.map +1 -0
  227. package/dist/Scheduling/components/scheduling-overview.component.d.ts +43 -0
  228. package/dist/Scheduling/components/scheduling-overview.component.d.ts.map +1 -0
  229. package/dist/Scheduling/components/scheduling-overview.component.js +595 -0
  230. package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -0
  231. package/dist/Scheduling/scheduling-dashboard.component.d.ts +22 -32
  232. package/dist/Scheduling/scheduling-dashboard.component.d.ts.map +1 -1
  233. package/dist/Scheduling/scheduling-dashboard.component.js +175 -169
  234. package/dist/Scheduling/scheduling-dashboard.component.js.map +1 -1
  235. package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts +49 -6
  236. package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts.map +1 -1
  237. package/dist/Scheduling/services/scheduling-instrumentation.service.js +218 -149
  238. package/dist/Scheduling/services/scheduling-instrumentation.service.js.map +1 -1
  239. package/dist/Testing/components/index.d.ts +7 -8
  240. package/dist/Testing/components/index.d.ts.map +1 -1
  241. package/dist/Testing/components/index.js +8 -9
  242. package/dist/Testing/components/index.js.map +1 -1
  243. package/dist/Testing/components/testing-analytics-resource.component.d.ts +0 -4
  244. package/dist/Testing/components/testing-analytics-resource.component.d.ts.map +1 -1
  245. package/dist/Testing/components/testing-analytics-resource.component.js +1 -7
  246. package/dist/Testing/components/testing-analytics-resource.component.js.map +1 -1
  247. package/dist/Testing/components/testing-analytics.component.d.ts +52 -37
  248. package/dist/Testing/components/testing-analytics.component.d.ts.map +1 -1
  249. package/dist/Testing/components/testing-analytics.component.js +1023 -569
  250. package/dist/Testing/components/testing-analytics.component.js.map +1 -1
  251. package/dist/Testing/components/testing-dashboard-tab-resource.component.d.ts +16 -0
  252. package/dist/Testing/components/testing-dashboard-tab-resource.component.d.ts.map +1 -0
  253. package/dist/Testing/components/testing-dashboard-tab-resource.component.js +47 -0
  254. package/dist/Testing/components/testing-dashboard-tab-resource.component.js.map +1 -0
  255. package/dist/Testing/components/testing-dashboard-tab.component.d.ts +57 -0
  256. package/dist/Testing/components/testing-dashboard-tab.component.d.ts.map +1 -0
  257. package/dist/Testing/components/testing-dashboard-tab.component.js +649 -0
  258. package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -0
  259. package/dist/{Scheduling/components/scheduling-types-resource.component.d.ts → Testing/components/testing-explorer-resource.component.d.ts} +5 -9
  260. package/dist/Testing/components/testing-explorer-resource.component.d.ts.map +1 -0
  261. package/dist/Testing/components/testing-explorer-resource.component.js +47 -0
  262. package/dist/Testing/components/testing-explorer-resource.component.js.map +1 -0
  263. package/dist/Testing/components/testing-explorer.component.d.ts +193 -0
  264. package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -0
  265. package/dist/Testing/components/testing-explorer.component.js +2212 -0
  266. package/dist/Testing/components/testing-explorer.component.js.map +1 -0
  267. package/dist/Testing/components/testing-review-resource.component.d.ts +16 -0
  268. package/dist/Testing/components/testing-review-resource.component.d.ts.map +1 -0
  269. package/dist/Testing/components/testing-review-resource.component.js +47 -0
  270. package/dist/Testing/components/testing-review-resource.component.js.map +1 -0
  271. package/dist/Testing/components/testing-review.component.d.ts +60 -0
  272. package/dist/Testing/components/testing-review.component.d.ts.map +1 -0
  273. package/dist/Testing/components/testing-review.component.js +985 -0
  274. package/dist/Testing/components/testing-review.component.js.map +1 -0
  275. package/dist/Testing/components/testing-runs-resource.component.d.ts +16 -0
  276. package/dist/Testing/components/testing-runs-resource.component.d.ts.map +1 -0
  277. package/dist/Testing/components/testing-runs-resource.component.js +47 -0
  278. package/dist/Testing/components/testing-runs-resource.component.js.map +1 -0
  279. package/dist/Testing/components/testing-runs.component.d.ts +82 -0
  280. package/dist/Testing/components/testing-runs.component.d.ts.map +1 -0
  281. package/dist/Testing/components/testing-runs.component.js +1067 -0
  282. package/dist/Testing/components/testing-runs.component.js.map +1 -0
  283. package/dist/Testing/testing-dashboard.component.d.ts +12 -15
  284. package/dist/Testing/testing-dashboard.component.d.ts.map +1 -1
  285. package/dist/Testing/testing-dashboard.component.js +46 -68
  286. package/dist/Testing/testing-dashboard.component.js.map +1 -1
  287. package/dist/module.d.ts +121 -104
  288. package/dist/module.d.ts.map +1 -1
  289. package/dist/module.js +185 -93
  290. package/dist/module.js.map +1 -1
  291. package/dist/public-api.d.ts +5 -3
  292. package/dist/public-api.d.ts.map +1 -1
  293. package/dist/public-api.js +23 -16
  294. package/dist/public-api.js.map +1 -1
  295. package/dist/shared/pipes/highlight-search.pipe.d.ts +17 -0
  296. package/dist/shared/pipes/highlight-search.pipe.d.ts.map +1 -0
  297. package/dist/shared/pipes/highlight-search.pipe.js +40 -0
  298. package/dist/shared/pipes/highlight-search.pipe.js.map +1 -0
  299. package/dist/shared/pipes/index.d.ts +2 -0
  300. package/dist/shared/pipes/index.d.ts.map +1 -0
  301. package/dist/shared/pipes/index.js +2 -0
  302. package/dist/shared/pipes/index.js.map +1 -0
  303. package/dist/shared/shared-pipes.module.d.ts +11 -0
  304. package/dist/shared/shared-pipes.module.d.ts.map +1 -0
  305. package/dist/shared/shared-pipes.module.js +24 -0
  306. package/dist/shared/shared-pipes.module.js.map +1 -0
  307. package/package.json +39 -32
  308. package/dist/Credentials/components/credential-category-edit-panel.component.d.ts +0 -44
  309. package/dist/Credentials/components/credential-category-edit-panel.component.d.ts.map +0 -1
  310. package/dist/Credentials/components/credential-category-edit-panel.component.js +0 -456
  311. package/dist/Credentials/components/credential-category-edit-panel.component.js.map +0 -1
  312. package/dist/Credentials/components/credential-edit-panel.component.d.ts +0 -70
  313. package/dist/Credentials/components/credential-edit-panel.component.d.ts.map +0 -1
  314. package/dist/Credentials/components/credential-edit-panel.component.js +0 -694
  315. package/dist/Credentials/components/credential-edit-panel.component.js.map +0 -1
  316. package/dist/Credentials/components/credential-type-edit-panel.component.d.ts +0 -56
  317. package/dist/Credentials/components/credential-type-edit-panel.component.d.ts.map +0 -1
  318. package/dist/Credentials/components/credential-type-edit-panel.component.js +0 -563
  319. package/dist/Credentials/components/credential-type-edit-panel.component.js.map +0 -1
  320. package/dist/Scheduling/components/scheduling-health-resource.component.d.ts +0 -20
  321. package/dist/Scheduling/components/scheduling-health-resource.component.d.ts.map +0 -1
  322. package/dist/Scheduling/components/scheduling-health-resource.component.js +0 -55
  323. package/dist/Scheduling/components/scheduling-health-resource.component.js.map +0 -1
  324. package/dist/Scheduling/components/scheduling-health.component.d.ts +0 -30
  325. package/dist/Scheduling/components/scheduling-health.component.d.ts.map +0 -1
  326. package/dist/Scheduling/components/scheduling-health.component.js +0 -315
  327. package/dist/Scheduling/components/scheduling-health.component.js.map +0 -1
  328. package/dist/Scheduling/components/scheduling-history-resource.component.d.ts +0 -20
  329. package/dist/Scheduling/components/scheduling-history-resource.component.d.ts.map +0 -1
  330. package/dist/Scheduling/components/scheduling-history-resource.component.js +0 -55
  331. package/dist/Scheduling/components/scheduling-history-resource.component.js.map +0 -1
  332. package/dist/Scheduling/components/scheduling-history.component.d.ts +0 -48
  333. package/dist/Scheduling/components/scheduling-history.component.d.ts.map +0 -1
  334. package/dist/Scheduling/components/scheduling-history.component.js +0 -377
  335. package/dist/Scheduling/components/scheduling-history.component.js.map +0 -1
  336. package/dist/Scheduling/components/scheduling-monitor-resource.component.d.ts +0 -20
  337. package/dist/Scheduling/components/scheduling-monitor-resource.component.d.ts.map +0 -1
  338. package/dist/Scheduling/components/scheduling-monitor-resource.component.js +0 -55
  339. package/dist/Scheduling/components/scheduling-monitor-resource.component.js.map +0 -1
  340. package/dist/Scheduling/components/scheduling-monitoring.component.d.ts +0 -37
  341. package/dist/Scheduling/components/scheduling-monitoring.component.d.ts.map +0 -1
  342. package/dist/Scheduling/components/scheduling-monitoring.component.js +0 -488
  343. package/dist/Scheduling/components/scheduling-monitoring.component.js.map +0 -1
  344. package/dist/Scheduling/components/scheduling-types-resource.component.d.ts.map +0 -1
  345. package/dist/Scheduling/components/scheduling-types-resource.component.js +0 -55
  346. package/dist/Scheduling/components/scheduling-types-resource.component.js.map +0 -1
  347. package/dist/Scheduling/components/scheduling-types.component.d.ts +0 -22
  348. package/dist/Scheduling/components/scheduling-types.component.d.ts.map +0 -1
  349. package/dist/Scheduling/components/scheduling-types.component.js +0 -165
  350. package/dist/Scheduling/components/scheduling-types.component.js.map +0 -1
  351. package/dist/Testing/components/testing-execution-resource.component.d.ts +0 -20
  352. package/dist/Testing/components/testing-execution-resource.component.d.ts.map +0 -1
  353. package/dist/Testing/components/testing-execution-resource.component.js +0 -55
  354. package/dist/Testing/components/testing-execution-resource.component.js.map +0 -1
  355. package/dist/Testing/components/testing-execution.component.d.ts +0 -71
  356. package/dist/Testing/components/testing-execution.component.d.ts.map +0 -1
  357. package/dist/Testing/components/testing-execution.component.js +0 -845
  358. package/dist/Testing/components/testing-execution.component.js.map +0 -1
  359. package/dist/Testing/components/testing-feedback-resource.component.d.ts +0 -20
  360. package/dist/Testing/components/testing-feedback-resource.component.d.ts.map +0 -1
  361. package/dist/Testing/components/testing-feedback-resource.component.js +0 -55
  362. package/dist/Testing/components/testing-feedback-resource.component.js.map +0 -1
  363. package/dist/Testing/components/testing-feedback.component.d.ts +0 -111
  364. package/dist/Testing/components/testing-feedback.component.d.ts.map +0 -1
  365. package/dist/Testing/components/testing-feedback.component.js +0 -1486
  366. package/dist/Testing/components/testing-feedback.component.js.map +0 -1
  367. package/dist/Testing/components/testing-overview-resource.component.d.ts +0 -20
  368. package/dist/Testing/components/testing-overview-resource.component.d.ts.map +0 -1
  369. package/dist/Testing/components/testing-overview-resource.component.js +0 -55
  370. package/dist/Testing/components/testing-overview-resource.component.js.map +0 -1
  371. package/dist/Testing/components/testing-overview.component.d.ts +0 -30
  372. package/dist/Testing/components/testing-overview.component.d.ts.map +0 -1
  373. package/dist/Testing/components/testing-overview.component.js +0 -361
  374. package/dist/Testing/components/testing-overview.component.js.map +0 -1
  375. package/dist/Testing/components/testing-version-comparison.component.d.ts +0 -62
  376. package/dist/Testing/components/testing-version-comparison.component.d.ts.map +0 -1
  377. package/dist/Testing/components/testing-version-comparison.component.js +0 -815
  378. package/dist/Testing/components/testing-version-comparison.component.js.map +0 -1
  379. package/dist/Testing/components/testing-version-resource.component.d.ts +0 -20
  380. package/dist/Testing/components/testing-version-resource.component.d.ts.map +0 -1
  381. package/dist/Testing/components/testing-version-resource.component.js +0 -55
  382. package/dist/Testing/components/testing-version-resource.component.js.map +0 -1
@@ -1,493 +1,897 @@
1
1
  import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
2
2
  import { Subject } from 'rxjs';
3
- import { takeUntil, map } from 'rxjs/operators';
3
+ import { map, shareReplay } from 'rxjs/operators';
4
4
  import * as i0 from "@angular/core";
5
5
  import * as i1 from "../services/testing-instrumentation.service";
6
- import * as i2 from "@angular/forms";
7
- import * as i3 from "@angular/common";
6
+ import * as i2 from "@angular/common";
8
7
  const _forTrack0 = ($index, $item) => $item.days;
9
8
  const _forTrack1 = ($index, $item) => $item.testName;
10
9
  const _forTrack2 = ($index, $item) => $item.label;
11
- const _c0 = () => [];
10
+ const _forTrack3 = ($index, $item) => $item.version;
12
11
  function TestingAnalyticsComponent_For_9_Template(rf, ctx) { if (rf & 1) {
13
- i0.ɵɵelementStart(0, "option", 6);
12
+ const _r1 = i0.ɵɵgetCurrentView();
13
+ i0.ɵɵelementStart(0, "button", 30);
14
+ i0.ɵɵlistener("click", function TestingAnalyticsComponent_For_9_Template_button_click_0_listener() { const range_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnTimeRangeSelect(range_r2.days)); });
14
15
  i0.ɵɵtext(1);
15
16
  i0.ɵɵelementEnd();
16
17
  } if (rf & 2) {
17
- const range_r1 = ctx.$implicit;
18
- i0.ɵɵproperty("value", range_r1.days);
18
+ const range_r2 = ctx.$implicit;
19
+ const ctx_r2 = i0.ɵɵnextContext();
20
+ i0.ɵɵclassProp("active", ctx_r2.SelectedDays === range_r2.days);
19
21
  i0.ɵɵadvance();
20
- i0.ɵɵtextInterpolate(range_r1.label);
22
+ i0.ɵɵtextInterpolate1(" ", range_r2.label, " ");
21
23
  } }
22
- function TestingAnalyticsComponent_For_20_Template(rf, ctx) { if (rf & 1) {
23
- i0.ɵɵelementStart(0, "div", 13)(1, "div", 41)(2, "span", 42);
24
- i0.ɵɵtext(3);
25
- i0.ɵɵpipe(4, "date");
24
+ function TestingAnalyticsComponent_Conditional_21_Conditional_0_Template(rf, ctx) { if (rf & 1) {
25
+ i0.ɵɵelementStart(0, "div", 28);
26
+ i0.ɵɵelement(1, "i", 31);
27
+ i0.ɵɵelementStart(2, "span");
28
+ i0.ɵɵtext(3, "No trend data");
29
+ i0.ɵɵelementEnd()();
30
+ } }
31
+ function TestingAnalyticsComponent_Conditional_21_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
32
+ i0.ɵɵelementStart(0, "div", 33)(1, "span", 35);
33
+ i0.ɵɵtext(2);
34
+ i0.ɵɵelementEnd();
35
+ i0.ɵɵelementStart(3, "div", 36);
36
+ i0.ɵɵelement(4, "div", 37);
26
37
  i0.ɵɵelementEnd();
27
- i0.ɵɵelementStart(5, "span", 43);
38
+ i0.ɵɵelementStart(5, "span", 38);
28
39
  i0.ɵɵtext(6);
40
+ i0.ɵɵpipe(7, "number");
29
41
  i0.ɵɵelementEnd()();
30
- i0.ɵɵelementStart(7, "div", 44)(8, "div", 45);
31
- i0.ɵɵelement(9, "i", 46);
32
- i0.ɵɵelementStart(10, "span");
33
- i0.ɵɵtext(11);
34
- i0.ɵɵelementEnd()();
35
- i0.ɵɵelementStart(12, "div", 47);
36
- i0.ɵɵelement(13, "i", 48);
37
- i0.ɵɵelementStart(14, "span");
38
- i0.ɵɵtext(15);
39
- i0.ɵɵelementEnd()();
40
- i0.ɵɵelementStart(16, "div", 49);
41
- i0.ɵɵelement(17, "i", 50);
42
- i0.ɵɵelementStart(18, "span");
43
- i0.ɵɵtext(19);
44
- i0.ɵɵelementEnd()()();
45
- i0.ɵɵelementStart(20, "div", 51);
46
- i0.ɵɵelement(21, "div", 52)(22, "div", 53)(23, "div", 54);
42
+ } if (rf & 2) {
43
+ const t_r4 = ctx.$implicit;
44
+ const ctx_r2 = i0.ɵɵnextContext(3);
45
+ i0.ɵɵadvance(2);
46
+ i0.ɵɵtextInterpolate(ctx_r2.FormatBucketDate(t_r4.timestamp));
47
+ i0.ɵɵadvance(2);
48
+ i0.ɵɵstyleProp("width", ctx_r2.PassRate(t_r4), "%");
49
+ i0.ɵɵadvance();
50
+ i0.ɵɵclassMap(ctx_r2.PassRateClass(t_r4));
51
+ i0.ɵɵadvance();
52
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(7, 6, ctx_r2.PassRate(t_r4), "1.0-0"), "%");
53
+ } }
54
+ function TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Conditional_0_Template(rf, ctx) { if (rf & 1) {
55
+ i0.ɵɵelement(0, "i", 39);
56
+ i0.ɵɵelementStart(1, "span", 40);
57
+ i0.ɵɵtext(2, "Improving");
47
58
  i0.ɵɵelementEnd();
48
- i0.ɵɵelementStart(24, "div", 55)(25, "span", 56);
49
- i0.ɵɵtext(26);
59
+ } }
60
+ function TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Conditional_1_Template(rf, ctx) { if (rf & 1) {
61
+ i0.ɵɵelement(0, "i", 41);
62
+ i0.ɵɵelementStart(1, "span", 42);
63
+ i0.ɵɵtext(2, "Declining");
50
64
  i0.ɵɵelementEnd();
51
- i0.ɵɵelementStart(27, "span", 57);
52
- i0.ɵɵtext(28);
53
- i0.ɵɵelementEnd()()();
65
+ } }
66
+ function TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Conditional_2_Template(rf, ctx) { if (rf & 1) {
67
+ i0.ɵɵelement(0, "i", 43);
68
+ i0.ɵɵelementStart(1, "span", 44);
69
+ i0.ɵɵtext(2, "Stable");
70
+ i0.ɵɵelementEnd();
71
+ } }
72
+ function TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Template(rf, ctx) { if (rf & 1) {
73
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Conditional_0_Template, 3, 0)(1, TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Conditional_1_Template, 3, 0)(2, TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Conditional_2_Template, 3, 0);
54
74
  } if (rf & 2) {
55
- const trend_r2 = ctx.$implicit;
75
+ const dir_r5 = ctx;
76
+ i0.ɵɵconditional(dir_r5 === "up" ? 0 : dir_r5 === "down" ? 1 : 2);
77
+ } }
78
+ function TestingAnalyticsComponent_Conditional_21_Conditional_1_Template(rf, ctx) { if (rf & 1) {
79
+ i0.ɵɵelementStart(0, "div", 32);
80
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_21_Conditional_1_For_2_Template, 8, 9, "div", 33, i0.ɵɵcomponentInstance().trackTrend, true);
81
+ i0.ɵɵelementEnd();
82
+ i0.ɵɵelementStart(3, "div", 34);
83
+ i0.ɵɵtemplate(4, TestingAnalyticsComponent_Conditional_21_Conditional_1_Conditional_4_Template, 3, 1);
84
+ i0.ɵɵpipe(5, "async");
85
+ i0.ɵɵelementEnd();
86
+ } if (rf & 2) {
87
+ let tmp_4_0;
88
+ const trends_r6 = i0.ɵɵnextContext();
56
89
  const ctx_r2 = i0.ɵɵnextContext();
90
+ i0.ɵɵadvance();
91
+ i0.ɵɵrepeater(trends_r6);
57
92
  i0.ɵɵadvance(3);
58
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(4, 19, trend_r2.timestamp, "short"));
59
- i0.ɵɵadvance(3);
60
- i0.ɵɵtextInterpolate1("", trend_r2.totalRuns, " runs");
61
- i0.ɵɵadvance(5);
62
- i0.ɵɵtextInterpolate(trend_r2.passed);
63
- i0.ɵɵadvance(4);
64
- i0.ɵɵtextInterpolate(trend_r2.failed);
65
- i0.ɵɵadvance(4);
66
- i0.ɵɵtextInterpolate(trend_r2.skipped);
93
+ i0.ɵɵconditional((tmp_4_0 = i0.ɵɵpipeBind1(5, 1, ctx_r2.PassRateTrendDirection$)) ? 4 : -1, tmp_4_0);
94
+ } }
95
+ function TestingAnalyticsComponent_Conditional_21_Template(rf, ctx) { if (rf & 1) {
96
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_21_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_21_Conditional_1_Template, 6, 3);
97
+ } if (rf & 2) {
98
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
99
+ } }
100
+ function TestingAnalyticsComponent_Conditional_27_Conditional_0_Template(rf, ctx) { if (rf & 1) {
101
+ i0.ɵɵelementStart(0, "div", 28);
102
+ i0.ɵɵelement(1, "i", 3);
103
+ i0.ɵɵelementStart(2, "span");
104
+ i0.ɵɵtext(3, "No volume data");
105
+ i0.ɵɵelementEnd()();
106
+ } }
107
+ function TestingAnalyticsComponent_Conditional_27_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
108
+ i0.ɵɵelementStart(0, "div", 33)(1, "span", 35);
109
+ i0.ɵɵtext(2);
110
+ i0.ɵɵelementEnd();
111
+ i0.ɵɵelementStart(3, "div", 49);
112
+ i0.ɵɵelement(4, "div", 50)(5, "div", 51)(6, "div", 52);
113
+ i0.ɵɵelementEnd();
114
+ i0.ɵɵelementStart(7, "span", 38);
115
+ i0.ɵɵtext(8);
116
+ i0.ɵɵelementEnd()();
117
+ } if (rf & 2) {
118
+ const t_r7 = ctx.$implicit;
119
+ const ctx_r2 = i0.ɵɵnextContext(3);
120
+ i0.ɵɵadvance(2);
121
+ i0.ɵɵtextInterpolate(ctx_r2.FormatBucketDate(t_r7.timestamp));
67
122
  i0.ɵɵadvance(2);
68
- i0.ɵɵstyleProp("width", ctx_r2.calculatePassRate(trend_r2), "%");
123
+ i0.ɵɵstyleProp("width", ctx_r2.SegmentPct(t_r7, "passed"), "%");
69
124
  i0.ɵɵadvance();
70
- i0.ɵɵstyleProp("width", ctx_r2.calculateFailRate(trend_r2), "%");
125
+ i0.ɵɵstyleProp("width", ctx_r2.SegmentPct(t_r7, "failed"), "%");
71
126
  i0.ɵɵadvance();
72
- i0.ɵɵstyleProp("width", ctx_r2.calculateSkipRate(trend_r2), "%");
127
+ i0.ɵɵstyleProp("width", ctx_r2.SegmentPct(t_r7, "skipped"), "%");
73
128
  i0.ɵɵadvance(2);
74
- i0.ɵɵclassProp("good", ctx_r2.calculatePassRate(trend_r2) >= 80)("warning", ctx_r2.calculatePassRate(trend_r2) >= 60 && ctx_r2.calculatePassRate(trend_r2) < 80)("poor", ctx_r2.calculatePassRate(trend_r2) < 60);
129
+ i0.ɵɵtextInterpolate(t_r7.totalRuns);
130
+ } }
131
+ function TestingAnalyticsComponent_Conditional_27_Conditional_1_Template(rf, ctx) { if (rf & 1) {
132
+ i0.ɵɵelementStart(0, "div", 32);
133
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_27_Conditional_1_For_2_Template, 9, 8, "div", 33, i0.ɵɵcomponentInstance().trackTrend, true);
134
+ i0.ɵɵelementEnd();
135
+ i0.ɵɵelementStart(3, "div", 45);
136
+ i0.ɵɵelement(4, "span", 46);
137
+ i0.ɵɵtext(5, " Passed ");
138
+ i0.ɵɵelement(6, "span", 47);
139
+ i0.ɵɵtext(7, " Failed ");
140
+ i0.ɵɵelement(8, "span", 48);
141
+ i0.ɵɵtext(9, " Skipped ");
142
+ i0.ɵɵelementEnd();
143
+ } if (rf & 2) {
144
+ const trends_r8 = i0.ɵɵnextContext();
75
145
  i0.ɵɵadvance();
76
- i0.ɵɵtextInterpolate1(" ", ctx_r2.calculatePassRate(trend_r2).toFixed(1), "% pass rate ");
146
+ i0.ɵɵrepeater(trends_r8);
147
+ } }
148
+ function TestingAnalyticsComponent_Conditional_27_Template(rf, ctx) { if (rf & 1) {
149
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_27_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_27_Conditional_1_Template, 10, 0);
150
+ } if (rf & 2) {
151
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
152
+ } }
153
+ function TestingAnalyticsComponent_Conditional_33_Conditional_0_Template(rf, ctx) { if (rf & 1) {
154
+ i0.ɵɵelementStart(0, "div", 28);
155
+ i0.ɵɵelement(1, "i", 53);
156
+ i0.ɵɵelementStart(2, "span");
157
+ i0.ɵɵtext(3, "No score data");
158
+ i0.ɵɵelementEnd()();
159
+ } }
160
+ function TestingAnalyticsComponent_Conditional_33_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
161
+ i0.ɵɵelementStart(0, "div", 33)(1, "span", 35);
162
+ i0.ɵɵtext(2);
163
+ i0.ɵɵelementEnd();
164
+ i0.ɵɵelementStart(3, "div", 36);
165
+ i0.ɵɵelement(4, "div", 54);
166
+ i0.ɵɵelementEnd();
167
+ i0.ɵɵelementStart(5, "span", 38);
168
+ i0.ɵɵtext(6);
169
+ i0.ɵɵpipe(7, "number");
170
+ i0.ɵɵelementEnd()();
171
+ } if (rf & 2) {
172
+ const t_r9 = ctx.$implicit;
173
+ const ctx_r2 = i0.ɵɵnextContext(3);
174
+ i0.ɵɵadvance(2);
175
+ i0.ɵɵtextInterpolate(ctx_r2.FormatBucketDate(t_r9.timestamp));
77
176
  i0.ɵɵadvance(2);
78
- i0.ɵɵtextInterpolate1("$", trend_r2.totalCost.toFixed(2), "");
177
+ i0.ɵɵclassMap(ctx_r2.ScoreBarClass(t_r9.averageScore));
178
+ i0.ɵɵstyleProp("width", t_r9.averageScore * 100, "%");
179
+ i0.ɵɵadvance();
180
+ i0.ɵɵclassMap(ctx_r2.ScoreTextClass(t_r9.averageScore));
181
+ i0.ɵɵadvance();
182
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(7, 8, t_r9.averageScore * 100, "1.0-0"), "%");
79
183
  } }
80
- function TestingAnalyticsComponent_For_30_Template(rf, ctx) { if (rf & 1) {
81
- i0.ɵɵelementStart(0, "div", 19)(1, "div", 58)(2, "div", 59);
82
- i0.ɵɵtext(3);
184
+ function TestingAnalyticsComponent_Conditional_33_Conditional_1_Template(rf, ctx) { if (rf & 1) {
185
+ i0.ɵɵelementStart(0, "div", 32);
186
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_33_Conditional_1_For_2_Template, 8, 11, "div", 33, i0.ɵɵcomponentInstance().trackTrend, true);
83
187
  i0.ɵɵelementEnd();
84
- i0.ɵɵelementStart(4, "div", 60);
85
- i0.ɵɵtext(5);
188
+ } if (rf & 2) {
189
+ const trends_r10 = i0.ɵɵnextContext();
190
+ i0.ɵɵadvance();
191
+ i0.ɵɵrepeater(trends_r10);
192
+ } }
193
+ function TestingAnalyticsComponent_Conditional_33_Template(rf, ctx) { if (rf & 1) {
194
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_33_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_33_Conditional_1_Template, 3, 0, "div", 32);
195
+ } if (rf & 2) {
196
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
197
+ } }
198
+ function TestingAnalyticsComponent_Conditional_39_Conditional_0_Template(rf, ctx) { if (rf & 1) {
199
+ i0.ɵɵelementStart(0, "div", 28);
200
+ i0.ɵɵelement(1, "i", 55);
201
+ i0.ɵɵelementStart(2, "span");
202
+ i0.ɵɵtext(3, "No cost data");
203
+ i0.ɵɵelementEnd()();
204
+ } }
205
+ function TestingAnalyticsComponent_Conditional_39_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
206
+ i0.ɵɵelementStart(0, "div", 33)(1, "span", 35);
207
+ i0.ɵɵtext(2);
208
+ i0.ɵɵelementEnd();
209
+ i0.ɵɵelementStart(3, "div", 36);
210
+ i0.ɵɵelement(4, "div", 56);
211
+ i0.ɵɵelementEnd();
212
+ i0.ɵɵelementStart(5, "span", 38);
213
+ i0.ɵɵtext(6);
214
+ i0.ɵɵpipe(7, "number");
86
215
  i0.ɵɵelementEnd()();
87
- i0.ɵɵelementStart(6, "div", 61)(7, "div", 62);
88
- i0.ɵɵelement(8, "div", 63);
89
- i0.ɵɵelementEnd()()();
90
216
  } if (rf & 2) {
91
- const test_r4 = ctx.$implicit;
92
- i0.ɵɵadvance(3);
93
- i0.ɵɵtextInterpolate(test_r4.testName);
217
+ const t_r11 = ctx.$implicit;
218
+ const trends_r12 = i0.ɵɵnextContext(2);
219
+ const ctx_r2 = i0.ɵɵnextContext();
94
220
  i0.ɵɵadvance(2);
95
- i0.ɵɵtextInterpolate2("", test_r4.failureCount, " failures \u2022 ", test_r4.failureRate.toFixed(1), "% fail rate");
96
- i0.ɵɵadvance(3);
97
- i0.ɵɵstyleProp("width", test_r4.failureRate, "%");
221
+ i0.ɵɵtextInterpolate(ctx_r2.FormatBucketDate(t_r11.timestamp));
222
+ i0.ɵɵadvance(2);
223
+ i0.ɵɵstyleProp("width", ctx_r2.CostBarPct(t_r11, trends_r12), "%");
224
+ i0.ɵɵadvance(2);
225
+ i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(7, 4, t_r11.totalCost, "1.2-2"), "");
98
226
  } }
99
- function TestingAnalyticsComponent_ForEmpty_31_Template(rf, ctx) { if (rf & 1) {
100
- i0.ɵɵelementStart(0, "div", 20);
101
- i0.ɵɵelement(1, "i", 46);
102
- i0.ɵɵelementStart(2, "p");
103
- i0.ɵɵtext(3, "No failing tests!");
227
+ function TestingAnalyticsComponent_Conditional_39_Conditional_1_Template(rf, ctx) { if (rf & 1) {
228
+ i0.ɵɵelementStart(0, "div", 32);
229
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_39_Conditional_1_For_2_Template, 8, 7, "div", 33, i0.ɵɵcomponentInstance().trackTrend, true);
230
+ i0.ɵɵelementEnd();
231
+ } if (rf & 2) {
232
+ const trends_r12 = i0.ɵɵnextContext();
233
+ i0.ɵɵadvance();
234
+ i0.ɵɵrepeater(trends_r12);
235
+ } }
236
+ function TestingAnalyticsComponent_Conditional_39_Template(rf, ctx) { if (rf & 1) {
237
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_39_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_39_Conditional_1_Template, 3, 0, "div", 32);
238
+ } if (rf & 2) {
239
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
240
+ } }
241
+ function TestingAnalyticsComponent_Conditional_49_Conditional_0_Template(rf, ctx) { if (rf & 1) {
242
+ i0.ɵɵelementStart(0, "div", 28);
243
+ i0.ɵɵelement(1, "i", 58);
244
+ i0.ɵɵelementStart(2, "span");
245
+ i0.ɵɵtext(3, "No failing tests");
104
246
  i0.ɵɵelementEnd()();
105
247
  } }
106
- function TestingAnalyticsComponent_For_40_Template(rf, ctx) { if (rf & 1) {
107
- i0.ɵɵelementStart(0, "div", 19)(1, "div", 58)(2, "div", 59);
108
- i0.ɵɵtext(3);
248
+ function TestingAnalyticsComponent_Conditional_49_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
249
+ i0.ɵɵelementStart(0, "div", 59)(1, "span", 60);
250
+ i0.ɵɵtext(2);
109
251
  i0.ɵɵelementEnd();
110
- i0.ɵɵelementStart(4, "div", 60);
111
- i0.ɵɵtext(5);
252
+ i0.ɵɵelementStart(3, "span", 61);
253
+ i0.ɵɵtext(4);
254
+ i0.ɵɵelementEnd();
255
+ i0.ɵɵelementStart(5, "span", 62);
256
+ i0.ɵɵtext(6);
257
+ i0.ɵɵpipe(7, "number");
112
258
  i0.ɵɵelementEnd()();
113
- i0.ɵɵelementStart(6, "div", 61)(7, "div", 64);
114
- i0.ɵɵtext(8);
115
- i0.ɵɵelementEnd()()();
116
259
  } if (rf & 2) {
117
- const test_r5 = ctx.$implicit;
118
- i0.ɵɵadvance(3);
119
- i0.ɵɵtextInterpolate(test_r5.testName);
260
+ const t_r13 = ctx.$implicit;
261
+ i0.ɵɵadvance();
262
+ i0.ɵɵproperty("title", t_r13.testName);
263
+ i0.ɵɵadvance();
264
+ i0.ɵɵtextInterpolate(t_r13.testName);
120
265
  i0.ɵɵadvance(2);
121
- i0.ɵɵtextInterpolate1("avg $", test_r5.avgCost.toFixed(4), "");
122
- i0.ɵɵadvance(3);
123
- i0.ɵɵtextInterpolate1("$", test_r5.totalCost.toFixed(2), "");
266
+ i0.ɵɵtextInterpolate(t_r13.failureCount);
267
+ i0.ɵɵadvance(2);
268
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(7, 4, t_r13.failureRate, "1.0-1"), "%");
124
269
  } }
125
- function TestingAnalyticsComponent_ForEmpty_41_Template(rf, ctx) { if (rf & 1) {
126
- i0.ɵɵelementStart(0, "div", 20);
127
- i0.ɵɵelement(1, "i", 21);
128
- i0.ɵɵelementStart(2, "p");
129
- i0.ɵɵtext(3, "No test data");
270
+ function TestingAnalyticsComponent_Conditional_49_Conditional_1_Template(rf, ctx) { if (rf & 1) {
271
+ i0.ɵɵelementStart(0, "div", 57);
272
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_49_Conditional_1_For_2_Template, 8, 7, "div", 59, _forTrack1);
273
+ i0.ɵɵelementEnd();
274
+ } if (rf & 2) {
275
+ const tests_r14 = i0.ɵɵnextContext();
276
+ i0.ɵɵadvance();
277
+ i0.ɵɵrepeater(tests_r14);
278
+ } }
279
+ function TestingAnalyticsComponent_Conditional_49_Template(rf, ctx) { if (rf & 1) {
280
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_49_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_49_Conditional_1_Template, 3, 0, "div", 57);
281
+ } if (rf & 2) {
282
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
283
+ } }
284
+ function TestingAnalyticsComponent_Conditional_55_Conditional_0_Template(rf, ctx) { if (rf & 1) {
285
+ i0.ɵɵelementStart(0, "div", 28);
286
+ i0.ɵɵelement(1, "i", 63);
287
+ i0.ɵɵelementStart(2, "span");
288
+ i0.ɵɵtext(3, "No duration data");
130
289
  i0.ɵɵelementEnd()();
131
290
  } }
132
- function TestingAnalyticsComponent_For_50_Template(rf, ctx) { if (rf & 1) {
133
- i0.ɵɵelementStart(0, "div", 19)(1, "div", 58)(2, "div", 59);
134
- i0.ɵɵtext(3);
291
+ function TestingAnalyticsComponent_Conditional_55_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
292
+ i0.ɵɵelementStart(0, "div", 59)(1, "span", 60);
293
+ i0.ɵɵtext(2);
135
294
  i0.ɵɵelementEnd();
136
- i0.ɵɵelementStart(4, "div", 60);
137
- i0.ɵɵtext(5);
295
+ i0.ɵɵelementStart(3, "span", 64);
296
+ i0.ɵɵtext(4);
297
+ i0.ɵɵelementEnd();
298
+ i0.ɵɵelementStart(5, "span", 62);
299
+ i0.ɵɵtext(6);
138
300
  i0.ɵɵelementEnd()();
139
- i0.ɵɵelementStart(6, "div", 61)(7, "div", 65);
140
- i0.ɵɵtext(8);
141
- i0.ɵɵelementEnd()()();
142
301
  } if (rf & 2) {
143
- const test_r6 = ctx.$implicit;
144
- const ctx_r2 = i0.ɵɵnextContext();
145
- i0.ɵɵadvance(3);
146
- i0.ɵɵtextInterpolate(test_r6.testName);
302
+ const t_r15 = ctx.$implicit;
303
+ const ctx_r2 = i0.ɵɵnextContext(3);
304
+ i0.ɵɵadvance();
305
+ i0.ɵɵproperty("title", t_r15.testName);
306
+ i0.ɵɵadvance();
307
+ i0.ɵɵtextInterpolate(t_r15.testName);
308
+ i0.ɵɵadvance(2);
309
+ i0.ɵɵtextInterpolate(ctx_r2.FormatDuration(t_r15.avgDuration));
310
+ i0.ɵɵadvance(2);
311
+ i0.ɵɵtextInterpolate1("max ", ctx_r2.FormatDuration(t_r15.maxDuration), "");
312
+ } }
313
+ function TestingAnalyticsComponent_Conditional_55_Conditional_1_Template(rf, ctx) { if (rf & 1) {
314
+ i0.ɵɵelementStart(0, "div", 57);
315
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_55_Conditional_1_For_2_Template, 7, 4, "div", 59, _forTrack1);
316
+ i0.ɵɵelementEnd();
317
+ } if (rf & 2) {
318
+ const tests_r16 = i0.ɵɵnextContext();
319
+ i0.ɵɵadvance();
320
+ i0.ɵɵrepeater(tests_r16);
321
+ } }
322
+ function TestingAnalyticsComponent_Conditional_55_Template(rf, ctx) { if (rf & 1) {
323
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_55_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_55_Conditional_1_Template, 3, 0, "div", 57);
324
+ } if (rf & 2) {
325
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
326
+ } }
327
+ function TestingAnalyticsComponent_Conditional_61_Conditional_0_Template(rf, ctx) { if (rf & 1) {
328
+ i0.ɵɵelementStart(0, "div", 28);
329
+ i0.ɵɵelement(1, "i", 16);
330
+ i0.ɵɵelementStart(2, "span");
331
+ i0.ɵɵtext(3, "No cost data");
332
+ i0.ɵɵelementEnd()();
333
+ } }
334
+ function TestingAnalyticsComponent_Conditional_61_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
335
+ i0.ɵɵelementStart(0, "div", 59)(1, "span", 60);
336
+ i0.ɵɵtext(2);
337
+ i0.ɵɵelementEnd();
338
+ i0.ɵɵelementStart(3, "span", 64);
339
+ i0.ɵɵtext(4);
340
+ i0.ɵɵpipe(5, "number");
341
+ i0.ɵɵelementEnd();
342
+ i0.ɵɵelementStart(6, "span", 62);
343
+ i0.ɵɵtext(7);
344
+ i0.ɵɵpipe(8, "number");
345
+ i0.ɵɵelementEnd()();
346
+ } if (rf & 2) {
347
+ const t_r17 = ctx.$implicit;
348
+ i0.ɵɵadvance();
349
+ i0.ɵɵproperty("title", t_r17.testName);
350
+ i0.ɵɵadvance();
351
+ i0.ɵɵtextInterpolate(t_r17.testName);
147
352
  i0.ɵɵadvance(2);
148
- i0.ɵɵtextInterpolate1("avg ", ctx_r2.formatDuration(test_r6.avgDuration), "");
353
+ i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(5, 4, t_r17.totalCost, "1.2-2"), "");
149
354
  i0.ɵɵadvance(3);
150
- i0.ɵɵtextInterpolate(ctx_r2.formatDuration(test_r6.maxDuration));
355
+ i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(8, 7, t_r17.avgCost, "1.4-4"), "/run");
356
+ } }
357
+ function TestingAnalyticsComponent_Conditional_61_Conditional_1_Template(rf, ctx) { if (rf & 1) {
358
+ i0.ɵɵelementStart(0, "div", 57);
359
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_61_Conditional_1_For_2_Template, 9, 10, "div", 59, _forTrack1);
360
+ i0.ɵɵelementEnd();
361
+ } if (rf & 2) {
362
+ const tests_r18 = i0.ɵɵnextContext();
363
+ i0.ɵɵadvance();
364
+ i0.ɵɵrepeater(tests_r18);
151
365
  } }
152
- function TestingAnalyticsComponent_ForEmpty_51_Template(rf, ctx) { if (rf & 1) {
153
- i0.ɵɵelementStart(0, "div", 20);
154
- i0.ɵɵelement(1, "i", 22);
155
- i0.ɵɵelementStart(2, "p");
156
- i0.ɵɵtext(3, "No test data");
366
+ function TestingAnalyticsComponent_Conditional_61_Template(rf, ctx) { if (rf & 1) {
367
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_61_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_61_Conditional_1_Template, 3, 0, "div", 57);
368
+ } if (rf & 2) {
369
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
370
+ } }
371
+ function TestingAnalyticsComponent_Conditional_67_Conditional_0_Template(rf, ctx) { if (rf & 1) {
372
+ i0.ɵɵelementStart(0, "div", 28);
373
+ i0.ɵɵelement(1, "i", 66);
374
+ i0.ɵɵelementStart(2, "span");
375
+ i0.ɵɵtext(3, "No score data available");
157
376
  i0.ɵɵelementEnd()();
158
377
  } }
159
- function TestingAnalyticsComponent_For_61_Template(rf, ctx) { if (rf & 1) {
160
- i0.ɵɵelementStart(0, "div", 25)(1, "div", 66);
378
+ function TestingAnalyticsComponent_Conditional_67_Conditional_1_For_2_Template(rf, ctx) { if (rf & 1) {
379
+ i0.ɵɵelementStart(0, "div", 67)(1, "span", 68);
161
380
  i0.ɵɵtext(2);
162
381
  i0.ɵɵelementEnd();
163
- i0.ɵɵelementStart(3, "div", 67);
164
- i0.ɵɵelement(4, "div", 68);
165
- i0.ɵɵelementStart(5, "span", 69);
382
+ i0.ɵɵelementStart(3, "div", 69);
383
+ i0.ɵɵelement(4, "div", 70);
384
+ i0.ɵɵelementEnd();
385
+ i0.ɵɵelementStart(5, "span", 71);
166
386
  i0.ɵɵtext(6);
167
- i0.ɵɵelementEnd()();
168
- i0.ɵɵelementStart(7, "div", 70);
387
+ i0.ɵɵelementEnd();
388
+ i0.ɵɵelementStart(7, "span", 72);
169
389
  i0.ɵɵtext(8);
390
+ i0.ɵɵpipe(9, "number");
170
391
  i0.ɵɵelementEnd()();
171
392
  } if (rf & 2) {
172
- const bucket_r7 = ctx.$implicit;
393
+ const b_r19 = ctx.$implicit;
394
+ i0.ɵɵadvance(2);
395
+ i0.ɵɵtextInterpolate(b_r19.label);
173
396
  i0.ɵɵadvance(2);
174
- i0.ɵɵtextInterpolate(bucket_r7.label);
397
+ i0.ɵɵstyleProp("width", b_r19.percentage, "%")("background", b_r19.color);
175
398
  i0.ɵɵadvance(2);
176
- i0.ɵɵclassMap(bucket_r7.class);
177
- i0.ɵɵstyleProp("width", bucket_r7.percentage, "%");
399
+ i0.ɵɵtextInterpolate(b_r19.count);
400
+ i0.ɵɵadvance(2);
401
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(9, 7, b_r19.percentage, "1.0-1"), "%");
402
+ } }
403
+ function TestingAnalyticsComponent_Conditional_67_Conditional_1_Template(rf, ctx) { if (rf & 1) {
404
+ i0.ɵɵelementStart(0, "div", 65);
405
+ i0.ɵɵrepeaterCreate(1, TestingAnalyticsComponent_Conditional_67_Conditional_1_For_2_Template, 10, 10, "div", 67, _forTrack2);
406
+ i0.ɵɵelementEnd();
407
+ } if (rf & 2) {
408
+ const buckets_r20 = i0.ɵɵnextContext();
409
+ i0.ɵɵadvance();
410
+ i0.ɵɵrepeater(buckets_r20);
411
+ } }
412
+ function TestingAnalyticsComponent_Conditional_67_Template(rf, ctx) { if (rf & 1) {
413
+ i0.ɵɵtemplate(0, TestingAnalyticsComponent_Conditional_67_Conditional_0_Template, 4, 0, "div", 28)(1, TestingAnalyticsComponent_Conditional_67_Conditional_1_Template, 3, 0, "div", 65);
414
+ } if (rf & 2) {
415
+ i0.ɵɵconditional(ctx.length === 0 ? 0 : 1);
416
+ } }
417
+ function TestingAnalyticsComponent_Conditional_75_Template(rf, ctx) { if (rf & 1) {
418
+ i0.ɵɵelementStart(0, "div", 28);
419
+ i0.ɵɵelement(1, "i", 73);
420
+ i0.ɵɵelementStart(2, "span");
421
+ i0.ɵɵtext(3, "Loading versions...");
422
+ i0.ɵɵelementEnd()();
423
+ } }
424
+ function TestingAnalyticsComponent_Conditional_76_Template(rf, ctx) { if (rf & 1) {
425
+ i0.ɵɵelementStart(0, "div", 28);
426
+ i0.ɵɵelement(1, "i", 74);
427
+ i0.ɵɵelementStart(2, "span");
428
+ i0.ɵɵtext(3, "No version data available");
429
+ i0.ɵɵelementEnd()();
430
+ } }
431
+ function TestingAnalyticsComponent_Conditional_77_For_16_Conditional_15_Conditional_1_Template(rf, ctx) { if (rf & 1) {
432
+ i0.ɵɵelement(0, "i", 81);
433
+ } }
434
+ function TestingAnalyticsComponent_Conditional_77_For_16_Conditional_15_Conditional_2_Template(rf, ctx) { if (rf & 1) {
435
+ i0.ɵɵelement(0, "i", 82);
436
+ } }
437
+ function TestingAnalyticsComponent_Conditional_77_For_16_Conditional_15_Template(rf, ctx) { if (rf & 1) {
438
+ i0.ɵɵelementStart(0, "span", 80);
439
+ i0.ɵɵtemplate(1, TestingAnalyticsComponent_Conditional_77_For_16_Conditional_15_Conditional_1_Template, 1, 0, "i", 81)(2, TestingAnalyticsComponent_Conditional_77_For_16_Conditional_15_Conditional_2_Template, 1, 0, "i", 82);
440
+ i0.ɵɵtext(3);
441
+ i0.ɵɵpipe(4, "number");
442
+ i0.ɵɵelementEnd();
443
+ } if (rf & 2) {
444
+ const row_r21 = i0.ɵɵnextContext().$implicit;
445
+ const ctx_r2 = i0.ɵɵnextContext(2);
446
+ i0.ɵɵclassProp("delta-up", row_r21.passRateDelta > 0)("delta-down", row_r21.passRateDelta < 0)("delta-neutral", row_r21.passRateDelta === 0);
447
+ i0.ɵɵadvance();
448
+ i0.ɵɵconditional(row_r21.passRateDelta > 0 ? 1 : row_r21.passRateDelta < 0 ? 2 : -1);
178
449
  i0.ɵɵadvance(2);
179
- i0.ɵɵtextInterpolate(bucket_r7.count);
450
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(4, 8, ctx_r2.AbsNum(row_r21.passRateDelta), "1.1-1"), "% ");
451
+ } }
452
+ function TestingAnalyticsComponent_Conditional_77_For_16_Conditional_20_Conditional_1_Template(rf, ctx) { if (rf & 1) {
453
+ i0.ɵɵelement(0, "i", 81);
454
+ } }
455
+ function TestingAnalyticsComponent_Conditional_77_For_16_Conditional_20_Conditional_2_Template(rf, ctx) { if (rf & 1) {
456
+ i0.ɵɵelement(0, "i", 82);
457
+ } }
458
+ function TestingAnalyticsComponent_Conditional_77_For_16_Conditional_20_Template(rf, ctx) { if (rf & 1) {
459
+ i0.ɵɵelementStart(0, "span", 80);
460
+ i0.ɵɵtemplate(1, TestingAnalyticsComponent_Conditional_77_For_16_Conditional_20_Conditional_1_Template, 1, 0, "i", 81)(2, TestingAnalyticsComponent_Conditional_77_For_16_Conditional_20_Conditional_2_Template, 1, 0, "i", 82);
461
+ i0.ɵɵtext(3);
462
+ i0.ɵɵpipe(4, "number");
463
+ i0.ɵɵelementEnd();
464
+ } if (rf & 2) {
465
+ const row_r21 = i0.ɵɵnextContext().$implicit;
466
+ const ctx_r2 = i0.ɵɵnextContext(2);
467
+ i0.ɵɵclassProp("delta-up", row_r21.costDelta < 0)("delta-down", row_r21.costDelta > 0)("delta-neutral", row_r21.costDelta === 0);
468
+ i0.ɵɵadvance();
469
+ i0.ɵɵconditional(row_r21.costDelta > 0 ? 1 : row_r21.costDelta < 0 ? 2 : -1);
180
470
  i0.ɵɵadvance(2);
181
- i0.ɵɵtextInterpolate1("", bucket_r7.percentage.toFixed(1), "%");
471
+ i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(4, 8, ctx_r2.AbsNum(row_r21.costDelta), "1.1-1"), "% ");
182
472
  } }
473
+ function TestingAnalyticsComponent_Conditional_77_For_16_Template(rf, ctx) { if (rf & 1) {
474
+ i0.ɵɵelementStart(0, "tr")(1, "td")(2, "span", 77);
475
+ i0.ɵɵtext(3);
476
+ i0.ɵɵelementEnd();
477
+ i0.ɵɵelementStart(4, "span", 78);
478
+ i0.ɵɵtext(5);
479
+ i0.ɵɵelementEnd()();
480
+ i0.ɵɵelementStart(6, "td");
481
+ i0.ɵɵtext(7);
482
+ i0.ɵɵpipe(8, "date");
483
+ i0.ɵɵelementEnd();
484
+ i0.ɵɵelementStart(9, "td", 76);
485
+ i0.ɵɵtext(10);
486
+ i0.ɵɵelementEnd();
487
+ i0.ɵɵelementStart(11, "td", 76)(12, "span");
488
+ i0.ɵɵtext(13);
489
+ i0.ɵɵpipe(14, "number");
490
+ i0.ɵɵelementEnd();
491
+ i0.ɵɵtemplate(15, TestingAnalyticsComponent_Conditional_77_For_16_Conditional_15_Template, 5, 11, "span", 79);
492
+ i0.ɵɵelementEnd();
493
+ i0.ɵɵelementStart(16, "td", 76)(17, "span");
494
+ i0.ɵɵtext(18);
495
+ i0.ɵɵpipe(19, "number");
496
+ i0.ɵɵelementEnd();
497
+ i0.ɵɵtemplate(20, TestingAnalyticsComponent_Conditional_77_For_16_Conditional_20_Template, 5, 11, "span", 79);
498
+ i0.ɵɵelementEnd()();
499
+ } if (rf & 2) {
500
+ const row_r21 = ctx.$implicit;
501
+ i0.ɵɵadvance(3);
502
+ i0.ɵɵtextInterpolate(row_r21.gitCommitShort);
503
+ i0.ɵɵadvance(2);
504
+ i0.ɵɵtextInterpolate(row_r21.agentVersion);
505
+ i0.ɵɵadvance(2);
506
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(8, 8, row_r21.date, "mediumDate"));
507
+ i0.ɵɵadvance(3);
508
+ i0.ɵɵtextInterpolate(row_r21.totalTests);
509
+ i0.ɵɵadvance(3);
510
+ i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(14, 11, row_r21.passRate, "1.1-1"), "%");
511
+ i0.ɵɵadvance(2);
512
+ i0.ɵɵconditional(row_r21.passRateDelta !== null ? 15 : -1);
513
+ i0.ɵɵadvance(3);
514
+ i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(19, 14, row_r21.totalCost, "1.2-2"), "");
515
+ i0.ɵɵadvance(2);
516
+ i0.ɵɵconditional(row_r21.costDelta !== null ? 20 : -1);
517
+ } }
518
+ function TestingAnalyticsComponent_Conditional_77_Template(rf, ctx) { if (rf & 1) {
519
+ i0.ɵɵelementStart(0, "div", 29)(1, "table", 75)(2, "thead")(3, "tr")(4, "th");
520
+ i0.ɵɵtext(5, "Version");
521
+ i0.ɵɵelementEnd();
522
+ i0.ɵɵelementStart(6, "th");
523
+ i0.ɵɵtext(7, "Date");
524
+ i0.ɵɵelementEnd();
525
+ i0.ɵɵelementStart(8, "th", 76);
526
+ i0.ɵɵtext(9, "Tests");
527
+ i0.ɵɵelementEnd();
528
+ i0.ɵɵelementStart(10, "th", 76);
529
+ i0.ɵɵtext(11, "Pass Rate");
530
+ i0.ɵɵelementEnd();
531
+ i0.ɵɵelementStart(12, "th", 76);
532
+ i0.ɵɵtext(13, "Cost");
533
+ i0.ɵɵelementEnd()()();
534
+ i0.ɵɵelementStart(14, "tbody");
535
+ i0.ɵɵrepeaterCreate(15, TestingAnalyticsComponent_Conditional_77_For_16_Template, 21, 17, "tr", null, _forTrack3);
536
+ i0.ɵɵelementEnd()()();
537
+ } if (rf & 2) {
538
+ const ctx_r2 = i0.ɵɵnextContext();
539
+ i0.ɵɵadvance(15);
540
+ i0.ɵɵrepeater(ctx_r2.VersionRows);
541
+ } }
542
+ // ---------------------------------------------------------------------------
543
+ // Component
544
+ // ---------------------------------------------------------------------------
183
545
  export class TestingAnalyticsComponent {
184
546
  instrumentationService;
185
547
  cdr;
186
- initialState;
548
+ // ------- Inputs / Outputs -------
549
+ initialState = null;
187
550
  stateChange = new EventEmitter();
188
- destroy$ = new Subject();
189
- selectedTimeRange = 30;
190
- timeRanges = [
551
+ // ------- Public state -------
552
+ TimeRanges = [
191
553
  { label: '7 Days', days: 7 },
192
554
  { label: '30 Days', days: 30 },
193
555
  { label: '90 Days', days: 90 }
194
556
  ];
195
- trends$;
196
- topFailingTests$;
197
- expensiveTests$;
198
- slowestTests$;
199
- scoreDistribution$;
200
- totalCost$;
201
- avgCostPerTest$;
202
- avgCostPerRun$;
203
- projectedMonthlyCost$;
204
- avgExecutionTime$;
205
- throughput$;
206
- reliabilityScore$;
557
+ SelectedDays = 30;
558
+ VersionRows = [];
559
+ IsLoadingVersions = false;
560
+ // ------- Observables -------
561
+ DisplayTrends$;
562
+ PassRateTrendDirection$;
563
+ TopFailingTests$;
564
+ SlowestTests$;
565
+ MostExpensiveTests$;
566
+ ScoreDistribution$;
567
+ // ------- Private -------
568
+ destroy$ = new Subject();
569
+ maxTrendRuns = 0; // cached for cost bar scaling
207
570
  constructor(instrumentationService, cdr) {
208
571
  this.instrumentationService = instrumentationService;
209
572
  this.cdr = cdr;
210
573
  }
574
+ // ===================================================================
575
+ // Lifecycle
576
+ // ===================================================================
211
577
  ngOnInit() {
578
+ this.restoreState();
212
579
  this.setupObservables();
213
- if (this.initialState?.selectedTimeRange) {
214
- this.selectedTimeRange = this.initialState.selectedTimeRange;
215
- }
580
+ this.loadVersionMetrics();
216
581
  }
217
582
  ngOnDestroy() {
218
583
  this.destroy$.next();
219
584
  this.destroy$.complete();
220
585
  }
221
- setupObservables() {
222
- this.trends$ = this.instrumentationService.trends$;
223
- const analytics$ = this.instrumentationService.analytics$;
224
- this.topFailingTests$ = analytics$.pipe(map(analytics => analytics.topFailingTests), takeUntil(this.destroy$));
225
- this.expensiveTests$ = analytics$.pipe(map(analytics => analytics.mostExpensiveTests), takeUntil(this.destroy$));
226
- this.slowestTests$ = analytics$.pipe(map(analytics => analytics.slowestTests), takeUntil(this.destroy$));
227
- this.scoreDistribution$ = this.instrumentationService.testRuns$.pipe(map(runs => {
228
- const buckets = [
229
- { label: 'Excellent (≥0.9)', min: 0.9, max: 1.0, class: 'excellent', count: 0 },
230
- { label: 'Good (0.8-0.9)', min: 0.8, max: 0.9, class: 'good', count: 0 },
231
- { label: 'Fair (0.6-0.8)', min: 0.6, max: 0.8, class: 'fair', count: 0 },
232
- { label: 'Poor (0.4-0.6)', min: 0.4, max: 0.6, class: 'poor', count: 0 },
233
- { label: 'Fail (<0.4)', min: 0, max: 0.4, class: 'fail', count: 0 }
234
- ];
235
- runs.forEach(run => {
236
- const bucket = buckets.find(b => run.score >= b.min && run.score < b.max);
237
- if (bucket)
238
- bucket.count++;
239
- });
240
- const total = runs.length || 1;
241
- return buckets.map(b => ({
242
- ...b,
243
- percentage: (b.count / total) * 100
244
- }));
245
- }), takeUntil(this.destroy$));
246
- this.totalCost$ = this.instrumentationService.testRuns$.pipe(map(runs => runs.reduce((sum, r) => sum + r.cost, 0)));
247
- this.avgCostPerTest$ = this.instrumentationService.testRuns$.pipe(map(runs => {
248
- if (runs.length === 0)
249
- return 0;
250
- const totalCost = runs.reduce((sum, r) => sum + r.cost, 0);
251
- const uniqueTests = new Set(runs.map(r => r.testName)).size;
252
- return uniqueTests > 0 ? totalCost / uniqueTests : 0;
253
- }));
254
- this.avgCostPerRun$ = this.instrumentationService.testRuns$.pipe(map(runs => {
255
- if (runs.length === 0)
256
- return 0;
257
- const totalCost = runs.reduce((sum, r) => sum + r.cost, 0);
258
- return totalCost / runs.length;
259
- }));
260
- this.projectedMonthlyCost$ = this.instrumentationService.testRuns$.pipe(map(runs => {
261
- if (runs.length === 0)
262
- return 0;
263
- const totalCost = runs.reduce((sum, r) => sum + r.cost, 0);
264
- const daysInPeriod = this.selectedTimeRange;
265
- return (totalCost / daysInPeriod) * 30;
266
- }));
267
- this.avgExecutionTime$ = this.instrumentationService.testRuns$.pipe(map(runs => {
268
- if (runs.length === 0)
269
- return 0;
270
- const totalDuration = runs.reduce((sum, r) => sum + r.duration, 0);
271
- return totalDuration / runs.length;
272
- }));
273
- this.throughput$ = this.instrumentationService.testRuns$.pipe(map(runs => {
274
- const daysInPeriod = this.selectedTimeRange;
275
- return runs.length / daysInPeriod;
276
- }));
277
- this.reliabilityScore$ = this.instrumentationService.testRuns$.pipe(map(runs => {
278
- if (runs.length === 0)
279
- return 0;
280
- const passed = runs.filter(r => r.status === 'Passed').length;
281
- return (passed / runs.length) * 100;
282
- }));
283
- }
284
- onTimeRangeChange() {
285
- const days = this.selectedTimeRange;
586
+ // ===================================================================
587
+ // Public methods (template-bound)
588
+ // ===================================================================
589
+ OnTimeRangeSelect(days) {
590
+ this.SelectedDays = days;
286
591
  const end = new Date();
287
592
  const start = new Date(end.getTime() - days * 24 * 60 * 60 * 1000);
288
593
  this.instrumentationService.setDateRange(start, end);
289
- this.emitStateChange();
594
+ this.loadVersionMetrics();
595
+ this.emitState();
290
596
  }
291
- refresh() {
597
+ Refresh() {
292
598
  this.instrumentationService.refresh();
599
+ this.loadVersionMetrics();
293
600
  }
294
- formatDuration(milliseconds) {
295
- if (milliseconds < 1000)
296
- return `${milliseconds}ms`;
297
- const seconds = Math.floor(milliseconds / 1000);
298
- const minutes = Math.floor(seconds / 60);
299
- if (minutes > 0)
300
- return `${minutes}m ${seconds % 60}s`;
301
- return `${seconds}s`;
601
+ // -- Trend helpers (called from template) --
602
+ PassRate(t) {
603
+ return t.totalRuns === 0 ? 0 : (t.passed / t.totalRuns) * 100;
302
604
  }
303
- calculatePassRate(trend) {
304
- if (trend.totalRuns === 0)
305
- return 0;
306
- return (trend.passed / trend.totalRuns) * 100;
605
+ PassRateClass(t) {
606
+ const rate = this.PassRate(t);
607
+ if (rate >= 80)
608
+ return 'text-pass-good';
609
+ if (rate >= 60)
610
+ return 'text-pass-warn';
611
+ return 'text-pass-bad';
307
612
  }
308
- calculateFailRate(trend) {
309
- if (trend.totalRuns === 0)
613
+ SegmentPct(t, segment) {
614
+ if (t.totalRuns === 0)
310
615
  return 0;
311
- return (trend.failed / trend.totalRuns) * 100;
616
+ const value = segment === 'passed' ? t.passed : segment === 'failed' ? t.failed : t.skipped;
617
+ return (value / t.totalRuns) * 100;
618
+ }
619
+ ScoreBarClass(score) {
620
+ if (score >= 0.9)
621
+ return 'bar-fill score-excellent';
622
+ if (score >= 0.7)
623
+ return 'bar-fill score-good';
624
+ if (score >= 0.5)
625
+ return 'bar-fill score-fair';
626
+ return 'bar-fill score-poor';
627
+ }
628
+ ScoreTextClass(score) {
629
+ if (score >= 0.9)
630
+ return 'text-excellent';
631
+ if (score >= 0.7)
632
+ return 'text-good';
633
+ if (score >= 0.5)
634
+ return 'text-fair';
635
+ return 'text-poor';
636
+ }
637
+ CostBarPct(t, all) {
638
+ const maxCost = Math.max(...all.map(x => x.totalCost), 0.01);
639
+ return (t.totalCost / maxCost) * 100;
640
+ }
641
+ FormatBucketDate(d) {
642
+ const dt = d instanceof Date ? d : new Date(d);
643
+ return `${dt.getMonth() + 1}/${dt.getDate()}`;
644
+ }
645
+ FormatDuration(ms) {
646
+ if (ms < 1000)
647
+ return `${Math.round(ms)}ms`;
648
+ const s = Math.floor(ms / 1000);
649
+ const m = Math.floor(s / 60);
650
+ if (m > 0)
651
+ return `${m}m ${s % 60}s`;
652
+ return `${s}s`;
653
+ }
654
+ AbsNum(n) {
655
+ return Math.abs(n);
656
+ }
657
+ /** trackBy for trend @for loops */
658
+ trackTrend(index, t) {
659
+ return index;
660
+ }
661
+ // ===================================================================
662
+ // Private helpers
663
+ // ===================================================================
664
+ restoreState() {
665
+ if (this.initialState != null) {
666
+ if (typeof this.initialState['selectedDays'] === 'number') {
667
+ this.SelectedDays = this.initialState['selectedDays'];
668
+ }
669
+ }
670
+ // Apply initial date range
671
+ const end = new Date();
672
+ const start = new Date(end.getTime() - this.SelectedDays * 24 * 60 * 60 * 1000);
673
+ this.instrumentationService.setDateRange(start, end);
312
674
  }
313
- calculateSkipRate(trend) {
314
- if (trend.totalRuns === 0)
675
+ setupObservables() {
676
+ // Limit trends to last 10 buckets for readability
677
+ this.DisplayTrends$ = this.instrumentationService.trends$.pipe(map(trends => trends.slice(-10)), shareReplay(1));
678
+ this.PassRateTrendDirection$ = this.DisplayTrends$.pipe(map(trends => this.calculateTrendDirection(trends)), shareReplay(1));
679
+ const analytics$ = this.instrumentationService.analytics$;
680
+ this.TopFailingTests$ = analytics$.pipe(map(a => a.topFailingTests.slice(0, 5)), shareReplay(1));
681
+ this.SlowestTests$ = analytics$.pipe(map(a => a.slowestTests.slice(0, 5)), shareReplay(1));
682
+ this.MostExpensiveTests$ = analytics$.pipe(map(a => a.mostExpensiveTests.slice(0, 5)), shareReplay(1));
683
+ this.ScoreDistribution$ = this.instrumentationService.testRuns$.pipe(map(runs => this.buildScoreDistribution(runs)), shareReplay(1));
684
+ }
685
+ calculateTrendDirection(trends) {
686
+ if (trends.length < 2)
687
+ return 'stable';
688
+ const recentHalf = trends.slice(Math.floor(trends.length / 2));
689
+ const olderHalf = trends.slice(0, Math.floor(trends.length / 2));
690
+ const avgRecent = this.averagePassRate(recentHalf);
691
+ const avgOlder = this.averagePassRate(olderHalf);
692
+ const diff = avgRecent - avgOlder;
693
+ if (diff > 2)
694
+ return 'up';
695
+ if (diff < -2)
696
+ return 'down';
697
+ return 'stable';
698
+ }
699
+ averagePassRate(trends) {
700
+ const withRuns = trends.filter(t => t.totalRuns > 0);
701
+ if (withRuns.length === 0)
315
702
  return 0;
316
- return (trend.skipped / trend.totalRuns) * 100;
703
+ return withRuns.reduce((s, t) => s + (t.passed / t.totalRuns) * 100, 0) / withRuns.length;
704
+ }
705
+ buildScoreDistribution(runs) {
706
+ const defs = [
707
+ { label: 'Excellent (>=0.9)', color: '#43a047', min: 0.9, max: 1.01 },
708
+ { label: 'Good (0.7-0.89)', color: '#1e88e5', min: 0.7, max: 0.9 },
709
+ { label: 'Fair (0.5-0.69)', color: '#f57c00', min: 0.5, max: 0.7 },
710
+ { label: 'Poor (0.3-0.49)', color: '#e64a19', min: 0.3, max: 0.5 },
711
+ { label: 'Fail (<0.3)', color: '#e53935', min: 0, max: 0.3 }
712
+ ];
713
+ const total = runs.length || 1;
714
+ return defs.map(d => {
715
+ const count = runs.filter(r => r.score >= d.min && r.score < d.max).length;
716
+ return {
717
+ label: d.label,
718
+ color: d.color,
719
+ count,
720
+ percentage: (count / total) * 100
721
+ };
722
+ });
317
723
  }
318
- emitStateChange() {
724
+ async loadVersionMetrics() {
725
+ this.IsLoadingVersions = true;
726
+ this.cdr.markForCheck();
727
+ try {
728
+ const metrics = await this.instrumentationService.getVersionMetrics();
729
+ this.VersionRows = this.buildVersionRows(metrics);
730
+ }
731
+ catch {
732
+ this.VersionRows = [];
733
+ }
734
+ finally {
735
+ this.IsLoadingVersions = false;
736
+ this.cdr.markForCheck();
737
+ }
738
+ }
739
+ buildVersionRows(metrics) {
740
+ // metrics are already sorted newest-first by the service
741
+ return metrics.map((m, i) => {
742
+ const prev = i < metrics.length - 1 ? metrics[i + 1] : null;
743
+ let passRateDelta = null;
744
+ let costDelta = null;
745
+ if (prev) {
746
+ passRateDelta = m.passRate - prev.passRate;
747
+ costDelta = prev.totalCost !== 0
748
+ ? ((m.totalCost - prev.totalCost) / prev.totalCost) * 100
749
+ : null;
750
+ }
751
+ return {
752
+ version: m.version,
753
+ gitCommitShort: m.gitCommit.length > 7 ? m.gitCommit.substring(0, 7) : m.gitCommit,
754
+ agentVersion: m.agentVersion,
755
+ date: m.runDate,
756
+ totalTests: m.totalTests,
757
+ passRate: m.passRate,
758
+ totalCost: m.totalCost,
759
+ passRateDelta,
760
+ costDelta
761
+ };
762
+ });
763
+ }
764
+ emitState() {
319
765
  this.stateChange.emit({
320
- selectedTimeRange: this.selectedTimeRange
766
+ selectedDays: this.SelectedDays
321
767
  });
322
768
  }
323
769
  static ɵfac = function TestingAnalyticsComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TestingAnalyticsComponent)(i0.ɵɵdirectiveInject(i1.TestingInstrumentationService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
324
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingAnalyticsComponent, selectors: [["app-testing-analytics"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, decls: 134, vars: 57, consts: [[1, "testing-analytics"], [1, "analytics-header"], [1, "header-left"], [1, "fa-solid", "fa-chart-bar"], [1, "header-actions"], [1, "time-range-select", 3, "ngModelChange", "change", "ngModel"], [3, "value"], [1, "action-btn", 3, "click"], [1, "fa-solid", "fa-refresh"], [1, "analytics-section"], [1, "section-header"], [1, "fa-solid", "fa-chart-line"], [1, "trends-grid"], [1, "trend-card"], [1, "analytics-grid"], [1, "analytics-card"], [1, "card-header"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "card-content"], [1, "analytics-row"], [1, "empty-state"], [1, "fa-solid", "fa-dollar-sign"], [1, "fa-solid", "fa-clock"], [1, "fa-solid", "fa-chart-pie"], [1, "score-distribution"], [1, "score-bucket"], [1, "fa-solid", "fa-wallet"], [1, "cost-summary"], [1, "cost-item", "total"], [1, "cost-label"], [1, "cost-amount"], [1, "cost-item"], [1, "fa-solid", "fa-gauge"], [1, "performance-metrics"], [1, "perf-metric"], [1, "perf-icon"], [1, "fa-solid", "fa-bolt"], [1, "perf-content"], [1, "perf-value"], [1, "perf-label"], [1, "fa-solid", "fa-check-double"], [1, "trend-header"], [1, "trend-date"], [1, "trend-total"], [1, "trend-metrics"], [1, "metric", "passed"], [1, "fa-solid", "fa-check-circle"], [1, "metric", "failed"], [1, "fa-solid", "fa-times-circle"], [1, "metric", "skipped"], [1, "fa-solid", "fa-minus-circle"], [1, "trend-chart"], [1, "chart-bar", "passed"], [1, "chart-bar", "failed"], [1, "chart-bar", "skipped"], [1, "trend-footer"], [1, "pass-rate"], [1, "cost"], [1, "row-info"], [1, "row-name"], [1, "row-meta"], [1, "row-metrics"], [1, "fail-bar-container"], [1, "fail-bar"], [1, "cost-value"], [1, "duration-value"], [1, "bucket-label"], [1, "bucket-bar-container"], [1, "bucket-bar"], [1, "bucket-count"], [1, "bucket-percentage"]], template: function TestingAnalyticsComponent_Template(rf, ctx) { if (rf & 1) {
770
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingAnalyticsComponent, selectors: [["app-testing-analytics"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, decls: 78, vars: 25, consts: [[1, "testing-analytics"], [1, "page-header"], [1, "header-left"], [1, "fa-solid", "fa-chart-bar"], [1, "header-actions"], [1, "time-range-pills"], [1, "pill", 3, "active"], [1, "refresh-btn", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], [1, "section-title"], [1, "fa-solid", "fa-chart-line"], [1, "trend-grid"], [1, "card", "trend-card"], [1, "fa-solid", "fa-check-double"], [1, "fa-solid", "fa-layer-group"], [1, "fa-solid", "fa-star-half-stroke"], [1, "fa-solid", "fa-dollar-sign"], [1, "fa-solid", "fa-magnifying-glass-chart"], [1, "insights-grid"], [1, "card", "insight-card"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "fa-solid", "fa-clock"], [1, "fa-solid", "fa-coins"], [1, "fa-solid", "fa-chart-pie"], [1, "card", "distribution-card"], [1, "fa-solid", "fa-code-compare"], [1, "section-subtitle"], [1, "card", "version-card"], [1, "empty-mini"], [1, "version-table-wrap"], [1, "pill", 3, "click"], [1, "fa-solid", "fa-chart-area"], [1, "bar-chart"], [1, "bar-row"], [1, "trend-direction"], [1, "bar-label"], [1, "bar-track"], [1, "bar-fill", "pass-rate-bar"], [1, "bar-value"], [1, "fa-solid", "fa-arrow-trend-up", "trend-up"], [1, "trend-up"], [1, "fa-solid", "fa-arrow-trend-down", "trend-down"], [1, "trend-down"], [1, "fa-solid", "fa-minus", "trend-neutral"], [1, "trend-neutral"], [1, "legend-row"], [1, "legend-dot", "passed-seg"], [1, "legend-dot", "failed-seg"], [1, "legend-dot", "skipped-seg"], [1, "bar-track", "stacked"], [1, "bar-segment", "passed-seg"], [1, "bar-segment", "failed-seg"], [1, "bar-segment", "skipped-seg"], [1, "fa-solid", "fa-star"], [1, "bar-fill"], [1, "fa-solid", "fa-wallet"], [1, "bar-fill", "cost-bar"], [1, "insight-list"], [1, "fa-solid", "fa-circle-check"], [1, "insight-row"], [1, "insight-name", 3, "title"], [1, "badge-fail"], [1, "insight-secondary"], [1, "fa-solid", "fa-bolt"], [1, "insight-primary"], [1, "distribution-bars"], [1, "fa-solid", "fa-chart-simple"], [1, "dist-row"], [1, "dist-label"], [1, "dist-track"], [1, "dist-fill"], [1, "dist-count"], [1, "dist-pct"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-code-branch"], [1, "version-table"], [1, "num"], [1, "version-label"], [1, "version-agent"], [1, "delta", 3, "delta-up", "delta-down", "delta-neutral"], [1, "delta"], [1, "fa-solid", "fa-arrow-up"], [1, "fa-solid", "fa-arrow-down"]], template: function TestingAnalyticsComponent_Template(rf, ctx) { if (rf & 1) {
325
771
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "h2");
326
772
  i0.ɵɵelement(4, "i", 3);
327
773
  i0.ɵɵtext(5, " Testing Analytics ");
328
774
  i0.ɵɵelementEnd()();
329
- i0.ɵɵelementStart(6, "div", 4)(7, "select", 5);
330
- i0.ɵɵtwoWayListener("ngModelChange", function TestingAnalyticsComponent_Template_select_ngModelChange_7_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.selectedTimeRange, $event) || (ctx.selectedTimeRange = $event); return $event; });
331
- i0.ɵɵlistener("change", function TestingAnalyticsComponent_Template_select_change_7_listener() { return ctx.onTimeRangeChange(); });
332
- i0.ɵɵrepeaterCreate(8, TestingAnalyticsComponent_For_9_Template, 2, 2, "option", 6, _forTrack0);
775
+ i0.ɵɵelementStart(6, "div", 4)(7, "div", 5);
776
+ i0.ɵɵrepeaterCreate(8, TestingAnalyticsComponent_For_9_Template, 2, 3, "button", 6, _forTrack0);
333
777
  i0.ɵɵelementEnd();
334
778
  i0.ɵɵelementStart(10, "button", 7);
335
- i0.ɵɵlistener("click", function TestingAnalyticsComponent_Template_button_click_10_listener() { return ctx.refresh(); });
779
+ i0.ɵɵlistener("click", function TestingAnalyticsComponent_Template_button_click_10_listener() { return ctx.Refresh(); });
336
780
  i0.ɵɵelement(11, "i", 8);
337
781
  i0.ɵɵtext(12, " Refresh ");
338
782
  i0.ɵɵelementEnd()()();
339
- i0.ɵɵelementStart(13, "div", 9)(14, "div", 10)(15, "h3");
340
- i0.ɵɵelement(16, "i", 11);
341
- i0.ɵɵtext(17, " Test Execution Trends ");
342
- i0.ɵɵelementEnd()();
343
- i0.ɵɵelementStart(18, "div", 12);
344
- i0.ɵɵrepeaterCreate(19, TestingAnalyticsComponent_For_20_Template, 29, 22, "div", 13, i0.ɵɵrepeaterTrackByIndex);
345
- i0.ɵɵpipe(21, "async");
346
- i0.ɵɵelementEnd()();
347
- i0.ɵɵelementStart(22, "div", 14)(23, "div", 15)(24, "div", 16)(25, "h4");
348
- i0.ɵɵelement(26, "i", 17);
349
- i0.ɵɵtext(27, " Top Failing Tests ");
350
- i0.ɵɵelementEnd()();
351
- i0.ɵɵelementStart(28, "div", 18);
352
- i0.ɵɵrepeaterCreate(29, TestingAnalyticsComponent_For_30_Template, 9, 5, "div", 19, _forTrack1, false, TestingAnalyticsComponent_ForEmpty_31_Template, 4, 0, "div", 20);
353
- i0.ɵɵpipe(32, "async");
354
- i0.ɵɵelementEnd()();
355
- i0.ɵɵelementStart(33, "div", 15)(34, "div", 16)(35, "h4");
356
- i0.ɵɵelement(36, "i", 21);
357
- i0.ɵɵtext(37, " Most Expensive Tests ");
358
- i0.ɵɵelementEnd()();
359
- i0.ɵɵelementStart(38, "div", 18);
360
- i0.ɵɵrepeaterCreate(39, TestingAnalyticsComponent_For_40_Template, 9, 3, "div", 19, _forTrack1, false, TestingAnalyticsComponent_ForEmpty_41_Template, 4, 0, "div", 20);
361
- i0.ɵɵpipe(42, "async");
362
- i0.ɵɵelementEnd()();
363
- i0.ɵɵelementStart(43, "div", 15)(44, "div", 16)(45, "h4");
364
- i0.ɵɵelement(46, "i", 22);
365
- i0.ɵɵtext(47, " Slowest Tests ");
366
- i0.ɵɵelementEnd()();
367
- i0.ɵɵelementStart(48, "div", 18);
368
- i0.ɵɵrepeaterCreate(49, TestingAnalyticsComponent_For_50_Template, 9, 3, "div", 19, _forTrack1, false, TestingAnalyticsComponent_ForEmpty_51_Template, 4, 0, "div", 20);
369
- i0.ɵɵpipe(52, "async");
370
- i0.ɵɵelementEnd()();
371
- i0.ɵɵelementStart(53, "div", 15)(54, "div", 16)(55, "h4");
372
- i0.ɵɵelement(56, "i", 23);
373
- i0.ɵɵtext(57, " Score Distribution ");
374
- i0.ɵɵelementEnd()();
375
- i0.ɵɵelementStart(58, "div", 18)(59, "div", 24);
376
- i0.ɵɵrepeaterCreate(60, TestingAnalyticsComponent_For_61_Template, 9, 7, "div", 25, _forTrack2);
377
- i0.ɵɵpipe(62, "async");
378
- i0.ɵɵelementEnd()()();
379
- i0.ɵɵelementStart(63, "div", 15)(64, "div", 16)(65, "h4");
380
- i0.ɵɵelement(66, "i", 26);
381
- i0.ɵɵtext(67, " Cost Breakdown ");
382
- i0.ɵɵelementEnd()();
383
- i0.ɵɵelementStart(68, "div", 18)(69, "div", 27)(70, "div", 28)(71, "span", 29);
384
- i0.ɵɵtext(72, "Total Cost");
783
+ i0.ɵɵelementStart(13, "div", 9);
784
+ i0.ɵɵelement(14, "i", 10);
785
+ i0.ɵɵtext(15, " Trend Overview ");
385
786
  i0.ɵɵelementEnd();
386
- i0.ɵɵelementStart(73, "span", 30);
387
- i0.ɵɵtext(74);
388
- i0.ɵɵpipe(75, "async");
389
- i0.ɵɵpipe(76, "number");
390
- i0.ɵɵelementEnd()();
391
- i0.ɵɵelementStart(77, "div", 31)(78, "span", 29);
392
- i0.ɵɵtext(79, "Avg per Test");
787
+ i0.ɵɵelementStart(16, "div", 11)(17, "div", 12)(18, "h4");
788
+ i0.ɵɵelement(19, "i", 13);
789
+ i0.ɵɵtext(20, " Pass Rate Trend");
393
790
  i0.ɵɵelementEnd();
394
- i0.ɵɵelementStart(80, "span", 30);
395
- i0.ɵɵtext(81);
396
- i0.ɵɵpipe(82, "async");
397
- i0.ɵɵpipe(83, "number");
398
- i0.ɵɵelementEnd()();
399
- i0.ɵɵelementStart(84, "div", 31)(85, "span", 29);
400
- i0.ɵɵtext(86, "Avg per Run");
791
+ i0.ɵɵtemplate(21, TestingAnalyticsComponent_Conditional_21_Template, 2, 1);
792
+ i0.ɵɵpipe(22, "async");
401
793
  i0.ɵɵelementEnd();
402
- i0.ɵɵelementStart(87, "span", 30);
403
- i0.ɵɵtext(88);
404
- i0.ɵɵpipe(89, "async");
405
- i0.ɵɵpipe(90, "number");
406
- i0.ɵɵelementEnd()();
407
- i0.ɵɵelementStart(91, "div", 31)(92, "span", 29);
408
- i0.ɵɵtext(93, "Projected Monthly");
794
+ i0.ɵɵelementStart(23, "div", 12)(24, "h4");
795
+ i0.ɵɵelement(25, "i", 14);
796
+ i0.ɵɵtext(26, " Run Volume");
797
+ i0.ɵɵelementEnd();
798
+ i0.ɵɵtemplate(27, TestingAnalyticsComponent_Conditional_27_Template, 2, 1);
799
+ i0.ɵɵpipe(28, "async");
800
+ i0.ɵɵelementEnd();
801
+ i0.ɵɵelementStart(29, "div", 12)(30, "h4");
802
+ i0.ɵɵelement(31, "i", 15);
803
+ i0.ɵɵtext(32, " Score Trend");
804
+ i0.ɵɵelementEnd();
805
+ i0.ɵɵtemplate(33, TestingAnalyticsComponent_Conditional_33_Template, 2, 1);
806
+ i0.ɵɵpipe(34, "async");
807
+ i0.ɵɵelementEnd();
808
+ i0.ɵɵelementStart(35, "div", 12)(36, "h4");
809
+ i0.ɵɵelement(37, "i", 16);
810
+ i0.ɵɵtext(38, " Cost Trend");
409
811
  i0.ɵɵelementEnd();
410
- i0.ɵɵelementStart(94, "span", 30);
411
- i0.ɵɵtext(95);
412
- i0.ɵɵpipe(96, "async");
413
- i0.ɵɵpipe(97, "number");
414
- i0.ɵɵelementEnd()()()()();
415
- i0.ɵɵelementStart(98, "div", 15)(99, "div", 16)(100, "h4");
416
- i0.ɵɵelement(101, "i", 32);
417
- i0.ɵɵtext(102, " Performance Metrics ");
812
+ i0.ɵɵtemplate(39, TestingAnalyticsComponent_Conditional_39_Template, 2, 1);
813
+ i0.ɵɵpipe(40, "async");
418
814
  i0.ɵɵelementEnd()();
419
- i0.ɵɵelementStart(103, "div", 18)(104, "div", 33)(105, "div", 34)(106, "div", 35);
420
- i0.ɵɵelement(107, "i", 36);
815
+ i0.ɵɵelementStart(41, "div", 9);
816
+ i0.ɵɵelement(42, "i", 17);
817
+ i0.ɵɵtext(43, " Insights ");
421
818
  i0.ɵɵelementEnd();
422
- i0.ɵɵelementStart(108, "div", 37)(109, "div", 38);
423
- i0.ɵɵtext(110);
424
- i0.ɵɵpipe(111, "async");
819
+ i0.ɵɵelementStart(44, "div", 18)(45, "div", 19)(46, "h4");
820
+ i0.ɵɵelement(47, "i", 20);
821
+ i0.ɵɵtext(48, " Top Failing Tests");
425
822
  i0.ɵɵelementEnd();
426
- i0.ɵɵelementStart(112, "div", 39);
427
- i0.ɵɵtext(113, "Avg Execution Time");
428
- i0.ɵɵelementEnd()()();
429
- i0.ɵɵelementStart(114, "div", 34)(115, "div", 35);
430
- i0.ɵɵelement(116, "i", 11);
823
+ i0.ɵɵtemplate(49, TestingAnalyticsComponent_Conditional_49_Template, 2, 1);
824
+ i0.ɵɵpipe(50, "async");
431
825
  i0.ɵɵelementEnd();
432
- i0.ɵɵelementStart(117, "div", 37)(118, "div", 38);
433
- i0.ɵɵtext(119);
434
- i0.ɵɵpipe(120, "async");
435
- i0.ɵɵpipe(121, "number");
826
+ i0.ɵɵelementStart(51, "div", 19)(52, "h4");
827
+ i0.ɵɵelement(53, "i", 21);
828
+ i0.ɵɵtext(54, " Slowest Tests");
436
829
  i0.ɵɵelementEnd();
437
- i0.ɵɵelementStart(122, "div", 39);
438
- i0.ɵɵtext(123, "Tests per Day");
439
- i0.ɵɵelementEnd()()();
440
- i0.ɵɵelementStart(124, "div", 34)(125, "div", 35);
441
- i0.ɵɵelement(126, "i", 40);
830
+ i0.ɵɵtemplate(55, TestingAnalyticsComponent_Conditional_55_Template, 2, 1);
831
+ i0.ɵɵpipe(56, "async");
832
+ i0.ɵɵelementEnd();
833
+ i0.ɵɵelementStart(57, "div", 19)(58, "h4");
834
+ i0.ɵɵelement(59, "i", 22);
835
+ i0.ɵɵtext(60, " Most Expensive Tests");
836
+ i0.ɵɵelementEnd();
837
+ i0.ɵɵtemplate(61, TestingAnalyticsComponent_Conditional_61_Template, 2, 1);
838
+ i0.ɵɵpipe(62, "async");
839
+ i0.ɵɵelementEnd()();
840
+ i0.ɵɵelementStart(63, "div", 9);
841
+ i0.ɵɵelement(64, "i", 23);
842
+ i0.ɵɵtext(65, " Score Distribution ");
442
843
  i0.ɵɵelementEnd();
443
- i0.ɵɵelementStart(127, "div", 37)(128, "div", 38);
444
- i0.ɵɵtext(129);
445
- i0.ɵɵpipe(130, "async");
446
- i0.ɵɵpipe(131, "number");
844
+ i0.ɵɵelementStart(66, "div", 24);
845
+ i0.ɵɵtemplate(67, TestingAnalyticsComponent_Conditional_67_Template, 2, 1);
846
+ i0.ɵɵpipe(68, "async");
447
847
  i0.ɵɵelementEnd();
448
- i0.ɵɵelementStart(132, "div", 39);
449
- i0.ɵɵtext(133, "Reliability Score");
450
- i0.ɵɵelementEnd()()()()()()()();
848
+ i0.ɵɵelementStart(69, "div", 9);
849
+ i0.ɵɵelement(70, "i", 25);
850
+ i0.ɵɵtext(71, " Version Comparison ");
851
+ i0.ɵɵelementStart(72, "span", 26);
852
+ i0.ɵɵtext(73, "Compare metrics across different builds");
853
+ i0.ɵɵelementEnd()();
854
+ i0.ɵɵelementStart(74, "div", 27);
855
+ i0.ɵɵtemplate(75, TestingAnalyticsComponent_Conditional_75_Template, 4, 0, "div", 28)(76, TestingAnalyticsComponent_Conditional_76_Template, 4, 0, "div", 28)(77, TestingAnalyticsComponent_Conditional_77_Template, 17, 0, "div", 29);
856
+ i0.ɵɵelementEnd()();
451
857
  } if (rf & 2) {
858
+ let tmp_1_0;
452
859
  let tmp_2_0;
453
860
  let tmp_3_0;
454
861
  let tmp_4_0;
455
862
  let tmp_5_0;
456
- i0.ɵɵadvance(7);
457
- i0.ɵɵtwoWayProperty("ngModel", ctx.selectedTimeRange);
458
- i0.ɵɵadvance();
459
- i0.ɵɵrepeater(ctx.timeRanges);
460
- i0.ɵɵadvance(11);
461
- i0.ɵɵrepeater((tmp_2_0 = i0.ɵɵpipeBind1(21, 11, ctx.trends$)) !== null && tmp_2_0 !== undefined ? tmp_2_0 : i0.ɵɵpureFunction0(53, _c0));
462
- i0.ɵɵadvance(10);
463
- i0.ɵɵrepeater((tmp_3_0 = i0.ɵɵpipeBind1(32, 13, ctx.topFailingTests$)) !== null && tmp_3_0 !== undefined ? tmp_3_0 : i0.ɵɵpureFunction0(54, _c0));
464
- i0.ɵɵadvance(10);
465
- i0.ɵɵrepeater((tmp_4_0 = i0.ɵɵpipeBind1(42, 15, ctx.expensiveTests$)) !== null && tmp_4_0 !== undefined ? tmp_4_0 : i0.ɵɵpureFunction0(55, _c0));
863
+ let tmp_6_0;
864
+ let tmp_7_0;
865
+ let tmp_8_0;
866
+ i0.ɵɵadvance(8);
867
+ i0.ɵɵrepeater(ctx.TimeRanges);
868
+ i0.ɵɵadvance(13);
869
+ i0.ɵɵconditional((tmp_1_0 = i0.ɵɵpipeBind1(22, 9, ctx.DisplayTrends$)) ? 21 : -1, tmp_1_0);
870
+ i0.ɵɵadvance(6);
871
+ i0.ɵɵconditional((tmp_2_0 = i0.ɵɵpipeBind1(28, 11, ctx.DisplayTrends$)) ? 27 : -1, tmp_2_0);
872
+ i0.ɵɵadvance(6);
873
+ i0.ɵɵconditional((tmp_3_0 = i0.ɵɵpipeBind1(34, 13, ctx.DisplayTrends$)) ? 33 : -1, tmp_3_0);
874
+ i0.ɵɵadvance(6);
875
+ i0.ɵɵconditional((tmp_4_0 = i0.ɵɵpipeBind1(40, 15, ctx.DisplayTrends$)) ? 39 : -1, tmp_4_0);
466
876
  i0.ɵɵadvance(10);
467
- i0.ɵɵrepeater((tmp_5_0 = i0.ɵɵpipeBind1(52, 17, ctx.slowestTests$)) !== null && tmp_5_0 !== undefined ? tmp_5_0 : i0.ɵɵpureFunction0(56, _c0));
468
- i0.ɵɵadvance(11);
469
- i0.ɵɵrepeater(i0.ɵɵpipeBind1(62, 19, ctx.scoreDistribution$));
470
- i0.ɵɵadvance(14);
471
- i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(76, 23, i0.ɵɵpipeBind1(75, 21, ctx.totalCost$) || 0, "1.2-2"), "");
472
- i0.ɵɵadvance(7);
473
- i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(83, 28, i0.ɵɵpipeBind1(82, 26, ctx.avgCostPerTest$) || 0, "1.4-4"), "");
474
- i0.ɵɵadvance(7);
475
- i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(90, 33, i0.ɵɵpipeBind1(89, 31, ctx.avgCostPerRun$) || 0, "1.4-4"), "");
476
- i0.ɵɵadvance(7);
477
- i0.ɵɵtextInterpolate1("$", i0.ɵɵpipeBind2(97, 38, i0.ɵɵpipeBind1(96, 36, ctx.projectedMonthlyCost$) || 0, "1.2-2"), "");
478
- i0.ɵɵadvance(15);
479
- i0.ɵɵtextInterpolate(ctx.formatDuration(i0.ɵɵpipeBind1(111, 41, ctx.avgExecutionTime$) || 0));
480
- i0.ɵɵadvance(9);
481
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(121, 45, i0.ɵɵpipeBind1(120, 43, ctx.throughput$) || 0, "1.1-1"));
482
- i0.ɵɵadvance(10);
483
- i0.ɵɵtextInterpolate1("", i0.ɵɵpipeBind2(131, 50, i0.ɵɵpipeBind1(130, 48, ctx.reliabilityScore$) || 0, "1.1-1"), "%");
484
- } }, dependencies: [i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.SelectControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.AsyncPipe, i3.DecimalPipe, i3.DatePipe], styles: [".testing-analytics[_ngcontent-%COMP%] {\n padding: 20px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n .analytics-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\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: 12px;\n }\n\n .header-left[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .time-range-select[_ngcontent-%COMP%] {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n background: white;\n cursor: pointer;\n }\n\n .action-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: 4px;\n background: white;\n color: #666;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .action-btn[_ngcontent-%COMP%]:hover {\n background: #f5f5f5;\n }\n\n .analytics-section[_ngcontent-%COMP%] {\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n margin-bottom: 24px;\n }\n\n .section-header[_ngcontent-%COMP%] {\n margin-bottom: 20px;\n padding-bottom: 12px;\n border-bottom: 2px solid #f0f0f0;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .trends-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 16px;\n }\n\n .trend-card[_ngcontent-%COMP%] {\n background: #f8f9fa;\n padding: 16px;\n border-radius: 6px;\n border: 1px solid #e0e0e0;\n }\n\n .trend-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n }\n\n .trend-date[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n font-weight: 600;\n }\n\n .trend-total[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #2196f3;\n font-weight: 600;\n }\n\n .trend-metrics[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n margin-bottom: 12px;\n }\n\n .metric[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 600;\n }\n\n .metric.passed[_ngcontent-%COMP%] {\n color: #4caf50;\n }\n\n .metric.failed[_ngcontent-%COMP%] {\n color: #f44336;\n }\n\n .metric.skipped[_ngcontent-%COMP%] {\n color: #9e9e9e;\n }\n\n .trend-chart[_ngcontent-%COMP%] {\n display: flex;\n height: 8px;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 12px;\n background: #e0e0e0;\n }\n\n .chart-bar[_ngcontent-%COMP%] {\n transition: width 0.3s ease;\n }\n\n .chart-bar.passed[_ngcontent-%COMP%] {\n background: #4caf50;\n }\n\n .chart-bar.failed[_ngcontent-%COMP%] {\n background: #f44336;\n }\n\n .chart-bar.skipped[_ngcontent-%COMP%] {\n background: #9e9e9e;\n }\n\n .trend-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 11px;\n }\n\n .pass-rate[_ngcontent-%COMP%] {\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 12px;\n }\n\n .pass-rate.good[_ngcontent-%COMP%] {\n background: #e8f5e9;\n color: #4caf50;\n }\n\n .pass-rate.warning[_ngcontent-%COMP%] {\n background: #fff3e0;\n color: #ff9800;\n }\n\n .pass-rate.poor[_ngcontent-%COMP%] {\n background: #ffebee;\n color: #f44336;\n }\n\n .cost[_ngcontent-%COMP%] {\n color: #666;\n font-weight: 600;\n }\n\n .analytics-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));\n gap: 20px;\n }\n\n .analytics-card[_ngcontent-%COMP%] {\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .card-header[_ngcontent-%COMP%] {\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .card-header[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .card-header[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .card-content[_ngcontent-%COMP%] {\n padding: 16px;\n max-height: 400px;\n overflow-y: auto;\n }\n\n .analytics-row[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.2s ease;\n }\n\n .analytics-row[_ngcontent-%COMP%]:hover {\n background: #f8f9fa;\n }\n\n .analytics-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n }\n\n .row-info[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .row-name[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: #333;\n margin-bottom: 4px;\n }\n\n .row-meta[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n }\n\n .row-metrics[_ngcontent-%COMP%] {\n min-width: 120px;\n text-align: right;\n }\n\n .fail-bar-container[_ngcontent-%COMP%] {\n width: 100px;\n height: 6px;\n background: #f0f0f0;\n border-radius: 3px;\n overflow: hidden;\n display: inline-block;\n vertical-align: middle;\n }\n\n .fail-bar[_ngcontent-%COMP%] {\n height: 100%;\n background: #f44336;\n transition: width 0.3s ease;\n }\n\n .cost-value[_ngcontent-%COMP%], \n .duration-value[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: #2196f3;\n }\n\n .empty-state[_ngcontent-%COMP%] {\n text-align: center;\n padding: 40px 20px;\n color: #999;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 36px;\n margin-bottom: 12px;\n opacity: 0.5;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 13px;\n margin: 0;\n }\n\n .score-distribution[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .score-bucket[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 80px 1fr 60px;\n gap: 12px;\n align-items: center;\n }\n\n .bucket-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n }\n\n .bucket-bar-container[_ngcontent-%COMP%] {\n position: relative;\n height: 24px;\n background: #f0f0f0;\n border-radius: 4px;\n overflow: hidden;\n }\n\n .bucket-bar[_ngcontent-%COMP%] {\n height: 100%;\n transition: width 0.3s ease;\n display: flex;\n align-items: center;\n padding: 0 8px;\n }\n\n .bucket-bar.excellent[_ngcontent-%COMP%] {\n background: #4caf50;\n }\n\n .bucket-bar.good[_ngcontent-%COMP%] {\n background: #8bc34a;\n }\n\n .bucket-bar.fair[_ngcontent-%COMP%] {\n background: #ff9800;\n }\n\n .bucket-bar.poor[_ngcontent-%COMP%] {\n background: #ff5722;\n }\n\n .bucket-bar.fail[_ngcontent-%COMP%] {\n background: #f44336;\n }\n\n .bucket-count[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: white;\n }\n\n .bucket-percentage[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n text-align: right;\n }\n\n .cost-summary[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .cost-item[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 6px;\n }\n\n .cost-item.total[_ngcontent-%COMP%] {\n background: #e3f2fd;\n border: 2px solid #2196f3;\n }\n\n .cost-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n }\n\n .cost-item.total[_ngcontent-%COMP%] .cost-label[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .cost-amount[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 700;\n color: #333;\n }\n\n .cost-item.total[_ngcontent-%COMP%] .cost-amount[_ngcontent-%COMP%] {\n color: #2196f3;\n font-size: 20px;\n }\n\n .performance-metrics[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .perf-metric[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 6px;\n }\n\n .perf-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n background: #2196f3;\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n }\n\n .perf-content[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .perf-value[_ngcontent-%COMP%] {\n font-size: 20px;\n font-weight: 700;\n color: #333;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .perf-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n font-weight: 600;\n text-transform: uppercase;\n }\n\n @media (max-width: 1200px) {\n .analytics-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }"], changeDetection: 0 });
877
+ i0.ɵɵconditional((tmp_5_0 = i0.ɵɵpipeBind1(50, 17, ctx.TopFailingTests$)) ? 49 : -1, tmp_5_0);
878
+ i0.ɵɵadvance(6);
879
+ i0.ɵɵconditional((tmp_6_0 = i0.ɵɵpipeBind1(56, 19, ctx.SlowestTests$)) ? 55 : -1, tmp_6_0);
880
+ i0.ɵɵadvance(6);
881
+ i0.ɵɵconditional((tmp_7_0 = i0.ɵɵpipeBind1(62, 21, ctx.MostExpensiveTests$)) ? 61 : -1, tmp_7_0);
882
+ i0.ɵɵadvance(6);
883
+ i0.ɵɵconditional((tmp_8_0 = i0.ɵɵpipeBind1(68, 23, ctx.ScoreDistribution$)) ? 67 : -1, tmp_8_0);
884
+ i0.ɵɵadvance(8);
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 });
485
887
  }
486
888
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingAnalyticsComponent, [{
487
889
  type: Component,
488
890
  args: [{ selector: 'app-testing-analytics', changeDetection: ChangeDetectionStrategy.OnPush, template: `
489
891
  <div class="testing-analytics">
490
- <div class="analytics-header">
892
+
893
+ <!-- ===== 1. Page Header ===== -->
894
+ <div class="page-header">
491
895
  <div class="header-left">
492
896
  <h2>
493
897
  <i class="fa-solid fa-chart-bar"></i>
@@ -495,252 +899,302 @@ export class TestingAnalyticsComponent {
495
899
  </h2>
496
900
  </div>
497
901
  <div class="header-actions">
498
- <select [(ngModel)]="selectedTimeRange" (change)="onTimeRangeChange()" class="time-range-select">
499
- @for (range of timeRanges; track range.days) {
500
- <option [value]="range.days">{{ range.label }}</option>
902
+ <div class="time-range-pills">
903
+ @for (range of TimeRanges; track range.days) {
904
+ <button
905
+ class="pill"
906
+ [class.active]="SelectedDays === range.days"
907
+ (click)="OnTimeRangeSelect(range.days)">
908
+ {{ range.label }}
909
+ </button>
501
910
  }
502
- </select>
503
- <button class="action-btn" (click)="refresh()">
504
- <i class="fa-solid fa-refresh"></i>
911
+ </div>
912
+ <button class="refresh-btn" (click)="Refresh()">
913
+ <i class="fa-solid fa-arrows-rotate"></i>
505
914
  Refresh
506
915
  </button>
507
916
  </div>
508
917
  </div>
509
918
 
510
- <!-- Trend Overview -->
511
- <div class="analytics-section">
512
- <div class="section-header">
513
- <h3>
514
- <i class="fa-solid fa-chart-line"></i>
515
- Test Execution Trends
516
- </h3>
517
- </div>
518
- <div class="trends-grid">
519
- @for (trend of (trends$ | async) ?? []; track $index) {
520
- <div class="trend-card">
521
- <div class="trend-header">
522
- <span class="trend-date">{{ trend.timestamp | date:'short' }}</span>
523
- <span class="trend-total">{{ trend.totalRuns }} runs</span>
524
- </div>
525
- <div class="trend-metrics">
526
- <div class="metric passed">
527
- <i class="fa-solid fa-check-circle"></i>
528
- <span>{{ trend.passed }}</span>
529
- </div>
530
- <div class="metric failed">
531
- <i class="fa-solid fa-times-circle"></i>
532
- <span>{{ trend.failed }}</span>
533
- </div>
534
- <div class="metric skipped">
535
- <i class="fa-solid fa-minus-circle"></i>
536
- <span>{{ trend.skipped }}</span>
537
- </div>
538
- </div>
539
- <div class="trend-chart">
540
- <div class="chart-bar passed" [style.width.%]="calculatePassRate(trend)"></div>
541
- <div class="chart-bar failed" [style.width.%]="calculateFailRate(trend)"></div>
542
- <div class="chart-bar skipped" [style.width.%]="calculateSkipRate(trend)"></div>
919
+ <!-- ===== 2. Trend Overview (2-column CSS charts) ===== -->
920
+ <div class="section-title">
921
+ <i class="fa-solid fa-chart-line"></i>
922
+ Trend Overview
923
+ </div>
924
+ <div class="trend-grid">
925
+
926
+ <!-- Pass Rate Trend -->
927
+ <div class="card trend-card">
928
+ <h4><i class="fa-solid fa-check-double"></i> Pass Rate Trend</h4>
929
+ @if (DisplayTrends$ | async; as trends) {
930
+ @if (trends.length === 0) {
931
+ <div class="empty-mini"><i class="fa-solid fa-chart-area"></i><span>No trend data</span></div>
932
+ } @else {
933
+ <div class="bar-chart">
934
+ @for (t of trends; track trackTrend($index, t)) {
935
+ <div class="bar-row">
936
+ <span class="bar-label">{{ FormatBucketDate(t.timestamp) }}</span>
937
+ <div class="bar-track">
938
+ <div class="bar-fill pass-rate-bar" [style.width.%]="PassRate(t)"></div>
939
+ </div>
940
+ <span class="bar-value" [class]="PassRateClass(t)">{{ PassRate(t) | number:'1.0-0' }}%</span>
941
+ </div>
942
+ }
543
943
  </div>
544
- <div class="trend-footer">
545
- <span class="pass-rate" [class.good]="calculatePassRate(trend) >= 80" [class.warning]="calculatePassRate(trend) >= 60 && calculatePassRate(trend) < 80" [class.poor]="calculatePassRate(trend) < 60">
546
- {{ calculatePassRate(trend).toFixed(1) }}% pass rate
547
- </span>
548
- <span class="cost">\${{ trend.totalCost.toFixed(2) }}</span>
944
+ <div class="trend-direction">
945
+ @if (PassRateTrendDirection$ | async; as dir) {
946
+ @if (dir === 'up') {
947
+ <i class="fa-solid fa-arrow-trend-up trend-up"></i> <span class="trend-up">Improving</span>
948
+ } @else if (dir === 'down') {
949
+ <i class="fa-solid fa-arrow-trend-down trend-down"></i> <span class="trend-down">Declining</span>
950
+ } @else {
951
+ <i class="fa-solid fa-minus trend-neutral"></i> <span class="trend-neutral">Stable</span>
952
+ }
953
+ }
549
954
  </div>
550
- </div>
955
+ }
551
956
  }
552
957
  </div>
553
- </div>
554
958
 
555
- <!-- Analytics Grid -->
556
- <div class="analytics-grid">
557
- <!-- Top Failing Tests -->
558
- <div class="analytics-card">
559
- <div class="card-header">
560
- <h4>
561
- <i class="fa-solid fa-exclamation-triangle"></i>
562
- Top Failing Tests
563
- </h4>
564
- </div>
565
- <div class="card-content">
566
- @for (test of (topFailingTests$ | async) ?? []; track test.testName) {
567
- <div class="analytics-row">
568
- <div class="row-info">
569
- <div class="row-name">{{ test.testName }}</div>
570
- <div class="row-meta">{{ test.failureCount }} failures • {{ test.failureRate.toFixed(1) }}% fail rate</div>
571
- </div>
572
- <div class="row-metrics">
573
- <div class="fail-bar-container">
574
- <div class="fail-bar" [style.width.%]="test.failureRate"></div>
959
+ <!-- Run Volume -->
960
+ <div class="card trend-card">
961
+ <h4><i class="fa-solid fa-layer-group"></i> Run Volume</h4>
962
+ @if (DisplayTrends$ | async; as trends) {
963
+ @if (trends.length === 0) {
964
+ <div class="empty-mini"><i class="fa-solid fa-chart-bar"></i><span>No volume data</span></div>
965
+ } @else {
966
+ <div class="bar-chart">
967
+ @for (t of trends; track trackTrend($index, t)) {
968
+ <div class="bar-row">
969
+ <span class="bar-label">{{ FormatBucketDate(t.timestamp) }}</span>
970
+ <div class="bar-track stacked">
971
+ <div class="bar-segment passed-seg" [style.width.%]="SegmentPct(t, 'passed')"></div>
972
+ <div class="bar-segment failed-seg" [style.width.%]="SegmentPct(t, 'failed')"></div>
973
+ <div class="bar-segment skipped-seg" [style.width.%]="SegmentPct(t, 'skipped')"></div>
974
+ </div>
975
+ <span class="bar-value">{{ t.totalRuns }}</span>
575
976
  </div>
576
- </div>
977
+ }
577
978
  </div>
578
- } @empty {
579
- <div class="empty-state">
580
- <i class="fa-solid fa-check-circle"></i>
581
- <p>No failing tests!</p>
979
+ <div class="legend-row">
980
+ <span class="legend-dot passed-seg"></span> Passed
981
+ <span class="legend-dot failed-seg"></span> Failed
982
+ <span class="legend-dot skipped-seg"></span> Skipped
582
983
  </div>
583
984
  }
584
- </div>
985
+ }
585
986
  </div>
586
987
 
587
- <!-- Most Expensive Tests -->
588
- <div class="analytics-card">
589
- <div class="card-header">
590
- <h4>
591
- <i class="fa-solid fa-dollar-sign"></i>
592
- Most Expensive Tests
593
- </h4>
594
- </div>
595
- <div class="card-content">
596
- @for (test of (expensiveTests$ | async) ?? []; track test.testName) {
597
- <div class="analytics-row">
598
- <div class="row-info">
599
- <div class="row-name">{{ test.testName }}</div>
600
- <div class="row-meta">avg \${{ test.avgCost.toFixed(4) }}</div>
601
- </div>
602
- <div class="row-metrics">
603
- <div class="cost-value">\${{ test.totalCost.toFixed(2) }}</div>
604
- </div>
605
- </div>
606
- } @empty {
607
- <div class="empty-state">
608
- <i class="fa-solid fa-dollar-sign"></i>
609
- <p>No test data</p>
988
+ <!-- Score Trend -->
989
+ <div class="card trend-card">
990
+ <h4><i class="fa-solid fa-star-half-stroke"></i> Score Trend</h4>
991
+ @if (DisplayTrends$ | async; as trends) {
992
+ @if (trends.length === 0) {
993
+ <div class="empty-mini"><i class="fa-solid fa-star"></i><span>No score data</span></div>
994
+ } @else {
995
+ <div class="bar-chart">
996
+ @for (t of trends; track trackTrend($index, t)) {
997
+ <div class="bar-row">
998
+ <span class="bar-label">{{ FormatBucketDate(t.timestamp) }}</span>
999
+ <div class="bar-track">
1000
+ <div class="bar-fill" [class]="ScoreBarClass(t.averageScore)" [style.width.%]="t.averageScore * 100"></div>
1001
+ </div>
1002
+ <span class="bar-value" [class]="ScoreTextClass(t.averageScore)">{{ (t.averageScore * 100) | number:'1.0-0' }}%</span>
1003
+ </div>
1004
+ }
610
1005
  </div>
611
1006
  }
612
- </div>
1007
+ }
613
1008
  </div>
614
1009
 
615
- <!-- Slowest Tests -->
616
- <div class="analytics-card">
617
- <div class="card-header">
618
- <h4>
619
- <i class="fa-solid fa-clock"></i>
620
- Slowest Tests
621
- </h4>
622
- </div>
623
- <div class="card-content">
624
- @for (test of (slowestTests$ | async) ?? []; track test.testName) {
625
- <div class="analytics-row">
626
- <div class="row-info">
627
- <div class="row-name">{{ test.testName }}</div>
628
- <div class="row-meta">avg {{ formatDuration(test.avgDuration) }}</div>
629
- </div>
630
- <div class="row-metrics">
631
- <div class="duration-value">{{ formatDuration(test.maxDuration) }}</div>
632
- </div>
633
- </div>
634
- } @empty {
635
- <div class="empty-state">
636
- <i class="fa-solid fa-clock"></i>
637
- <p>No test data</p>
1010
+ <!-- Cost Trend -->
1011
+ <div class="card trend-card">
1012
+ <h4><i class="fa-solid fa-dollar-sign"></i> Cost Trend</h4>
1013
+ @if (DisplayTrends$ | async; as trends) {
1014
+ @if (trends.length === 0) {
1015
+ <div class="empty-mini"><i class="fa-solid fa-wallet"></i><span>No cost data</span></div>
1016
+ } @else {
1017
+ <div class="bar-chart">
1018
+ @for (t of trends; track trackTrend($index, t)) {
1019
+ <div class="bar-row">
1020
+ <span class="bar-label">{{ FormatBucketDate(t.timestamp) }}</span>
1021
+ <div class="bar-track">
1022
+ <div class="bar-fill cost-bar" [style.width.%]="CostBarPct(t, trends)"></div>
1023
+ </div>
1024
+ <span class="bar-value">\${{ t.totalCost | number:'1.2-2' }}</span>
1025
+ </div>
1026
+ }
638
1027
  </div>
639
1028
  }
640
- </div>
1029
+ }
641
1030
  </div>
1031
+ </div>
642
1032
 
643
- <!-- Test Score Distribution -->
644
- <div class="analytics-card">
645
- <div class="card-header">
646
- <h4>
647
- <i class="fa-solid fa-chart-pie"></i>
648
- Score Distribution
649
- </h4>
650
- </div>
651
- <div class="card-content">
652
- <div class="score-distribution">
653
- @for (bucket of scoreDistribution$ | async; track bucket.label) {
654
- <div class="score-bucket">
655
- <div class="bucket-label">{{ bucket.label }}</div>
656
- <div class="bucket-bar-container">
657
- <div class="bucket-bar" [style.width.%]="bucket.percentage" [class]="bucket.class"></div>
658
- <span class="bucket-count">{{ bucket.count }}</span>
1033
+ <!-- ===== 3. Insights Section ===== -->
1034
+ <div class="section-title">
1035
+ <i class="fa-solid fa-magnifying-glass-chart"></i>
1036
+ Insights
1037
+ </div>
1038
+ <div class="insights-grid">
1039
+
1040
+ <!-- Top Failing Tests -->
1041
+ <div class="card insight-card">
1042
+ <h4><i class="fa-solid fa-triangle-exclamation"></i> Top Failing Tests</h4>
1043
+ @if (TopFailingTests$ | async; as tests) {
1044
+ @if (tests.length === 0) {
1045
+ <div class="empty-mini"><i class="fa-solid fa-circle-check"></i><span>No failing tests</span></div>
1046
+ } @else {
1047
+ <div class="insight-list">
1048
+ @for (t of tests; track t.testName) {
1049
+ <div class="insight-row">
1050
+ <span class="insight-name" [title]="t.testName">{{ t.testName }}</span>
1051
+ <span class="badge-fail">{{ t.failureCount }}</span>
1052
+ <span class="insight-secondary">{{ t.failureRate | number:'1.0-1' }}%</span>
659
1053
  </div>
660
- <div class="bucket-percentage">{{ bucket.percentage.toFixed(1) }}%</div>
661
- </div>
662
- }
663
- </div>
664
- </div>
1054
+ }
1055
+ </div>
1056
+ }
1057
+ }
665
1058
  </div>
666
1059
 
667
- <!-- Cost Breakdown -->
668
- <div class="analytics-card">
669
- <div class="card-header">
670
- <h4>
671
- <i class="fa-solid fa-wallet"></i>
672
- Cost Breakdown
673
- </h4>
674
- </div>
675
- <div class="card-content">
676
- <div class="cost-summary">
677
- <div class="cost-item total">
678
- <span class="cost-label">Total Cost</span>
679
- <span class="cost-amount">\${{ (totalCost$ | async) || 0 | number:'1.2-2' }}</span>
680
- </div>
681
- <div class="cost-item">
682
- <span class="cost-label">Avg per Test</span>
683
- <span class="cost-amount">\${{ (avgCostPerTest$ | async) || 0 | number:'1.4-4' }}</span>
684
- </div>
685
- <div class="cost-item">
686
- <span class="cost-label">Avg per Run</span>
687
- <span class="cost-amount">\${{ (avgCostPerRun$ | async) || 0 | number:'1.4-4' }}</span>
688
- </div>
689
- <div class="cost-item">
690
- <span class="cost-label">Projected Monthly</span>
691
- <span class="cost-amount">\${{ (projectedMonthlyCost$ | async) || 0 | number:'1.2-2' }}</span>
1060
+ <!-- Slowest Tests -->
1061
+ <div class="card insight-card">
1062
+ <h4><i class="fa-solid fa-clock"></i> Slowest Tests</h4>
1063
+ @if (SlowestTests$ | async; as tests) {
1064
+ @if (tests.length === 0) {
1065
+ <div class="empty-mini"><i class="fa-solid fa-bolt"></i><span>No duration data</span></div>
1066
+ } @else {
1067
+ <div class="insight-list">
1068
+ @for (t of tests; track t.testName) {
1069
+ <div class="insight-row">
1070
+ <span class="insight-name" [title]="t.testName">{{ t.testName }}</span>
1071
+ <span class="insight-primary">{{ FormatDuration(t.avgDuration) }}</span>
1072
+ <span class="insight-secondary">max {{ FormatDuration(t.maxDuration) }}</span>
1073
+ </div>
1074
+ }
692
1075
  </div>
693
- </div>
694
- </div>
1076
+ }
1077
+ }
695
1078
  </div>
696
1079
 
697
- <!-- Performance Metrics -->
698
- <div class="analytics-card">
699
- <div class="card-header">
700
- <h4>
701
- <i class="fa-solid fa-gauge"></i>
702
- Performance Metrics
703
- </h4>
704
- </div>
705
- <div class="card-content">
706
- <div class="performance-metrics">
707
- <div class="perf-metric">
708
- <div class="perf-icon">
709
- <i class="fa-solid fa-bolt"></i>
710
- </div>
711
- <div class="perf-content">
712
- <div class="perf-value">{{ formatDuration((avgExecutionTime$ | async) || 0) }}</div>
713
- <div class="perf-label">Avg Execution Time</div>
714
- </div>
715
- </div>
716
- <div class="perf-metric">
717
- <div class="perf-icon">
718
- <i class="fa-solid fa-chart-line"></i>
719
- </div>
720
- <div class="perf-content">
721
- <div class="perf-value">{{ (throughput$ | async) || 0 | number:'1.1-1' }}</div>
722
- <div class="perf-label">Tests per Day</div>
723
- </div>
1080
+ <!-- Most Expensive Tests -->
1081
+ <div class="card insight-card">
1082
+ <h4><i class="fa-solid fa-coins"></i> Most Expensive Tests</h4>
1083
+ @if (MostExpensiveTests$ | async; as tests) {
1084
+ @if (tests.length === 0) {
1085
+ <div class="empty-mini"><i class="fa-solid fa-dollar-sign"></i><span>No cost data</span></div>
1086
+ } @else {
1087
+ <div class="insight-list">
1088
+ @for (t of tests; track t.testName) {
1089
+ <div class="insight-row">
1090
+ <span class="insight-name" [title]="t.testName">{{ t.testName }}</span>
1091
+ <span class="insight-primary">\${{ t.totalCost | number:'1.2-2' }}</span>
1092
+ <span class="insight-secondary">\${{ t.avgCost | number:'1.4-4' }}/run</span>
1093
+ </div>
1094
+ }
724
1095
  </div>
725
- <div class="perf-metric">
726
- <div class="perf-icon">
727
- <i class="fa-solid fa-check-double"></i>
728
- </div>
729
- <div class="perf-content">
730
- <div class="perf-value">{{ (reliabilityScore$ | async) || 0 | number:'1.1-1' }}%</div>
731
- <div class="perf-label">Reliability Score</div>
1096
+ }
1097
+ }
1098
+ </div>
1099
+ </div>
1100
+
1101
+ <!-- ===== 4. Score Distribution ===== -->
1102
+ <div class="section-title">
1103
+ <i class="fa-solid fa-chart-pie"></i>
1104
+ Score Distribution
1105
+ </div>
1106
+ <div class="card distribution-card">
1107
+ @if (ScoreDistribution$ | async; as buckets) {
1108
+ @if (buckets.length === 0) {
1109
+ <div class="empty-mini"><i class="fa-solid fa-chart-simple"></i><span>No score data available</span></div>
1110
+ } @else {
1111
+ <div class="distribution-bars">
1112
+ @for (b of buckets; track b.label) {
1113
+ <div class="dist-row">
1114
+ <span class="dist-label">{{ b.label }}</span>
1115
+ <div class="dist-track">
1116
+ <div class="dist-fill" [style.width.%]="b.percentage" [style.background]="b.color"></div>
1117
+ </div>
1118
+ <span class="dist-count">{{ b.count }}</span>
1119
+ <span class="dist-pct">{{ b.percentage | number:'1.0-1' }}%</span>
732
1120
  </div>
733
- </div>
1121
+ }
734
1122
  </div>
1123
+ }
1124
+ }
1125
+ </div>
1126
+
1127
+ <!-- ===== 5. Version Comparison ===== -->
1128
+ <div class="section-title">
1129
+ <i class="fa-solid fa-code-compare"></i>
1130
+ Version Comparison
1131
+ <span class="section-subtitle">Compare metrics across different builds</span>
1132
+ </div>
1133
+ <div class="card version-card">
1134
+ @if (IsLoadingVersions) {
1135
+ <div class="empty-mini"><i class="fa-solid fa-spinner fa-spin"></i><span>Loading versions...</span></div>
1136
+ } @else if (VersionRows.length === 0) {
1137
+ <div class="empty-mini"><i class="fa-solid fa-code-branch"></i><span>No version data available</span></div>
1138
+ } @else {
1139
+ <div class="version-table-wrap">
1140
+ <table class="version-table">
1141
+ <thead>
1142
+ <tr>
1143
+ <th>Version</th>
1144
+ <th>Date</th>
1145
+ <th class="num">Tests</th>
1146
+ <th class="num">Pass Rate</th>
1147
+ <th class="num">Cost</th>
1148
+ </tr>
1149
+ </thead>
1150
+ <tbody>
1151
+ @for (row of VersionRows; track row.version) {
1152
+ <tr>
1153
+ <td>
1154
+ <span class="version-label">{{ row.gitCommitShort }}</span>
1155
+ <span class="version-agent">{{ row.agentVersion }}</span>
1156
+ </td>
1157
+ <td>{{ row.date | date:'mediumDate' }}</td>
1158
+ <td class="num">{{ row.totalTests }}</td>
1159
+ <td class="num">
1160
+ <span>{{ row.passRate | number:'1.1-1' }}%</span>
1161
+ @if (row.passRateDelta !== null) {
1162
+ <span class="delta" [class.delta-up]="row.passRateDelta > 0" [class.delta-down]="row.passRateDelta < 0" [class.delta-neutral]="row.passRateDelta === 0">
1163
+ @if (row.passRateDelta > 0) {
1164
+ <i class="fa-solid fa-arrow-up"></i>
1165
+ } @else if (row.passRateDelta < 0) {
1166
+ <i class="fa-solid fa-arrow-down"></i>
1167
+ }
1168
+ {{ AbsNum(row.passRateDelta) | number:'1.1-1' }}%
1169
+ </span>
1170
+ }
1171
+ </td>
1172
+ <td class="num">
1173
+ <span>\${{ row.totalCost | number:'1.2-2' }}</span>
1174
+ @if (row.costDelta !== null) {
1175
+ <span class="delta" [class.delta-up]="row.costDelta < 0" [class.delta-down]="row.costDelta > 0" [class.delta-neutral]="row.costDelta === 0">
1176
+ @if (row.costDelta > 0) {
1177
+ <i class="fa-solid fa-arrow-up"></i>
1178
+ } @else if (row.costDelta < 0) {
1179
+ <i class="fa-solid fa-arrow-down"></i>
1180
+ }
1181
+ {{ AbsNum(row.costDelta) | number:'1.1-1' }}%
1182
+ </span>
1183
+ }
1184
+ </td>
1185
+ </tr>
1186
+ }
1187
+ </tbody>
1188
+ </table>
735
1189
  </div>
736
- </div>
1190
+ }
737
1191
  </div>
738
1192
  </div>
739
- `, styles: ["\n .testing-analytics {\n padding: 20px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n .analytics-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\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: 12px;\n }\n\n .header-left h2 i {\n color: #2196f3;\n }\n\n .header-actions {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .time-range-select {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n background: white;\n cursor: pointer;\n }\n\n .action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid #ddd;\n border-radius: 4px;\n background: white;\n color: #666;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .action-btn:hover {\n background: #f5f5f5;\n }\n\n .analytics-section {\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n margin-bottom: 24px;\n }\n\n .section-header {\n margin-bottom: 20px;\n padding-bottom: 12px;\n border-bottom: 2px solid #f0f0f0;\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header h3 i {\n color: #2196f3;\n }\n\n .trends-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 16px;\n }\n\n .trend-card {\n background: #f8f9fa;\n padding: 16px;\n border-radius: 6px;\n border: 1px solid #e0e0e0;\n }\n\n .trend-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 12px;\n }\n\n .trend-date {\n font-size: 11px;\n color: #666;\n font-weight: 600;\n }\n\n .trend-total {\n font-size: 11px;\n color: #2196f3;\n font-weight: 600;\n }\n\n .trend-metrics {\n display: flex;\n gap: 12px;\n margin-bottom: 12px;\n }\n\n .metric {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 12px;\n font-weight: 600;\n }\n\n .metric.passed {\n color: #4caf50;\n }\n\n .metric.failed {\n color: #f44336;\n }\n\n .metric.skipped {\n color: #9e9e9e;\n }\n\n .trend-chart {\n display: flex;\n height: 8px;\n border-radius: 4px;\n overflow: hidden;\n margin-bottom: 12px;\n background: #e0e0e0;\n }\n\n .chart-bar {\n transition: width 0.3s ease;\n }\n\n .chart-bar.passed {\n background: #4caf50;\n }\n\n .chart-bar.failed {\n background: #f44336;\n }\n\n .chart-bar.skipped {\n background: #9e9e9e;\n }\n\n .trend-footer {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 11px;\n }\n\n .pass-rate {\n font-weight: 600;\n padding: 2px 8px;\n border-radius: 12px;\n }\n\n .pass-rate.good {\n background: #e8f5e9;\n color: #4caf50;\n }\n\n .pass-rate.warning {\n background: #fff3e0;\n color: #ff9800;\n }\n\n .pass-rate.poor {\n background: #ffebee;\n color: #f44336;\n }\n\n .cost {\n color: #666;\n font-weight: 600;\n }\n\n .analytics-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));\n gap: 20px;\n }\n\n .analytics-card {\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .card-header {\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n }\n\n .card-header h4 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .card-header h4 i {\n color: #2196f3;\n }\n\n .card-content {\n padding: 16px;\n max-height: 400px;\n overflow-y: auto;\n }\n\n .analytics-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.2s ease;\n }\n\n .analytics-row:hover {\n background: #f8f9fa;\n }\n\n .analytics-row:last-child {\n border-bottom: none;\n }\n\n .row-info {\n flex: 1;\n }\n\n .row-name {\n font-size: 13px;\n font-weight: 500;\n color: #333;\n margin-bottom: 4px;\n }\n\n .row-meta {\n font-size: 11px;\n color: #666;\n }\n\n .row-metrics {\n min-width: 120px;\n text-align: right;\n }\n\n .fail-bar-container {\n width: 100px;\n height: 6px;\n background: #f0f0f0;\n border-radius: 3px;\n overflow: hidden;\n display: inline-block;\n vertical-align: middle;\n }\n\n .fail-bar {\n height: 100%;\n background: #f44336;\n transition: width 0.3s ease;\n }\n\n .cost-value,\n .duration-value {\n font-size: 13px;\n font-weight: 600;\n color: #2196f3;\n }\n\n .empty-state {\n text-align: center;\n padding: 40px 20px;\n color: #999;\n }\n\n .empty-state i {\n font-size: 36px;\n margin-bottom: 12px;\n opacity: 0.5;\n }\n\n .empty-state p {\n font-size: 13px;\n margin: 0;\n }\n\n .score-distribution {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n\n .score-bucket {\n display: grid;\n grid-template-columns: 80px 1fr 60px;\n gap: 12px;\n align-items: center;\n }\n\n .bucket-label {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n }\n\n .bucket-bar-container {\n position: relative;\n height: 24px;\n background: #f0f0f0;\n border-radius: 4px;\n overflow: hidden;\n }\n\n .bucket-bar {\n height: 100%;\n transition: width 0.3s ease;\n display: flex;\n align-items: center;\n padding: 0 8px;\n }\n\n .bucket-bar.excellent {\n background: #4caf50;\n }\n\n .bucket-bar.good {\n background: #8bc34a;\n }\n\n .bucket-bar.fair {\n background: #ff9800;\n }\n\n .bucket-bar.poor {\n background: #ff5722;\n }\n\n .bucket-bar.fail {\n background: #f44336;\n }\n\n .bucket-count {\n font-size: 11px;\n font-weight: 600;\n color: white;\n }\n\n .bucket-percentage {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n text-align: right;\n }\n\n .cost-summary {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .cost-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 6px;\n }\n\n .cost-item.total {\n background: #e3f2fd;\n border: 2px solid #2196f3;\n }\n\n .cost-label {\n font-size: 12px;\n font-weight: 600;\n color: #666;\n }\n\n .cost-item.total .cost-label {\n color: #2196f3;\n }\n\n .cost-amount {\n font-size: 16px;\n font-weight: 700;\n color: #333;\n }\n\n .cost-item.total .cost-amount {\n color: #2196f3;\n font-size: 20px;\n }\n\n .performance-metrics {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .perf-metric {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 12px;\n background: #f8f9fa;\n border-radius: 6px;\n }\n\n .perf-icon {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n background: #2196f3;\n color: white;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n }\n\n .perf-content {\n flex: 1;\n }\n\n .perf-value {\n font-size: 20px;\n font-weight: 700;\n color: #333;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .perf-label {\n font-size: 11px;\n color: #666;\n font-weight: 600;\n text-transform: uppercase;\n }\n\n @media (max-width: 1200px) {\n .analytics-grid {\n grid-template-columns: 1fr;\n }\n }\n "] }]
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 "] }]
740
1194
  }], () => [{ type: i1.TestingInstrumentationService }, { type: i0.ChangeDetectorRef }], { initialState: [{
741
1195
  type: Input
742
1196
  }], stateChange: [{
743
1197
  type: Output
744
1198
  }] }); })();
745
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingAnalyticsComponent, { className: "TestingAnalyticsComponent", filePath: "src/Testing/components/testing-analytics.component.ts", lineNumber: 756 }); })();
1199
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingAnalyticsComponent, { className: "TestingAnalyticsComponent", filePath: "src/Testing/components/testing-analytics.component.ts", lineNumber: 778 }); })();
746
1200
  //# sourceMappingURL=testing-analytics.component.js.map