@memberjunction/ng-core-entity-forms 6.2.0-edge.0 → 6.2.0-edge.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/dist/lib/custom/AIAgentCategories/ai-agent-category-overview.panel.d.ts +1 -1
  2. package/dist/lib/custom/AIAgentCategories/ai-agent-category-overview.panel.js +2 -2
  3. package/dist/lib/custom/AIAgentCategories/ai-agent-category-overview.panel.js.map +1 -1
  4. package/dist/lib/custom/AIAgentSessions/ai-agent-session-form.component.js.map +1 -1
  5. package/dist/lib/custom/AIAgents/add-action-dialog.component.d.ts +90 -16
  6. package/dist/lib/custom/AIAgents/add-action-dialog.component.d.ts.map +1 -1
  7. package/dist/lib/custom/AIAgents/add-action-dialog.component.js +218 -70
  8. package/dist/lib/custom/AIAgents/add-action-dialog.component.js.map +1 -1
  9. package/dist/lib/custom/AIAgents/agent-form-tabs.d.ts +1 -0
  10. package/dist/lib/custom/AIAgents/agent-form-tabs.d.ts.map +1 -1
  11. package/dist/lib/custom/AIAgents/agent-form-tabs.js +2 -0
  12. package/dist/lib/custom/AIAgents/agent-form-tabs.js.map +1 -1
  13. package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.d.ts +80 -16
  14. package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.d.ts.map +1 -1
  15. package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.js +146 -46
  16. package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.js.map +1 -1
  17. package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts +329 -27
  18. package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts.map +1 -1
  19. package/dist/lib/custom/AIAgents/ai-agent-form.component.js +1322 -783
  20. package/dist/lib/custom/AIAgents/ai-agent-form.component.js.map +1 -1
  21. package/dist/lib/custom/AIAgents/ai-agent-management.service.d.ts +76 -0
  22. package/dist/lib/custom/AIAgents/ai-agent-management.service.d.ts.map +1 -1
  23. package/dist/lib/custom/AIAgents/ai-agent-management.service.js +77 -33
  24. package/dist/lib/custom/AIAgents/ai-agent-management.service.js.map +1 -1
  25. package/dist/lib/custom/AIAgents/create-prompt-dialog.component.d.ts +69 -15
  26. package/dist/lib/custom/AIAgents/create-prompt-dialog.component.d.ts.map +1 -1
  27. package/dist/lib/custom/AIAgents/create-prompt-dialog.component.js +182 -74
  28. package/dist/lib/custom/AIAgents/create-prompt-dialog.component.js.map +1 -1
  29. package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.d.ts +105 -25
  30. package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.d.ts.map +1 -1
  31. package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.js +272 -112
  32. package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.js.map +1 -1
  33. package/dist/lib/custom/AIAgents/new-agent-dialog.component.d.ts +32 -6
  34. package/dist/lib/custom/AIAgents/new-agent-dialog.component.d.ts.map +1 -1
  35. package/dist/lib/custom/AIAgents/new-agent-dialog.component.js +71 -19
  36. package/dist/lib/custom/AIAgents/new-agent-dialog.component.js.map +1 -1
  37. package/dist/lib/custom/AIAgents/new-agent-dialog.service.d.ts +10 -0
  38. package/dist/lib/custom/AIAgents/new-agent-dialog.service.d.ts.map +1 -1
  39. package/dist/lib/custom/AIAgents/new-agent-dialog.service.js +27 -7
  40. package/dist/lib/custom/AIAgents/new-agent-dialog.service.js.map +1 -1
  41. package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.d.ts +65 -15
  42. package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.d.ts.map +1 -1
  43. package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.js +152 -52
  44. package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.js.map +1 -1
  45. package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.d.ts +81 -15
  46. package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.d.ts.map +1 -1
  47. package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.js +145 -45
  48. package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.js.map +1 -1
  49. package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.d.ts +71 -17
  50. package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.d.ts.map +1 -1
  51. package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.js +175 -67
  52. package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.js.map +1 -1
  53. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts +173 -27
  54. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts.map +1 -1
  55. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js +385 -93
  56. package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js.map +1 -1
  57. package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.d.ts +64 -4
  58. package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.d.ts.map +1 -1
  59. package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.js +129 -29
  60. package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.js.map +1 -1
  61. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts +260 -27
  62. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts.map +1 -1
  63. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js +694 -252
  64. package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js.map +1 -1
  65. package/dist/lib/custom/AIPrompts/ai-prompt-management.service.d.ts +10 -0
  66. package/dist/lib/custom/AIPrompts/ai-prompt-management.service.d.ts.map +1 -1
  67. package/dist/lib/custom/AIPrompts/ai-prompt-management.service.js +17 -9
  68. package/dist/lib/custom/AIPrompts/ai-prompt-management.service.js.map +1 -1
  69. package/dist/lib/custom/AIPrompts/pending-records.d.ts +11 -0
  70. package/dist/lib/custom/AIPrompts/pending-records.d.ts.map +1 -0
  71. package/dist/lib/custom/AIPrompts/pending-records.js +21 -0
  72. package/dist/lib/custom/AIPrompts/pending-records.js.map +1 -0
  73. package/dist/lib/custom/AIPrompts/template-selector-dialog.component.d.ts +74 -16
  74. package/dist/lib/custom/AIPrompts/template-selector-dialog.component.d.ts.map +1 -1
  75. package/dist/lib/custom/AIPrompts/template-selector-dialog.component.js +174 -58
  76. package/dist/lib/custom/AIPrompts/template-selector-dialog.component.js.map +1 -1
  77. package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts +108 -14
  78. package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts.map +1 -1
  79. package/dist/lib/custom/Actions/action-execution-log-form.component.js +258 -70
  80. package/dist/lib/custom/Actions/action-execution-log-form.component.js.map +1 -1
  81. package/dist/lib/custom/Actions/action-form.component.d.ts +198 -14
  82. package/dist/lib/custom/Actions/action-form.component.d.ts.map +1 -1
  83. package/dist/lib/custom/Actions/action-form.component.js +387 -107
  84. package/dist/lib/custom/Actions/action-form.component.js.map +1 -1
  85. package/dist/lib/custom/BridgeProviders/bridge-provider-features.d.ts +12 -6
  86. package/dist/lib/custom/BridgeProviders/bridge-provider-features.d.ts.map +1 -1
  87. package/dist/lib/custom/BridgeProviders/bridge-provider-features.js +41 -29
  88. package/dist/lib/custom/BridgeProviders/bridge-provider-features.js.map +1 -1
  89. package/dist/lib/custom/BridgeProviders/bridge-provider-form.component.js +14 -14
  90. package/dist/lib/custom/BridgeProviders/bridge-provider-form.component.js.map +1 -1
  91. package/dist/lib/custom/Companies/company-overview.panel.d.ts +1 -1
  92. package/dist/lib/custom/Companies/company-overview.panel.js +2 -2
  93. package/dist/lib/custom/Companies/company-overview.panel.js.map +1 -1
  94. package/dist/lib/custom/Conversations/conversation-overview.panel.d.ts +1 -1
  95. package/dist/lib/custom/Conversations/conversation-overview.panel.js +2 -2
  96. package/dist/lib/custom/Conversations/conversation-overview.panel.js.map +1 -1
  97. package/dist/lib/custom/Employees/employee-overview.panel.d.ts +1 -1
  98. package/dist/lib/custom/Employees/employee-overview.panel.js +2 -2
  99. package/dist/lib/custom/Employees/employee-overview.panel.js.map +1 -1
  100. package/dist/lib/custom/Entities/entity-form.component.d.ts +217 -29
  101. package/dist/lib/custom/Entities/entity-form.component.d.ts.map +1 -1
  102. package/dist/lib/custom/Entities/entity-form.component.js +734 -285
  103. package/dist/lib/custom/Entities/entity-form.component.js.map +1 -1
  104. package/dist/lib/custom/EntityActions/entityaction.form.component.d.ts +5 -1
  105. package/dist/lib/custom/EntityActions/entityaction.form.component.d.ts.map +1 -1
  106. package/dist/lib/custom/EntityActions/entityaction.form.component.js +10 -2
  107. package/dist/lib/custom/EntityActions/entityaction.form.component.js.map +1 -1
  108. package/dist/lib/custom/Files/file-form.component.d.ts +1 -1
  109. package/dist/lib/custom/Files/file-form.component.js +2 -2
  110. package/dist/lib/custom/Files/file-form.component.js.map +1 -1
  111. package/dist/lib/custom/Lists/list-form.component.d.ts +300 -36
  112. package/dist/lib/custom/Lists/list-form.component.d.ts.map +1 -1
  113. package/dist/lib/custom/Lists/list-form.component.js +664 -208
  114. package/dist/lib/custom/Lists/list-form.component.js.map +1 -1
  115. package/dist/lib/custom/MLModels/ml-model-view-models.d.ts +31 -0
  116. package/dist/lib/custom/MLModels/ml-model-view-models.d.ts.map +1 -1
  117. package/dist/lib/custom/MLModels/ml-model-view-models.js +52 -16
  118. package/dist/lib/custom/MLModels/ml-model-view-models.js.map +1 -1
  119. package/dist/lib/custom/MLModels/ps-model-detail.component.d.ts +107 -10
  120. package/dist/lib/custom/MLModels/ps-model-detail.component.d.ts.map +1 -1
  121. package/dist/lib/custom/MLModels/ps-model-detail.component.js +291 -101
  122. package/dist/lib/custom/MLModels/ps-model-detail.component.js.map +1 -1
  123. package/dist/lib/custom/Queries/query-form.component.d.ts +201 -26
  124. package/dist/lib/custom/Queries/query-form.component.d.ts.map +1 -1
  125. package/dist/lib/custom/Queries/query-form.component.js +429 -109
  126. package/dist/lib/custom/Queries/query-form.component.js.map +1 -1
  127. package/dist/lib/custom/Queries/query-run-dialog.component.d.ts +107 -14
  128. package/dist/lib/custom/Queries/query-run-dialog.component.d.ts.map +1 -1
  129. package/dist/lib/custom/Queries/query-run-dialog.component.js +249 -79
  130. package/dist/lib/custom/Queries/query-run-dialog.component.js.map +1 -1
  131. package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts +129 -4
  132. package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts.map +1 -1
  133. package/dist/lib/custom/RecordProcesses/record-process-form.component.js +904 -90
  134. package/dist/lib/custom/RecordProcesses/record-process-form.component.js.map +1 -1
  135. package/dist/lib/custom/Rubrics/evaluation-form.component.d.ts +37 -0
  136. package/dist/lib/custom/Rubrics/evaluation-form.component.d.ts.map +1 -0
  137. package/dist/lib/custom/Rubrics/evaluation-form.component.js +399 -0
  138. package/dist/lib/custom/Rubrics/evaluation-form.component.js.map +1 -0
  139. package/dist/lib/custom/Rubrics/record-forms.component.d.ts +69 -0
  140. package/dist/lib/custom/Rubrics/record-forms.component.d.ts.map +1 -0
  141. package/dist/lib/custom/Rubrics/record-forms.component.js +587 -0
  142. package/dist/lib/custom/Rubrics/record-forms.component.js.map +1 -0
  143. package/dist/lib/custom/Rubrics/rubric-form.component.d.ts +67 -0
  144. package/dist/lib/custom/Rubrics/rubric-form.component.d.ts.map +1 -0
  145. package/dist/lib/custom/Rubrics/rubric-form.component.js +534 -0
  146. package/dist/lib/custom/Rubrics/rubric-form.component.js.map +1 -0
  147. package/dist/lib/custom/Rubrics/rubric-form.panels.d.ts +25 -0
  148. package/dist/lib/custom/Rubrics/rubric-form.panels.d.ts.map +1 -0
  149. package/dist/lib/custom/Rubrics/rubric-form.panels.js +299 -0
  150. package/dist/lib/custom/Rubrics/rubric-form.panels.js.map +1 -0
  151. package/dist/lib/custom/Rubrics/scale-form.component.d.ts +31 -0
  152. package/dist/lib/custom/Rubrics/scale-form.component.d.ts.map +1 -0
  153. package/dist/lib/custom/Rubrics/scale-form.component.js +395 -0
  154. package/dist/lib/custom/Rubrics/scale-form.component.js.map +1 -0
  155. package/dist/lib/custom/SearchScopes/searchscope-form.component.d.ts +1 -1
  156. package/dist/lib/custom/SearchScopes/searchscope-form.component.js +4 -4
  157. package/dist/lib/custom/SearchScopes/searchscope-form.component.js.map +1 -1
  158. package/dist/lib/custom/SessionBridges/session-bridge-form.component.js.map +1 -1
  159. package/dist/lib/custom/Templates/template-param-dialog.component.d.ts +77 -13
  160. package/dist/lib/custom/Templates/template-param-dialog.component.d.ts.map +1 -1
  161. package/dist/lib/custom/Templates/template-param-dialog.component.js +196 -78
  162. package/dist/lib/custom/Templates/template-param-dialog.component.js.map +1 -1
  163. package/dist/lib/custom/Templates/template-params-grid.component.d.ts +49 -7
  164. package/dist/lib/custom/Templates/template-params-grid.component.d.ts.map +1 -1
  165. package/dist/lib/custom/Templates/template-params-grid.component.js +132 -44
  166. package/dist/lib/custom/Templates/template-params-grid.component.js.map +1 -1
  167. package/dist/lib/custom/Templates/templates-form.component.d.ts +153 -19
  168. package/dist/lib/custom/Templates/templates-form.component.d.ts.map +1 -1
  169. package/dist/lib/custom/Templates/templates-form.component.js +434 -217
  170. package/dist/lib/custom/Templates/templates-form.component.js.map +1 -1
  171. package/dist/lib/custom/Tests/entity-link-pill.component.d.ts +24 -4
  172. package/dist/lib/custom/Tests/entity-link-pill.component.d.ts.map +1 -1
  173. package/dist/lib/custom/Tests/entity-link-pill.component.js +64 -20
  174. package/dist/lib/custom/Tests/entity-link-pill.component.js.map +1 -1
  175. package/dist/lib/custom/Tests/test-form.component.d.ts +223 -27
  176. package/dist/lib/custom/Tests/test-form.component.d.ts.map +1 -1
  177. package/dist/lib/custom/Tests/test-form.component.js +951 -530
  178. package/dist/lib/custom/Tests/test-form.component.js.map +1 -1
  179. package/dist/lib/custom/Tests/test-run-checks.d.ts +2 -0
  180. package/dist/lib/custom/Tests/test-run-checks.d.ts.map +1 -1
  181. package/dist/lib/custom/Tests/test-run-checks.js +5 -1
  182. package/dist/lib/custom/Tests/test-run-checks.js.map +1 -1
  183. package/dist/lib/custom/Tests/test-run-feedback-form.component.d.ts +2 -0
  184. package/dist/lib/custom/Tests/test-run-feedback-form.component.d.ts.map +1 -1
  185. package/dist/lib/custom/Tests/test-run-feedback-form.component.js +5 -1
  186. package/dist/lib/custom/Tests/test-run-feedback-form.component.js.map +1 -1
  187. package/dist/lib/custom/Tests/test-run-form.component.d.ts +146 -18
  188. package/dist/lib/custom/Tests/test-run-form.component.d.ts.map +1 -1
  189. package/dist/lib/custom/Tests/test-run-form.component.js +362 -106
  190. package/dist/lib/custom/Tests/test-run-form.component.js.map +1 -1
  191. package/dist/lib/custom/Tests/test-suite-form.component.d.ts +395 -47
  192. package/dist/lib/custom/Tests/test-suite-form.component.d.ts.map +1 -1
  193. package/dist/lib/custom/Tests/test-suite-form.component.js +1719 -1007
  194. package/dist/lib/custom/Tests/test-suite-form.component.js.map +1 -1
  195. package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts +214 -26
  196. package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts.map +1 -1
  197. package/dist/lib/custom/Tests/test-suite-run-form.component.js +535 -159
  198. package/dist/lib/custom/Tests/test-suite-run-form.component.js.map +1 -1
  199. package/dist/lib/custom/Users/user-overview.panel.d.ts +1 -1
  200. package/dist/lib/custom/Users/user-overview.panel.js +3 -3
  201. package/dist/lib/custom/Users/user-overview.panel.js.map +1 -1
  202. package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts +208 -29
  203. package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts.map +1 -1
  204. package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js +446 -138
  205. package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js.map +1 -1
  206. package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts +6 -0
  207. package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts.map +1 -1
  208. package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js +16 -4
  209. package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js.map +1 -1
  210. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts +50 -6
  211. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts.map +1 -1
  212. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js +84 -12
  213. package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js.map +1 -1
  214. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts +89 -5
  215. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts.map +1 -1
  216. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js +216 -70
  217. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js.map +1 -1
  218. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.d.ts +62 -3
  219. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.d.ts.map +1 -1
  220. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.js +140 -42
  221. package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.js.map +1 -1
  222. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts +81 -10
  223. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts.map +1 -1
  224. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js +165 -37
  225. package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js.map +1 -1
  226. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +140 -15
  227. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
  228. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +326 -100
  229. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
  230. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.d.ts +101 -15
  231. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.d.ts.map +1 -1
  232. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.js +281 -97
  233. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.js.map +1 -1
  234. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.d.ts +43 -25
  235. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.d.ts.map +1 -1
  236. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.js +100 -64
  237. package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.js.map +1 -1
  238. package/dist/lib/custom/ai-agent-run/flow/constellation.component.d.ts +14 -2
  239. package/dist/lib/custom/ai-agent-run/flow/constellation.component.d.ts.map +1 -1
  240. package/dist/lib/custom/ai-agent-run/flow/constellation.component.js +71 -53
  241. package/dist/lib/custom/ai-agent-run/flow/constellation.component.js.map +1 -1
  242. package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.d.ts +14 -2
  243. package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.d.ts.map +1 -1
  244. package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.js +70 -52
  245. package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.js.map +1 -1
  246. package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.d.ts +8 -0
  247. package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.d.ts.map +1 -1
  248. package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.js +22 -6
  249. package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.js.map +1 -1
  250. package/dist/lib/custom/ai-agent-run/flow/flowchart.component.d.ts +24 -2
  251. package/dist/lib/custom/ai-agent-run/flow/flowchart.component.d.ts.map +1 -1
  252. package/dist/lib/custom/ai-agent-run/flow/flowchart.component.js +91 -59
  253. package/dist/lib/custom/ai-agent-run/flow/flowchart.component.js.map +1 -1
  254. package/dist/lib/custom/ai-agent-run/flow/pan-zoom.d.ts +10 -2
  255. package/dist/lib/custom/ai-agent-run/flow/pan-zoom.d.ts.map +1 -1
  256. package/dist/lib/custom/ai-agent-run/flow/pan-zoom.js +33 -17
  257. package/dist/lib/custom/ai-agent-run/flow/pan-zoom.js.map +1 -1
  258. package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.d.ts +2 -0
  259. package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.d.ts.map +1 -1
  260. package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.js +34 -30
  261. package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.js.map +1 -1
  262. package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.d.ts +14 -2
  263. package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.d.ts.map +1 -1
  264. package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.js +69 -51
  265. package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.js.map +1 -1
  266. package/dist/lib/custom/custom-forms.module.d.ts +56 -48
  267. package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
  268. package/dist/lib/custom/custom-forms.module.js +60 -0
  269. package/dist/lib/custom/custom-forms.module.js.map +1 -1
  270. package/dist/lib/custom/shared/entity-selector-dialog.component.d.ts +38 -12
  271. package/dist/lib/custom/shared/entity-selector-dialog.component.d.ts.map +1 -1
  272. package/dist/lib/custom/shared/entity-selector-dialog.component.js +103 -51
  273. package/dist/lib/custom/shared/entity-selector-dialog.component.js.map +1 -1
  274. package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.d.ts.map +1 -1
  275. package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.js +61 -43
  276. package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.js.map +1 -1
  277. package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.d.ts +10 -0
  278. package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.d.ts.map +1 -0
  279. package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.js +74 -0
  280. package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.js.map +1 -0
  281. package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.d.ts.map +1 -1
  282. package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.js +37 -19
  283. package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.js.map +1 -1
  284. package/dist/lib/generated/Entities/MJAIPromptRun/mjaipromptrun.form.component.d.ts.map +1 -1
  285. package/dist/lib/generated/Entities/MJAIPromptRun/mjaipromptrun.form.component.js +78 -52
  286. package/dist/lib/generated/Entities/MJAIPromptRun/mjaipromptrun.form.component.js.map +1 -1
  287. package/dist/lib/generated/Entities/MJAIVendor/mjaivendor.form.component.js +76 -74
  288. package/dist/lib/generated/Entities/MJAIVendor/mjaivendor.form.component.js.map +1 -1
  289. package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.d.ts.map +1 -1
  290. package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.js +156 -84
  291. package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.js.map +1 -1
  292. package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.d.ts +10 -0
  293. package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.d.ts.map +1 -0
  294. package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.js +62 -0
  295. package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.js.map +1 -0
  296. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.d.ts.map +1 -1
  297. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js +24 -6
  298. package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js.map +1 -1
  299. package/dist/lib/generated/Entities/MJProject/mjproject.form.component.js +27 -25
  300. package/dist/lib/generated/Entities/MJProject/mjproject.form.component.js.map +1 -1
  301. package/dist/lib/generated/Entities/MJRecordChange/mjrecordchange.form.component.d.ts.map +1 -1
  302. package/dist/lib/generated/Entities/MJRecordChange/mjrecordchange.form.component.js +24 -16
  303. package/dist/lib/generated/Entities/MJRecordChange/mjrecordchange.form.component.js.map +1 -1
  304. package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.d.ts +10 -0
  305. package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.d.ts.map +1 -0
  306. package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.js +107 -0
  307. package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.js.map +1 -0
  308. package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.d.ts +10 -0
  309. package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.d.ts.map +1 -0
  310. package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.js +74 -0
  311. package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.js.map +1 -0
  312. package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.d.ts +10 -0
  313. package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.d.ts.map +1 -0
  314. package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.js +135 -0
  315. package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.js.map +1 -0
  316. package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.d.ts +10 -0
  317. package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.d.ts.map +1 -0
  318. package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.js +87 -0
  319. package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.js.map +1 -0
  320. package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.d.ts +10 -0
  321. package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.d.ts.map +1 -0
  322. package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.js +107 -0
  323. package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.js.map +1 -0
  324. package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.d.ts +10 -0
  325. package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.d.ts.map +1 -0
  326. package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.js +155 -0
  327. package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.js.map +1 -0
  328. package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.d.ts +10 -0
  329. package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.d.ts.map +1 -0
  330. package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.js +62 -0
  331. package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.js.map +1 -0
  332. package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.d.ts +10 -0
  333. package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.d.ts.map +1 -0
  334. package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.js +183 -0
  335. package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.js.map +1 -0
  336. package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.d.ts +10 -0
  337. package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.d.ts.map +1 -0
  338. package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.js +120 -0
  339. package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.js.map +1 -0
  340. package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.d.ts +10 -0
  341. package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.d.ts.map +1 -0
  342. package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.js +107 -0
  343. package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.js.map +1 -0
  344. package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.d.ts +10 -0
  345. package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.d.ts.map +1 -0
  346. package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.js +103 -0
  347. package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.js.map +1 -0
  348. package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.d.ts +10 -0
  349. package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.d.ts.map +1 -0
  350. package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.js +171 -0
  351. package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.js.map +1 -0
  352. package/dist/lib/generated/Entities/MJTest/mjtest.form.component.d.ts.map +1 -1
  353. package/dist/lib/generated/Entities/MJTest/mjtest.form.component.js +24 -16
  354. package/dist/lib/generated/Entities/MJTest/mjtest.form.component.js.map +1 -1
  355. package/dist/lib/generated/Entities/MJTestSuite/mjtestsuite.form.component.js +27 -25
  356. package/dist/lib/generated/Entities/MJTestSuite/mjtestsuite.form.component.js.map +1 -1
  357. package/dist/lib/generated/Entities/MJTestSuiteRun/mjtestsuiterun.form.component.d.ts.map +1 -1
  358. package/dist/lib/generated/Entities/MJTestSuiteRun/mjtestsuiterun.form.component.js +19 -11
  359. package/dist/lib/generated/Entities/MJTestSuiteRun/mjtestsuiterun.form.component.js.map +1 -1
  360. package/dist/lib/generated/Entities/MJUser/mjuser.form.component.d.ts.map +1 -1
  361. package/dist/lib/generated/Entities/MJUser/mjuser.form.component.js +209 -119
  362. package/dist/lib/generated/Entities/MJUser/mjuser.form.component.js.map +1 -1
  363. package/dist/lib/generated/generated-forms.module.d.ts +436 -422
  364. package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
  365. package/dist/lib/generated/generated-forms.module.js +44 -2
  366. package/dist/lib/generated/generated-forms.module.js.map +1 -1
  367. package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.d.ts +5 -1
  368. package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.d.ts.map +1 -1
  369. package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.js +11 -3
  370. package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.js.map +1 -1
  371. package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.d.ts +2 -0
  372. package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.d.ts.map +1 -1
  373. package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.js +16 -12
  374. package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.js.map +1 -1
  375. package/dist/lib/panels/content-sources/website-crawler-settings.panel.d.ts +1 -1
  376. package/dist/lib/panels/content-sources/website-crawler-settings.panel.js +2 -2
  377. package/dist/lib/panels/content-sources/website-crawler-settings.panel.js.map +1 -1
  378. package/dist/lib/panels/model-predictions/model-prediction.logic.d.ts +45 -19
  379. package/dist/lib/panels/model-predictions/model-prediction.logic.d.ts.map +1 -1
  380. package/dist/lib/panels/model-predictions/model-prediction.logic.js +95 -43
  381. package/dist/lib/panels/model-predictions/model-prediction.logic.js.map +1 -1
  382. package/dist/lib/panels/model-predictions/model-prediction.panel.d.ts +20 -16
  383. package/dist/lib/panels/model-predictions/model-prediction.panel.d.ts.map +1 -1
  384. package/dist/lib/panels/model-predictions/model-prediction.panel.js +73 -65
  385. package/dist/lib/panels/model-predictions/model-prediction.panel.js.map +1 -1
  386. package/dist/lib/panels/record-clone/entity-clone-config-editor.component.d.ts +116 -0
  387. package/dist/lib/panels/record-clone/entity-clone-config-editor.component.d.ts.map +1 -0
  388. package/dist/lib/panels/record-clone/entity-clone-config-editor.component.js +820 -0
  389. package/dist/lib/panels/record-clone/entity-clone-config-editor.component.js.map +1 -0
  390. package/dist/lib/shared/components/template-editor.component.d.ts +158 -15
  391. package/dist/lib/shared/components/template-editor.component.d.ts.map +1 -1
  392. package/dist/lib/shared/components/template-editor.component.js +389 -127
  393. package/dist/lib/shared/components/template-editor.component.js.map +1 -1
  394. package/dist/public-api.d.ts +5 -0
  395. package/dist/public-api.d.ts.map +1 -1
  396. package/dist/public-api.js +5 -0
  397. package/dist/public-api.js.map +1 -1
  398. package/package.json +47 -42
@@ -14,7 +14,7 @@ import { RegisterClass } from '@memberjunction/global';
14
14
  import { SharedService, NavigationService } from '@memberjunction/ng-shared';
15
15
  import { ApplicationManager } from '@memberjunction/ng-base-application';
16
16
  import { MJTestFormComponent } from '../../generated/Entities/MJTest/mjtest.form.component';
17
- import { TestingDialogService, TagsHelper, EvaluationPreferencesService } from '@memberjunction/ng-testing';
17
+ import { TestingDialogService, TagsHelper, EvaluationPreferencesService, RubricPickerOptions } from '@memberjunction/ng-testing';
18
18
  import { createCopyOnlyToolbar } from '@memberjunction/ng-code-editor';
19
19
  import { MJConfirmService } from '@memberjunction/ng-ui-components';
20
20
  import * as i0 from "@angular/core";
@@ -27,7 +27,8 @@ import * as i6 from "@memberjunction/ng-shared-generic";
27
27
  import * as i7 from "./entity-link-pill.component";
28
28
  const _c0 = () => [1, 2, 3, 4, 5];
29
29
  const _c1 = () => [1, 2, 3];
30
- const _forTrack0 = ($index, $item) => $item.ID;
30
+ const _forTrack0 = ($index, $item) => $item.id;
31
+ const _forTrack1 = ($index, $item) => $item.ID;
31
32
  function MJTestFormComponentExtended_Conditional_21_Template(rf, ctx) { if (rf & 1) {
32
33
  i0.ɵɵelementStart(0, "span", 14);
33
34
  i0.ɵɵtext(1, "\u25CF");
@@ -257,30 +258,40 @@ function MJTestFormComponentExtended_Conditional_64_For_25_Template(rf, ctx) { i
257
258
  i0.ɵɵadvance();
258
259
  i0.ɵɵtextInterpolate(opt_r4);
259
260
  } }
260
- function MJTestFormComponentExtended_Conditional_64_For_31_Template(rf, ctx) { if (rf & 1) {
261
+ function MJTestFormComponentExtended_Conditional_64_For_33_Template(rf, ctx) { if (rf & 1) {
261
262
  i0.ɵɵelementStart(0, "option", 93);
262
263
  i0.ɵɵtext(1);
263
264
  i0.ɵɵelementEnd();
264
265
  } if (rf & 2) {
265
- const t_r5 = ctx.$implicit;
266
- i0.ɵɵproperty("ngValue", t_r5.ID);
266
+ const rubric_r5 = ctx.$implicit;
267
+ i0.ɵɵproperty("ngValue", rubric_r5.id);
267
268
  i0.ɵɵadvance();
268
- i0.ɵɵtextInterpolate(t_r5.Name);
269
+ i0.ɵɵtextInterpolate(rubric_r5.name);
269
270
  } }
270
- function MJTestFormComponentExtended_Conditional_64_For_37_Template(rf, ctx) { if (rf & 1) {
271
- const _r7 = i0.ɵɵgetCurrentView();
272
- i0.ɵɵelementStart(0, "span", 98);
271
+ function MJTestFormComponentExtended_Conditional_64_For_39_Template(rf, ctx) { if (rf & 1) {
272
+ i0.ɵɵelementStart(0, "option", 93);
273
273
  i0.ɵɵtext(1);
274
- i0.ɵɵelementStart(2, "button", 121);
275
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_64_For_37_Template_button_click_2_listener($event) { const tag_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); ctx_r0.removeTag(tag_r8); return i0.ɵɵresetView($event.stopPropagation()); });
276
- i0.ɵɵelement(3, "i", 122);
274
+ i0.ɵɵelementEnd();
275
+ } if (rf & 2) {
276
+ const t_r6 = ctx.$implicit;
277
+ i0.ɵɵproperty("ngValue", t_r6.ID);
278
+ i0.ɵɵadvance();
279
+ i0.ɵɵtextInterpolate(t_r6.Name);
280
+ } }
281
+ function MJTestFormComponentExtended_Conditional_64_For_45_Template(rf, ctx) { if (rf & 1) {
282
+ const _r8 = i0.ɵɵgetCurrentView();
283
+ i0.ɵɵelementStart(0, "span", 100);
284
+ i0.ɵɵtext(1);
285
+ i0.ɵɵelementStart(2, "button", 123);
286
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_64_For_45_Template_button_click_2_listener($event) { const tag_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); ctx_r0.removeTag(tag_r9); return i0.ɵɵresetView($event.stopPropagation()); });
287
+ i0.ɵɵelement(3, "i", 124);
277
288
  i0.ɵɵelementEnd()();
278
289
  } if (rf & 2) {
279
- const tag_r8 = ctx.$implicit;
290
+ const tag_r9 = ctx.$implicit;
280
291
  i0.ɵɵadvance();
281
- i0.ɵɵtextInterpolate1(" ", tag_r8, " ");
292
+ i0.ɵɵtextInterpolate1(" ", tag_r9, " ");
282
293
  i0.ɵɵadvance();
283
- i0.ɵɵattribute("aria-label", "Remove tag " + tag_r8);
294
+ i0.ɵɵattribute("aria-label", "Remove tag " + tag_r9);
284
295
  } }
285
296
  function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf & 1) {
286
297
  const _r3 = i0.ɵɵgetCurrentView();
@@ -313,108 +324,118 @@ function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf &
313
324
  i0.ɵɵrepeaterCreate(24, MJTestFormComponentExtended_Conditional_64_For_25_Template, 2, 2, "option", 93, i0.ɵɵrepeaterTrackByIdentity);
314
325
  i0.ɵɵelementEnd()()();
315
326
  i0.ɵɵelementStart(26, "div", 89)(27, "label", 94);
316
- i0.ɵɵtext(28, "Test Type");
327
+ i0.ɵɵtext(28, "Rubric");
317
328
  i0.ɵɵelementEnd();
318
329
  i0.ɵɵelementStart(29, "select", 95);
319
- i0.ɵɵtwoWayListener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_select_ngModelChange_29_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.TypeID, $event) || (ctx_r0.record.TypeID = $event); return i0.ɵɵresetView($event); });
320
- i0.ɵɵrepeaterCreate(30, MJTestFormComponentExtended_Conditional_64_For_31_Template, 2, 2, "option", 93, _forTrack0);
330
+ i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_select_ngModelChange_29_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.RubricID = $event || null); });
331
+ i0.ɵɵelementStart(30, "option", 93);
332
+ i0.ɵɵtext(31, "\u2014 None \u2014");
333
+ i0.ɵɵelementEnd();
334
+ i0.ɵɵrepeaterCreate(32, MJTestFormComponentExtended_Conditional_64_For_33_Template, 2, 2, "option", 93, _forTrack0);
321
335
  i0.ɵɵelementEnd()();
322
- i0.ɵɵelementStart(32, "div", 83)(33, "label", 96);
323
- i0.ɵɵtext(34, "Tags");
324
- i0.ɵɵelementEnd();
325
- i0.ɵɵelementStart(35, "div", 97);
326
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_64_Template_div_click_35_listener() { i0.ɵɵrestoreView(_r3); const tagInput_r6 = i0.ɵɵreference(39); return i0.ɵɵresetView(tagInput_r6.focus()); });
327
- i0.ɵɵrepeaterCreate(36, MJTestFormComponentExtended_Conditional_64_For_37_Template, 4, 2, "span", 98, i0.ɵɵrepeaterTrackByIdentity);
328
- i0.ɵɵelementStart(38, "input", 99, 0);
329
- i0.ɵɵtwoWayListener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_38_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.tagDraft, $event) || (ctx_r0.tagDraft = $event); return i0.ɵɵresetView($event); });
330
- i0.ɵɵlistener("keydown", function MJTestFormComponentExtended_Conditional_64_Template_input_keydown_38_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onTagInputKeydown($event)); })("blur", function MJTestFormComponentExtended_Conditional_64_Template_input_blur_38_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.addTagFromDraft()); });
336
+ i0.ɵɵelementStart(34, "div", 89)(35, "label", 96);
337
+ i0.ɵɵtext(36, "Test Type");
338
+ i0.ɵɵelementEnd();
339
+ i0.ɵɵelementStart(37, "select", 97);
340
+ i0.ɵɵtwoWayListener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_select_ngModelChange_37_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.TypeID, $event) || (ctx_r0.record.TypeID = $event); return i0.ɵɵresetView($event); });
341
+ i0.ɵɵrepeaterCreate(38, MJTestFormComponentExtended_Conditional_64_For_39_Template, 2, 2, "option", 93, _forTrack1);
342
+ i0.ɵɵelementEnd()();
343
+ i0.ɵɵelementStart(40, "div", 83)(41, "label", 98);
344
+ i0.ɵɵtext(42, "Tags");
345
+ i0.ɵɵelementEnd();
346
+ i0.ɵɵelementStart(43, "div", 99);
347
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_64_Template_div_click_43_listener() { i0.ɵɵrestoreView(_r3); const tagInput_r7 = i0.ɵɵreference(47); return i0.ɵɵresetView(tagInput_r7.focus()); });
348
+ i0.ɵɵrepeaterCreate(44, MJTestFormComponentExtended_Conditional_64_For_45_Template, 4, 2, "span", 100, i0.ɵɵrepeaterTrackByIdentity);
349
+ i0.ɵɵelementStart(46, "input", 101, 0);
350
+ i0.ɵɵtwoWayListener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_46_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.tagDraft, $event) || (ctx_r0.tagDraft = $event); return i0.ɵɵresetView($event); });
351
+ i0.ɵɵlistener("keydown", function MJTestFormComponentExtended_Conditional_64_Template_input_keydown_46_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onTagInputKeydown($event)); })("blur", function MJTestFormComponentExtended_Conditional_64_Template_input_blur_46_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.addTagFromDraft()); });
331
352
  i0.ɵɵelementEnd()();
332
- i0.ɵɵelementStart(40, "span", 100);
333
- i0.ɵɵtext(41, "Press Enter or comma to add a tag. Backspace on empty input removes the last tag.");
353
+ i0.ɵɵelementStart(48, "span", 102);
354
+ i0.ɵɵtext(49, "Press Enter or comma to add a tag. Backspace on empty input removes the last tag.");
334
355
  i0.ɵɵelementEnd()()()();
335
- i0.ɵɵelementStart(42, "section", 78)(43, "header", 79)(44, "h3");
336
- i0.ɵɵelement(45, "i", 101);
337
- i0.ɵɵtext(46, " Execution Settings");
356
+ i0.ɵɵelementStart(50, "section", 78)(51, "header", 79)(52, "h3");
357
+ i0.ɵɵelement(53, "i", 103);
358
+ i0.ɵɵtext(54, " Execution Settings");
338
359
  i0.ɵɵelementEnd()();
339
- i0.ɵɵelementStart(47, "div", 82)(48, "div", 89)(49, "label", 102);
340
- i0.ɵɵtext(50, "Priority");
360
+ i0.ɵɵelementStart(55, "div", 82)(56, "div", 89)(57, "label", 104);
361
+ i0.ɵɵtext(58, "Priority");
341
362
  i0.ɵɵelementEnd();
342
- i0.ɵɵelementStart(51, "input", 103);
343
- i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_51_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Priority = $event === "" || $event === null ? null : +$event); });
363
+ i0.ɵɵelementStart(59, "input", 105);
364
+ i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_59_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Priority = $event === "" || $event === null ? null : +$event); });
344
365
  i0.ɵɵelementEnd();
345
- i0.ɵɵelementStart(52, "span", 100);
346
- i0.ɵɵtext(53, "Lower numbers run first.");
366
+ i0.ɵɵelementStart(60, "span", 102);
367
+ i0.ɵɵtext(61, "Lower numbers run first.");
347
368
  i0.ɵɵelementEnd()();
348
- i0.ɵɵelementStart(54, "div", 89)(55, "label", 104);
349
- i0.ɵɵtext(56, "Repeat Count");
369
+ i0.ɵɵelementStart(62, "div", 89)(63, "label", 106);
370
+ i0.ɵɵtext(64, "Repeat Count");
350
371
  i0.ɵɵelementEnd();
351
- i0.ɵɵelementStart(57, "input", 105);
352
- i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_57_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.RepeatCount = $event === "" || $event === null ? null : +$event); });
372
+ i0.ɵɵelementStart(65, "input", 107);
373
+ i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_65_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.RepeatCount = $event === "" || $event === null ? null : +$event); });
353
374
  i0.ɵɵelementEnd();
354
- i0.ɵɵelementStart(58, "span", 100);
355
- i0.ɵɵtext(59, "Number of times to repeat for statistical analysis.");
375
+ i0.ɵɵelementStart(66, "span", 102);
376
+ i0.ɵɵtext(67, "Number of times to repeat for statistical analysis.");
356
377
  i0.ɵɵelementEnd()();
357
- i0.ɵɵelementStart(60, "div", 89)(61, "label", 106);
358
- i0.ɵɵtext(62, "Estimated Duration (seconds)");
378
+ i0.ɵɵelementStart(68, "div", 89)(69, "label", 108);
379
+ i0.ɵɵtext(70, "Estimated Duration (seconds)");
359
380
  i0.ɵɵelementEnd();
360
- i0.ɵɵelementStart(63, "input", 107);
361
- i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_63_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.EstimatedDurationSeconds = $event === "" || $event === null ? null : +$event); });
381
+ i0.ɵɵelementStart(71, "input", 109);
382
+ i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_71_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.EstimatedDurationSeconds = $event === "" || $event === null ? null : +$event); });
362
383
  i0.ɵɵelementEnd()();
363
- i0.ɵɵelementStart(64, "div", 89)(65, "label", 108);
364
- i0.ɵɵtext(66, "Estimated Cost (USD)");
384
+ i0.ɵɵelementStart(72, "div", 89)(73, "label", 110);
385
+ i0.ɵɵtext(74, "Estimated Cost (USD)");
365
386
  i0.ɵɵelementEnd();
366
- i0.ɵɵelementStart(67, "input", 109);
367
- i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_67_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.EstimatedCostUSD = $event === "" || $event === null ? null : +$event); });
387
+ i0.ɵɵelementStart(75, "input", 111);
388
+ i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_75_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.EstimatedCostUSD = $event === "" || $event === null ? null : +$event); });
368
389
  i0.ɵɵelementEnd()();
369
- i0.ɵɵelementStart(68, "div", 83)(69, "label", 110);
370
- i0.ɵɵtext(70, "Max Execution Time (ms)");
390
+ i0.ɵɵelementStart(76, "div", 83)(77, "label", 112);
391
+ i0.ɵɵtext(78, "Max Execution Time (ms)");
371
392
  i0.ɵɵelementEnd();
372
- i0.ɵɵelementStart(71, "input", 111);
373
- i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_71_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.MaxExecutionTimeMS = $event === "" || $event === null ? null : +$event); });
393
+ i0.ɵɵelementStart(79, "input", 113);
394
+ i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_79_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.MaxExecutionTimeMS = $event === "" || $event === null ? null : +$event); });
374
395
  i0.ɵɵelementEnd();
375
- i0.ɵɵelementStart(72, "span", 100);
376
- i0.ɵɵtext(73);
396
+ i0.ɵɵelementStart(80, "span", 102);
397
+ i0.ɵɵtext(81);
377
398
  i0.ɵɵelementEnd()()()();
378
- i0.ɵɵelementStart(74, "div", 112)(75, "h3");
379
- i0.ɵɵelement(76, "i", 72);
380
- i0.ɵɵtext(77, " Input Definition (JSON)");
399
+ i0.ɵɵelementStart(82, "div", 114)(83, "h3");
400
+ i0.ɵɵelement(84, "i", 72);
401
+ i0.ɵɵtext(85, " Input Definition (JSON)");
381
402
  i0.ɵɵelementEnd();
382
- i0.ɵɵelementStart(78, "div", 113)(79, "mj-code-editor", 114);
383
- i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_79_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.InputDefinition = $event); });
403
+ i0.ɵɵelementStart(86, "div", 115)(87, "mj-code-editor", 116);
404
+ i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_87_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.InputDefinition = $event); });
384
405
  i0.ɵɵelementEnd()()();
385
- i0.ɵɵelementStart(80, "div", 112)(81, "h3");
386
- i0.ɵɵelement(82, "i", 73);
387
- i0.ɵɵtext(83, " Expected Outcomes (JSON)");
406
+ i0.ɵɵelementStart(88, "div", 114)(89, "h3");
407
+ i0.ɵɵelement(90, "i", 73);
408
+ i0.ɵɵtext(91, " Expected Outcomes (JSON)");
388
409
  i0.ɵɵelementEnd();
389
- i0.ɵɵelementStart(84, "div", 113)(85, "mj-code-editor", 114);
390
- i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_85_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.ExpectedOutcomes = $event); });
410
+ i0.ɵɵelementStart(92, "div", 115)(93, "mj-code-editor", 116);
411
+ i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_93_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.ExpectedOutcomes = $event); });
391
412
  i0.ɵɵelementEnd()()();
392
- i0.ɵɵelementStart(86, "div", 112)(87, "h3");
393
- i0.ɵɵelement(88, "i", 74);
394
- i0.ɵɵtext(89, " Configuration (JSON)");
413
+ i0.ɵɵelementStart(94, "div", 114)(95, "h3");
414
+ i0.ɵɵelement(96, "i", 74);
415
+ i0.ɵɵtext(97, " Configuration (JSON)");
395
416
  i0.ɵɵelementEnd();
396
- i0.ɵɵelementStart(90, "div", 113)(91, "mj-code-editor", 114);
397
- i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_91_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Configuration = $event); });
417
+ i0.ɵɵelementStart(98, "div", 115)(99, "mj-code-editor", 116);
418
+ i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_99_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Configuration = $event); });
398
419
  i0.ɵɵelementEnd()()();
399
- i0.ɵɵelementStart(92, "section", 115)(93, "div", 116)(94, "div", 117)(95, "div", 118);
400
- i0.ɵɵtext(96, "Created");
420
+ i0.ɵɵelementStart(100, "section", 117)(101, "div", 118)(102, "div", 119)(103, "div", 120);
421
+ i0.ɵɵtext(104, "Created");
401
422
  i0.ɵɵelementEnd();
402
- i0.ɵɵelementStart(97, "div", 119);
403
- i0.ɵɵtext(98);
404
- i0.ɵɵpipe(99, "date");
423
+ i0.ɵɵelementStart(105, "div", 121);
424
+ i0.ɵɵtext(106);
425
+ i0.ɵɵpipe(107, "date");
405
426
  i0.ɵɵelementEnd()();
406
- i0.ɵɵelementStart(100, "div", 117)(101, "div", 118);
407
- i0.ɵɵtext(102, "Last updated");
427
+ i0.ɵɵelementStart(108, "div", 119)(109, "div", 120);
428
+ i0.ɵɵtext(110, "Last updated");
408
429
  i0.ɵɵelementEnd();
409
- i0.ɵɵelementStart(103, "div", 119);
410
- i0.ɵɵtext(104);
411
- i0.ɵɵpipe(105, "date");
430
+ i0.ɵɵelementStart(111, "div", 121);
431
+ i0.ɵɵtext(112);
432
+ i0.ɵɵpipe(113, "date");
412
433
  i0.ɵɵelementEnd()();
413
- i0.ɵɵelementStart(106, "div", 117)(107, "div", 118);
414
- i0.ɵɵtext(108, "Test ID");
434
+ i0.ɵɵelementStart(114, "div", 119)(115, "div", 120);
435
+ i0.ɵɵtext(116, "Test ID");
415
436
  i0.ɵɵelementEnd();
416
- i0.ɵɵelementStart(109, "div", 120);
417
- i0.ɵɵtext(110);
437
+ i0.ɵɵelementStart(117, "div", 122);
438
+ i0.ɵɵtext(118);
418
439
  i0.ɵɵelementEnd()()()()();
419
440
  } if (rf & 2) {
420
441
  const ctx_r0 = i0.ɵɵnextContext();
@@ -427,6 +448,12 @@ function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf &
427
448
  i0.ɵɵadvance();
428
449
  i0.ɵɵrepeater(ctx_r0.statusOptions);
429
450
  i0.ɵɵadvance(5);
451
+ i0.ɵɵproperty("ngModel", ctx_r0.record.RubricID);
452
+ i0.ɵɵadvance();
453
+ i0.ɵɵproperty("ngValue", null);
454
+ i0.ɵɵadvance(2);
455
+ i0.ɵɵrepeater(ctx_r0.RubricOptions);
456
+ i0.ɵɵadvance(5);
430
457
  i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.TypeID);
431
458
  i0.ɵɵadvance();
432
459
  i0.ɵɵrepeater(ctx_r0.testTypeOptions);
@@ -454,22 +481,22 @@ function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf &
454
481
  i0.ɵɵadvance(6);
455
482
  i0.ɵɵproperty("value", ctx_r0.record.Configuration || "")("readonly", false)("lineWrapping", true);
456
483
  i0.ɵɵadvance(7);
457
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(99, 24, ctx_r0.record.__mj_CreatedAt, "medium"));
484
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(107, 26, ctx_r0.record.__mj_CreatedAt, "medium"));
458
485
  i0.ɵɵadvance(6);
459
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(105, 27, ctx_r0.record.__mj_UpdatedAt, "medium"));
486
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(113, 29, ctx_r0.record.__mj_UpdatedAt, "medium"));
460
487
  i0.ɵɵadvance(6);
461
488
  i0.ɵɵtextInterpolate(ctx_r0.record.ID);
462
489
  } }
463
490
  function MJTestFormComponentExtended_Conditional_65_Conditional_1_For_3_Template(rf, ctx) { if (rf & 1) {
464
- i0.ɵɵelementStart(0, "div", 127);
465
- i0.ɵɵelement(1, "div", 128);
466
- i0.ɵɵelementStart(2, "div", 129);
467
- i0.ɵɵelement(3, "div", 130)(4, "div", 131);
491
+ i0.ɵɵelementStart(0, "div", 129);
492
+ i0.ɵɵelement(1, "div", 130);
493
+ i0.ɵɵelementStart(2, "div", 131);
494
+ i0.ɵɵelement(3, "div", 132)(4, "div", 133);
468
495
  i0.ɵɵelementEnd()();
469
496
  } }
470
497
  function MJTestFormComponentExtended_Conditional_65_Conditional_1_Template(rf, ctx) { if (rf & 1) {
471
- i0.ɵɵelementStart(0, "div", 123)(1, "div", 126);
472
- i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_1_For_3_Template, 5, 0, "div", 127, i0.ɵɵrepeaterTrackByIdentity);
498
+ i0.ɵɵelementStart(0, "div", 125)(1, "div", 128);
499
+ i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_1_For_3_Template, 5, 0, "div", 129, i0.ɵɵrepeaterTrackByIdentity);
473
500
  i0.ɵɵelementEnd()();
474
501
  } if (rf & 2) {
475
502
  i0.ɵɵadvance(2);
@@ -477,190 +504,190 @@ function MJTestFormComponentExtended_Conditional_65_Conditional_1_Template(rf, c
477
504
  } }
478
505
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
479
506
  i0.ɵɵelementStart(0, "span");
480
- i0.ɵɵelement(1, "i", 147);
507
+ i0.ɵɵelement(1, "i", 149);
481
508
  i0.ɵɵtext(2);
482
509
  i0.ɵɵelementEnd();
483
510
  } if (rf & 2) {
484
- const run_r10 = i0.ɵɵnextContext().$implicit;
511
+ const run_r11 = i0.ɵɵnextContext().$implicit;
485
512
  const ctx_r0 = i0.ɵɵnextContext(3);
486
513
  i0.ɵɵadvance(2);
487
- i0.ɵɵtextInterpolate1(" ", ctx_r0.formatDuration(run_r10.DurationSeconds));
514
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.formatDuration(run_r11.DurationSeconds));
488
515
  } }
489
516
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_14_Template(rf, ctx) { if (rf & 1) {
490
517
  i0.ɵɵelementStart(0, "span");
491
- i0.ɵɵelement(1, "i", 148);
518
+ i0.ɵɵelement(1, "i", 150);
492
519
  i0.ɵɵtext(2);
493
520
  i0.ɵɵelementEnd();
494
521
  } if (rf & 2) {
495
- const run_r10 = i0.ɵɵnextContext().$implicit;
522
+ const run_r11 = i0.ɵɵnextContext().$implicit;
496
523
  const ctx_r0 = i0.ɵɵnextContext(3);
497
524
  i0.ɵɵadvance(2);
498
- i0.ɵɵtextInterpolate1(" ", ctx_r0.formatCost(run_r10.CostUSD));
525
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.formatCost(run_r11.CostUSD));
499
526
  } }
500
527
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_15_Template(rf, ctx) { if (rf & 1) {
501
- i0.ɵɵelement(0, "mj-entity-link-pill", 142);
528
+ i0.ɵɵelement(0, "mj-entity-link-pill", 144);
502
529
  } if (rf & 2) {
503
- const run_r10 = i0.ɵɵnextContext().$implicit;
504
- i0.ɵɵproperty("entityName", run_r10.TargetLogEntity)("recordId", run_r10.TargetLogID);
530
+ const run_r11 = i0.ɵɵnextContext().$implicit;
531
+ i0.ɵɵproperty("entityName", run_r11.TargetLogEntity)("recordId", run_r11.TargetLogID);
505
532
  } }
506
533
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
507
- i0.ɵɵelementStart(0, "span", 144);
508
- i0.ɵɵelement(1, "i", 135);
534
+ i0.ɵɵelementStart(0, "span", 146);
535
+ i0.ɵɵelement(1, "i", 137);
509
536
  i0.ɵɵelementStart(2, "span");
510
537
  i0.ɵɵtext(3);
511
538
  i0.ɵɵelementEnd()();
512
539
  } if (rf & 2) {
513
- const run_r10 = i0.ɵɵnextContext().$implicit;
540
+ const run_r11 = i0.ɵɵnextContext().$implicit;
514
541
  const ctx_r0 = i0.ɵɵnextContext(3);
515
- i0.ɵɵproperty("ngClass", "status-" + run_r10.Status.toLowerCase())("title", ctx_r0.getStatusTooltip(run_r10.Status));
542
+ i0.ɵɵproperty("ngClass", "status-" + run_r11.Status.toLowerCase())("title", ctx_r0.getStatusTooltip(run_r11.Status));
516
543
  i0.ɵɵadvance();
517
- i0.ɵɵclassProp("fa-check", run_r10.Status === "Passed")("fa-times", run_r10.Status === "Failed")("fa-exclamation", run_r10.Status === "Error")("fa-hourglass-end", run_r10.Status === "Timeout")("fa-forward", run_r10.Status === "Skipped")("fa-spinner", run_r10.Status === "Running")("fa-clock", run_r10.Status === "Pending");
544
+ i0.ɵɵclassProp("fa-check", run_r11.Status === "Passed")("fa-times", run_r11.Status === "Failed")("fa-exclamation", run_r11.Status === "Error")("fa-hourglass-end", run_r11.Status === "Timeout")("fa-forward", run_r11.Status === "Skipped")("fa-spinner", run_r11.Status === "Running")("fa-clock", run_r11.Status === "Pending");
518
545
  i0.ɵɵadvance(2);
519
- i0.ɵɵtextInterpolate(run_r10.Status);
546
+ i0.ɵɵtextInterpolate(run_r11.Status);
520
547
  } }
521
548
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_0_Template(rf, ctx) { if (rf & 1) {
522
- i0.ɵɵelementStart(0, "span", 149);
523
- i0.ɵɵelement(1, "i", 151);
549
+ i0.ɵɵelementStart(0, "span", 151);
550
+ i0.ɵɵelement(1, "i", 153);
524
551
  i0.ɵɵelementEnd();
525
552
  } }
526
553
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_1_Template(rf, ctx) { if (rf & 1) {
527
- i0.ɵɵelementStart(0, "span", 152);
528
- i0.ɵɵelement(1, "i", 153);
554
+ i0.ɵɵelementStart(0, "span", 154);
555
+ i0.ɵɵelement(1, "i", 155);
529
556
  i0.ɵɵelementStart(2, "span");
530
557
  i0.ɵɵtext(3);
531
558
  i0.ɵɵelementEnd()();
532
559
  } if (rf & 2) {
533
560
  let tmp_19_0;
534
- const rating_r11 = ctx;
535
- const run_r10 = i0.ɵɵnextContext(2).$implicit;
561
+ const rating_r12 = ctx;
562
+ const run_r11 = i0.ɵɵnextContext(2).$implicit;
536
563
  const ctx_r0 = i0.ɵɵnextContext(3);
537
- i0.ɵɵclassProp("rating-low", rating_r11 <= 4)("rating-medium", rating_r11 >= 5 && rating_r11 <= 6)("rating-good", rating_r11 >= 7 && rating_r11 <= 8)("rating-excellent", rating_r11 >= 9);
538
- i0.ɵɵproperty("title", ctx_r0.getHumanTooltip(rating_r11, ((tmp_19_0 = ctx_r0.getFeedbackForRun(run_r10.ID)) == null ? null : tmp_19_0.CorrectionSummary) || ((tmp_19_0 = ctx_r0.getFeedbackForRun(run_r10.ID)) == null ? null : tmp_19_0.Comments) || null));
564
+ i0.ɵɵclassProp("rating-low", rating_r12 <= 4)("rating-medium", rating_r12 >= 5 && rating_r12 <= 6)("rating-good", rating_r12 >= 7 && rating_r12 <= 8)("rating-excellent", rating_r12 >= 9);
565
+ i0.ɵɵproperty("title", ctx_r0.getHumanTooltip(rating_r12, ((tmp_19_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_19_0.CorrectionSummary) || ((tmp_19_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_19_0.Comments) || null));
539
566
  i0.ɵɵadvance(3);
540
- i0.ɵɵtextInterpolate(rating_r11);
567
+ i0.ɵɵtextInterpolate(rating_r12);
541
568
  } }
542
569
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
543
- i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_0_Template, 2, 0, "span", 149);
544
- i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_1_Template, 4, 10, "span", 150);
570
+ i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_0_Template, 2, 0, "span", 151);
571
+ i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_1_Template, 4, 10, "span", 152);
545
572
  } if (rf & 2) {
546
573
  let tmp_13_0;
547
574
  let tmp_14_0;
548
- const run_r10 = i0.ɵɵnextContext().$implicit;
575
+ const run_r11 = i0.ɵɵnextContext().$implicit;
549
576
  const ctx_r0 = i0.ɵɵnextContext(3);
550
- i0.ɵɵconditional(!((tmp_13_0 = ctx_r0.getFeedbackForRun(run_r10.ID)) == null ? null : tmp_13_0.Rating) ? 0 : -1);
577
+ i0.ɵɵconditional(!((tmp_13_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_13_0.Rating) ? 0 : -1);
551
578
  i0.ɵɵadvance();
552
- i0.ɵɵconditional((tmp_14_0 = (tmp_14_0 = ctx_r0.getFeedbackForRun(run_r10.ID)) == null ? null : tmp_14_0.Rating) ? 1 : -1, tmp_14_0);
579
+ i0.ɵɵconditional((tmp_14_0 = (tmp_14_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_14_0.Rating) ? 1 : -1, tmp_14_0);
553
580
  } }
554
581
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_0_Template(rf, ctx) { if (rf & 1) {
555
- i0.ɵɵelementStart(0, "span", 154);
556
- i0.ɵɵelement(1, "i", 156);
582
+ i0.ɵɵelementStart(0, "span", 156);
583
+ i0.ɵɵelement(1, "i", 158);
557
584
  i0.ɵɵelementEnd();
558
585
  } }
559
586
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_1_Template(rf, ctx) { if (rf & 1) {
560
- i0.ɵɵelementStart(0, "span", 157);
561
- i0.ɵɵelement(1, "i", 156);
587
+ i0.ɵɵelementStart(0, "span", 159);
588
+ i0.ɵɵelement(1, "i", 158);
562
589
  i0.ɵɵelementStart(2, "span");
563
590
  i0.ɵɵtext(3);
564
591
  i0.ɵɵelementEnd()();
565
592
  } if (rf & 2) {
566
- const run_r10 = i0.ɵɵnextContext(2).$implicit;
567
- i0.ɵɵclassProp("score-low", run_r10.Score < 0.5)("score-medium", run_r10.Score >= 0.5 && run_r10.Score < 0.7)("score-good", run_r10.Score >= 0.7 && run_r10.Score < 0.85)("score-excellent", run_r10.Score >= 0.85);
568
- i0.ɵɵproperty("title", "Auto Score: " + (run_r10.Score * 100).toFixed(0) + "% automated evaluation");
593
+ const run_r11 = i0.ɵɵnextContext(2).$implicit;
594
+ i0.ɵɵclassProp("score-low", run_r11.Score < 0.5)("score-medium", run_r11.Score >= 0.5 && run_r11.Score < 0.7)("score-good", run_r11.Score >= 0.7 && run_r11.Score < 0.85)("score-excellent", run_r11.Score >= 0.85);
595
+ i0.ɵɵproperty("title", "Auto Score: " + (run_r11.Score * 100).toFixed(0) + "% automated evaluation");
569
596
  i0.ɵɵadvance(3);
570
- i0.ɵɵtextInterpolate1("", (run_r10.Score * 100).toFixed(0), "%");
597
+ i0.ɵɵtextInterpolate1("", (run_r11.Score * 100).toFixed(0), "%");
571
598
  } }
572
599
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
573
- i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_0_Template, 2, 0, "span", 154);
574
- i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_1_Template, 4, 10, "span", 155);
600
+ i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_0_Template, 2, 0, "span", 156);
601
+ i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_1_Template, 4, 10, "span", 157);
575
602
  } if (rf & 2) {
576
- const run_r10 = i0.ɵɵnextContext().$implicit;
577
- i0.ɵɵconditional(run_r10.Score == null ? 0 : -1);
603
+ const run_r11 = i0.ɵɵnextContext().$implicit;
604
+ i0.ɵɵconditional(run_r11.Score == null ? 0 : -1);
578
605
  i0.ɵɵadvance();
579
- i0.ɵɵconditional(run_r10.Score != null ? 1 : -1);
606
+ i0.ɵɵconditional(run_r11.Score != null ? 1 : -1);
580
607
  } }
581
608
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_For_2_Template(rf, ctx) { if (rf & 1) {
582
- i0.ɵɵelementStart(0, "span", 158);
609
+ i0.ɵɵelementStart(0, "span", 160);
583
610
  i0.ɵɵtext(1);
584
611
  i0.ɵɵelementEnd();
585
612
  } if (rf & 2) {
586
- const tag_r12 = ctx.$implicit;
613
+ const tag_r13 = ctx.$implicit;
587
614
  i0.ɵɵadvance();
588
- i0.ɵɵtextInterpolate(tag_r12);
615
+ i0.ɵɵtextInterpolate(tag_r13);
589
616
  } }
590
617
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Conditional_3_Template(rf, ctx) { if (rf & 1) {
591
- i0.ɵɵelementStart(0, "span", 159);
618
+ i0.ɵɵelementStart(0, "span", 161);
592
619
  i0.ɵɵtext(1);
593
620
  i0.ɵɵelementEnd();
594
621
  } if (rf & 2) {
595
- const run_r10 = i0.ɵɵnextContext(2).$implicit;
622
+ const run_r11 = i0.ɵɵnextContext(2).$implicit;
596
623
  const ctx_r0 = i0.ɵɵnextContext(3);
597
624
  i0.ɵɵadvance();
598
- i0.ɵɵtextInterpolate1("+", ctx_r0.getRunTags(run_r10).length - 3);
625
+ i0.ɵɵtextInterpolate1("+", ctx_r0.getRunTags(run_r11).length - 3);
599
626
  } }
600
627
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
601
- i0.ɵɵelementStart(0, "div", 145);
602
- i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_For_2_Template, 2, 1, "span", 158, i0.ɵɵrepeaterTrackByIdentity);
603
- i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Conditional_3_Template, 2, 1, "span", 159);
628
+ i0.ɵɵelementStart(0, "div", 147);
629
+ i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_For_2_Template, 2, 1, "span", 160, i0.ɵɵrepeaterTrackByIdentity);
630
+ i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Conditional_3_Template, 2, 1, "span", 161);
604
631
  i0.ɵɵelementEnd();
605
632
  } if (rf & 2) {
606
- const run_r10 = i0.ɵɵnextContext().$implicit;
633
+ const run_r11 = i0.ɵɵnextContext().$implicit;
607
634
  const ctx_r0 = i0.ɵɵnextContext(3);
608
635
  i0.ɵɵadvance();
609
- i0.ɵɵrepeater(ctx_r0.getRunTags(run_r10).slice(0, 3));
636
+ i0.ɵɵrepeater(ctx_r0.getRunTags(run_r11).slice(0, 3));
610
637
  i0.ɵɵadvance(2);
611
- i0.ɵɵconditional(ctx_r0.getRunTags(run_r10).length > 3 ? 3 : -1);
638
+ i0.ɵɵconditional(ctx_r0.getRunTags(run_r11).length > 3 ? 3 : -1);
612
639
  } }
613
640
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
614
- const _r9 = i0.ɵɵgetCurrentView();
615
- i0.ɵɵelementStart(0, "div", 133);
616
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template_div_click_0_listener() { const run_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestRun(run_r10.ID)); });
617
- i0.ɵɵelementStart(1, "div", 134);
618
- i0.ɵɵelement(2, "i", 135);
641
+ const _r10 = i0.ɵɵgetCurrentView();
642
+ i0.ɵɵelementStart(0, "div", 135);
643
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template_div_click_0_listener() { const run_r11 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestRun(run_r11.ID)); });
644
+ i0.ɵɵelementStart(1, "div", 136);
645
+ i0.ɵɵelement(2, "i", 137);
619
646
  i0.ɵɵelementEnd();
620
- i0.ɵɵelementStart(3, "div", 136)(4, "div", 137)(5, "span", 138);
647
+ i0.ɵɵelementStart(3, "div", 138)(4, "div", 139)(5, "span", 140);
621
648
  i0.ɵɵtext(6);
622
649
  i0.ɵɵelementEnd();
623
- i0.ɵɵelementStart(7, "span", 139);
650
+ i0.ɵɵelementStart(7, "span", 141);
624
651
  i0.ɵɵtext(8);
625
652
  i0.ɵɵelementEnd()();
626
- i0.ɵɵelementStart(9, "div", 140)(10, "span");
627
- i0.ɵɵelement(11, "i", 141);
653
+ i0.ɵɵelementStart(9, "div", 142)(10, "span");
654
+ i0.ɵɵelement(11, "i", 143);
628
655
  i0.ɵɵtext(12);
629
656
  i0.ɵɵelementEnd();
630
657
  i0.ɵɵconditionalCreate(13, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_13_Template, 3, 1, "span");
631
658
  i0.ɵɵconditionalCreate(14, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_14_Template, 3, 1, "span");
632
- i0.ɵɵconditionalCreate(15, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_15_Template, 1, 2, "mj-entity-link-pill", 142);
659
+ i0.ɵɵconditionalCreate(15, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_15_Template, 1, 2, "mj-entity-link-pill", 144);
633
660
  i0.ɵɵelementEnd();
634
- i0.ɵɵelementStart(16, "div", 143);
635
- i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_17_Template, 4, 17, "span", 144);
661
+ i0.ɵɵelementStart(16, "div", 145);
662
+ i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_17_Template, 4, 17, "span", 146);
636
663
  i0.ɵɵconditionalCreate(18, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Template, 2, 2);
637
664
  i0.ɵɵconditionalCreate(19, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Template, 2, 2);
638
665
  i0.ɵɵelementEnd();
639
- i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Template, 4, 1, "div", 145);
666
+ i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Template, 4, 1, "div", 147);
640
667
  i0.ɵɵelementEnd();
641
- i0.ɵɵelement(21, "i", 146);
668
+ i0.ɵɵelement(21, "i", 148);
642
669
  i0.ɵɵelementEnd();
643
670
  } if (rf & 2) {
644
- const run_r10 = ctx.$implicit;
671
+ const run_r11 = ctx.$implicit;
645
672
  const ctx_r0 = i0.ɵɵnextContext(3);
646
673
  i0.ɵɵadvance();
647
- i0.ɵɵstyleProp("background-color", ctx_r0.getRunStatusColor(run_r10.Status));
674
+ i0.ɵɵstyleProp("background-color", ctx_r0.getRunStatusColor(run_r11.Status));
648
675
  i0.ɵɵadvance();
649
- i0.ɵɵclassProp("fa-check", run_r10.Status === "Passed")("fa-times", run_r10.Status === "Failed")("fa-exclamation", run_r10.Status === "Error")("fa-stopwatch", run_r10.Status === "Timeout")("fa-spinner", run_r10.Status === "Running")("fa-clock", run_r10.Status === "Pending");
676
+ i0.ɵɵclassProp("fa-check", run_r11.Status === "Passed")("fa-times", run_r11.Status === "Failed")("fa-exclamation", run_r11.Status === "Error")("fa-stopwatch", run_r11.Status === "Timeout")("fa-spinner", run_r11.Status === "Running")("fa-clock", run_r11.Status === "Pending");
650
677
  i0.ɵɵadvance(4);
651
- i0.ɵɵtextInterpolate1("Run #", run_r10.ID.substring(0, 8));
678
+ i0.ɵɵtextInterpolate1("Run #", run_r11.ID.substring(0, 8));
652
679
  i0.ɵɵadvance();
653
- i0.ɵɵstyleProp("color", ctx_r0.getRunStatusColor(run_r10.Status));
680
+ i0.ɵɵstyleProp("color", ctx_r0.getRunStatusColor(run_r11.Status));
654
681
  i0.ɵɵadvance();
655
- i0.ɵɵtextInterpolate(run_r10.Status);
682
+ i0.ɵɵtextInterpolate(run_r11.Status);
656
683
  i0.ɵɵadvance(4);
657
- i0.ɵɵtextInterpolate1(" ", ctx_r0.getRelativeTime(run_r10.StartedAt));
684
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.getRelativeTime(run_r11.StartedAt));
658
685
  i0.ɵɵadvance();
659
- i0.ɵɵconditional(run_r10.DurationSeconds ? 13 : -1);
686
+ i0.ɵɵconditional(run_r11.DurationSeconds ? 13 : -1);
660
687
  i0.ɵɵadvance();
661
- i0.ɵɵconditional(run_r10.CostUSD ? 14 : -1);
688
+ i0.ɵɵconditional(run_r11.CostUSD ? 14 : -1);
662
689
  i0.ɵɵadvance();
663
- i0.ɵɵconditional(run_r10.TargetLogEntityID && run_r10.TargetLogID ? 15 : -1);
690
+ i0.ɵɵconditional(run_r11.TargetLogEntityID && run_r11.TargetLogID ? 15 : -1);
664
691
  i0.ɵɵadvance(2);
665
692
  i0.ɵɵconditional(ctx_r0.evalPreferences.showExecution ? 17 : -1);
666
693
  i0.ɵɵadvance();
@@ -668,11 +695,11 @@ function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template
668
695
  i0.ɵɵadvance();
669
696
  i0.ɵɵconditional(ctx_r0.evalPreferences.showAuto ? 19 : -1);
670
697
  i0.ɵɵadvance();
671
- i0.ɵɵconditional(ctx_r0.getRunTags(run_r10).length > 0 ? 20 : -1);
698
+ i0.ɵɵconditional(ctx_r0.getRunTags(run_r11).length > 0 ? 20 : -1);
672
699
  } }
673
700
  function MJTestFormComponentExtended_Conditional_65_Conditional_2_Template(rf, ctx) { if (rf & 1) {
674
- i0.ɵɵelementStart(0, "div", 124);
675
- i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template, 22, 26, "div", 132, i0.ɵɵrepeaterTrackByIdentity);
701
+ i0.ɵɵelementStart(0, "div", 126);
702
+ i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template, 22, 26, "div", 134, i0.ɵɵrepeaterTrackByIdentity);
676
703
  i0.ɵɵelementEnd();
677
704
  } if (rf & 2) {
678
705
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -680,16 +707,16 @@ function MJTestFormComponentExtended_Conditional_65_Conditional_2_Template(rf, c
680
707
  i0.ɵɵrepeater(ctx_r0.testRuns);
681
708
  } }
682
709
  function MJTestFormComponentExtended_Conditional_65_Conditional_3_Template(rf, ctx) { if (rf & 1) {
683
- const _r13 = i0.ɵɵgetCurrentView();
684
- i0.ɵɵelementStart(0, "mj-empty-state", 160);
685
- i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_65_Conditional_3_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r13); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.runTest()); });
710
+ const _r14 = i0.ɵɵgetCurrentView();
711
+ i0.ɵɵelementStart(0, "mj-empty-state", 162);
712
+ i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_65_Conditional_3_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.runTest()); });
686
713
  i0.ɵɵelementEnd();
687
714
  } }
688
715
  function MJTestFormComponentExtended_Conditional_65_Template(rf, ctx) { if (rf & 1) {
689
716
  i0.ɵɵelementStart(0, "div", 38);
690
- i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_1_Template, 4, 1, "div", 123);
691
- i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_2_Template, 3, 0, "div", 124);
692
- i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_3_Template, 1, 0, "mj-empty-state", 125);
717
+ i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_1_Template, 4, 1, "div", 125);
718
+ i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_2_Template, 3, 0, "div", 126);
719
+ i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_3_Template, 1, 0, "mj-empty-state", 127);
693
720
  i0.ɵɵelementEnd();
694
721
  } if (rf & 2) {
695
722
  const ctx_r0 = i0.ɵɵnextContext();
@@ -701,15 +728,15 @@ function MJTestFormComponentExtended_Conditional_65_Template(rf, ctx) { if (rf &
701
728
  i0.ɵɵconditional(ctx_r0.testRunsLoaded && !ctx_r0.loadingRuns && ctx_r0.testRuns.length === 0 ? 3 : -1);
702
729
  } }
703
730
  function MJTestFormComponentExtended_Conditional_66_Conditional_1_For_3_Template(rf, ctx) { if (rf & 1) {
704
- i0.ɵɵelementStart(0, "div", 127);
705
- i0.ɵɵelement(1, "div", 128);
706
- i0.ɵɵelementStart(2, "div", 129);
707
- i0.ɵɵelement(3, "div", 130)(4, "div", 131);
731
+ i0.ɵɵelementStart(0, "div", 129);
732
+ i0.ɵɵelement(1, "div", 130);
733
+ i0.ɵɵelementStart(2, "div", 131);
734
+ i0.ɵɵelement(3, "div", 132)(4, "div", 133);
708
735
  i0.ɵɵelementEnd()();
709
736
  } }
710
737
  function MJTestFormComponentExtended_Conditional_66_Conditional_1_Template(rf, ctx) { if (rf & 1) {
711
- i0.ɵɵelementStart(0, "div", 123)(1, "div", 126);
712
- i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_1_For_3_Template, 5, 0, "div", 127, i0.ɵɵrepeaterTrackByIdentity);
738
+ i0.ɵɵelementStart(0, "div", 125)(1, "div", 128);
739
+ i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_1_For_3_Template, 5, 0, "div", 129, i0.ɵɵrepeaterTrackByIdentity);
713
740
  i0.ɵɵelementEnd()();
714
741
  } if (rf & 2) {
715
742
  i0.ɵɵadvance(2);
@@ -721,40 +748,40 @@ function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Conditio
721
748
  i0.ɵɵtext(2);
722
749
  i0.ɵɵelementEnd();
723
750
  } if (rf & 2) {
724
- const suiteTest_r15 = i0.ɵɵnextContext().$implicit;
751
+ const suiteTest_r16 = i0.ɵɵnextContext().$implicit;
725
752
  i0.ɵɵadvance(2);
726
- i0.ɵɵtextInterpolate1(" ", suiteTest_r15.Status);
753
+ i0.ɵɵtextInterpolate1(" ", suiteTest_r16.Status);
727
754
  } }
728
755
  function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
729
- const _r14 = i0.ɵɵgetCurrentView();
730
- i0.ɵɵelementStart(0, "div", 164);
731
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template_div_click_0_listener() { const suiteTest_r15 = i0.ɵɵrestoreView(_r14).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestSuite(suiteTest_r15.SuiteID)); });
732
- i0.ɵɵelementStart(1, "div", 165);
756
+ const _r15 = i0.ɵɵgetCurrentView();
757
+ i0.ɵɵelementStart(0, "div", 166);
758
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template_div_click_0_listener() { const suiteTest_r16 = i0.ɵɵrestoreView(_r15).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestSuite(suiteTest_r16.SuiteID)); });
759
+ i0.ɵɵelementStart(1, "div", 167);
733
760
  i0.ɵɵelement(2, "i", 33);
734
761
  i0.ɵɵelementEnd();
735
- i0.ɵɵelementStart(3, "div", 166)(4, "div", 167);
762
+ i0.ɵɵelementStart(3, "div", 168)(4, "div", 169);
736
763
  i0.ɵɵtext(5);
737
764
  i0.ɵɵelementEnd();
738
- i0.ɵɵelementStart(6, "div", 168)(7, "span");
739
- i0.ɵɵelement(8, "i", 169);
765
+ i0.ɵɵelementStart(6, "div", 170)(7, "span");
766
+ i0.ɵɵelement(8, "i", 171);
740
767
  i0.ɵɵtext(9);
741
768
  i0.ɵɵelementEnd();
742
769
  i0.ɵɵconditionalCreate(10, MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Conditional_10_Template, 3, 1, "span");
743
770
  i0.ɵɵelementEnd()();
744
- i0.ɵɵelement(11, "i", 146);
771
+ i0.ɵɵelement(11, "i", 148);
745
772
  i0.ɵɵelementEnd();
746
773
  } if (rf & 2) {
747
- const suiteTest_r15 = ctx.$implicit;
774
+ const suiteTest_r16 = ctx.$implicit;
748
775
  i0.ɵɵadvance(5);
749
- i0.ɵɵtextInterpolate(suiteTest_r15.Suite);
776
+ i0.ɵɵtextInterpolate(suiteTest_r16.Suite);
750
777
  i0.ɵɵadvance(4);
751
- i0.ɵɵtextInterpolate1(" Sequence: ", suiteTest_r15.Sequence);
778
+ i0.ɵɵtextInterpolate1(" Sequence: ", suiteTest_r16.Sequence);
752
779
  i0.ɵɵadvance();
753
- i0.ɵɵconditional(suiteTest_r15.Status ? 10 : -1);
780
+ i0.ɵɵconditional(suiteTest_r16.Status ? 10 : -1);
754
781
  } }
755
782
  function MJTestFormComponentExtended_Conditional_66_Conditional_2_Template(rf, ctx) { if (rf & 1) {
756
- i0.ɵɵelementStart(0, "div", 161);
757
- i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template, 12, 3, "div", 163, i0.ɵɵrepeaterTrackByIdentity);
783
+ i0.ɵɵelementStart(0, "div", 163);
784
+ i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template, 12, 3, "div", 165, i0.ɵɵrepeaterTrackByIdentity);
758
785
  i0.ɵɵelementEnd();
759
786
  } if (rf & 2) {
760
787
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -762,13 +789,13 @@ function MJTestFormComponentExtended_Conditional_66_Conditional_2_Template(rf, c
762
789
  i0.ɵɵrepeater(ctx_r0.suiteTests);
763
790
  } }
764
791
  function MJTestFormComponentExtended_Conditional_66_Conditional_3_Template(rf, ctx) { if (rf & 1) {
765
- i0.ɵɵelement(0, "mj-empty-state", 162);
792
+ i0.ɵɵelement(0, "mj-empty-state", 164);
766
793
  } }
767
794
  function MJTestFormComponentExtended_Conditional_66_Template(rf, ctx) { if (rf & 1) {
768
795
  i0.ɵɵelementStart(0, "div", 39);
769
- i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_1_Template, 4, 1, "div", 123);
770
- i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_2_Template, 3, 0, "div", 161);
771
- i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_66_Conditional_3_Template, 1, 0, "mj-empty-state", 162);
796
+ i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_1_Template, 4, 1, "div", 125);
797
+ i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_2_Template, 3, 0, "div", 163);
798
+ i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_66_Conditional_3_Template, 1, 0, "mj-empty-state", 164);
772
799
  i0.ɵɵelementEnd();
773
800
  } if (rf & 2) {
774
801
  const ctx_r0 = i0.ɵɵnextContext();
@@ -780,19 +807,19 @@ function MJTestFormComponentExtended_Conditional_66_Template(rf, ctx) { if (rf &
780
807
  i0.ɵɵconditional(ctx_r0.suiteTestsLoaded && !ctx_r0.loadingSuites && ctx_r0.suiteTests.length === 0 ? 3 : -1);
781
808
  } }
782
809
  function MJTestFormComponentExtended_Conditional_67_Conditional_1_Template(rf, ctx) { if (rf & 1) {
783
- i0.ɵɵelementStart(0, "div", 123);
784
- i0.ɵɵelement(1, "mj-loading", 171);
810
+ i0.ɵɵelementStart(0, "div", 125);
811
+ i0.ɵɵelement(1, "mj-loading", 173);
785
812
  i0.ɵɵelementEnd();
786
813
  } }
787
814
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_9_Template(rf, ctx) { if (rf & 1) {
788
- i0.ɵɵelementStart(0, "div", 181)(1, "div", 187);
789
- i0.ɵɵelement(2, "i", 188);
815
+ i0.ɵɵelementStart(0, "div", 183)(1, "div", 189);
816
+ i0.ɵɵelement(2, "i", 190);
790
817
  i0.ɵɵelementEnd();
791
- i0.ɵɵelementStart(3, "div", 184)(4, "div", 185);
818
+ i0.ɵɵelementStart(3, "div", 186)(4, "div", 187);
792
819
  i0.ɵɵtext(5);
793
- i0.ɵɵelement(6, "i", 189);
820
+ i0.ɵɵelement(6, "i", 191);
794
821
  i0.ɵɵelementEnd();
795
- i0.ɵɵelementStart(7, "div", 186);
822
+ i0.ɵɵelementStart(7, "div", 188);
796
823
  i0.ɵɵtext(8, "Pass Rate");
797
824
  i0.ɵɵelementEnd()()();
798
825
  } if (rf & 2) {
@@ -803,13 +830,13 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17
803
830
  i0.ɵɵclassProp("fa-arrow-up", ctx_r0.getPassRateTrend() === "up")("fa-arrow-down", ctx_r0.getPassRateTrend() === "down")("fa-minus", ctx_r0.getPassRateTrend() === "stable")("trend-up", ctx_r0.getPassRateTrend() === "up")("trend-down", ctx_r0.getPassRateTrend() === "down");
804
831
  } }
805
832
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_10_Template(rf, ctx) { if (rf & 1) {
806
- i0.ɵɵelementStart(0, "div", 181)(1, "div", 182);
807
- i0.ɵɵelement(2, "i", 190);
833
+ i0.ɵɵelementStart(0, "div", 183)(1, "div", 184);
834
+ i0.ɵɵelement(2, "i", 192);
808
835
  i0.ɵɵelementEnd();
809
- i0.ɵɵelementStart(3, "div", 184)(4, "div", 185);
836
+ i0.ɵɵelementStart(3, "div", 186)(4, "div", 187);
810
837
  i0.ɵɵtext(5);
811
838
  i0.ɵɵelementEnd();
812
- i0.ɵɵelementStart(6, "div", 186);
839
+ i0.ɵɵelementStart(6, "div", 188);
813
840
  i0.ɵɵtext(7, "Avg Score");
814
841
  i0.ɵɵelementEnd()()();
815
842
  } if (rf & 2) {
@@ -818,33 +845,33 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17
818
845
  i0.ɵɵtextInterpolate1("", (ctx_r0.getOverallAvgScore() * 100).toFixed(1), "%");
819
846
  } }
820
847
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
821
- i0.ɵɵelementStart(0, "div", 178)(1, "div", 181)(2, "div", 182);
822
- i0.ɵɵelement(3, "i", 183);
848
+ i0.ɵɵelementStart(0, "div", 180)(1, "div", 183)(2, "div", 184);
849
+ i0.ɵɵelement(3, "i", 185);
823
850
  i0.ɵɵelementEnd();
824
- i0.ɵɵelementStart(4, "div", 184)(5, "div", 185);
851
+ i0.ɵɵelementStart(4, "div", 186)(5, "div", 187);
825
852
  i0.ɵɵtext(6);
826
853
  i0.ɵɵelementEnd();
827
- i0.ɵɵelementStart(7, "div", 186);
854
+ i0.ɵɵelementStart(7, "div", 188);
828
855
  i0.ɵɵtext(8, "Total Runs");
829
856
  i0.ɵɵelementEnd()()();
830
- i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_9_Template, 9, 11, "div", 181);
831
- i0.ɵɵconditionalCreate(10, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_10_Template, 8, 1, "div", 181);
832
- i0.ɵɵelementStart(11, "div", 181)(12, "div", 182);
833
- i0.ɵɵelement(13, "i", 147);
857
+ i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_9_Template, 9, 11, "div", 183);
858
+ i0.ɵɵconditionalCreate(10, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_10_Template, 8, 1, "div", 183);
859
+ i0.ɵɵelementStart(11, "div", 183)(12, "div", 184);
860
+ i0.ɵɵelement(13, "i", 149);
834
861
  i0.ɵɵelementEnd();
835
- i0.ɵɵelementStart(14, "div", 184)(15, "div", 185);
862
+ i0.ɵɵelementStart(14, "div", 186)(15, "div", 187);
836
863
  i0.ɵɵtext(16);
837
864
  i0.ɵɵelementEnd();
838
- i0.ɵɵelementStart(17, "div", 186);
865
+ i0.ɵɵelementStart(17, "div", 188);
839
866
  i0.ɵɵtext(18, "Avg Duration");
840
867
  i0.ɵɵelementEnd()()();
841
- i0.ɵɵelementStart(19, "div", 181)(20, "div", 182);
842
- i0.ɵɵelement(21, "i", 148);
868
+ i0.ɵɵelementStart(19, "div", 183)(20, "div", 184);
869
+ i0.ɵɵelement(21, "i", 150);
843
870
  i0.ɵɵelementEnd();
844
- i0.ɵɵelementStart(22, "div", 184)(23, "div", 185);
871
+ i0.ɵɵelementStart(22, "div", 186)(23, "div", 187);
845
872
  i0.ɵɵtext(24);
846
873
  i0.ɵɵelementEnd();
847
- i0.ɵɵelementStart(25, "div", 186);
874
+ i0.ɵɵelementStart(25, "div", 188);
848
875
  i0.ɵɵtext(26, "Avg Cost");
849
876
  i0.ɵɵelementEnd()()()();
850
877
  } if (rf & 2) {
@@ -881,70 +908,70 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18
881
908
  i0.ɵɵelementEnd();
882
909
  } }
883
910
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_6_Template(rf, ctx) { if (rf & 1) {
884
- i0.ɵɵelementStart(0, "td", 196);
911
+ i0.ɵɵelementStart(0, "td", 198);
885
912
  i0.ɵɵtext(1);
886
913
  i0.ɵɵelementEnd();
887
914
  } if (rf & 2) {
888
- const day_r17 = i0.ɵɵnextContext().$implicit;
915
+ const day_r18 = i0.ɵɵnextContext().$implicit;
889
916
  i0.ɵɵadvance();
890
- i0.ɵɵtextInterpolate(day_r17.passCount);
917
+ i0.ɵɵtextInterpolate(day_r18.passCount);
891
918
  } }
892
919
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_7_Template(rf, ctx) { if (rf & 1) {
893
- i0.ɵɵelementStart(0, "td", 197);
920
+ i0.ɵɵelementStart(0, "td", 199);
894
921
  i0.ɵɵtext(1);
895
922
  i0.ɵɵelementEnd();
896
923
  } if (rf & 2) {
897
- const day_r17 = i0.ɵɵnextContext().$implicit;
924
+ const day_r18 = i0.ɵɵnextContext().$implicit;
898
925
  i0.ɵɵadvance();
899
- i0.ɵɵtextInterpolate(day_r17.failCount);
926
+ i0.ɵɵtextInterpolate(day_r18.failCount);
900
927
  } }
901
928
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_8_Template(rf, ctx) { if (rf & 1) {
902
- i0.ɵɵelementStart(0, "td", 198)(1, "div", 202);
903
- i0.ɵɵelement(2, "div", 203);
904
- i0.ɵɵelementStart(3, "span", 204);
929
+ i0.ɵɵelementStart(0, "td", 200)(1, "div", 204);
930
+ i0.ɵɵelement(2, "div", 205);
931
+ i0.ɵɵelementStart(3, "span", 206);
905
932
  i0.ɵɵtext(4);
906
933
  i0.ɵɵelementEnd()()();
907
934
  } if (rf & 2) {
908
- const day_r17 = i0.ɵɵnextContext().$implicit;
935
+ const day_r18 = i0.ɵɵnextContext().$implicit;
909
936
  i0.ɵɵadvance(2);
910
- i0.ɵɵstyleProp("width", day_r17.passRate, "%");
937
+ i0.ɵɵstyleProp("width", day_r18.passRate, "%");
911
938
  i0.ɵɵadvance(2);
912
- i0.ɵɵtextInterpolate1("", day_r17.passRate.toFixed(1), "%");
939
+ i0.ɵɵtextInterpolate1("", day_r18.passRate.toFixed(1), "%");
913
940
  } }
914
941
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_9_Template(rf, ctx) { if (rf & 1) {
915
- i0.ɵɵelementStart(0, "td", 199);
942
+ i0.ɵɵelementStart(0, "td", 201);
916
943
  i0.ɵɵtext(1);
917
944
  i0.ɵɵelementEnd();
918
945
  } if (rf & 2) {
919
- const day_r17 = i0.ɵɵnextContext().$implicit;
946
+ const day_r18 = i0.ɵɵnextContext().$implicit;
920
947
  i0.ɵɵadvance();
921
- i0.ɵɵtextInterpolate1("", (day_r17.avgScore * 100).toFixed(1), "%");
948
+ i0.ɵɵtextInterpolate1("", (day_r18.avgScore * 100).toFixed(1), "%");
922
949
  } }
923
950
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Template(rf, ctx) { if (rf & 1) {
924
- i0.ɵɵelementStart(0, "tr")(1, "td", 194);
951
+ i0.ɵɵelementStart(0, "tr")(1, "td", 196);
925
952
  i0.ɵɵtext(2);
926
953
  i0.ɵɵpipe(3, "date");
927
954
  i0.ɵɵelementEnd();
928
- i0.ɵɵelementStart(4, "td", 195);
955
+ i0.ɵɵelementStart(4, "td", 197);
929
956
  i0.ɵɵtext(5);
930
957
  i0.ɵɵelementEnd();
931
- i0.ɵɵconditionalCreate(6, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_6_Template, 2, 1, "td", 196);
932
- i0.ɵɵconditionalCreate(7, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_7_Template, 2, 1, "td", 197);
933
- i0.ɵɵconditionalCreate(8, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_8_Template, 5, 3, "td", 198);
934
- i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_9_Template, 2, 1, "td", 199);
935
- i0.ɵɵelementStart(10, "td", 200);
958
+ i0.ɵɵconditionalCreate(6, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_6_Template, 2, 1, "td", 198);
959
+ i0.ɵɵconditionalCreate(7, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_7_Template, 2, 1, "td", 199);
960
+ i0.ɵɵconditionalCreate(8, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_8_Template, 5, 3, "td", 200);
961
+ i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_9_Template, 2, 1, "td", 201);
962
+ i0.ɵɵelementStart(10, "td", 202);
936
963
  i0.ɵɵtext(11);
937
964
  i0.ɵɵelementEnd();
938
- i0.ɵɵelementStart(12, "td", 201);
965
+ i0.ɵɵelementStart(12, "td", 203);
939
966
  i0.ɵɵtext(13);
940
967
  i0.ɵɵelementEnd()();
941
968
  } if (rf & 2) {
942
- const day_r17 = ctx.$implicit;
969
+ const day_r18 = ctx.$implicit;
943
970
  const ctx_r0 = i0.ɵɵnextContext(4);
944
971
  i0.ɵɵadvance(2);
945
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, day_r17.date, "mediumDate"));
972
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, day_r18.date, "mediumDate"));
946
973
  i0.ɵɵadvance(3);
947
- i0.ɵɵtextInterpolate(day_r17.runCount);
974
+ i0.ɵɵtextInterpolate(day_r18.runCount);
948
975
  i0.ɵɵadvance();
949
976
  i0.ɵɵconditional(ctx_r0.evalPreferences.showExecution ? 6 : -1);
950
977
  i0.ɵɵadvance();
@@ -954,16 +981,16 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18
954
981
  i0.ɵɵadvance();
955
982
  i0.ɵɵconditional(ctx_r0.evalPreferences.showAuto ? 9 : -1);
956
983
  i0.ɵɵadvance(2);
957
- i0.ɵɵtextInterpolate(ctx_r0.formatDuration(day_r17.avgDuration));
984
+ i0.ɵɵtextInterpolate(ctx_r0.formatDuration(day_r18.avgDuration));
958
985
  i0.ɵɵadvance(2);
959
- i0.ɵɵtextInterpolate(ctx_r0.formatCost(day_r17.avgCost));
986
+ i0.ɵɵtextInterpolate(ctx_r0.formatCost(day_r18.avgCost));
960
987
  } }
961
988
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
962
- i0.ɵɵelementStart(0, "div", 179)(1, "h3");
963
- i0.ɵɵelement(2, "i", 191);
989
+ i0.ɵɵelementStart(0, "div", 181)(1, "h3");
990
+ i0.ɵɵelement(2, "i", 193);
964
991
  i0.ɵɵtext(3, " Daily Performance");
965
992
  i0.ɵɵelementEnd();
966
- i0.ɵɵelementStart(4, "div", 192)(5, "table", 193)(6, "thead")(7, "tr")(8, "th");
993
+ i0.ɵɵelementStart(4, "div", 194)(5, "table", 195)(6, "thead")(7, "tr")(8, "th");
967
994
  i0.ɵɵtext(9, "Date");
968
995
  i0.ɵɵelementEnd();
969
996
  i0.ɵɵelementStart(10, "th");
@@ -996,134 +1023,134 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18
996
1023
  i0.ɵɵrepeater(ctx_r0.historyData);
997
1024
  } }
998
1025
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
999
- i0.ɵɵelementStart(0, "span", 216);
1026
+ i0.ɵɵelementStart(0, "span", 218);
1000
1027
  i0.ɵɵtext(1);
1001
1028
  i0.ɵɵelementEnd();
1002
1029
  } if (rf & 2) {
1003
- const tag_r20 = ctx.$implicit;
1030
+ const tag_r21 = ctx.$implicit;
1004
1031
  i0.ɵɵadvance();
1005
- i0.ɵɵtextInterpolate(tag_r20);
1032
+ i0.ɵɵtextInterpolate(tag_r21);
1006
1033
  } }
1007
1034
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
1008
- i0.ɵɵelementStart(0, "span", 217);
1035
+ i0.ɵɵelementStart(0, "span", 219);
1009
1036
  i0.ɵɵtext(1);
1010
1037
  i0.ɵɵelementEnd();
1011
1038
  } if (rf & 2) {
1012
- const suite_r19 = i0.ɵɵnextContext(2).$implicit;
1039
+ const suite_r20 = i0.ɵɵnextContext(2).$implicit;
1013
1040
  i0.ɵɵadvance();
1014
- i0.ɵɵtextInterpolate1("+", suite_r19.tags.length - 3);
1041
+ i0.ɵɵtextInterpolate1("+", suite_r20.tags.length - 3);
1015
1042
  } }
1016
1043
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Template(rf, ctx) { if (rf & 1) {
1017
- i0.ɵɵelementStart(0, "div", 210);
1018
- i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_For_2_Template, 2, 1, "span", 216, i0.ɵɵrepeaterTrackByIdentity);
1019
- i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Conditional_3_Template, 2, 1, "span", 217);
1044
+ i0.ɵɵelementStart(0, "div", 212);
1045
+ i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_For_2_Template, 2, 1, "span", 218, i0.ɵɵrepeaterTrackByIdentity);
1046
+ i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Conditional_3_Template, 2, 1, "span", 219);
1020
1047
  i0.ɵɵelementEnd();
1021
1048
  } if (rf & 2) {
1022
- const suite_r19 = i0.ɵɵnextContext().$implicit;
1049
+ const suite_r20 = i0.ɵɵnextContext().$implicit;
1023
1050
  i0.ɵɵadvance();
1024
- i0.ɵɵrepeater(suite_r19.tags.slice(0, 3));
1051
+ i0.ɵɵrepeater(suite_r20.tags.slice(0, 3));
1025
1052
  i0.ɵɵadvance(2);
1026
- i0.ɵɵconditional(suite_r19.tags.length > 3 ? 3 : -1);
1053
+ i0.ɵɵconditional(suite_r20.tags.length > 3 ? 3 : -1);
1027
1054
  } }
1028
1055
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_11_Template(rf, ctx) { if (rf & 1) {
1029
- i0.ɵɵelementStart(0, "div", 212)(1, "span", 218);
1056
+ i0.ɵɵelementStart(0, "div", 214)(1, "span", 220);
1030
1057
  i0.ɵɵtext(2);
1031
1058
  i0.ɵɵelementEnd();
1032
- i0.ɵɵelementStart(3, "span", 214);
1059
+ i0.ɵɵelementStart(3, "span", 216);
1033
1060
  i0.ɵɵtext(4, "Pass Rate");
1034
1061
  i0.ɵɵelementEnd()();
1035
1062
  } if (rf & 2) {
1036
- const suite_r19 = i0.ɵɵnextContext().$implicit;
1063
+ const suite_r20 = i0.ɵɵnextContext().$implicit;
1037
1064
  i0.ɵɵadvance();
1038
- i0.ɵɵclassProp("high", suite_r19.passRate >= 80)("low", suite_r19.passRate < 50);
1065
+ i0.ɵɵclassProp("high", suite_r20.passRate >= 80)("low", suite_r20.passRate < 50);
1039
1066
  i0.ɵɵadvance();
1040
- i0.ɵɵtextInterpolate1(" ", suite_r19.passRate.toFixed(1), "% ");
1067
+ i0.ɵɵtextInterpolate1(" ", suite_r20.passRate.toFixed(1), "% ");
1041
1068
  } }
1042
1069
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_12_Template(rf, ctx) { if (rf & 1) {
1043
- i0.ɵɵelementStart(0, "div", 212)(1, "span", 213);
1070
+ i0.ɵɵelementStart(0, "div", 214)(1, "span", 215);
1044
1071
  i0.ɵɵtext(2);
1045
1072
  i0.ɵɵelementEnd();
1046
- i0.ɵɵelementStart(3, "span", 214);
1073
+ i0.ɵɵelementStart(3, "span", 216);
1047
1074
  i0.ɵɵtext(4, "Avg Score");
1048
1075
  i0.ɵɵelementEnd()();
1049
1076
  } if (rf & 2) {
1050
- const suite_r19 = i0.ɵɵnextContext().$implicit;
1077
+ const suite_r20 = i0.ɵɵnextContext().$implicit;
1051
1078
  i0.ɵɵadvance(2);
1052
- i0.ɵɵtextInterpolate1("", (suite_r19.avgScore * 100).toFixed(1), "%");
1079
+ i0.ɵɵtextInterpolate1("", (suite_r20.avgScore * 100).toFixed(1), "%");
1053
1080
  } }
1054
1081
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_23_Template(rf, ctx) { if (rf & 1) {
1055
- i0.ɵɵelementStart(0, "div", 212)(1, "span", 213);
1082
+ i0.ɵɵelementStart(0, "div", 214)(1, "span", 215);
1056
1083
  i0.ɵɵtext(2);
1057
1084
  i0.ɵɵelementEnd();
1058
- i0.ɵɵelementStart(3, "span", 214);
1085
+ i0.ɵɵelementStart(3, "span", 216);
1059
1086
  i0.ɵɵtext(4, "Last Run");
1060
1087
  i0.ɵɵelementEnd()();
1061
1088
  } if (rf & 2) {
1062
- const suite_r19 = i0.ɵɵnextContext().$implicit;
1089
+ const suite_r20 = i0.ɵɵnextContext().$implicit;
1063
1090
  const ctx_r0 = i0.ɵɵnextContext(4);
1064
1091
  i0.ɵɵadvance(2);
1065
- i0.ɵɵtextInterpolate(ctx_r0.getRelativeTime(suite_r19.lastRun));
1092
+ i0.ɵɵtextInterpolate(ctx_r0.getRelativeTime(suite_r20.lastRun));
1066
1093
  } }
1067
1094
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template(rf, ctx) { if (rf & 1) {
1068
- const _r18 = i0.ɵɵgetCurrentView();
1069
- i0.ɵɵelementStart(0, "div", 207);
1070
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template_div_click_0_listener() { const suite_r19 = i0.ɵɵrestoreView(_r18).$implicit; const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.openSuiteFromHistory(suite_r19.suiteId)); });
1071
- i0.ɵɵelementStart(1, "div", 208)(2, "div", 209);
1095
+ const _r19 = i0.ɵɵgetCurrentView();
1096
+ i0.ɵɵelementStart(0, "div", 209);
1097
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template_div_click_0_listener() { const suite_r20 = i0.ɵɵrestoreView(_r19).$implicit; const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.openSuiteFromHistory(suite_r20.suiteId)); });
1098
+ i0.ɵɵelementStart(1, "div", 210)(2, "div", 211);
1072
1099
  i0.ɵɵtext(3);
1073
1100
  i0.ɵɵelementEnd();
1074
- i0.ɵɵconditionalCreate(4, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Template, 4, 1, "div", 210);
1101
+ i0.ɵɵconditionalCreate(4, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Template, 4, 1, "div", 212);
1075
1102
  i0.ɵɵelementEnd();
1076
- i0.ɵɵelementStart(5, "div", 211)(6, "div", 212)(7, "span", 213);
1103
+ i0.ɵɵelementStart(5, "div", 213)(6, "div", 214)(7, "span", 215);
1077
1104
  i0.ɵɵtext(8);
1078
1105
  i0.ɵɵelementEnd();
1079
- i0.ɵɵelementStart(9, "span", 214);
1106
+ i0.ɵɵelementStart(9, "span", 216);
1080
1107
  i0.ɵɵtext(10, "Runs");
1081
1108
  i0.ɵɵelementEnd()();
1082
- i0.ɵɵconditionalCreate(11, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_11_Template, 5, 5, "div", 212);
1083
- i0.ɵɵconditionalCreate(12, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_12_Template, 5, 1, "div", 212);
1084
- i0.ɵɵelementStart(13, "div", 212)(14, "span", 213);
1109
+ i0.ɵɵconditionalCreate(11, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_11_Template, 5, 5, "div", 214);
1110
+ i0.ɵɵconditionalCreate(12, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_12_Template, 5, 1, "div", 214);
1111
+ i0.ɵɵelementStart(13, "div", 214)(14, "span", 215);
1085
1112
  i0.ɵɵtext(15);
1086
1113
  i0.ɵɵelementEnd();
1087
- i0.ɵɵelementStart(16, "span", 214);
1114
+ i0.ɵɵelementStart(16, "span", 216);
1088
1115
  i0.ɵɵtext(17, "Avg Duration");
1089
1116
  i0.ɵɵelementEnd()();
1090
- i0.ɵɵelementStart(18, "div", 212)(19, "span", 213);
1117
+ i0.ɵɵelementStart(18, "div", 214)(19, "span", 215);
1091
1118
  i0.ɵɵtext(20);
1092
1119
  i0.ɵɵelementEnd();
1093
- i0.ɵɵelementStart(21, "span", 214);
1120
+ i0.ɵɵelementStart(21, "span", 216);
1094
1121
  i0.ɵɵtext(22, "Avg Cost");
1095
1122
  i0.ɵɵelementEnd()();
1096
- i0.ɵɵconditionalCreate(23, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_23_Template, 5, 1, "div", 212);
1123
+ i0.ɵɵconditionalCreate(23, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_23_Template, 5, 1, "div", 214);
1097
1124
  i0.ɵɵelementEnd();
1098
- i0.ɵɵelement(24, "i", 215);
1125
+ i0.ɵɵelement(24, "i", 217);
1099
1126
  i0.ɵɵelementEnd();
1100
1127
  } if (rf & 2) {
1101
- const suite_r19 = ctx.$implicit;
1128
+ const suite_r20 = ctx.$implicit;
1102
1129
  const ctx_r0 = i0.ɵɵnextContext(4);
1103
1130
  i0.ɵɵadvance(3);
1104
- i0.ɵɵtextInterpolate(suite_r19.suiteName);
1131
+ i0.ɵɵtextInterpolate(suite_r20.suiteName);
1105
1132
  i0.ɵɵadvance();
1106
- i0.ɵɵconditional(suite_r19.tags.length > 0 ? 4 : -1);
1133
+ i0.ɵɵconditional(suite_r20.tags.length > 0 ? 4 : -1);
1107
1134
  i0.ɵɵadvance(4);
1108
- i0.ɵɵtextInterpolate(suite_r19.totalRuns);
1135
+ i0.ɵɵtextInterpolate(suite_r20.totalRuns);
1109
1136
  i0.ɵɵadvance(3);
1110
1137
  i0.ɵɵconditional(ctx_r0.evalPreferences.showExecution ? 11 : -1);
1111
1138
  i0.ɵɵadvance();
1112
1139
  i0.ɵɵconditional(ctx_r0.evalPreferences.showAuto ? 12 : -1);
1113
1140
  i0.ɵɵadvance(3);
1114
- i0.ɵɵtextInterpolate(ctx_r0.formatDuration(suite_r19.avgDuration));
1141
+ i0.ɵɵtextInterpolate(ctx_r0.formatDuration(suite_r20.avgDuration));
1115
1142
  i0.ɵɵadvance(5);
1116
- i0.ɵɵtextInterpolate(ctx_r0.formatCost(suite_r19.avgCost));
1143
+ i0.ɵɵtextInterpolate(ctx_r0.formatCost(suite_r20.avgCost));
1117
1144
  i0.ɵɵadvance(3);
1118
- i0.ɵɵconditional(suite_r19.lastRun ? 23 : -1);
1145
+ i0.ɵɵconditional(suite_r20.lastRun ? 23 : -1);
1119
1146
  } }
1120
1147
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
1121
- i0.ɵɵelementStart(0, "div", 179)(1, "h3");
1148
+ i0.ɵɵelementStart(0, "div", 181)(1, "h3");
1122
1149
  i0.ɵɵelement(2, "i", 33);
1123
1150
  i0.ɵɵtext(3, " Performance by Suite");
1124
1151
  i0.ɵɵelementEnd();
1125
- i0.ɵɵelementStart(4, "div", 205);
1126
- i0.ɵɵrepeaterCreate(5, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template, 25, 8, "div", 206, i0.ɵɵrepeaterTrackByIdentity);
1152
+ i0.ɵɵelementStart(4, "div", 207);
1153
+ i0.ɵɵrepeaterCreate(5, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template, 25, 8, "div", 208, i0.ɵɵrepeaterTrackByIdentity);
1127
1154
  i0.ɵɵelementEnd()();
1128
1155
  } if (rf & 2) {
1129
1156
  const ctx_r0 = i0.ɵɵnextContext(3);
@@ -1131,41 +1158,41 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19
1131
1158
  i0.ɵɵrepeater(ctx_r0.suitePerformance);
1132
1159
  } }
1133
1160
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
1134
- const _r21 = i0.ɵɵgetCurrentView();
1135
- i0.ɵɵelementStart(0, "mj-empty-state", 219);
1136
- i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r21); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.runTest()); });
1161
+ const _r22 = i0.ɵɵgetCurrentView();
1162
+ i0.ɵɵelementStart(0, "mj-empty-state", 221);
1163
+ i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r22); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.runTest()); });
1137
1164
  i0.ɵɵelementEnd();
1138
1165
  } }
1139
1166
  function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template(rf, ctx) { if (rf & 1) {
1140
- const _r16 = i0.ɵɵgetCurrentView();
1141
- i0.ɵɵelementStart(0, "div", 170)(1, "div", 172)(2, "div", 173)(3, "span", 174);
1167
+ const _r17 = i0.ɵɵgetCurrentView();
1168
+ i0.ɵɵelementStart(0, "div", 172)(1, "div", 174)(2, "div", 175)(3, "span", 176);
1142
1169
  i0.ɵɵtext(4, "Time Range:");
1143
1170
  i0.ɵɵelementEnd();
1144
- i0.ɵɵelementStart(5, "button", 175);
1145
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r16); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("7d")); });
1171
+ i0.ɵɵelementStart(5, "button", 177);
1172
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("7d")); });
1146
1173
  i0.ɵɵtext(6, "7 Days");
1147
1174
  i0.ɵɵelementEnd();
1148
- i0.ɵɵelementStart(7, "button", 175);
1149
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r16); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("30d")); });
1175
+ i0.ɵɵelementStart(7, "button", 177);
1176
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("30d")); });
1150
1177
  i0.ɵɵtext(8, "30 Days");
1151
1178
  i0.ɵɵelementEnd();
1152
- i0.ɵɵelementStart(9, "button", 175);
1153
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r16); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("90d")); });
1179
+ i0.ɵɵelementStart(9, "button", 177);
1180
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("90d")); });
1154
1181
  i0.ɵɵtext(10, "90 Days");
1155
1182
  i0.ɵɵelementEnd();
1156
- i0.ɵɵelementStart(11, "button", 175);
1157
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r16); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("all")); });
1183
+ i0.ɵɵelementStart(11, "button", 177);
1184
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("all")); });
1158
1185
  i0.ɵɵtext(12, "All Time");
1159
1186
  i0.ɵɵelementEnd()();
1160
- i0.ɵɵelementStart(13, "div", 176)(14, "button", 23);
1161
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r16); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.exportHistoryToCSV()); });
1162
- i0.ɵɵelement(15, "i", 177);
1187
+ i0.ɵɵelementStart(13, "div", 178)(14, "button", 23);
1188
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.exportHistoryToCSV()); });
1189
+ i0.ɵɵelement(15, "i", 179);
1163
1190
  i0.ɵɵtext(16, " Export CSV ");
1164
1191
  i0.ɵɵelementEnd()()();
1165
- i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Template, 27, 5, "div", 178);
1166
- i0.ɵɵconditionalCreate(18, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_Template, 23, 4, "div", 179);
1167
- i0.ɵɵconditionalCreate(19, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_Template, 7, 0, "div", 179);
1168
- i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template, 1, 0, "mj-empty-state", 180);
1192
+ i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Template, 27, 5, "div", 180);
1193
+ i0.ɵɵconditionalCreate(18, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_Template, 23, 4, "div", 181);
1194
+ i0.ɵɵconditionalCreate(19, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_Template, 7, 0, "div", 181);
1195
+ i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template, 1, 0, "mj-empty-state", 182);
1169
1196
  i0.ɵɵelementEnd();
1170
1197
  } if (rf & 2) {
1171
1198
  const ctx_r0 = i0.ɵɵnextContext(2);
@@ -1190,8 +1217,8 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template(rf, c
1190
1217
  } }
1191
1218
  function MJTestFormComponentExtended_Conditional_67_Template(rf, ctx) { if (rf & 1) {
1192
1219
  i0.ɵɵelementStart(0, "div", 40);
1193
- i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_1_Template, 2, 0, "div", 123);
1194
- i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_67_Conditional_2_Template, 21, 13, "div", 170);
1220
+ i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_1_Template, 2, 0, "div", 125);
1221
+ i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_67_Conditional_2_Template, 21, 13, "div", 172);
1195
1222
  i0.ɵɵelementEnd();
1196
1223
  } if (rf & 2) {
1197
1224
  const ctx_r0 = i0.ɵɵnextContext();
@@ -1201,45 +1228,45 @@ function MJTestFormComponentExtended_Conditional_67_Template(rf, ctx) { if (rf &
1201
1228
  i0.ɵɵconditional(!ctx_r0.loadingHistory && ctx_r0.historyLoaded ? 2 : -1);
1202
1229
  } }
1203
1230
  function MJTestFormComponentExtended_Conditional_70_Template(rf, ctx) { if (rf & 1) {
1204
- const _r22 = i0.ɵɵgetCurrentView();
1205
- i0.ɵɵelementStart(0, "div", 43)(1, "div", 220);
1231
+ const _r23 = i0.ɵɵgetCurrentView();
1232
+ i0.ɵɵelementStart(0, "div", 43)(1, "div", 222);
1206
1233
  i0.ɵɵelement(2, "i", 42);
1207
1234
  i0.ɵɵtext(3, " Shortcuts ");
1208
- i0.ɵɵelementStart(4, "button", 221);
1209
- i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_70_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r22); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.toggleShortcuts()); });
1210
- i0.ɵɵelement(5, "i", 122);
1235
+ i0.ɵɵelementStart(4, "button", 223);
1236
+ i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_70_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r23); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.toggleShortcuts()); });
1237
+ i0.ɵɵelement(5, "i", 124);
1211
1238
  i0.ɵɵelementEnd()();
1212
- i0.ɵɵelementStart(6, "div", 222)(7, "div", 223)(8, "span");
1239
+ i0.ɵɵelementStart(6, "div", 224)(7, "div", 225)(8, "span");
1213
1240
  i0.ɵɵtext(9, "Save changes");
1214
1241
  i0.ɵɵelementEnd();
1215
- i0.ɵɵelementStart(10, "span", 224)(11, "kbd");
1242
+ i0.ɵɵelementStart(10, "span", 226)(11, "kbd");
1216
1243
  i0.ɵɵtext(12, "Cmd");
1217
1244
  i0.ɵɵelementEnd();
1218
1245
  i0.ɵɵelementStart(13, "kbd");
1219
1246
  i0.ɵɵtext(14, "S");
1220
1247
  i0.ɵɵelementEnd()()();
1221
- i0.ɵɵelementStart(15, "div", 223)(16, "span");
1248
+ i0.ɵɵelementStart(15, "div", 225)(16, "span");
1222
1249
  i0.ɵɵtext(17, "Refresh");
1223
1250
  i0.ɵɵelementEnd();
1224
- i0.ɵɵelementStart(18, "span", 224)(19, "kbd");
1251
+ i0.ɵɵelementStart(18, "span", 226)(19, "kbd");
1225
1252
  i0.ɵɵtext(20, "Cmd");
1226
1253
  i0.ɵɵelementEnd();
1227
1254
  i0.ɵɵelementStart(21, "kbd");
1228
1255
  i0.ɵɵtext(22, "R");
1229
1256
  i0.ɵɵelementEnd()()();
1230
- i0.ɵɵelementStart(23, "div", 223)(24, "span");
1257
+ i0.ɵɵelementStart(23, "div", 225)(24, "span");
1231
1258
  i0.ɵɵtext(25, "Run Test");
1232
1259
  i0.ɵɵelementEnd();
1233
- i0.ɵɵelementStart(26, "span", 224)(27, "kbd");
1260
+ i0.ɵɵelementStart(26, "span", 226)(27, "kbd");
1234
1261
  i0.ɵɵtext(28, "Cmd");
1235
1262
  i0.ɵɵelementEnd();
1236
1263
  i0.ɵɵelementStart(29, "kbd");
1237
1264
  i0.ɵɵtext(30, "Enter");
1238
1265
  i0.ɵɵelementEnd()()();
1239
- i0.ɵɵelementStart(31, "div", 223)(32, "span");
1266
+ i0.ɵɵelementStart(31, "div", 225)(32, "span");
1240
1267
  i0.ɵɵtext(33, "Switch Tabs");
1241
1268
  i0.ɵɵelementEnd();
1242
- i0.ɵɵelementStart(34, "span", 224)(35, "kbd");
1269
+ i0.ɵɵelementStart(34, "span", 226)(35, "kbd");
1243
1270
  i0.ɵɵtext(36, "1");
1244
1271
  i0.ɵɵelementEnd();
1245
1272
  i0.ɵɵtext(37, "-");
@@ -1248,11 +1275,11 @@ function MJTestFormComponentExtended_Conditional_70_Template(rf, ctx) { if (rf &
1248
1275
  i0.ɵɵelementEnd()()()()();
1249
1276
  } }
1250
1277
  function MJTestFormComponentExtended_Conditional_71_Template(rf, ctx) { if (rf & 1) {
1251
- const _r23 = i0.ɵɵgetCurrentView();
1252
- i0.ɵɵelementStart(0, "mj-slide-panel", 225);
1253
- i0.ɵɵlistener("Closed", function MJTestFormComponentExtended_Conditional_71_Template_mj_slide_panel_Closed_0_listener() { i0.ɵɵrestoreView(_r23); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnPanelClosed()); });
1254
- i0.ɵɵelementStart(1, "app-test-run-dialog", 226);
1255
- i0.ɵɵlistener("PanelClose", function MJTestFormComponentExtended_Conditional_71_Template_app_test_run_dialog_PanelClose_1_listener() { i0.ɵɵrestoreView(_r23); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnPanelClosed()); });
1278
+ const _r24 = i0.ɵɵgetCurrentView();
1279
+ i0.ɵɵelementStart(0, "mj-slide-panel", 227);
1280
+ i0.ɵɵlistener("Closed", function MJTestFormComponentExtended_Conditional_71_Template_mj_slide_panel_Closed_0_listener() { i0.ɵɵrestoreView(_r24); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnPanelClosed()); });
1281
+ i0.ɵɵelementStart(1, "app-test-run-dialog", 228);
1282
+ i0.ɵɵlistener("PanelClose", function MJTestFormComponentExtended_Conditional_71_Template_app_test_run_dialog_PanelClose_1_listener() { i0.ɵɵrestoreView(_r24); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnPanelClosed()); });
1256
1283
  i0.ɵɵelementEnd()();
1257
1284
  } if (rf & 2) {
1258
1285
  const ctx_r0 = i0.ɵɵnextContext();
@@ -1273,13 +1300,13 @@ function MJTestFormComponentExtended_Conditional_81_Template(rf, ctx) { if (rf &
1273
1300
  i0.ɵɵtextInterpolate1(" ", ctx_r0.dirtyFieldNames.length, " fields edited ");
1274
1301
  } }
1275
1302
  function MJTestFormComponentExtended_Conditional_84_Template(rf, ctx) { if (rf & 1) {
1276
- i0.ɵɵelement(0, "i", 227);
1303
+ i0.ɵɵelement(0, "i", 229);
1277
1304
  i0.ɵɵtext(1, " Saving\u2026 ");
1278
1305
  } }
1279
1306
  function MJTestFormComponentExtended_Conditional_85_Template(rf, ctx) { if (rf & 1) {
1280
- i0.ɵɵelement(0, "i", 228);
1307
+ i0.ɵɵelement(0, "i", 230);
1281
1308
  i0.ɵɵtext(1, " Save changes ");
1282
- i0.ɵɵelementStart(2, "span", 229)(3, "kbd");
1309
+ i0.ɵɵelementStart(2, "span", 231)(3, "kbd");
1283
1310
  i0.ɵɵtext(4, "\u2318");
1284
1311
  i0.ɵɵelementEnd();
1285
1312
  i0.ɵɵelementStart(5, "kbd");
@@ -1293,67 +1320,281 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1293
1320
  super(...arguments);
1294
1321
  this.destroy$ = new Subject();
1295
1322
  // UI state
1296
- this.activeTab = 'overview';
1297
- this.loading = false;
1298
- this.loadingRuns = false;
1299
- this.loadingSuites = false;
1323
+ this.ActiveTab = 'overview';
1324
+ this.Loading = false;
1325
+ this.LoadingRuns = false;
1326
+ this.LoadingSuites = false;
1300
1327
  this.error = null;
1301
- this.testRunsLoaded = false;
1302
- this.suiteTestsLoaded = false;
1303
- this.isRefreshing = false;
1328
+ this.TestRunsLoaded = false;
1329
+ this.SuiteTestsLoaded = false;
1330
+ this.isRefreshing = false; // case-violation-ok-legacy-back-compat: an ancestor class already declares the PascalCase name
1304
1331
  // Related data
1305
- this.testRuns = [];
1306
- this.suiteTests = [];
1332
+ this.TestRuns = [];
1333
+ this.SuiteTests = [];
1307
1334
  // Human feedback map: testRunId -> feedback entity
1308
- this.feedbackMap = new Map();
1335
+ this.FeedbackMap = new Map();
1309
1336
  // History tab data
1310
- this.historyLoaded = false;
1311
- this.loadingHistory = false;
1312
- this.historyTimeRange = '30d';
1313
- this.historyData = [];
1314
- this.suitePerformance = [];
1315
- this.uniqueTags = [];
1316
- this.selectedTagFilter = null;
1337
+ this.HistoryLoaded = false;
1338
+ this.LoadingHistory = false;
1339
+ this.HistoryTimeRange = '30d';
1340
+ this.HistoryData = [];
1341
+ this.SuitePerformance = [];
1342
+ this.UniqueTags = [];
1343
+ this.SelectedTagFilter = null;
1317
1344
  // Parsed JSON fields
1318
- this.parsedData = {};
1345
+ this.ParsedData = {};
1319
1346
  // Active JSON view
1320
- this.activeJsonView = 'input';
1347
+ this.ActiveJsonView = 'input';
1321
1348
  // Code editor configuration
1322
- this.jsonToolbar = createCopyOnlyToolbar();
1349
+ this.JsonToolbar = createCopyOnlyToolbar();
1323
1350
  // Keyboard shortcuts
1324
- this.keyboardShortcutsEnabled = true;
1325
- this.showShortcuts = false; // Hidden by default
1351
+ this.KeyboardShortcutsEnabled = true;
1352
+ this.ShowShortcuts = false;
1326
1353
  this.shortcutsSettingEntity = null;
1327
1354
  // Evaluation preferences
1328
- this.evalPreferences = { showExecution: true, showHuman: true, showAuto: false };
1355
+ this.EvalPreferences = { showExecution: true, showHuman: true, showAuto: false };
1329
1356
  // Service injections
1330
1357
  this.navigationService = inject(NavigationService);
1331
- this.testingDialogService = inject(TestingDialogService);
1358
+ this.TestingDialogService = inject(TestingDialogService);
1332
1359
  this.evalPrefsService = inject(EvaluationPreferencesService);
1333
1360
  this.viewContainerRef = inject(ViewContainerRef);
1334
1361
  this.appManager = inject(ApplicationManager);
1335
1362
  this.confirmService = inject(MJConfirmService);
1336
1363
  // Edit state
1337
- this.isSaving = false;
1338
- this.testTypeOptions = [];
1339
- this.tagDraft = '';
1340
- this.statusOptions = ['Active', 'Pending', 'Disabled'];
1364
+ this.IsSaving = false;
1365
+ this.TestTypeOptions = [];
1366
+ this.RubricOptions = [];
1367
+ this.TagDraft = '';
1368
+ this.StatusOptions = ['Active', 'Pending', 'Disabled'];
1369
+ }
1370
+ /** @deprecated Use {@link ActiveTab}. */
1371
+ get activeTab() {
1372
+ return this.ActiveTab;
1373
+ }
1374
+ /** @deprecated Use {@link ActiveTab}. */
1375
+ set activeTab(value) {
1376
+ this.ActiveTab = value;
1377
+ }
1378
+ /** @deprecated Use {@link Loading}. */
1379
+ get loading() {
1380
+ return this.Loading;
1381
+ }
1382
+ /** @deprecated Use {@link Loading}. */
1383
+ set loading(value) {
1384
+ this.Loading = value;
1385
+ }
1386
+ /** @deprecated Use {@link LoadingRuns}. */
1387
+ get loadingRuns() {
1388
+ return this.LoadingRuns;
1389
+ }
1390
+ /** @deprecated Use {@link LoadingRuns}. */
1391
+ set loadingRuns(value) {
1392
+ this.LoadingRuns = value;
1393
+ }
1394
+ /** @deprecated Use {@link LoadingSuites}. */
1395
+ get loadingSuites() {
1396
+ return this.LoadingSuites;
1397
+ }
1398
+ /** @deprecated Use {@link LoadingSuites}. */
1399
+ set loadingSuites(value) {
1400
+ this.LoadingSuites = value;
1401
+ }
1402
+ /** @deprecated Use {@link TestRunsLoaded}. */
1403
+ get testRunsLoaded() {
1404
+ return this.TestRunsLoaded;
1405
+ }
1406
+ /** @deprecated Use {@link TestRunsLoaded}. */
1407
+ set testRunsLoaded(value) {
1408
+ this.TestRunsLoaded = value;
1409
+ }
1410
+ /** @deprecated Use {@link SuiteTestsLoaded}. */
1411
+ get suiteTestsLoaded() {
1412
+ return this.SuiteTestsLoaded;
1413
+ }
1414
+ /** @deprecated Use {@link SuiteTestsLoaded}. */
1415
+ set suiteTestsLoaded(value) {
1416
+ this.SuiteTestsLoaded = value;
1417
+ }
1418
+ /** @deprecated Use {@link TestRuns}. */
1419
+ get testRuns() {
1420
+ return this.TestRuns;
1421
+ }
1422
+ /** @deprecated Use {@link TestRuns}. */
1423
+ set testRuns(value) {
1424
+ this.TestRuns = value;
1425
+ }
1426
+ /** @deprecated Use {@link SuiteTests}. */
1427
+ get suiteTests() {
1428
+ return this.SuiteTests;
1429
+ }
1430
+ /** @deprecated Use {@link SuiteTests}. */
1431
+ set suiteTests(value) {
1432
+ this.SuiteTests = value;
1433
+ }
1434
+ /** @deprecated Use {@link FeedbackMap}. */
1435
+ get feedbackMap() {
1436
+ return this.FeedbackMap;
1437
+ }
1438
+ /** @deprecated Use {@link FeedbackMap}. */
1439
+ set feedbackMap(value) {
1440
+ this.FeedbackMap = value;
1441
+ }
1442
+ /** @deprecated Use {@link HistoryLoaded}. */
1443
+ get historyLoaded() {
1444
+ return this.HistoryLoaded;
1445
+ }
1446
+ /** @deprecated Use {@link HistoryLoaded}. */
1447
+ set historyLoaded(value) {
1448
+ this.HistoryLoaded = value;
1449
+ }
1450
+ /** @deprecated Use {@link LoadingHistory}. */
1451
+ get loadingHistory() {
1452
+ return this.LoadingHistory;
1341
1453
  }
1454
+ /** @deprecated Use {@link LoadingHistory}. */
1455
+ set loadingHistory(value) {
1456
+ this.LoadingHistory = value;
1457
+ }
1458
+ /** @deprecated Use {@link HistoryTimeRange}. */
1459
+ get historyTimeRange() {
1460
+ return this.HistoryTimeRange;
1461
+ }
1462
+ /** @deprecated Use {@link HistoryTimeRange}. */
1463
+ set historyTimeRange(value) {
1464
+ this.HistoryTimeRange = value;
1465
+ }
1466
+ /** @deprecated Use {@link HistoryData}. */
1467
+ get historyData() {
1468
+ return this.HistoryData;
1469
+ }
1470
+ /** @deprecated Use {@link HistoryData}. */
1471
+ set historyData(value) {
1472
+ this.HistoryData = value;
1473
+ }
1474
+ /** @deprecated Use {@link SuitePerformance}. */
1475
+ get suitePerformance() {
1476
+ return this.SuitePerformance;
1477
+ }
1478
+ /** @deprecated Use {@link SuitePerformance}. */
1479
+ set suitePerformance(value) {
1480
+ this.SuitePerformance = value;
1481
+ }
1482
+ /** @deprecated Use {@link UniqueTags}. */
1483
+ get uniqueTags() {
1484
+ return this.UniqueTags;
1485
+ }
1486
+ /** @deprecated Use {@link UniqueTags}. */
1487
+ set uniqueTags(value) {
1488
+ this.UniqueTags = value;
1489
+ }
1490
+ /** @deprecated Use {@link SelectedTagFilter}. */
1491
+ get selectedTagFilter() {
1492
+ return this.SelectedTagFilter;
1493
+ }
1494
+ /** @deprecated Use {@link SelectedTagFilter}. */
1495
+ set selectedTagFilter(value) {
1496
+ this.SelectedTagFilter = value;
1497
+ }
1498
+ /** @deprecated Use {@link ParsedData}. */
1499
+ get parsedData() {
1500
+ return this.ParsedData;
1501
+ }
1502
+ /** @deprecated Use {@link ParsedData}. */
1503
+ set parsedData(value) {
1504
+ this.ParsedData = value;
1505
+ }
1506
+ /** @deprecated Use {@link ActiveJsonView}. */
1507
+ get activeJsonView() {
1508
+ return this.ActiveJsonView;
1509
+ }
1510
+ /** @deprecated Use {@link ActiveJsonView}. */
1511
+ set activeJsonView(value) {
1512
+ this.ActiveJsonView = value;
1513
+ }
1514
+ /** @deprecated Use {@link JsonToolbar}. */
1515
+ get jsonToolbar() {
1516
+ return this.JsonToolbar;
1517
+ }
1518
+ /** @deprecated Use {@link JsonToolbar}. */
1519
+ set jsonToolbar(value) {
1520
+ this.JsonToolbar = value;
1521
+ }
1522
+ /** @deprecated Use {@link KeyboardShortcutsEnabled}. */
1523
+ get keyboardShortcutsEnabled() {
1524
+ return this.KeyboardShortcutsEnabled;
1525
+ }
1526
+ /** @deprecated Use {@link KeyboardShortcutsEnabled}. */
1527
+ set keyboardShortcutsEnabled(value) {
1528
+ this.KeyboardShortcutsEnabled = value;
1529
+ }
1530
+ /** @deprecated Use {@link ShowShortcuts}. */
1531
+ get showShortcuts() {
1532
+ return this.ShowShortcuts;
1533
+ }
1534
+ /** @deprecated Use {@link ShowShortcuts}. */
1535
+ set showShortcuts(value) {
1536
+ this.ShowShortcuts = value;
1537
+ } // Hidden by default
1342
1538
  get metadata() { return this.ProviderToUse; }
1539
+ /** @deprecated Use {@link EvalPreferences}. */
1540
+ get evalPreferences() {
1541
+ return this.EvalPreferences;
1542
+ }
1543
+ /** @deprecated Use {@link EvalPreferences}. */
1544
+ set evalPreferences(value) {
1545
+ this.EvalPreferences = value;
1546
+ }
1547
+ /** @deprecated Use {@link TestingDialogService}. */
1548
+ get testingDialogService() {
1549
+ return this.TestingDialogService;
1550
+ }
1551
+ /** @deprecated Use {@link TestingDialogService}. */
1552
+ set testingDialogService(value) {
1553
+ this.TestingDialogService = value;
1554
+ }
1555
+ /** @deprecated Use {@link IsSaving}. */
1556
+ get isSaving() {
1557
+ return this.IsSaving;
1558
+ }
1559
+ /** @deprecated Use {@link IsSaving}. */
1560
+ set isSaving(value) {
1561
+ this.IsSaving = value;
1562
+ }
1563
+ /** @deprecated Use {@link TestTypeOptions}. */
1564
+ get testTypeOptions() {
1565
+ return this.TestTypeOptions;
1566
+ }
1567
+ /** @deprecated Use {@link TestTypeOptions}. */
1568
+ set testTypeOptions(value) {
1569
+ this.TestTypeOptions = value;
1570
+ }
1571
+ /** @deprecated Use {@link TagDraft}. */
1572
+ get tagDraft() {
1573
+ return this.TagDraft;
1574
+ }
1575
+ /** @deprecated Use {@link TagDraft}. */
1576
+ set tagDraft(value) {
1577
+ this.TagDraft = value;
1578
+ }
1579
+ /** @deprecated Use {@link StatusOptions}. */
1580
+ get statusOptions() {
1581
+ return this.StatusOptions;
1582
+ }
1343
1583
  async ngOnInit() {
1344
1584
  await super.ngOnInit();
1345
1585
  this.loadShortcutsSetting();
1346
1586
  // Fire-and-forget: test type list for the edit form
1347
1587
  this.loadTestTypeOptions();
1588
+ void this.loadRubricOptions();
1348
1589
  // Subscribe to evaluation preferences
1349
1590
  this.evalPrefsService.preferences$
1350
1591
  .pipe(takeUntil(this.destroy$))
1351
1592
  .subscribe(prefs => {
1352
- this.evalPreferences = prefs;
1593
+ this.EvalPreferences = prefs;
1353
1594
  this.cdr.markForCheck();
1354
1595
  });
1355
1596
  // Subscribe to panel state changes so the slide panel renders in this form
1356
- this.testingDialogService.PanelStateChanged$
1597
+ this.TestingDialogService.PanelStateChanged$
1357
1598
  .pipe(takeUntil(this.destroy$))
1358
1599
  .subscribe(() => {
1359
1600
  this.cdr.detectChanges();
@@ -1368,52 +1609,52 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1368
1609
  }
1369
1610
  // Keyboard shortcuts
1370
1611
  handleKeyboardShortcut(event) {
1371
- if (!this.keyboardShortcutsEnabled)
1612
+ if (!this.KeyboardShortcutsEnabled)
1372
1613
  return;
1373
1614
  // Cmd/Ctrl + S: Save (if dirty)
1374
1615
  if ((event.metaKey || event.ctrlKey) && event.key === 's' && !event.shiftKey) {
1375
- if (this.isDirty) {
1616
+ if (this.IsDirty) {
1376
1617
  event.preventDefault();
1377
- this.saveChanges();
1618
+ this.SaveChanges();
1378
1619
  }
1379
1620
  return;
1380
1621
  }
1381
1622
  // Cmd/Ctrl + R: Refresh
1382
1623
  if ((event.metaKey || event.ctrlKey) && event.key === 'r' && !event.shiftKey) {
1383
1624
  event.preventDefault();
1384
- this.refresh();
1625
+ this.Refresh();
1385
1626
  return;
1386
1627
  }
1387
1628
  // Cmd/Ctrl + Enter: Run test
1388
1629
  if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
1389
1630
  event.preventDefault();
1390
- this.runTest();
1631
+ this.RunTest();
1391
1632
  return;
1392
1633
  }
1393
1634
  // Number keys for tabs (1-5) — skip when typing into form inputs
1394
1635
  if (!event.metaKey && !event.ctrlKey && !event.altKey && !this.isTextInputFocused()) {
1395
1636
  switch (event.key) {
1396
1637
  case '1':
1397
- this.changeTab('overview');
1638
+ this.ChangeTab('overview');
1398
1639
  break;
1399
1640
  case '2':
1400
- this.changeTab('config');
1641
+ this.ChangeTab('config');
1401
1642
  break;
1402
1643
  case '3':
1403
- this.changeTab('runs');
1644
+ this.ChangeTab('runs');
1404
1645
  break;
1405
1646
  case '4':
1406
- this.changeTab('suites');
1647
+ this.ChangeTab('suites');
1407
1648
  break;
1408
1649
  case '5':
1409
- this.changeTab('analytics');
1650
+ this.ChangeTab('analytics');
1410
1651
  break;
1411
1652
  }
1412
1653
  }
1413
1654
  }
1414
1655
  // Warn before tab close / hard navigation when there are unsaved changes
1415
1656
  handleBeforeUnload(event) {
1416
- if (this.isDirty) {
1657
+ if (this.IsDirty) {
1417
1658
  event.preventDefault();
1418
1659
  event.returnValue = '';
1419
1660
  }
@@ -1426,9 +1667,9 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1426
1667
  return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
1427
1668
  }
1428
1669
  async loadTestRuns() {
1429
- if (this.testRunsLoaded)
1670
+ if (this.TestRunsLoaded)
1430
1671
  return;
1431
- this.loadingRuns = true;
1672
+ this.LoadingRuns = true;
1432
1673
  this.cdr.markForCheck();
1433
1674
  try {
1434
1675
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
@@ -1440,20 +1681,20 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1440
1681
  ResultType: 'entity_object'
1441
1682
  });
1442
1683
  if (result.Success) {
1443
- this.testRuns = result.Results || [];
1684
+ this.TestRuns = result.Results || [];
1444
1685
  // Load feedbacks for all test runs
1445
- if (this.testRuns.length > 0) {
1446
- await this.loadFeedbacksForTestRuns(this.testRuns.map(r => r.ID));
1686
+ if (this.TestRuns.length > 0) {
1687
+ await this.loadFeedbacksForTestRuns(this.TestRuns.map(r => r.ID));
1447
1688
  }
1448
1689
  }
1449
- this.testRunsLoaded = true;
1690
+ this.TestRunsLoaded = true;
1450
1691
  }
1451
1692
  catch (error) {
1452
1693
  console.error('Error loading test runs:', error);
1453
1694
  SharedService.Instance.CreateSimpleNotification('Failed to load test runs', 'error', 3000);
1454
1695
  }
1455
1696
  finally {
1456
- this.loadingRuns = false;
1697
+ this.LoadingRuns = false;
1457
1698
  this.cdr.markForCheck();
1458
1699
  }
1459
1700
  }
@@ -1476,7 +1717,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1476
1717
  });
1477
1718
  if (result.Success && result.Results) {
1478
1719
  for (const feedback of result.Results) {
1479
- this.feedbackMap.set(feedback.TestRunID, feedback);
1720
+ this.FeedbackMap.set(feedback.TestRunID, feedback);
1480
1721
  }
1481
1722
  }
1482
1723
  }
@@ -1488,13 +1729,17 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1488
1729
  /**
1489
1730
  * Get feedback for a specific test run
1490
1731
  */
1732
+ GetFeedbackForRun(testRunId) {
1733
+ return this.FeedbackMap.get(testRunId);
1734
+ }
1735
+ /** @deprecated Use {@link GetFeedbackForRun}. */
1491
1736
  getFeedbackForRun(testRunId) {
1492
- return this.feedbackMap.get(testRunId);
1737
+ return this.GetFeedbackForRun(testRunId);
1493
1738
  }
1494
1739
  /**
1495
1740
  * Get tooltip for status indicator
1496
1741
  */
1497
- getStatusTooltip(status) {
1742
+ GetStatusTooltip(status) {
1498
1743
  switch (status) {
1499
1744
  case 'Passed': return 'Status: Passed - Test completed without error';
1500
1745
  case 'Failed': return 'Status: Failed - Test assertions did not pass';
@@ -1506,10 +1751,14 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1506
1751
  default: return `Status: ${status}`;
1507
1752
  }
1508
1753
  }
1754
+ /** @deprecated Use {@link GetStatusTooltip}. */
1755
+ getStatusTooltip(status) {
1756
+ return this.GetStatusTooltip(status);
1757
+ }
1509
1758
  /**
1510
1759
  * Get tooltip for human review with rating and optional comments
1511
1760
  */
1512
- getHumanTooltip(rating, comments) {
1761
+ GetHumanTooltip(rating, comments) {
1513
1762
  let tooltip = `Human Review: ${rating}/10 rating`;
1514
1763
  if (comments) {
1515
1764
  const truncated = comments.length > 200 ? comments.substring(0, 200) + '...' : comments;
@@ -1517,10 +1766,14 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1517
1766
  }
1518
1767
  return tooltip;
1519
1768
  }
1769
+ /** @deprecated Use {@link GetHumanTooltip}. */
1770
+ getHumanTooltip(rating, comments) {
1771
+ return this.GetHumanTooltip(rating, comments);
1772
+ }
1520
1773
  async loadSuiteTests() {
1521
- if (this.suiteTestsLoaded)
1774
+ if (this.SuiteTestsLoaded)
1522
1775
  return;
1523
- this.loadingSuites = true;
1776
+ this.LoadingSuites = true;
1524
1777
  this.cdr.markForCheck();
1525
1778
  try {
1526
1779
  const rv = RunView.FromMetadataProvider(this.ProviderToUse);
@@ -1531,57 +1784,65 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1531
1784
  ResultType: 'entity_object'
1532
1785
  });
1533
1786
  if (result.Success) {
1534
- this.suiteTests = result.Results || [];
1787
+ this.SuiteTests = result.Results || [];
1535
1788
  }
1536
- this.suiteTestsLoaded = true;
1789
+ this.SuiteTestsLoaded = true;
1537
1790
  }
1538
1791
  catch (error) {
1539
1792
  console.error('Error loading suite tests:', error);
1540
1793
  SharedService.Instance.CreateSimpleNotification('Failed to load test suites', 'error', 3000);
1541
1794
  }
1542
1795
  finally {
1543
- this.loadingSuites = false;
1796
+ this.LoadingSuites = false;
1544
1797
  this.cdr.markForCheck();
1545
1798
  }
1546
1799
  }
1547
1800
  parseJsonFields() {
1548
1801
  try {
1549
1802
  if (this.record.InputDefinition) {
1550
- this.parsedData.inputDefinition = JSON.parse(this.record.InputDefinition);
1803
+ this.ParsedData.inputDefinition = JSON.parse(this.record.InputDefinition);
1551
1804
  }
1552
1805
  if (this.record.ExpectedOutcomes) {
1553
- this.parsedData.expectedOutcomes = JSON.parse(this.record.ExpectedOutcomes);
1806
+ this.ParsedData.expectedOutcomes = JSON.parse(this.record.ExpectedOutcomes);
1554
1807
  }
1555
1808
  if (this.record.Configuration) {
1556
- this.parsedData.configuration = JSON.parse(this.record.Configuration);
1809
+ this.ParsedData.configuration = JSON.parse(this.record.Configuration);
1557
1810
  }
1558
1811
  if (this.record.Tags) {
1559
- this.parsedData.tags = JSON.parse(this.record.Tags);
1812
+ this.ParsedData.tags = JSON.parse(this.record.Tags);
1560
1813
  }
1561
1814
  }
1562
1815
  catch (error) {
1563
1816
  console.error('Error parsing JSON fields:', error);
1564
1817
  }
1565
1818
  }
1566
- changeTab(tab) {
1567
- this.activeTab = tab;
1819
+ ChangeTab(tab) {
1820
+ this.ActiveTab = tab;
1568
1821
  // Lazy load tabs
1569
- if (tab === 'runs' && !this.testRunsLoaded) {
1822
+ if (tab === 'runs' && !this.TestRunsLoaded) {
1570
1823
  this.loadTestRuns();
1571
1824
  }
1572
- if (tab === 'suites' && !this.suiteTestsLoaded) {
1825
+ if (tab === 'suites' && !this.SuiteTestsLoaded) {
1573
1826
  this.loadSuiteTests();
1574
1827
  }
1575
- if (tab === 'analytics' && !this.historyLoaded) {
1576
- this.loadHistory();
1828
+ if (tab === 'analytics' && !this.HistoryLoaded) {
1829
+ this.LoadHistory();
1577
1830
  }
1578
1831
  this.cdr.markForCheck();
1579
1832
  }
1580
- setJsonView(view) {
1581
- this.activeJsonView = view;
1833
+ /** @deprecated Use {@link ChangeTab}. */
1834
+ changeTab(tab) {
1835
+ return this.ChangeTab(tab);
1836
+ }
1837
+ SetJsonView(view) {
1838
+ this.ActiveJsonView = view;
1582
1839
  this.cdr.markForCheck();
1583
1840
  }
1584
- getStatusColor() {
1841
+ /** @deprecated Use {@link SetJsonView}. */
1842
+ setJsonView(view) {
1843
+ return this.SetJsonView(view);
1844
+ }
1845
+ GetStatusColor() {
1585
1846
  switch (this.record.Status) {
1586
1847
  case 'Active': return '#10b981';
1587
1848
  case 'Disabled': return '#6b7280';
@@ -1589,7 +1850,11 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1589
1850
  default: return '#9ca3af';
1590
1851
  }
1591
1852
  }
1592
- getStatusIcon() {
1853
+ /** @deprecated Use {@link GetStatusColor}. */
1854
+ getStatusColor() {
1855
+ return this.GetStatusColor();
1856
+ }
1857
+ GetStatusIcon() {
1593
1858
  switch (this.record.Status) {
1594
1859
  case 'Active': return 'fa-circle-check';
1595
1860
  case 'Disabled': return 'fa-circle-stop';
@@ -1597,10 +1862,18 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1597
1862
  default: return 'fa-circle-question';
1598
1863
  }
1599
1864
  }
1600
- getStatusClass() {
1865
+ /** @deprecated Use {@link GetStatusIcon}. */
1866
+ getStatusIcon() {
1867
+ return this.GetStatusIcon();
1868
+ }
1869
+ GetStatusClass() {
1601
1870
  return `status-${this.record.Status?.toLowerCase() || 'unknown'}`;
1602
1871
  }
1603
- getRunStatusColor(status) {
1872
+ /** @deprecated Use {@link GetStatusClass}. */
1873
+ getStatusClass() {
1874
+ return this.GetStatusClass();
1875
+ }
1876
+ GetRunStatusColor(status) {
1604
1877
  switch (status) {
1605
1878
  case 'Passed': return '#10b981';
1606
1879
  case 'Failed': return '#ef4444';
@@ -1611,23 +1884,39 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1611
1884
  default: return '#6b7280';
1612
1885
  }
1613
1886
  }
1887
+ /** @deprecated Use {@link GetRunStatusColor}. */
1888
+ getRunStatusColor(status) {
1889
+ return this.GetRunStatusColor(status);
1890
+ }
1891
+ GetPassRate() {
1892
+ if (this.TestRuns.length === 0)
1893
+ return 0;
1894
+ const passed = this.TestRuns.filter(r => r.Status === 'Passed').length;
1895
+ return (passed / this.TestRuns.length) * 100;
1896
+ }
1897
+ /** @deprecated Use {@link GetPassRate}. */
1614
1898
  getPassRate() {
1615
- if (this.testRuns.length === 0)
1899
+ return this.GetPassRate();
1900
+ }
1901
+ GetAverageCost() {
1902
+ if (this.TestRuns.length === 0)
1616
1903
  return 0;
1617
- const passed = this.testRuns.filter(r => r.Status === 'Passed').length;
1618
- return (passed / this.testRuns.length) * 100;
1904
+ const totalCost = this.TestRuns.reduce((sum, r) => sum + (r.CostUSD || 0), 0);
1905
+ return totalCost / this.TestRuns.length;
1619
1906
  }
1907
+ /** @deprecated Use {@link GetAverageCost}. */
1620
1908
  getAverageCost() {
1621
- if (this.testRuns.length === 0)
1909
+ return this.GetAverageCost();
1910
+ }
1911
+ GetAverageDuration() {
1912
+ if (this.TestRuns.length === 0)
1622
1913
  return 0;
1623
- const totalCost = this.testRuns.reduce((sum, r) => sum + (r.CostUSD || 0), 0);
1624
- return totalCost / this.testRuns.length;
1914
+ const totalDuration = this.TestRuns.reduce((sum, r) => sum + (r.DurationSeconds || 0), 0);
1915
+ return totalDuration / this.TestRuns.length;
1625
1916
  }
1917
+ /** @deprecated Use {@link GetAverageDuration}. */
1626
1918
  getAverageDuration() {
1627
- if (this.testRuns.length === 0)
1628
- return 0;
1629
- const totalDuration = this.testRuns.reduce((sum, r) => sum + (r.DurationSeconds || 0), 0);
1630
- return totalDuration / this.testRuns.length;
1919
+ return this.GetAverageDuration();
1631
1920
  }
1632
1921
  formatDuration(seconds) {
1633
1922
  if (seconds < 60)
@@ -1641,10 +1930,14 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1641
1930
  const mins = Math.floor((seconds % 3600) / 60);
1642
1931
  return `${hours}h ${mins}m`;
1643
1932
  }
1644
- formatCost(cost) {
1933
+ FormatCost(cost) {
1645
1934
  return `$${cost.toFixed(6)}`;
1646
1935
  }
1647
- formatTimeout(ms) {
1936
+ /** @deprecated Use {@link FormatCost}. */
1937
+ formatCost(cost) {
1938
+ return this.FormatCost(cost);
1939
+ }
1940
+ FormatTimeout(ms) {
1648
1941
  if (ms === null || ms === undefined)
1649
1942
  return 'Default (5 min)';
1650
1943
  if (ms < 1000)
@@ -1660,45 +1953,69 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1660
1953
  const mins = Math.floor((ms % 3600000) / 60000);
1661
1954
  return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
1662
1955
  }
1663
- openTestRun(runId) {
1956
+ /** @deprecated Use {@link FormatTimeout}. */
1957
+ formatTimeout(ms) {
1958
+ return this.FormatTimeout(ms);
1959
+ }
1960
+ OpenTestRun(runId) {
1664
1961
  SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(runId));
1665
1962
  }
1666
- getRunTags(run) {
1963
+ /** @deprecated Use {@link OpenTestRun}. */
1964
+ openTestRun(runId) {
1965
+ return this.OpenTestRun(runId);
1966
+ }
1967
+ GetRunTags(run) {
1667
1968
  return TagsHelper.parseTags(run.Tags);
1668
1969
  }
1669
- openTestSuite(suiteId) {
1970
+ /** @deprecated Use {@link GetRunTags}. */
1971
+ getRunTags(run) {
1972
+ return this.GetRunTags(run);
1973
+ }
1974
+ OpenTestSuite(suiteId) {
1670
1975
  SharedService.Instance.OpenEntityRecord('MJ: Test Suites', CompositeKey.FromID(suiteId));
1671
1976
  }
1672
- navigateToTestingDashboard() {
1977
+ /** @deprecated Use {@link OpenTestSuite}. */
1978
+ openTestSuite(suiteId) {
1979
+ return this.OpenTestSuite(suiteId);
1980
+ }
1981
+ NavigateToTestingDashboard() {
1673
1982
  const testingApp = this.appManager.GetAppByName('Testing');
1674
1983
  if (testingApp) {
1675
1984
  this.navigationService.SwitchToApp(testingApp.ID);
1676
1985
  }
1677
1986
  }
1678
- async runTest() {
1987
+ /** @deprecated Use {@link NavigateToTestingDashboard}. */
1988
+ navigateToTestingDashboard() {
1989
+ return this.NavigateToTestingDashboard();
1990
+ }
1991
+ async RunTest() {
1679
1992
  if (this.record?.ID) {
1680
- this.testingDialogService.OpenTestPanel(this.record.ID);
1993
+ this.TestingDialogService.OpenTestPanel(this.record.ID);
1681
1994
  }
1682
1995
  }
1996
+ /** @deprecated Use {@link RunTest}. */
1997
+ async runTest() {
1998
+ return this.RunTest();
1999
+ }
1683
2000
  OnPanelClosed() {
1684
- this.testingDialogService.ClosePanel();
2001
+ this.TestingDialogService.ClosePanel();
1685
2002
  this.cdr.markForCheck();
1686
2003
  }
1687
- async refresh() {
2004
+ async Refresh() {
1688
2005
  this.isRefreshing = true;
1689
2006
  this.cdr.markForCheck();
1690
2007
  try {
1691
2008
  await this.record.Load(this.record.ID);
1692
2009
  this.parseJsonFields();
1693
2010
  // Reset lazy-loaded data to force reload
1694
- if (this.testRunsLoaded) {
1695
- this.testRunsLoaded = false;
1696
- this.testRuns = [];
2011
+ if (this.TestRunsLoaded) {
2012
+ this.TestRunsLoaded = false;
2013
+ this.TestRuns = [];
1697
2014
  await this.loadTestRuns();
1698
2015
  }
1699
- if (this.suiteTestsLoaded) {
1700
- this.suiteTestsLoaded = false;
1701
- this.suiteTests = [];
2016
+ if (this.SuiteTestsLoaded) {
2017
+ this.SuiteTestsLoaded = false;
2018
+ this.SuiteTests = [];
1702
2019
  await this.loadSuiteTests();
1703
2020
  }
1704
2021
  SharedService.Instance.CreateSimpleNotification('Refreshed successfully', 'success', 2000);
@@ -1711,25 +2028,33 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1711
2028
  this.cdr.markForCheck();
1712
2029
  }
1713
2030
  }
1714
- getJsonData() {
2031
+ /** @deprecated Use {@link Refresh}. */
2032
+ async refresh() {
2033
+ return this.Refresh();
2034
+ }
2035
+ GetJsonData() {
1715
2036
  let data;
1716
- switch (this.activeJsonView) {
2037
+ switch (this.ActiveJsonView) {
1717
2038
  case 'input':
1718
- data = this.parsedData.inputDefinition;
2039
+ data = this.ParsedData.inputDefinition;
1719
2040
  break;
1720
2041
  case 'expected':
1721
- data = this.parsedData.expectedOutcomes;
2042
+ data = this.ParsedData.expectedOutcomes;
1722
2043
  break;
1723
2044
  case 'config':
1724
- data = this.parsedData.configuration;
2045
+ data = this.ParsedData.configuration;
1725
2046
  break;
1726
2047
  case 'tags':
1727
- data = this.parsedData.tags;
2048
+ data = this.ParsedData.tags;
1728
2049
  break;
1729
2050
  }
1730
2051
  return data ? JSON.stringify(data, null, 2) : '// No data available';
1731
2052
  }
1732
- getRelativeTime(date) {
2053
+ /** @deprecated Use {@link GetJsonData}. */
2054
+ getJsonData() {
2055
+ return this.GetJsonData();
2056
+ }
2057
+ GetRelativeTime(date) {
1733
2058
  if (!date)
1734
2059
  return 'N/A';
1735
2060
  const d = new Date(date);
@@ -1748,13 +2073,17 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1748
2073
  return `${diffDays}d ago`;
1749
2074
  return d.toLocaleDateString();
1750
2075
  }
2076
+ /** @deprecated Use {@link GetRelativeTime}. */
2077
+ getRelativeTime(date) {
2078
+ return this.GetRelativeTime(date);
2079
+ }
1751
2080
  // ===========================
1752
2081
  // History Tab Methods
1753
2082
  // ===========================
1754
- async loadHistory() {
1755
- if (this.historyLoaded)
2083
+ async LoadHistory() {
2084
+ if (this.HistoryLoaded)
1756
2085
  return;
1757
- this.loadingHistory = true;
2086
+ this.LoadingHistory = true;
1758
2087
  this.cdr.markForCheck();
1759
2088
  try {
1760
2089
  // Load all test runs for this test
@@ -1768,23 +2097,27 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1768
2097
  if (runsResult.Success && runsResult.Results) {
1769
2098
  const allRuns = runsResult.Results;
1770
2099
  // Extract unique tags from all runs
1771
- this.uniqueTags = TagsHelper.getUniqueTags(allRuns.map(r => r.Tags));
2100
+ this.UniqueTags = TagsHelper.getUniqueTags(allRuns.map(r => r.Tags));
1772
2101
  // Build history data (aggregated by date)
1773
- this.historyData = this.buildHistoryData(allRuns);
2102
+ this.HistoryData = this.buildHistoryData(allRuns);
1774
2103
  // Build suite performance data
1775
2104
  await this.buildSuitePerformance(allRuns);
1776
2105
  }
1777
- this.historyLoaded = true;
2106
+ this.HistoryLoaded = true;
1778
2107
  }
1779
2108
  catch (error) {
1780
2109
  console.error('Error loading history:', error);
1781
2110
  SharedService.Instance.CreateSimpleNotification('Failed to load history', 'error', 3000);
1782
2111
  }
1783
2112
  finally {
1784
- this.loadingHistory = false;
2113
+ this.LoadingHistory = false;
1785
2114
  this.cdr.markForCheck();
1786
2115
  }
1787
2116
  }
2117
+ /** @deprecated Use {@link LoadHistory}. */
2118
+ async loadHistory() {
2119
+ return this.LoadHistory();
2120
+ }
1788
2121
  buildHistoryData(runs) {
1789
2122
  // Filter by time range
1790
2123
  const filteredRuns = this.filterRunsByTimeRange(runs);
@@ -1822,11 +2155,11 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1822
2155
  return dataPoints.sort((a, b) => b.date.getTime() - a.date.getTime());
1823
2156
  }
1824
2157
  filterRunsByTimeRange(runs) {
1825
- if (this.historyTimeRange === 'all')
2158
+ if (this.HistoryTimeRange === 'all')
1826
2159
  return runs;
1827
2160
  const now = new Date();
1828
2161
  let cutoff;
1829
- switch (this.historyTimeRange) {
2162
+ switch (this.HistoryTimeRange) {
1830
2163
  case '7d':
1831
2164
  cutoff = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
1832
2165
  break;
@@ -1874,7 +2207,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1874
2207
  suiteIdMap.get(suiteId).runs.push(...testRuns);
1875
2208
  }
1876
2209
  // Build performance data for each suite
1877
- this.suitePerformance = [];
2210
+ this.SuitePerformance = [];
1878
2211
  for (const [suiteId, data] of suiteIdMap) {
1879
2212
  const suiteName = data.suiteRuns[0]?.Suite || 'Unknown Suite';
1880
2213
  const totalRuns = data.runs.length;
@@ -1889,7 +2222,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1889
2222
  const lastRun = data.runs
1890
2223
  .filter(r => r.StartedAt)
1891
2224
  .sort((a, b) => new Date(b.StartedAt).getTime() - new Date(a.StartedAt).getTime())[0];
1892
- this.suitePerformance.push({
2225
+ this.SuitePerformance.push({
1893
2226
  suiteId,
1894
2227
  suiteName,
1895
2228
  totalRuns,
@@ -1904,57 +2237,89 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1904
2237
  });
1905
2238
  }
1906
2239
  // Sort by total runs descending
1907
- this.suitePerformance.sort((a, b) => b.totalRuns - a.totalRuns);
2240
+ this.SuitePerformance.sort((a, b) => b.totalRuns - a.totalRuns);
1908
2241
  }
1909
2242
  }
1910
2243
  }
2244
+ SetHistoryTimeRange(range) {
2245
+ this.HistoryTimeRange = range;
2246
+ this.HistoryLoaded = false;
2247
+ this.LoadHistory();
2248
+ }
2249
+ /** @deprecated Use {@link SetHistoryTimeRange}. */
1911
2250
  setHistoryTimeRange(range) {
1912
- this.historyTimeRange = range;
1913
- this.historyLoaded = false;
1914
- this.loadHistory();
2251
+ return this.SetHistoryTimeRange(range);
1915
2252
  }
1916
- setTagFilter(tag) {
1917
- this.selectedTagFilter = tag;
2253
+ SetTagFilter(tag) {
2254
+ this.SelectedTagFilter = tag;
1918
2255
  this.cdr.markForCheck();
1919
2256
  }
2257
+ /** @deprecated Use {@link SetTagFilter}. */
2258
+ setTagFilter(tag) {
2259
+ return this.SetTagFilter(tag);
2260
+ }
2261
+ GetFilteredHistoryData() {
2262
+ return this.HistoryData;
2263
+ }
2264
+ /** @deprecated Use {@link GetFilteredHistoryData}. */
1920
2265
  getFilteredHistoryData() {
1921
- return this.historyData;
2266
+ return this.GetFilteredHistoryData();
1922
2267
  }
1923
- getOverallPassRate() {
1924
- if (this.historyData.length === 0)
2268
+ GetOverallPassRate() {
2269
+ if (this.HistoryData.length === 0)
1925
2270
  return 0;
1926
- const totalRuns = this.historyData.reduce((sum, d) => sum + d.runCount, 0);
1927
- const totalPassed = this.historyData.reduce((sum, d) => sum + d.passCount, 0);
2271
+ const totalRuns = this.HistoryData.reduce((sum, d) => sum + d.runCount, 0);
2272
+ const totalPassed = this.HistoryData.reduce((sum, d) => sum + d.passCount, 0);
1928
2273
  return totalRuns > 0 ? (totalPassed / totalRuns) * 100 : 0;
1929
2274
  }
1930
- getOverallAvgScore() {
1931
- if (this.historyData.length === 0)
2275
+ /** @deprecated Use {@link GetOverallPassRate}. */
2276
+ getOverallPassRate() {
2277
+ return this.GetOverallPassRate();
2278
+ }
2279
+ GetOverallAvgScore() {
2280
+ if (this.HistoryData.length === 0)
1932
2281
  return 0;
1933
- const scores = this.historyData.filter(d => d.avgScore > 0).map(d => d.avgScore);
2282
+ const scores = this.HistoryData.filter(d => d.avgScore > 0).map(d => d.avgScore);
1934
2283
  return scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0;
1935
2284
  }
1936
- getOverallAvgDuration() {
1937
- if (this.historyData.length === 0)
2285
+ /** @deprecated Use {@link GetOverallAvgScore}. */
2286
+ getOverallAvgScore() {
2287
+ return this.GetOverallAvgScore();
2288
+ }
2289
+ GetOverallAvgDuration() {
2290
+ if (this.HistoryData.length === 0)
1938
2291
  return 0;
1939
- const durations = this.historyData.filter(d => d.avgDuration > 0).map(d => d.avgDuration);
2292
+ const durations = this.HistoryData.filter(d => d.avgDuration > 0).map(d => d.avgDuration);
1940
2293
  return durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
1941
2294
  }
1942
- getOverallAvgCost() {
1943
- if (this.historyData.length === 0)
2295
+ /** @deprecated Use {@link GetOverallAvgDuration}. */
2296
+ getOverallAvgDuration() {
2297
+ return this.GetOverallAvgDuration();
2298
+ }
2299
+ GetOverallAvgCost() {
2300
+ if (this.HistoryData.length === 0)
1944
2301
  return 0;
1945
- const costs = this.historyData.filter(d => d.avgCost > 0).map(d => d.avgCost);
2302
+ const costs = this.HistoryData.filter(d => d.avgCost > 0).map(d => d.avgCost);
1946
2303
  return costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0;
1947
2304
  }
2305
+ /** @deprecated Use {@link GetOverallAvgCost}. */
2306
+ getOverallAvgCost() {
2307
+ return this.GetOverallAvgCost();
2308
+ }
2309
+ GetTotalRuns() {
2310
+ return this.HistoryData.reduce((sum, d) => sum + d.runCount, 0);
2311
+ }
2312
+ /** @deprecated Use {@link GetTotalRuns}. */
1948
2313
  getTotalRuns() {
1949
- return this.historyData.reduce((sum, d) => sum + d.runCount, 0);
2314
+ return this.GetTotalRuns();
1950
2315
  }
1951
- getPassRateTrend() {
1952
- if (this.historyData.length < 2)
2316
+ GetPassRateTrend() {
2317
+ if (this.HistoryData.length < 2)
1953
2318
  return 'stable';
1954
2319
  // Compare recent half to older half
1955
- const mid = Math.floor(this.historyData.length / 2);
1956
- const recentData = this.historyData.slice(0, mid);
1957
- const olderData = this.historyData.slice(mid);
2320
+ const mid = Math.floor(this.HistoryData.length / 2);
2321
+ const recentData = this.HistoryData.slice(0, mid);
2322
+ const olderData = this.HistoryData.slice(mid);
1958
2323
  const recentRate = recentData.reduce((sum, d) => sum + d.passRate, 0) / recentData.length;
1959
2324
  const olderRate = olderData.reduce((sum, d) => sum + d.passRate, 0) / olderData.length;
1960
2325
  const diff = recentRate - olderRate;
@@ -1964,9 +2329,13 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1964
2329
  return 'down';
1965
2330
  return 'stable';
1966
2331
  }
1967
- exportHistoryToCSV() {
2332
+ /** @deprecated Use {@link GetPassRateTrend}. */
2333
+ getPassRateTrend() {
2334
+ return this.GetPassRateTrend();
2335
+ }
2336
+ ExportHistoryToCSV() {
1968
2337
  const headers = ['Date', 'Run Count', 'Passed', 'Failed', 'Pass Rate (%)', 'Avg Score', 'Avg Duration (s)', 'Avg Cost (USD)'];
1969
- const rows = this.historyData.map(d => [
2338
+ const rows = this.HistoryData.map(d => [
1970
2339
  d.date.toISOString().split('T')[0],
1971
2340
  d.runCount.toString(),
1972
2341
  d.passCount.toString(),
@@ -1987,9 +2356,17 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
1987
2356
  URL.revokeObjectURL(link.href);
1988
2357
  SharedService.Instance.CreateSimpleNotification('Export complete', 'success', 2000);
1989
2358
  }
1990
- openSuiteFromHistory(suiteId) {
2359
+ /** @deprecated Use {@link ExportHistoryToCSV}. */
2360
+ exportHistoryToCSV() {
2361
+ return this.ExportHistoryToCSV();
2362
+ }
2363
+ OpenSuiteFromHistory(suiteId) {
1991
2364
  SharedService.Instance.OpenEntityRecord('MJ: Test Suites', CompositeKey.FromID(suiteId));
1992
2365
  }
2366
+ /** @deprecated Use {@link OpenSuiteFromHistory}. */
2367
+ openSuiteFromHistory(suiteId) {
2368
+ return this.OpenSuiteFromHistory(suiteId);
2369
+ }
1993
2370
  // ===========================
1994
2371
  // Keyboard Shortcuts Settings
1995
2372
  // ===========================
@@ -2002,11 +2379,11 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2002
2379
  const setting = engine.UserSettings.find(s => s.Setting === SHORTCUTS_SETTINGS_KEY);
2003
2380
  if (setting) {
2004
2381
  this.shortcutsSettingEntity = setting;
2005
- this.showShortcuts = setting.Value === 'true';
2382
+ this.ShowShortcuts = setting.Value === 'true';
2006
2383
  }
2007
2384
  else {
2008
2385
  // Default to hidden
2009
- this.showShortcuts = false;
2386
+ this.ShowShortcuts = false;
2010
2387
  }
2011
2388
  this.cdr.markForCheck();
2012
2389
  }
@@ -2017,8 +2394,8 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2017
2394
  /**
2018
2395
  * Toggle keyboard shortcuts visibility and save preference
2019
2396
  */
2020
- async toggleShortcuts() {
2021
- this.showShortcuts = !this.showShortcuts;
2397
+ async ToggleShortcuts() {
2398
+ this.ShowShortcuts = !this.ShowShortcuts;
2022
2399
  this.cdr.markForCheck();
2023
2400
  try {
2024
2401
  const userId = this.metadata.CurrentUser?.ID;
@@ -2036,30 +2413,54 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2036
2413
  this.shortcutsSettingEntity.Setting = SHORTCUTS_SETTINGS_KEY;
2037
2414
  }
2038
2415
  }
2039
- this.shortcutsSettingEntity.Value = this.showShortcuts ? 'true' : 'false';
2416
+ this.shortcutsSettingEntity.Value = this.ShowShortcuts ? 'true' : 'false';
2040
2417
  await this.shortcutsSettingEntity.Save();
2041
2418
  }
2042
2419
  catch (error) {
2043
2420
  console.warn('Failed to save shortcuts setting:', error);
2044
2421
  }
2045
2422
  }
2423
+ /** @deprecated Use {@link ToggleShortcuts}. */
2424
+ async toggleShortcuts() {
2425
+ return this.ToggleShortcuts();
2426
+ }
2046
2427
  // ==========================================
2047
2428
  // Edit / Save Methods
2048
2429
  // ==========================================
2049
2430
  /** True if the test record has any unsaved field changes. */
2050
- get isDirty() {
2431
+ get IsDirty() {
2051
2432
  return this.record?.Dirty === true;
2052
2433
  }
2434
+ /** @deprecated Use {@link IsDirty}. */
2435
+ get isDirty() {
2436
+ return this.IsDirty;
2437
+ }
2053
2438
  /** Names of fields with pending edits, used by the save bar. */
2054
- get dirtyFieldNames() {
2439
+ get DirtyFieldNames() {
2055
2440
  if (!this.record?.Fields)
2056
2441
  return [];
2057
2442
  return this.record.Fields.filter(f => f.Dirty).map(f => f.Name);
2058
2443
  }
2444
+ /** @deprecated Use {@link DirtyFieldNames}. */
2445
+ get dirtyFieldNames() {
2446
+ return this.DirtyFieldNames;
2447
+ }
2059
2448
  /** Parsed tags as a plain array — derived from record.Tags JSON. */
2060
2449
  get tags() {
2061
2450
  return TagsHelper.parseTags(this.record?.Tags);
2062
2451
  }
2452
+ /** Active rubrics for the RubricID picker. */
2453
+ async loadRubricOptions() {
2454
+ try {
2455
+ const rv = RunView.FromMetadataProvider(this.ProviderToUse);
2456
+ const result = await rv.RunView({ EntityName: 'MJ: Rubrics', ExtraFilter: `Status='Active'`, OrderBy: 'Name', ResultType: 'simple', MaxRows: 500 });
2457
+ this.RubricOptions = RubricPickerOptions((result.Results ?? []));
2458
+ this.cdr.markForCheck();
2459
+ }
2460
+ catch (error) {
2461
+ console.warn('Failed to load rubric options:', error);
2462
+ }
2463
+ }
2063
2464
  /** Load test types for the Type dropdown. */
2064
2465
  async loadTestTypeOptions() {
2065
2466
  try {
@@ -2070,7 +2471,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2070
2471
  ResultType: 'entity_object'
2071
2472
  });
2072
2473
  if (result.Success) {
2073
- this.testTypeOptions = result.Results || [];
2474
+ this.TestTypeOptions = result.Results || [];
2074
2475
  this.cdr.markForCheck();
2075
2476
  }
2076
2477
  }
@@ -2079,10 +2480,10 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2079
2480
  }
2080
2481
  }
2081
2482
  /** Save all pending changes on the test record. */
2082
- async saveChanges() {
2083
- if (!this.isDirty || this.isSaving)
2483
+ async SaveChanges() {
2484
+ if (!this.IsDirty || this.IsSaving)
2084
2485
  return;
2085
- this.isSaving = true;
2486
+ this.IsSaving = true;
2086
2487
  this.cdr.markForCheck();
2087
2488
  try {
2088
2489
  const ok = await this.SaveRecord(false);
@@ -2101,24 +2502,32 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2101
2502
  SharedService.Instance.CreateSimpleNotification(msg, 'error', 4000);
2102
2503
  }
2103
2504
  finally {
2104
- this.isSaving = false;
2505
+ this.IsSaving = false;
2105
2506
  this.cdr.markForCheck();
2106
2507
  }
2107
2508
  }
2509
+ /** @deprecated Use {@link SaveChanges}. */
2510
+ async saveChanges() {
2511
+ return this.SaveChanges();
2512
+ }
2108
2513
  /** Discard all pending field changes on the test. */
2109
- async discardChanges() {
2110
- if (!this.isDirty)
2514
+ async DiscardChanges() {
2515
+ if (!this.IsDirty)
2111
2516
  return;
2112
2517
  if (!(await this.confirmService.Confirm('Discard your unsaved changes?')))
2113
2518
  return;
2114
2519
  this.record.Revert();
2115
- this.tagDraft = '';
2520
+ this.TagDraft = '';
2116
2521
  this.parseJsonFields();
2117
2522
  this.cdr.markForCheck();
2118
2523
  }
2524
+ /** @deprecated Use {@link DiscardChanges}. */
2525
+ async discardChanges() {
2526
+ return this.DiscardChanges();
2527
+ }
2119
2528
  /** Add a tag from tagDraft (called on Enter / comma / blur). */
2120
- addTagFromDraft() {
2121
- const raw = this.tagDraft.trim();
2529
+ AddTagFromDraft() {
2530
+ const raw = this.TagDraft.trim();
2122
2531
  if (!raw)
2123
2532
  return;
2124
2533
  const incoming = raw.split(',').map(t => t.trim()).filter(t => t.length > 0);
@@ -2129,33 +2538,45 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2129
2538
  merged.push(t);
2130
2539
  }
2131
2540
  this.record.Tags = TagsHelper.toJson(merged);
2132
- this.tagDraft = '';
2541
+ this.TagDraft = '';
2133
2542
  this.cdr.markForCheck();
2134
2543
  }
2544
+ /** @deprecated Use {@link AddTagFromDraft}. */
2545
+ addTagFromDraft() {
2546
+ return this.AddTagFromDraft();
2547
+ }
2135
2548
  /** Remove a single tag. */
2136
- removeTag(tag) {
2549
+ RemoveTag(tag) {
2137
2550
  this.record.Tags = TagsHelper.removeTag(this.record.Tags, tag);
2138
2551
  this.cdr.markForCheck();
2139
2552
  }
2553
+ /** @deprecated Use {@link RemoveTag}. */
2554
+ removeTag(tag) {
2555
+ return this.RemoveTag(tag);
2556
+ }
2140
2557
  /** Handle Enter / comma in the tag input to commit the draft tag. */
2141
- onTagInputKeydown(event) {
2558
+ OnTagInputKeydown(event) {
2142
2559
  if (event.key === 'Enter' || event.key === ',') {
2143
2560
  event.preventDefault();
2144
- this.addTagFromDraft();
2561
+ this.AddTagFromDraft();
2145
2562
  return;
2146
2563
  }
2147
- if (event.key === 'Backspace' && !this.tagDraft) {
2564
+ if (event.key === 'Backspace' && !this.TagDraft) {
2148
2565
  const all = this.tags;
2149
2566
  if (all.length > 0) {
2150
2567
  event.preventDefault();
2151
- this.removeTag(all[all.length - 1]);
2568
+ this.RemoveTag(all[all.length - 1]);
2152
2569
  }
2153
2570
  }
2154
2571
  }
2572
+ /** @deprecated Use {@link OnTagInputKeydown}. */
2573
+ onTagInputKeydown(event) {
2574
+ return this.OnTagInputKeydown(event);
2575
+ }
2155
2576
  static { this.ɵfac = /*@__PURE__*/ (() => { let ɵMJTestFormComponentExtended_BaseFactory; return function MJTestFormComponentExtended_Factory(__ngFactoryType__) { return (ɵMJTestFormComponentExtended_BaseFactory || (ɵMJTestFormComponentExtended_BaseFactory = i0.ɵɵgetInheritedFactory(MJTestFormComponentExtended)))(__ngFactoryType__ || MJTestFormComponentExtended); }; })(); }
2156
2577
  static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJTestFormComponentExtended, selectors: [["mj-test-form"]], hostBindings: function MJTestFormComponentExtended_HostBindings(rf, ctx) { if (rf & 1) {
2157
2578
  i0.ɵɵlistener("keydown", function MJTestFormComponentExtended_keydown_HostBindingHandler($event) { return ctx.handleKeyboardShortcut($event); }, i0.ɵɵresolveDocument)("beforeunload", function MJTestFormComponentExtended_beforeunload_HostBindingHandler($event) { return ctx.handleBeforeUnload($event); }, i0.ɵɵresolveWindow);
2158
- } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 89, vars: 51, consts: [["tagInput", ""], [1, "test-form"], [1, "test-header"], ["aria-label", "Breadcrumb", 1, "breadcrumb"], ["href", "javascript:void(0)", 3, "click"], [1, "fas", "fa-vial"], [1, "breadcrumb-text"], [1, "current"], [1, "fas", "fa-chevron-right", "separator"], [1, "fas", "fa-flask"], [1, "header-content"], [1, "header-left"], [1, "test-icon"], [1, "test-info"], ["title", "You have unsaved changes", "aria-label", "Unsaved changes", 1, "dirty-indicator"], [1, "test-meta"], [1, "status-badge", 3, "ngClass"], [1, "fas", 3, "ngClass"], [1, "test-type"], ["title", "You have unsaved changes", 1, "unsaved-pill"], [1, "header-actions"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fas", "fa-play"], ["mjButton", "", 3, "click", "disabled"], [1, "test-description"], [1, "metrics-bar"], [1, "tabs-container"], ["role", "tablist", 1, "tabs"], ["role", "tab", 1, "tab", 3, "click"], [1, "fas", "fa-th-large"], [1, "fas", "fa-sliders-h"], [1, "fas", "fa-history"], [1, "tab-badge"], [1, "fas", "fa-layer-group"], [1, "fas", "fa-chart-line"], [1, "tab-content"], [1, "overview-tab"], [1, "config-tab"], [1, "runs-tab"], [1, "suites-tab"], [1, "history-tab"], [1, "shortcuts-toggle", 3, "click", "title"], [1, "fas", "fa-keyboard"], [1, "keyboard-shortcuts"], ["Mode", "slide", 3, "Title", "Resizable"], ["role", "region", "aria-label", "Unsaved changes", 1, "save-bar"], [1, "save-bar-message"], ["aria-hidden", "true", 1, "save-bar-dot"], [1, "save-bar-text"], [1, "save-bar-fields"], [1, "save-bar-actions"], ["type", "button", "title", "Save changes (\u2318S)", 1, "save-bar-btn", "save-bar-btn--primary", 3, "click", "disabled"], ["type", "button", "title", "Discard changes", 1, "save-bar-btn", "save-bar-btn--ghost", 3, "click", "disabled"], [1, "fas", "fa-undo"], [1, "fas", "fa-tag"], [1, "fas", "fa-circle"], [1, "metric-card"], [1, "metric-label"], [1, "metric-value"], [1, "metric-progress"], [1, "metric-progress-fill"], [1, "info-section"], [1, "fas", "fa-info-circle"], [1, "info-grid"], [1, "info-item"], [1, "info-label"], [1, "info-value"], [1, "status-badge-inline", 3, "ngClass"], [1, "json-section"], [1, "fas", "fa-code"], [1, "json-tabs"], [1, "json-tab", 3, "click"], [1, "fas", "fa-sign-in-alt"], [1, "fas", "fa-check-double"], [1, "fas", "fa-cog"], [1, "fas", "fa-tags"], [1, "code-editor-container"], ["language", "json", 3, "value", "readonly", "toolbar", "lineWrapping"], [1, "edit-section"], [1, "edit-section-header"], [1, "fas", "fa-pen-to-square"], [1, "edit-section-sub"], [1, "edit-grid"], [1, "edit-field", "edit-field--full"], ["for", "test-name"], [1, "required"], ["id", "test-name", "type", "text", "placeholder", "e.g., Agent Response Quality Test", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-description"], ["id", "test-description", "rows", "3", "placeholder", "What does this test evaluate?", 1, "config-input", "config-textarea", 3, "ngModelChange", "ngModel"], [1, "edit-field"], ["for", "test-status"], [1, "select-wrapper"], ["id", "test-status", 1, "config-input", 3, "ngModelChange", "ngModel"], [3, "ngValue"], ["for", "test-type"], ["id", "test-type", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-tags"], [1, "tag-editor", 3, "click"], [1, "tag-chip-editable"], ["id", "test-tags", "type", "text", 1, "tag-input-inline", 3, "ngModelChange", "keydown", "blur", "placeholder", "ngModel"], [1, "config-hint"], [1, "fas", "fa-cogs"], ["for", "test-priority"], ["id", "test-priority", "type", "number", "placeholder", "0 (lower runs first)", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-repeat"], ["id", "test-repeat", "type", "number", "min", "1", "placeholder", "1", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-duration"], ["id", "test-duration", "type", "number", "placeholder", "0", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-cost"], ["id", "test-cost", "type", "number", "step", "0.000001", "placeholder", "0.00", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-timeout"], ["id", "test-timeout", "type", "number", "placeholder", "Default: 300000 (5 min)", 1, "config-input", 3, "ngModelChange", "ngModel"], [1, "config-section"], [1, "config-editor-container"], ["language", "json", 3, "change", "value", "readonly", "lineWrapping"], [1, "meta-section"], [1, "meta-grid"], [1, "meta-item"], [1, "meta-label"], [1, "meta-value"], [1, "meta-value", "meta-mono"], ["type", "button", 1, "tag-remove", 3, "click"], [1, "fas", "fa-times"], [1, "loading-state"], [1, "runs-list"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "skeleton-list"], [1, "skeleton-card"], [1, "skeleton-icon"], [1, "skeleton-content"], [1, "skeleton-line", "wide"], [1, "skeleton-line", "narrow"], [1, "run-item"], [1, "run-item", 3, "click"], [1, "run-icon"], [1, "fas"], [1, "run-content"], [1, "run-header"], [1, "run-id"], [1, "run-status"], [1, "run-meta"], [1, "fas", "fa-calendar"], [3, "entityName", "recordId"], [1, "run-eval-stack"], [1, "eval-pill", "status-pill", 3, "ngClass", "title"], [1, "run-tags"], [1, "fas", "fa-chevron-right"], [1, "fas", "fa-clock"], [1, "fas", "fa-dollar-sign"], ["title", "Human Review: No rating submitted yet", 1, "eval-pill", "human-pill", "no-feedback"], [1, "eval-pill", "human-pill", "has-feedback", 3, "rating-low", "rating-medium", "rating-good", "rating-excellent", "title"], [1, "fas", "fa-user-slash"], [1, "eval-pill", "human-pill", "has-feedback", 3, "title"], [1, "fas", "fa-user"], ["title", "Auto Score: No automated score available", 1, "eval-pill", "auto-pill", "no-score"], [1, "eval-pill", "auto-pill", "has-score", 3, "score-low", "score-medium", "score-good", "score-excellent", "title"], [1, "fas", "fa-robot"], [1, "eval-pill", "auto-pill", "has-score", 3, "title"], [1, "run-tag"], [1, "run-tag-more"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "suites-list"], ["Icon", "fa-solid fa-folder-open", "Title", "Not Part of Any Suite", "Message", "This test is not included in any test suites. Add it to a suite to run it with other tests."], [1, "suite-item"], [1, "suite-item", 3, "click"], [1, "suite-icon"], [1, "suite-content"], [1, "suite-name"], [1, "suite-meta"], [1, "fas", "fa-sort-numeric-up"], [1, "history-content"], ["text", "Loading history..."], [1, "history-filters"], [1, "time-range-filters"], [1, "filter-label"], [1, "filter-btn", 3, "click"], [1, "history-actions"], [1, "fas", "fa-download"], [1, "history-kpi-cards"], [1, "history-section"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "kpi-card"], [1, "kpi-icon"], [1, "fas", "fa-play-circle"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-icon", "pass-rate"], [1, "fas", "fa-percentage"], [1, "fas", "trend-icon"], [1, "fas", "fa-star"], [1, "fas", "fa-calendar-alt"], [1, "history-table-container"], [1, "history-table"], [1, "date-cell"], [1, "runs-cell"], [1, "passed-cell"], [1, "failed-cell"], [1, "pass-rate-cell"], [1, "score-cell"], [1, "duration-cell"], [1, "cost-cell"], [1, "pass-rate-bar"], [1, "pass-rate-fill"], [1, "pass-rate-text"], [1, "suite-performance-list"], [1, "suite-perf-card"], [1, "suite-perf-card", 3, "click"], [1, "suite-perf-header"], [1, "suite-perf-name"], [1, "suite-perf-tags"], [1, "suite-perf-stats"], [1, "suite-stat"], [1, "stat-value"], [1, "stat-label"], [1, "fas", "fa-chevron-right", "suite-perf-arrow"], [1, "tag-mini"], [1, "tag-more"], [1, "stat-value", "pass-rate"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "shortcuts-header"], ["title", "Hide shortcuts", 1, "shortcuts-close", 3, "click"], [1, "shortcut-list"], [1, "shortcut-item"], [1, "shortcut-keys"], ["Mode", "slide", 3, "Closed", "Title", "Resizable"], [3, "PanelClose", "PanelMode", "selectedTestId", "selectedSuiteId", "runMode"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-check"], [1, "save-bar-kbd"]], template: function MJTestFormComponentExtended_Template(rf, ctx) { if (rf & 1) {
2579
+ } }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 89, vars: 51, consts: [["tagInput", ""], [1, "test-form"], [1, "test-header"], ["aria-label", "Breadcrumb", 1, "breadcrumb"], ["href", "javascript:void(0)", 3, "click"], [1, "fas", "fa-vial"], [1, "breadcrumb-text"], [1, "current"], [1, "fas", "fa-chevron-right", "separator"], [1, "fas", "fa-flask"], [1, "header-content"], [1, "header-left"], [1, "test-icon"], [1, "test-info"], ["title", "You have unsaved changes", "aria-label", "Unsaved changes", 1, "dirty-indicator"], [1, "test-meta"], [1, "status-badge", 3, "ngClass"], [1, "fas", 3, "ngClass"], [1, "test-type"], ["title", "You have unsaved changes", 1, "unsaved-pill"], [1, "header-actions"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fas", "fa-play"], ["mjButton", "", 3, "click", "disabled"], [1, "test-description"], [1, "metrics-bar"], [1, "tabs-container"], ["role", "tablist", 1, "tabs"], ["role", "tab", 1, "tab", 3, "click"], [1, "fas", "fa-th-large"], [1, "fas", "fa-sliders-h"], [1, "fas", "fa-history"], [1, "tab-badge"], [1, "fas", "fa-layer-group"], [1, "fas", "fa-chart-line"], [1, "tab-content"], [1, "overview-tab"], [1, "config-tab"], [1, "runs-tab"], [1, "suites-tab"], [1, "history-tab"], [1, "shortcuts-toggle", 3, "click", "title"], [1, "fas", "fa-keyboard"], [1, "keyboard-shortcuts"], ["Mode", "slide", 3, "Title", "Resizable"], ["role", "region", "aria-label", "Unsaved changes", 1, "save-bar"], [1, "save-bar-message"], ["aria-hidden", "true", 1, "save-bar-dot"], [1, "save-bar-text"], [1, "save-bar-fields"], [1, "save-bar-actions"], ["type", "button", "title", "Save changes (\u2318S)", 1, "save-bar-btn", "save-bar-btn--primary", 3, "click", "disabled"], ["type", "button", "title", "Discard changes", 1, "save-bar-btn", "save-bar-btn--ghost", 3, "click", "disabled"], [1, "fas", "fa-undo"], [1, "fas", "fa-tag"], [1, "fas", "fa-circle"], [1, "metric-card"], [1, "metric-label"], [1, "metric-value"], [1, "metric-progress"], [1, "metric-progress-fill"], [1, "info-section"], [1, "fas", "fa-info-circle"], [1, "info-grid"], [1, "info-item"], [1, "info-label"], [1, "info-value"], [1, "status-badge-inline", 3, "ngClass"], [1, "json-section"], [1, "fas", "fa-code"], [1, "json-tabs"], [1, "json-tab", 3, "click"], [1, "fas", "fa-sign-in-alt"], [1, "fas", "fa-check-double"], [1, "fas", "fa-cog"], [1, "fas", "fa-tags"], [1, "code-editor-container"], ["language", "json", 3, "value", "readonly", "toolbar", "lineWrapping"], [1, "edit-section"], [1, "edit-section-header"], [1, "fas", "fa-pen-to-square"], [1, "edit-section-sub"], [1, "edit-grid"], [1, "edit-field", "edit-field--full"], ["for", "test-name"], [1, "required"], ["id", "test-name", "type", "text", "placeholder", "e.g., Agent Response Quality Test", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-description"], ["id", "test-description", "rows", "3", "placeholder", "What does this test evaluate?", 1, "config-input", "config-textarea", 3, "ngModelChange", "ngModel"], [1, "edit-field"], ["for", "test-status"], [1, "select-wrapper"], ["id", "test-status", 1, "config-input", 3, "ngModelChange", "ngModel"], [3, "ngValue"], ["for", "test-rubric"], ["id", "test-rubric", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-type"], ["id", "test-type", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-tags"], [1, "tag-editor", 3, "click"], [1, "tag-chip-editable"], ["id", "test-tags", "type", "text", 1, "tag-input-inline", 3, "ngModelChange", "keydown", "blur", "placeholder", "ngModel"], [1, "config-hint"], [1, "fas", "fa-cogs"], ["for", "test-priority"], ["id", "test-priority", "type", "number", "placeholder", "0 (lower runs first)", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-repeat"], ["id", "test-repeat", "type", "number", "min", "1", "placeholder", "1", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-duration"], ["id", "test-duration", "type", "number", "placeholder", "0", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-cost"], ["id", "test-cost", "type", "number", "step", "0.000001", "placeholder", "0.00", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-timeout"], ["id", "test-timeout", "type", "number", "placeholder", "Default: 300000 (5 min)", 1, "config-input", 3, "ngModelChange", "ngModel"], [1, "config-section"], [1, "config-editor-container"], ["language", "json", 3, "change", "value", "readonly", "lineWrapping"], [1, "meta-section"], [1, "meta-grid"], [1, "meta-item"], [1, "meta-label"], [1, "meta-value"], [1, "meta-value", "meta-mono"], ["type", "button", 1, "tag-remove", 3, "click"], [1, "fas", "fa-times"], [1, "loading-state"], [1, "runs-list"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "skeleton-list"], [1, "skeleton-card"], [1, "skeleton-icon"], [1, "skeleton-content"], [1, "skeleton-line", "wide"], [1, "skeleton-line", "narrow"], [1, "run-item"], [1, "run-item", 3, "click"], [1, "run-icon"], [1, "fas"], [1, "run-content"], [1, "run-header"], [1, "run-id"], [1, "run-status"], [1, "run-meta"], [1, "fas", "fa-calendar"], [3, "entityName", "recordId"], [1, "run-eval-stack"], [1, "eval-pill", "status-pill", 3, "ngClass", "title"], [1, "run-tags"], [1, "fas", "fa-chevron-right"], [1, "fas", "fa-clock"], [1, "fas", "fa-dollar-sign"], ["title", "Human Review: No rating submitted yet", 1, "eval-pill", "human-pill", "no-feedback"], [1, "eval-pill", "human-pill", "has-feedback", 3, "rating-low", "rating-medium", "rating-good", "rating-excellent", "title"], [1, "fas", "fa-user-slash"], [1, "eval-pill", "human-pill", "has-feedback", 3, "title"], [1, "fas", "fa-user"], ["title", "Auto Score: No automated score available", 1, "eval-pill", "auto-pill", "no-score"], [1, "eval-pill", "auto-pill", "has-score", 3, "score-low", "score-medium", "score-good", "score-excellent", "title"], [1, "fas", "fa-robot"], [1, "eval-pill", "auto-pill", "has-score", 3, "title"], [1, "run-tag"], [1, "run-tag-more"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "suites-list"], ["Icon", "fa-solid fa-folder-open", "Title", "Not Part of Any Suite", "Message", "This test is not included in any test suites. Add it to a suite to run it with other tests."], [1, "suite-item"], [1, "suite-item", 3, "click"], [1, "suite-icon"], [1, "suite-content"], [1, "suite-name"], [1, "suite-meta"], [1, "fas", "fa-sort-numeric-up"], [1, "history-content"], ["text", "Loading history..."], [1, "history-filters"], [1, "time-range-filters"], [1, "filter-label"], [1, "filter-btn", 3, "click"], [1, "history-actions"], [1, "fas", "fa-download"], [1, "history-kpi-cards"], [1, "history-section"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "kpi-card"], [1, "kpi-icon"], [1, "fas", "fa-play-circle"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-icon", "pass-rate"], [1, "fas", "fa-percentage"], [1, "fas", "trend-icon"], [1, "fas", "fa-star"], [1, "fas", "fa-calendar-alt"], [1, "history-table-container"], [1, "history-table"], [1, "date-cell"], [1, "runs-cell"], [1, "passed-cell"], [1, "failed-cell"], [1, "pass-rate-cell"], [1, "score-cell"], [1, "duration-cell"], [1, "cost-cell"], [1, "pass-rate-bar"], [1, "pass-rate-fill"], [1, "pass-rate-text"], [1, "suite-performance-list"], [1, "suite-perf-card"], [1, "suite-perf-card", 3, "click"], [1, "suite-perf-header"], [1, "suite-perf-name"], [1, "suite-perf-tags"], [1, "suite-perf-stats"], [1, "suite-stat"], [1, "stat-value"], [1, "stat-label"], [1, "fas", "fa-chevron-right", "suite-perf-arrow"], [1, "tag-mini"], [1, "tag-more"], [1, "stat-value", "pass-rate"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "shortcuts-header"], ["title", "Hide shortcuts", 1, "shortcuts-close", 3, "click"], [1, "shortcut-list"], [1, "shortcut-item"], [1, "shortcut-keys"], ["Mode", "slide", 3, "Closed", "Title", "Resizable"], [3, "PanelClose", "PanelMode", "selectedTestId", "selectedSuiteId", "runMode"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-check"], [1, "save-bar-kbd"]], template: function MJTestFormComponentExtended_Template(rf, ctx) { if (rf & 1) {
2159
2580
  i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "nav", 3)(3, "ol")(4, "li")(5, "a", 4);
2160
2581
  i0.ɵɵlistener("click", function MJTestFormComponentExtended_Template_a_click_5_listener() { return ctx.navigateToTestingDashboard(); });
2161
2582
  i0.ɵɵelement(6, "i", 5);
@@ -2232,7 +2653,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
2232
2653
  i0.ɵɵelementEnd()()()();
2233
2654
  i0.ɵɵelementStart(62, "div", 35);
2234
2655
  i0.ɵɵconditionalCreate(63, MJTestFormComponentExtended_Conditional_63_Template, 78, 29, "div", 36);
2235
- i0.ɵɵconditionalCreate(64, MJTestFormComponentExtended_Conditional_64_Template, 111, 30, "div", 37);
2656
+ i0.ɵɵconditionalCreate(64, MJTestFormComponentExtended_Conditional_64_Template, 119, 32, "div", 37);
2236
2657
  i0.ɵɵconditionalCreate(65, MJTestFormComponentExtended_Conditional_65_Template, 4, 3, "div", 38);
2237
2658
  i0.ɵɵconditionalCreate(66, MJTestFormComponentExtended_Conditional_66_Template, 4, 3, "div", 39);
2238
2659
  i0.ɵɵconditionalCreate(67, MJTestFormComponentExtended_Conditional_67_Template, 3, 2, "div", 40);
@@ -2348,7 +2769,7 @@ MJTestFormComponentExtended = __decorate([
2348
2769
  export { MJTestFormComponentExtended };
2349
2770
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJTestFormComponentExtended, [{
2350
2771
  type: Component,
2351
- args: [{ standalone: false, selector: 'mj-test-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"test-form\">\n <!-- Header Section -->\n <div class=\"test-header\">\n <!-- Breadcrumb Navigation -->\n <nav class=\"breadcrumb\" aria-label=\"Breadcrumb\">\n <ol>\n <li>\n <a href=\"javascript:void(0)\" (click)=\"navigateToTestingDashboard()\">\n <i class=\"fas fa-vial\"></i>\n <span class=\"breadcrumb-text\">Testing</span>\n </a>\n </li>\n <li class=\"current\">\n <i class=\"fas fa-chevron-right separator\"></i>\n <i class=\"fas fa-flask\"></i>\n <span>{{ record.Name }}</span>\n </li>\n </ol>\n </nav>\n\n <div class=\"header-content\">\n <div class=\"header-left\">\n <div class=\"test-icon\" [style.background-color]=\"getStatusColor()\">\n <i class=\"fas fa-flask\"></i>\n </div>\n <div class=\"test-info\">\n <h1>\n {{ record.Name }}\n @if (isDirty) {\n <span class=\"dirty-indicator\" title=\"You have unsaved changes\" aria-label=\"Unsaved changes\">\u25CF</span>\n }\n </h1>\n <div class=\"test-meta\">\n <span class=\"status-badge\" [ngClass]=\"getStatusClass()\">\n <i class=\"fas\" [ngClass]=\"getStatusIcon()\"></i>\n {{ record.Status }}\n </span>\n @if (record.Type) {\n <span class=\"test-type\">\n <i class=\"fas fa-tag\"></i>\n {{ record.Type }}\n </span>\n }\n @if (isDirty) {\n <span class=\"unsaved-pill\" title=\"You have unsaved changes\">\n <i class=\"fas fa-circle\"></i> Unsaved\n </span>\n }\n </div>\n </div>\n </div>\n <div class=\"header-actions\">\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n <button mjButton (click)=\"runTest()\" variant=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test\n </button>\n <button mjButton (click)=\"refresh()\" [disabled]=\"isRefreshing\">\n <i class=\"fas\" [ngClass]=\"isRefreshing ? 'fa-sync fa-spin' : 'fa-sync'\"></i>\n {{ isRefreshing ? 'Refreshing...' : 'Refresh' }}\n </button>\n </div>\n </div>\n\n <!-- Test Description -->\n @if (record.Description) {\n <div class=\"test-description\">\n <p>{{ record.Description }}</p>\n </div>\n }\n\n <!-- Metrics Bar -->\n @if (testRunsLoaded && testRuns.length > 0) {\n <div class=\"metrics-bar\">\n <div class=\"metric-card\">\n <div class=\"metric-label\">Total Runs</div>\n <div class=\"metric-value\">{{ testRuns.length }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Pass Rate</div>\n <div class=\"metric-value\">{{ getPassRate().toFixed(1) }}%</div>\n <div class=\"metric-progress\">\n <div class=\"metric-progress-fill\" [style.width.%]=\"getPassRate()\"></div>\n </div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Cost</div>\n <div class=\"metric-value\">{{ formatCost(getAverageCost()) }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Duration</div>\n <div class=\"metric-value\">{{ formatDuration(getAverageDuration()) }}</div>\n </div>\n </div>\n }\n </div>\n\n <!-- Tabs -->\n <div class=\"tabs-container\">\n <div class=\"tabs\" role=\"tablist\">\n <button class=\"tab\"\n [class.active]=\"activeTab === 'overview'\"\n (click)=\"changeTab('overview')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'overview'\">\n <i class=\"fas fa-th-large\"></i>\n <span>Overview</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'config'\"\n (click)=\"changeTab('config')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'config'\">\n <i class=\"fas fa-sliders-h\"></i>\n <span>Configuration</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'runs'\"\n (click)=\"changeTab('runs')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'runs'\">\n <i class=\"fas fa-history\"></i>\n <span>Runs</span>\n @if (testRunsLoaded) {\n <span class=\"tab-badge\">{{ testRuns.length }}</span>\n }\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'suites'\"\n (click)=\"changeTab('suites')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'suites'\">\n <i class=\"fas fa-layer-group\"></i>\n <span>Test Suites</span>\n @if (suiteTestsLoaded) {\n <span class=\"tab-badge\">{{ suiteTests.length }}</span>\n }\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'analytics'\"\n (click)=\"changeTab('analytics')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'analytics'\">\n <i class=\"fas fa-chart-line\"></i>\n <span>Analytics</span>\n </button>\n </div>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tab-content\" [class.has-savebar]=\"isDirty\">\n <!-- Overview Tab -->\n @if (activeTab === 'overview') {\n <div class=\"overview-tab\">\n <!-- Basic Information -->\n <div class=\"info-section\">\n <h3><i class=\"fas fa-info-circle\"></i> Test Information</h3>\n <div class=\"info-grid\">\n <div class=\"info-item\">\n <div class=\"info-label\">Name</div>\n <div class=\"info-value\">{{ record.Name }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Type</div>\n <div class=\"info-value\">{{ record.Type || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Status</div>\n <div class=\"info-value\">\n <span class=\"status-badge-inline\" [ngClass]=\"getStatusClass()\">{{ record.Status }}</span>\n </div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Priority</div>\n <div class=\"info-value\">{{ record.Priority || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Duration</div>\n <div class=\"info-value\">{{ record.EstimatedDurationSeconds ? formatDuration(record.EstimatedDurationSeconds) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Cost</div>\n <div class=\"info-value\">{{ record.EstimatedCostUSD ? formatCost(record.EstimatedCostUSD) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Repeat Count</div>\n <div class=\"info-value\">{{ record.RepeatCount || 1 }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Max Execution Time</div>\n <div class=\"info-value\">{{ formatTimeout(record.MaxExecutionTimeMS) }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Created</div>\n <div class=\"info-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Updated</div>\n <div class=\"info-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n </div>\n </div>\n <!-- JSON Data Views -->\n <div class=\"json-section\">\n <h3><i class=\"fas fa-code\"></i> Test Definition</h3>\n <div class=\"json-tabs\">\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'input'\"\n (click)=\"setJsonView('input')\">\n <i class=\"fas fa-sign-in-alt\"></i>\n Input Definition\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'expected'\"\n (click)=\"setJsonView('expected')\">\n <i class=\"fas fa-check-double\"></i>\n Expected Outcomes\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'config'\"\n (click)=\"setJsonView('config')\">\n <i class=\"fas fa-cog\"></i>\n Configuration\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'tags'\"\n (click)=\"setJsonView('tags')\">\n <i class=\"fas fa-tags\"></i>\n Tags\n </button>\n </div>\n <div class=\"code-editor-container\">\n <mj-code-editor\n [value]=\"getJsonData()\"\n language=\"json\"\n [readonly]=\"true\"\n [toolbar]=\"jsonToolbar\"\n [lineWrapping]=\"true\">\n </mj-code-editor>\n </div>\n </div>\n </div>\n }\n\n <!-- Configuration Tab -->\n @if (activeTab === 'config') {\n <div class=\"config-tab\">\n <!-- Details -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-pen-to-square\"></i> Details</h3>\n <p class=\"edit-section-sub\">Edit any field \u2014 changes are saved with the bar at the bottom.</p>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-name\">Name <span class=\"required\">*</span></label>\n <input\n id=\"test-name\"\n type=\"text\"\n class=\"config-input\"\n placeholder=\"e.g., Agent Response Quality Test\"\n [(ngModel)]=\"record.Name\"\n />\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-description\">Description</label>\n <textarea\n id=\"test-description\"\n class=\"config-input config-textarea\"\n rows=\"3\"\n placeholder=\"What does this test evaluate?\"\n [ngModel]=\"record.Description\"\n (ngModelChange)=\"record.Description = $event || null\"\n ></textarea>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-status\">Status</label>\n <div class=\"select-wrapper\">\n <select\n id=\"test-status\"\n class=\"config-input\"\n [(ngModel)]=\"record.Status\"\n >\n @for (opt of statusOptions; track opt) {\n <option [ngValue]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-type\">Test Type</label>\n <select\n id=\"test-type\"\n class=\"config-input\"\n [(ngModel)]=\"record.TypeID\"\n >\n @for (t of testTypeOptions; track t.ID) {\n <option [ngValue]=\"t.ID\">{{ t.Name }}</option>\n }\n </select>\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-tags\">Tags</label>\n <div class=\"tag-editor\" (click)=\"tagInput.focus()\">\n @for (tag of tags; track tag) {\n <span class=\"tag-chip-editable\">\n {{ tag }}\n <button type=\"button\" class=\"tag-remove\" (click)=\"removeTag(tag); $event.stopPropagation()\" [attr.aria-label]=\"'Remove tag ' + tag\">\n <i class=\"fas fa-times\"></i>\n </button>\n </span>\n }\n <input\n #tagInput\n id=\"test-tags\"\n type=\"text\"\n class=\"tag-input-inline\"\n [placeholder]=\"tags.length === 0 ? 'Add tags, press Enter or comma...' : ''\"\n [(ngModel)]=\"tagDraft\"\n (keydown)=\"onTagInputKeydown($event)\"\n (blur)=\"addTagFromDraft()\"\n />\n </div>\n <span class=\"config-hint\">Press Enter or comma to add a tag. Backspace on empty input removes the last tag.</span>\n </div>\n </div>\n </section>\n\n <!-- Execution Settings -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-cogs\"></i> Execution Settings</h3>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field\">\n <label for=\"test-priority\">Priority</label>\n <input\n id=\"test-priority\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0 (lower runs first)\"\n [ngModel]=\"record.Priority\"\n (ngModelChange)=\"record.Priority = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Lower numbers run first.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-repeat\">Repeat Count</label>\n <input\n id=\"test-repeat\"\n type=\"number\"\n class=\"config-input\"\n min=\"1\"\n placeholder=\"1\"\n [ngModel]=\"record.RepeatCount\"\n (ngModelChange)=\"record.RepeatCount = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Number of times to repeat for statistical analysis.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-duration\">Estimated Duration (seconds)</label>\n <input\n id=\"test-duration\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0\"\n [ngModel]=\"record.EstimatedDurationSeconds\"\n (ngModelChange)=\"record.EstimatedDurationSeconds = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field\">\n <label for=\"test-cost\">Estimated Cost (USD)</label>\n <input\n id=\"test-cost\"\n type=\"number\"\n step=\"0.000001\"\n class=\"config-input\"\n placeholder=\"0.00\"\n [ngModel]=\"record.EstimatedCostUSD\"\n (ngModelChange)=\"record.EstimatedCostUSD = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-timeout\">Max Execution Time (ms)</label>\n <input\n id=\"test-timeout\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"Default: 300000 (5 min)\"\n [ngModel]=\"record.MaxExecutionTimeMS\"\n (ngModelChange)=\"record.MaxExecutionTimeMS = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Leave empty for the default 5 minute timeout. Currently: {{ formatTimeout(record.MaxExecutionTimeMS) }}</span>\n </div>\n </div>\n </section>\n\n <!-- JSON editors (preserved from original layout) -->\n <div class=\"config-section\">\n <h3><i class=\"fas fa-sign-in-alt\"></i> Input Definition (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.InputDefinition || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.InputDefinition = $event\">\n </mj-code-editor>\n </div>\n </div>\n <div class=\"config-section\">\n <h3><i class=\"fas fa-check-double\"></i> Expected Outcomes (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.ExpectedOutcomes || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.ExpectedOutcomes = $event\">\n </mj-code-editor>\n </div>\n </div>\n <div class=\"config-section\">\n <h3><i class=\"fas fa-cog\"></i> Configuration (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.Configuration || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.Configuration = $event\">\n </mj-code-editor>\n </div>\n </div>\n\n <!-- Metadata (read-only) -->\n <section class=\"meta-section\">\n <div class=\"meta-grid\">\n <div class=\"meta-item\">\n <div class=\"meta-label\">Created</div>\n <div class=\"meta-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Last updated</div>\n <div class=\"meta-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Test ID</div>\n <div class=\"meta-value meta-mono\">{{ record.ID }}</div>\n </div>\n </div>\n </section>\n </div>\n }\n\n <!-- Test Runs Tab -->\n @if (activeTab === 'runs') {\n <div class=\"runs-tab\">\n <!-- Loading State -->\n @if (loadingRuns) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3,4,5]; track i) {\n <div class=\"skeleton-card\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line wide\"></div>\n <div class=\"skeleton-line narrow\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n <!-- Runs List -->\n @if (!loadingRuns && testRuns.length > 0) {\n <div class=\"runs-list\">\n @for (run of testRuns; track run) {\n <div class=\"run-item\" (click)=\"openTestRun(run.ID)\">\n <div class=\"run-icon\" [style.background-color]=\"getRunStatusColor(run.Status)\">\n <i class=\"fas\"\n [class.fa-check]=\"run.Status === 'Passed'\"\n [class.fa-times]=\"run.Status === 'Failed'\"\n [class.fa-exclamation]=\"run.Status === 'Error'\"\n [class.fa-stopwatch]=\"run.Status === 'Timeout'\"\n [class.fa-spinner]=\"run.Status === 'Running'\"\n [class.fa-clock]=\"run.Status === 'Pending'\"></i>\n </div>\n <div class=\"run-content\">\n <div class=\"run-header\">\n <span class=\"run-id\">Run #{{ run.ID.substring(0, 8) }}</span>\n <span class=\"run-status\" [style.color]=\"getRunStatusColor(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-meta\">\n <span><i class=\"fas fa-calendar\"></i> {{ getRelativeTime(run.StartedAt) }}</span>\n @if (run.DurationSeconds) {\n <span><i class=\"fas fa-clock\"></i> {{ formatDuration(run.DurationSeconds) }}</span>\n }\n @if (run.CostUSD) {\n <span><i class=\"fas fa-dollar-sign\"></i> {{ formatCost(run.CostUSD) }}</span>\n }\n @if (run.TargetLogEntityID && run.TargetLogID) {\n <mj-entity-link-pill\n [entityName]=\"run.TargetLogEntity\"\n [recordId]=\"run.TargetLogID\">\n </mj-entity-link-pill>\n }\n </div>\n <!-- Evaluation indicators -->\n <div class=\"run-eval-stack\">\n <!-- Status indicator -->\n @if (evalPreferences.showExecution) {\n <span class=\"eval-pill status-pill\"\n [ngClass]=\"'status-' + run.Status.toLowerCase()\"\n [title]=\"getStatusTooltip(run.Status)\">\n <i class=\"fas\"\n [class.fa-check]=\"run.Status === 'Passed'\"\n [class.fa-times]=\"run.Status === 'Failed'\"\n [class.fa-exclamation]=\"run.Status === 'Error'\"\n [class.fa-hourglass-end]=\"run.Status === 'Timeout'\"\n [class.fa-forward]=\"run.Status === 'Skipped'\"\n [class.fa-spinner]=\"run.Status === 'Running'\"\n [class.fa-clock]=\"run.Status === 'Pending'\"></i>\n <span>{{ run.Status }}</span>\n </span>\n }\n <!-- Human feedback indicator -->\n @if (evalPreferences.showHuman) {\n @if (!getFeedbackForRun(run.ID)?.Rating) {\n <span class=\"eval-pill human-pill no-feedback\"\n title=\"Human Review: No rating submitted yet\">\n <i class=\"fas fa-user-slash\"></i>\n </span>\n }\n @if (getFeedbackForRun(run.ID)?.Rating; as rating) {\n <span class=\"eval-pill human-pill has-feedback\"\n [class.rating-low]=\"rating <= 4\"\n [class.rating-medium]=\"rating >= 5 && rating <= 6\"\n [class.rating-good]=\"rating >= 7 && rating <= 8\"\n [class.rating-excellent]=\"rating >= 9\"\n [title]=\"getHumanTooltip(rating, getFeedbackForRun(run.ID)?.CorrectionSummary || getFeedbackForRun(run.ID)?.Comments || null)\">\n <i class=\"fas fa-user\"></i>\n <span>{{ rating }}</span>\n </span>\n }\n }\n <!-- Auto score indicator -->\n @if (evalPreferences.showAuto) {\n @if (run.Score == null) {\n <span class=\"eval-pill auto-pill no-score\"\n title=\"Auto Score: No automated score available\">\n <i class=\"fas fa-robot\"></i>\n </span>\n }\n @if (run.Score != null) {\n <span class=\"eval-pill auto-pill has-score\"\n [class.score-low]=\"run.Score < 0.5\"\n [class.score-medium]=\"run.Score >= 0.5 && run.Score < 0.7\"\n [class.score-good]=\"run.Score >= 0.7 && run.Score < 0.85\"\n [class.score-excellent]=\"run.Score >= 0.85\"\n [title]=\"'Auto Score: ' + (run.Score * 100).toFixed(0) + '% automated evaluation'\">\n <i class=\"fas fa-robot\"></i>\n <span>{{ (run.Score * 100).toFixed(0) }}%</span>\n </span>\n }\n }\n </div>\n @if (getRunTags(run).length > 0) {\n <div class=\"run-tags\">\n @for (tag of getRunTags(run).slice(0, 3); track tag) {\n <span class=\"run-tag\">{{ tag }}</span>\n }\n @if (getRunTags(run).length > 3) {\n <span class=\"run-tag-more\">+{{ getRunTags(run).length - 3 }}</span>\n }\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (testRunsLoaded && !loadingRuns && testRuns.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-circle-play\"\n Title=\"No Test Runs Yet\"\n Message=\"Run this test to see execution history and results here.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n\n <!-- Test Suites Tab -->\n @if (activeTab === 'suites') {\n <div class=\"suites-tab\">\n <!-- Loading State -->\n @if (loadingSuites) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3]; track i) {\n <div class=\"skeleton-card\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line wide\"></div>\n <div class=\"skeleton-line narrow\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n <!-- Suites List -->\n @if (!loadingSuites && suiteTests.length > 0) {\n <div class=\"suites-list\">\n @for (suiteTest of suiteTests; track suiteTest) {\n <div class=\"suite-item\" (click)=\"openTestSuite(suiteTest.SuiteID)\">\n <div class=\"suite-icon\">\n <i class=\"fas fa-layer-group\"></i>\n </div>\n <div class=\"suite-content\">\n <div class=\"suite-name\">{{ suiteTest.Suite }}</div>\n <div class=\"suite-meta\">\n <span><i class=\"fas fa-sort-numeric-up\"></i> Sequence: {{ suiteTest.Sequence }}</span>\n @if (suiteTest.Status) {\n <span><i class=\"fas fa-info-circle\"></i> {{ suiteTest.Status }}</span>\n }\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (suiteTestsLoaded && !loadingSuites && suiteTests.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-open\"\n Title=\"Not Part of Any Suite\"\n Message=\"This test is not included in any test suites. Add it to a suite to run it with other tests.\" />\n }\n </div>\n }\n\n <!-- Analytics Tab -->\n @if (activeTab === 'analytics') {\n <div class=\"history-tab\">\n <!-- Loading State -->\n @if (loadingHistory) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading history...\"></mj-loading>\n </div>\n }\n <!-- History Content -->\n @if (!loadingHistory && historyLoaded) {\n <div class=\"history-content\">\n <!-- Filters -->\n <div class=\"history-filters\">\n <div class=\"time-range-filters\">\n <span class=\"filter-label\">Time Range:</span>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '7d'\"\n (click)=\"setHistoryTimeRange('7d')\">7 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '30d'\"\n (click)=\"setHistoryTimeRange('30d')\">30 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '90d'\"\n (click)=\"setHistoryTimeRange('90d')\">90 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === 'all'\"\n (click)=\"setHistoryTimeRange('all')\">All Time</button>\n </div>\n <div class=\"history-actions\">\n <button mjButton (click)=\"exportHistoryToCSV()\" [disabled]=\"historyData.length === 0\">\n <i class=\"fas fa-download\"></i> Export CSV\n </button>\n </div>\n </div>\n <!-- KPI Cards -->\n @if (historyData.length > 0) {\n <div class=\"history-kpi-cards\">\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-play-circle\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ getTotalRuns() }}</div>\n <div class=\"kpi-label\">Total Runs</div>\n </div>\n </div>\n @if (evalPreferences.showExecution) {\n <div class=\"kpi-card\">\n <div class=\"kpi-icon pass-rate\">\n <i class=\"fas fa-percentage\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">\n {{ getOverallPassRate().toFixed(1) }}%\n <i class=\"fas trend-icon\"\n [class.fa-arrow-up]=\"getPassRateTrend() === 'up'\"\n [class.fa-arrow-down]=\"getPassRateTrend() === 'down'\"\n [class.fa-minus]=\"getPassRateTrend() === 'stable'\"\n [class.trend-up]=\"getPassRateTrend() === 'up'\"\n [class.trend-down]=\"getPassRateTrend() === 'down'\"></i>\n </div>\n <div class=\"kpi-label\">Pass Rate</div>\n </div>\n </div>\n }\n @if (evalPreferences.showAuto) {\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-star\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ (getOverallAvgScore() * 100).toFixed(1) }}%</div>\n <div class=\"kpi-label\">Avg Score</div>\n </div>\n </div>\n }\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-clock\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ formatDuration(getOverallAvgDuration()) }}</div>\n <div class=\"kpi-label\">Avg Duration</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-dollar-sign\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ formatCost(getOverallAvgCost()) }}</div>\n <div class=\"kpi-label\">Avg Cost</div>\n </div>\n </div>\n </div>\n }\n <!-- Daily History Table -->\n @if (historyData.length > 0) {\n <div class=\"history-section\">\n <h3><i class=\"fas fa-calendar-alt\"></i> Daily Performance</h3>\n <div class=\"history-table-container\">\n <table class=\"history-table\">\n <thead>\n <tr>\n <th>Date</th>\n <th>Runs</th>\n @if (evalPreferences.showExecution) {\n <th>Passed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Failed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Pass Rate</th>\n }\n @if (evalPreferences.showAuto) {\n <th>Avg Score</th>\n }\n <th>Avg Duration</th>\n <th>Avg Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (day of historyData; track day) {\n <tr>\n <td class=\"date-cell\">{{ day.date | date:'mediumDate' }}</td>\n <td class=\"runs-cell\">{{ day.runCount }}</td>\n @if (evalPreferences.showExecution) {\n <td class=\"passed-cell\">{{ day.passCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"failed-cell\">{{ day.failCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"pass-rate-cell\">\n <div class=\"pass-rate-bar\">\n <div class=\"pass-rate-fill\" [style.width.%]=\"day.passRate\"></div>\n <span class=\"pass-rate-text\">{{ day.passRate.toFixed(1) }}%</span>\n </div>\n </td>\n }\n @if (evalPreferences.showAuto) {\n <td class=\"score-cell\">{{ (day.avgScore * 100).toFixed(1) }}%</td>\n }\n <td class=\"duration-cell\">{{ formatDuration(day.avgDuration) }}</td>\n <td class=\"cost-cell\">{{ formatCost(day.avgCost) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n <!-- Suite Performance -->\n @if (suitePerformance.length > 0) {\n <div class=\"history-section\">\n <h3><i class=\"fas fa-layer-group\"></i> Performance by Suite</h3>\n <div class=\"suite-performance-list\">\n @for (suite of suitePerformance; track suite) {\n <div class=\"suite-perf-card\" (click)=\"openSuiteFromHistory(suite.suiteId)\">\n <div class=\"suite-perf-header\">\n <div class=\"suite-perf-name\">{{ suite.suiteName }}</div>\n @if (suite.tags.length > 0) {\n <div class=\"suite-perf-tags\">\n @for (tag of suite.tags.slice(0, 3); track tag) {\n <span class=\"tag-mini\">{{ tag }}</span>\n }\n @if (suite.tags.length > 3) {\n <span class=\"tag-more\">+{{ suite.tags.length - 3 }}</span>\n }\n </div>\n }\n </div>\n <div class=\"suite-perf-stats\">\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ suite.totalRuns }}</span>\n <span class=\"stat-label\">Runs</span>\n </div>\n @if (evalPreferences.showExecution) {\n <div class=\"suite-stat\">\n <span class=\"stat-value pass-rate\" [class.high]=\"suite.passRate >= 80\" [class.low]=\"suite.passRate < 50\">\n {{ suite.passRate.toFixed(1) }}%\n </span>\n <span class=\"stat-label\">Pass Rate</span>\n </div>\n }\n @if (evalPreferences.showAuto) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ (suite.avgScore * 100).toFixed(1) }}%</span>\n <span class=\"stat-label\">Avg Score</span>\n </div>\n }\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ formatDuration(suite.avgDuration) }}</span>\n <span class=\"stat-label\">Avg Duration</span>\n </div>\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ formatCost(suite.avgCost) }}</span>\n <span class=\"stat-label\">Avg Cost</span>\n </div>\n @if (suite.lastRun) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ getRelativeTime(suite.lastRun) }}</span>\n <span class=\"stat-label\">Last Run</span>\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right suite-perf-arrow\"></i>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty State -->\n @if (historyData.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-chart-line\"\n Title=\"No History Available\"\n Message=\"Run this test to start building history and analytics data.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Keyboard Shortcuts Toggle Button -->\n <button class=\"shortcuts-toggle\" (click)=\"toggleShortcuts()\" [title]=\"showShortcuts ? 'Hide keyboard shortcuts' : 'Show keyboard shortcuts'\">\n <i class=\"fas fa-keyboard\"></i>\n </button>\n\n <!-- Keyboard Shortcuts Hint (Desktop Only) -->\n @if (showShortcuts) {\n <div class=\"keyboard-shortcuts\">\n <div class=\"shortcuts-header\">\n <i class=\"fas fa-keyboard\"></i>\n Shortcuts\n <button class=\"shortcuts-close\" (click)=\"toggleShortcuts()\" title=\"Hide shortcuts\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"shortcut-list\">\n <div class=\"shortcut-item\">\n <span>Save changes</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>S</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Refresh</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>R</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Run Test</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>Enter</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Switch Tabs</span>\n <span class=\"shortcut-keys\"><kbd>1</kbd>-<kbd>5</kbd></span>\n </div>\n </div>\n </div>\n }\n\n <!-- Slide Panel for Test Execution -->\n @if (testingDialogService.IsPanelOpen) {\n <mj-slide-panel\n Mode=\"slide\"\n [Title]=\"testingDialogService.PanelOptions?.testId ? 'Test Execution' : 'Run Test'\"\n [Resizable]=\"true\"\n (Closed)=\"OnPanelClosed()\">\n <app-test-run-dialog\n [PanelMode]=\"true\"\n [selectedTestId]=\"testingDialogService.PanelOptions?.testId ?? null\"\n [selectedSuiteId]=\"testingDialogService.PanelOptions?.suiteId ?? null\"\n [runMode]=\"testingDialogService.PanelOptions?.mode ?? 'test'\"\n (PanelClose)=\"OnPanelClosed()\">\n </app-test-run-dialog>\n </mj-slide-panel>\n }\n\n <!-- Sticky Save Bar \u2014 always in the DOM so CSS transitions can fire reliably.\n Visibility is controlled by the .save-bar--visible class, driven by isDirty. -->\n <div\n class=\"save-bar\"\n [class.save-bar--visible]=\"isDirty\"\n [attr.aria-hidden]=\"!isDirty\"\n role=\"region\"\n aria-label=\"Unsaved changes\"\n >\n <div class=\"save-bar-message\">\n <span class=\"save-bar-dot\" aria-hidden=\"true\">\u25CF</span>\n <span class=\"save-bar-text\">\n <strong>Unsaved changes</strong>\n <span class=\"save-bar-fields\">\n @if (dirtyFieldNames.length === 1) {\n {{ dirtyFieldNames[0] }} edited\n } @else if (dirtyFieldNames.length > 1) {\n {{ dirtyFieldNames.length }} fields edited\n }\n </span>\n </span>\n </div>\n <div class=\"save-bar-actions\">\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--primary\"\n (click)=\"saveChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Save changes (\u2318S)\"\n >\n @if (isSaving) {\n <i class=\"fas fa-spinner fa-spin\"></i> Saving\u2026\n } @else {\n <i class=\"fas fa-check\"></i> Save changes\n <span class=\"save-bar-kbd\"><kbd>\u2318</kbd><kbd>S</kbd></span>\n }\n </button>\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--ghost\"\n (click)=\"discardChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Discard changes\"\n >\n <i class=\"fas fa-undo\"></i> Discard\n </button>\n </div>\n </div>\n</div>\n", styles: ["/* ===========================\n Test Form - World-Class UX\n Premium Test Definition Styles\n =========================== */\n\n/* CSS Custom Properties for Theming - using :host for Angular encapsulation */\n:host {\n --test-primary: var(--mj-brand-primary);\n --test-primary-light: var(--mj-brand-primary);\n --test-primary-dark: var(--mj-brand-primary-hover);\n --test-success: var(--mj-status-success);\n --test-success-light: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n --test-error: var(--mj-status-error);\n --test-error-light: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n --test-warning: var(--mj-status-warning);\n --test-warning-light: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n --test-disabled: var(--mj-text-muted);\n --test-bg: var(--mj-bg-surface-card);\n --test-surface: var(--mj-bg-surface);\n --test-border: var(--mj-border-default);\n --test-text: var(--mj-text-primary);\n --test-text-secondary: var(--mj-text-muted);\n --test-text-muted: var(--mj-text-disabled);\n --test-radius-sm: 6px;\n --test-radius-md: 10px;\n --test-radius-lg: 16px;\n --test-shadow-sm: var(--mj-shadow-sm);\n --test-shadow-md: var(--mj-shadow-md);\n --test-shadow-lg: var(--mj-shadow-lg);\n --test-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: block;\n height: 100%;\n}\n\n/* Breadcrumb */\n.breadcrumb {\n margin-bottom: 16px;\n}\n\n.breadcrumb ol {\n display: flex;\n align-items: center;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n font-size: 13px;\n}\n\n.breadcrumb li {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.breadcrumb a {\n color: var(--test-primary);\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.15s;\n}\n\n.breadcrumb a:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n text-decoration: none;\n}\n\n.breadcrumb .separator {\n font-size: 10px;\n color: var(--test-text-muted);\n margin: 0 4px;\n}\n\n.breadcrumb .current {\n color: var(--test-text-secondary);\n font-weight: 500;\n}\n\n.breadcrumb-text {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Base Container */\n.test-form {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--test-bg);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ===========================\n Header Section\n =========================== */\n.test-header {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n padding: 20px;\n}\n\n.header-content {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n gap: 16px;\n}\n\n.header-left {\n display: flex;\n gap: 16px;\n flex: 1;\n min-width: 0;\n}\n\n/* Test Icon */\n.test-icon {\n width: 56px;\n height: 56px;\n border-radius: var(--test-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-inverse);\n font-size: 24px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-md);\n transition: var(--test-transition);\n}\n\n.test-icon:hover {\n transform: scale(1.05);\n}\n\n/* Test Info */\n.test-info {\n flex: 1;\n min-width: 0;\n}\n\n.test-info h1 {\n margin: 0 0 8px 0;\n font-size: clamp(18px, 4vw, 24px);\n font-weight: 700;\n color: var(--test-text);\n line-height: 1.2;\n word-wrap: break-word;\n}\n\n.test-meta {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border-radius: 20px;\n color: var(--mj-text-inverse);\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.status-badge.status-active { background: var(--mj-status-success); }\n.status-badge.status-disabled { background: var(--mj-text-secondary); }\n.status-badge.status-pending { background: var(--mj-status-warning); }\n\n.status-badge-inline {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n border-radius: 10px;\n color: var(--mj-text-inverse);\n font-size: 11px;\n font-weight: 600;\n}\n\n.status-badge-inline.status-active { background: var(--test-success); }\n.status-badge-inline.status-disabled { background: var(--test-disabled); }\n.status-badge-inline.status-pending { background: var(--test-warning); }\n\n.test-type {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: var(--test-text-secondary);\n padding: 4px 10px;\n background: var(--test-bg);\n border-radius: var(--test-radius-sm);\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.header-actions button {\n white-space: nowrap;\n}\n\n/* Test Description */\n.test-description {\n padding: 16px;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--test-radius-md);\n margin-bottom: 16px;\n border: 1px solid var(--test-border);\n}\n\n.test-description p {\n margin: 0;\n color: var(--test-text-secondary);\n line-height: 1.6;\n font-size: 14px;\n}\n\n/* ===========================\n Metrics Bar\n =========================== */\n.metrics-bar {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: 12px;\n}\n\n.metric-card {\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 14px;\n text-align: center;\n transition: var(--test-transition);\n}\n\n.metric-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.metric-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n margin-bottom: 6px;\n}\n\n.metric-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n/* Progress bar in metric card */\n.metric-progress {\n margin-top: 8px;\n height: 4px;\n background: var(--test-border);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.metric-progress-fill {\n height: 100%;\n background: var(--mj-status-success);\n border-radius: 2px;\n transition: width 0.5s ease-out;\n}\n\n/* ===========================\n Tabs Navigation\n =========================== */\n.tabs-container {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tabs {\n display: flex;\n padding: 0 20px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.tabs::-webkit-scrollbar {\n display: none;\n}\n\n.tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 18px;\n border: none;\n background: transparent;\n border-bottom: 3px solid transparent;\n color: var(--test-text-secondary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n white-space: nowrap;\n}\n\n.tab:hover {\n color: var(--test-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n}\n\n.tab.active {\n color: var(--test-primary);\n border-bottom-color: var(--test-primary);\n font-weight: 600;\n}\n\n.tab i {\n font-size: 15px;\n}\n\n.tab-badge {\n background: var(--test-border);\n color: var(--test-text-secondary);\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.tab.active .tab-badge {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--test-primary);\n}\n\n.tab-shortcut {\n font-size: 10px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n margin-left: 4px;\n}\n\n/* ===========================\n Tab Content Area\n =========================== */\n.tab-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n scroll-behavior: smooth;\n}\n\n/* ===========================\n Overview Tab\n =========================== */\n.overview-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: fadeIn 0.3s ease-out;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Info Section */\n.info-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.info-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.info-section h3 i {\n color: var(--test-primary);\n}\n\n.info-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n}\n\n.info-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.info-label {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.info-value {\n font-size: 14px;\n color: var(--test-text);\n word-wrap: break-word;\n font-weight: 500;\n}\n\n/* JSON Section */\n.json-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-section h3 {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.json-section h3 i {\n color: var(--test-primary);\n}\n\n.json-tabs {\n display: flex;\n gap: 4px;\n margin-bottom: 16px;\n background: var(--test-bg);\n border-radius: var(--test-radius-md);\n padding: 4px;\n flex-wrap: wrap;\n}\n\n.json-tab {\n flex: 1;\n min-width: 100px;\n padding: 10px 14px;\n border: none;\n background: transparent;\n color: var(--test-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n border-radius: var(--test-radius-sm);\n transition: var(--test-transition);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n\n.json-tab:hover {\n color: var(--test-text);\n background: var(--mj-bg-overlay);\n}\n\n.json-tab.active {\n background: var(--test-surface);\n color: var(--test-primary);\n font-weight: 600;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-tab i {\n font-size: 12px;\n}\n\n.code-editor-container {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n max-height: 400px;\n}\n\n/* ===========================\n Configuration Tab\n =========================== */\n.config-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: fadeIn 0.3s ease-out;\n}\n\n.config-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.config-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.config-section h3 i {\n color: var(--test-primary);\n}\n\n.config-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n gap: 20px;\n}\n\n.config-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.config-item.full-width {\n grid-column: 1 / -1;\n}\n\n.config-item label {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.config-input {\n padding: 10px 14px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n font-size: 14px;\n transition: var(--test-transition);\n background: var(--test-surface);\n}\n\n.config-input:focus {\n outline: none;\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.config-input::placeholder {\n color: var(--test-text-muted);\n}\n\n.config-hint {\n font-size: 11px;\n color: var(--test-text-muted);\n margin-top: 4px;\n}\n\n.config-editor-container {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n}\n\n.config-editor-container.small {\n min-height: 100px;\n max-height: 150px;\n}\n\n/* ===========================\n Runs Tab\n =========================== */\n.runs-tab,\n.suites-tab {\n animation: fadeIn 0.3s ease-out;\n}\n\n/* Loading States & Skeletons */\n.loading-state {\n padding: 0;\n}\n\n.skeleton-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.skeleton-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n border: 1px solid var(--test-border);\n}\n\n.skeleton-icon {\n width: 44px;\n height: 44px;\n border-radius: var(--test-radius-md);\n background: linear-gradient(90deg, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n flex-shrink: 0;\n}\n\n.skeleton-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.skeleton-line {\n height: 14px;\n border-radius: 4px;\n background: linear-gradient(90deg, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skeleton-line.wide { width: 70%; }\n.skeleton-line.narrow { width: 40%; }\n\n@keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Runs List */\n.runs-list,\n.suites-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.run-item,\n.suite-item {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.run-item:hover,\n.suite-item:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n box-shadow: var(--test-shadow-sm);\n}\n\n.run-icon,\n.suite-icon {\n width: 44px;\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--test-radius-md);\n color: var(--mj-text-inverse);\n font-size: 18px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-sm);\n}\n\n.suite-icon {\n background: var(--mj-status-warning);\n}\n\n.run-content,\n.suite-content {\n flex: 1;\n min-width: 0;\n}\n\n.run-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 4px;\n}\n\n.run-id {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n}\n\n.run-status {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.suite-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 4px;\n}\n\n.run-meta,\n.suite-meta {\n display: flex;\n gap: 16px;\n font-size: 12px;\n color: var(--test-text-secondary);\n flex-wrap: wrap;\n}\n\n.run-meta span,\n.suite-meta span {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.run-meta span i,\n.suite-meta span i {\n color: var(--test-text-muted);\n font-size: 11px;\n}\n\n.run-item > i.fa-chevron-right,\n.suite-item > i.fa-chevron-right {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.run-item:hover > i.fa-chevron-right,\n.suite-item:hover > i.fa-chevron-right {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n/* Run Evaluation Stack */\n.run-eval-stack {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n/* Evaluation Pills */\n.eval-pill {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.eval-pill i {\n font-size: 10px;\n}\n\n/* Status pill colors */\n.eval-pill.status-pill.status-passed {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.status-pill.status-failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.status-pill.status-error {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-timeout {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-skipped,\n.eval-pill.status-pill.status-pending {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.eval-pill.status-pill.status-running {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Human feedback pills */\n.eval-pill.human-pill.no-feedback {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.human-pill.has-feedback.rating-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.human-pill.has-feedback.rating-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.human-pill.has-feedback.rating-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.human-pill.has-feedback.rating-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n/* Auto score pills */\n.eval-pill.auto-pill.no-score {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.auto-pill.has-score.score-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.auto-pill.has-score.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.auto-pill.has-score.score-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.auto-pill.has-score.score-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n/* Run Tags */\n.run-tags {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n.run-tag {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n.run-tag-more {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n/* ===========================\n Keyboard Shortcuts Popup\n =========================== */\n/* Keyboard shortcuts toggle button */\n.shortcuts-toggle {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n box-shadow: var(--test-shadow-md);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--test-text-secondary);\n font-size: 14px;\n z-index: 99;\n transition: var(--test-transition);\n opacity: 0.7;\n}\n\n.shortcuts-toggle:hover {\n opacity: 1;\n transform: scale(1.1);\n color: var(--test-primary);\n border-color: var(--test-primary);\n}\n\n.keyboard-shortcuts {\n position: fixed;\n bottom: 20px;\n right: 20px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 12px 16px;\n box-shadow: var(--test-shadow-lg);\n font-size: 12px;\n color: var(--test-text-secondary);\n z-index: 100;\n max-width: 260px;\n}\n\n.shortcuts-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 10px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--test-border);\n font-weight: 600;\n color: var(--test-text);\n}\n\n.shortcuts-close {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--test-text-muted);\n font-size: 12px;\n padding: 2px 4px;\n border-radius: 4px;\n transition: var(--test-transition);\n}\n\n.shortcuts-close:hover {\n color: var(--test-text);\n background: var(--test-border);\n}\n\n.shortcut-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.shortcut-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.shortcut-keys {\n display: flex;\n gap: 4px;\n}\n\n.shortcut-keys kbd {\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: 4px;\n padding: 2px 6px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 11px;\n color: var(--test-text);\n}\n\n/* ===========================\n Responsive Design - Tablet\n =========================== */\n@media (max-width: 1024px) {\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .info-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .config-grid {\n grid-template-columns: 1fr;\n }\n\n .keyboard-shortcuts, .shortcuts-toggle {\n display: none;\n }\n}\n\n/* ===========================\n Responsive Design - Mobile\n =========================== */\n@media (max-width: 768px) {\n .test-form {\n height: auto;\n min-height: 100%;\n }\n\n .test-header {\n padding: 16px;\n }\n\n .header-content {\n flex-direction: column;\n gap: 16px;\n }\n\n .header-left {\n width: 100%;\n }\n\n .test-icon {\n width: 48px;\n height: 48px;\n font-size: 20px;\n }\n\n .test-info h1 {\n font-size: 18px;\n }\n\n .test-meta {\n gap: 8px;\n }\n\n .status-badge {\n padding: 4px 10px;\n font-size: 11px;\n }\n\n .header-actions {\n width: 100%;\n justify-content: stretch;\n }\n\n .header-actions button {\n flex: 1;\n }\n\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n }\n\n .metric-card {\n padding: 12px;\n }\n\n .metric-value {\n font-size: 16px;\n }\n\n .tabs {\n padding: 0 12px;\n }\n\n .tab {\n padding: 12px 14px;\n font-size: 13px;\n gap: 6px;\n }\n\n .tab i {\n font-size: 14px;\n }\n\n .tab-shortcut {\n display: none;\n }\n\n .tab-content {\n padding: 16px;\n }\n\n .info-section,\n .json-section,\n .config-section {\n padding: 18px;\n }\n\n .info-grid {\n grid-template-columns: 1fr;\n }\n\n .json-tabs {\n flex-direction: column;\n }\n\n .json-tab {\n min-width: 0;\n justify-content: flex-start;\n }\n\n .run-item,\n .suite-item {\n padding: 14px;\n }\n\n .run-icon,\n .suite-icon {\n width: 40px;\n height: 40px;\n font-size: 16px;\n }\n\n .run-meta,\n .suite-meta {\n flex-direction: column;\n gap: 4px;\n }\n\n}\n\n/* ===========================\n Responsive Design - Small Mobile\n =========================== */\n@media (max-width: 480px) {\n .test-header {\n padding: 12px;\n }\n\n .header-left {\n gap: 12px;\n }\n\n .test-icon {\n width: 40px;\n height: 40px;\n font-size: 18px;\n border-radius: 8px;\n }\n\n .test-info h1 {\n font-size: 16px;\n }\n\n .metrics-bar {\n grid-template-columns: 1fr 1fr;\n }\n\n .tabs {\n padding: 0 8px;\n }\n\n .tab {\n padding: 10px 12px;\n font-size: 12px;\n }\n\n .tab-badge {\n display: none;\n }\n\n .tab-content {\n padding: 12px;\n }\n\n .run-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n}\n\n/* ===========================\n Touch Device Optimizations\n =========================== */\n@media (hover: none) and (pointer: coarse) {\n .tab,\n .json-tab,\n .run-item,\n .suite-item {\n -webkit-tap-highlight-color: transparent;\n }\n\n .run-item:active,\n .suite-item:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n transform: scale(0.98);\n }\n\n .tab:active,\n .json-tab:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n /* Larger touch targets */\n .tab {\n min-height: 48px;\n }\n\n .run-item,\n .suite-item {\n min-height: 64px;\n }\n}\n\n/* ===========================\n Reduced Motion\n =========================== */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n\n .skeleton-icon,\n .skeleton-line {\n animation: none;\n background: var(--mj-border-default);\n }\n}\n\n/* ===========================\n History Tab\n =========================== */\n.history-tab {\n animation: fadeIn 0.3s ease-out;\n}\n\n.history-content {\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* History Filters */\n.history-filters {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n}\n\n.time-range-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.filter-label {\n font-size: 13px;\n font-weight: 600;\n color: var(--test-text-secondary);\n margin-right: 4px;\n}\n\n.filter-btn {\n padding: 8px 16px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n color: var(--test-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.filter-btn:hover {\n border-color: var(--test-primary-light);\n color: var(--test-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n}\n\n.filter-btn.active {\n background: var(--test-primary);\n color: var(--mj-text-inverse);\n border-color: var(--test-primary);\n}\n\n.history-actions {\n display: flex;\n gap: 8px;\n}\n\n/* KPI Cards */\n.history-kpi-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 16px;\n}\n\n.kpi-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n transition: var(--test-transition);\n}\n\n.kpi-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.kpi-icon {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: var(--test-radius-md);\n color: var(--mj-text-inverse);\n font-size: 20px;\n flex-shrink: 0;\n}\n\n.kpi-icon.pass-rate {\n background: var(--mj-status-success);\n}\n\n.kpi-content {\n flex: 1;\n min-width: 0;\n}\n\n.kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.kpi-label {\n font-size: 12px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n}\n\n.trend-icon {\n font-size: 14px;\n}\n\n.trend-icon.trend-up {\n color: var(--test-success);\n}\n\n.trend-icon.trend-down {\n color: var(--test-error);\n}\n\n/* History Section */\n.history-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.history-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.history-section h3 i {\n color: var(--test-primary);\n}\n\n/* History Table */\n.history-table-container {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.history-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n}\n\n.history-table th,\n.history-table td {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--test-border);\n}\n\n.history-table th {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n}\n\n.history-table tbody tr:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, transparent);\n}\n\n.date-cell {\n font-weight: 600;\n color: var(--test-text);\n}\n\n.passed-cell {\n color: var(--test-success);\n font-weight: 600;\n}\n\n.failed-cell {\n color: var(--test-error);\n font-weight: 600;\n}\n\n.pass-rate-cell {\n min-width: 120px;\n}\n\n.pass-rate-bar {\n position: relative;\n height: 24px;\n background: var(--test-bg);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.pass-rate-fill {\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: var(--mj-status-success);\n border-radius: 12px;\n transition: width 0.5s ease-out;\n}\n\n.pass-rate-text {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n font-size: 11px;\n font-weight: 700;\n color: var(--test-text);\n z-index: 1;\n}\n\n/* Suite Performance List */\n.suite-performance-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.suite-perf-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 18px;\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.suite-perf-card:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n}\n\n.suite-perf-header {\n flex: 1;\n min-width: 0;\n}\n\n.suite-perf-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 6px;\n}\n\n.suite-perf-tags {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.tag-mini {\n display: inline-flex;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.tag-more {\n display: inline-flex;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.suite-perf-stats {\n display: flex;\n gap: 24px;\n flex-wrap: wrap;\n}\n\n.suite-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n}\n\n.suite-stat .stat-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n.suite-stat .stat-value.pass-rate.high {\n color: var(--test-success);\n}\n\n.suite-stat .stat-value.pass-rate.low {\n color: var(--test-error);\n}\n\n.suite-stat .stat-label {\n font-size: 10px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.suite-perf-arrow {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.suite-perf-card:hover .suite-perf-arrow {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n/* ===========================\n Print Styles\n =========================== */\n@media print {\n .test-form {\n background: var(--mj-bg-surface);\n height: auto;\n }\n\n .header-actions,\n .tabs-container,\n .keyboard-shortcuts {\n display: none !important;\n }\n\n .tab-content {\n overflow: visible;\n padding: 0;\n }\n\n .info-section,\n .json-section,\n .config-section {\n break-inside: avoid;\n box-shadow: none;\n border: 1px solid var(--mj-border-default);\n }\n}\n\n/* ===========================\n Editable Form Sections (Config tab)\n =========================== */\n\n.edit-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.edit-section-header {\n margin: 0 0 18px 0;\n}\n\n.edit-section-header h3 {\n margin: 0 0 4px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.edit-section-header h3 i {\n color: var(--test-primary);\n}\n\n.edit-section-sub {\n margin: 0;\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.edit-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 18px;\n}\n\n@media (max-width: 720px) {\n .edit-grid {\n grid-template-columns: 1fr;\n }\n}\n\n.edit-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.edit-field--full {\n grid-column: 1 / -1;\n}\n\n.edit-field label {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.edit-field .required {\n color: var(--mj-status-error);\n margin-left: 2px;\n}\n\n.config-textarea {\n resize: vertical;\n min-height: 72px;\n font-family: inherit;\n line-height: 1.5;\n}\n\n.select-wrapper {\n position: relative;\n}\n\n.select-wrapper select.config-input {\n padding-right: 36px;\n appearance: none;\n background-color: var(--test-surface);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2364748b' d='M1.41 0L6 4.59 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 14px center;\n background-size: 10px 7px;\n}\n\n/* Tag editor (chips + inline input) */\n.tag-editor {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 10px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n transition: var(--test-transition);\n cursor: text;\n min-height: 42px;\n align-items: center;\n}\n\n.tag-editor:focus-within {\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tag-chip-editable {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 4px 3px 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary-hover);\n border-radius: 999px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n}\n\n.tag-chip-editable .tag-remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 10px;\n transition: background 0.15s;\n}\n\n.tag-chip-editable .tag-remove:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n color: var(--mj-brand-primary-hover);\n}\n\n.tag-input-inline {\n flex: 1;\n min-width: 140px;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--test-text);\n padding: 4px 2px;\n}\n\n.tag-input-inline::placeholder {\n color: var(--test-text-muted);\n}\n\n/* Read-only metadata block at bottom of Config tab */\n.meta-section {\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--test-radius-md);\n padding: 14px 18px;\n border: 1px solid var(--test-border);\n}\n\n.meta-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 14px;\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.meta-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.meta-value {\n font-size: 12px;\n color: var(--test-text-secondary);\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.meta-mono {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n font-size: 11px;\n}\n\n/* Dirty indicator next to the title */\n.dirty-indicator {\n display: inline-block;\n margin-left: 8px;\n font-size: 14px;\n color: var(--mj-status-warning);\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n vertical-align: middle;\n}\n\n@keyframes pulseDot {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.85); }\n}\n\n.unsaved-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 30%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.unsaved-pill i {\n font-size: 6px;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n/* Pad the bottom of the tab content when the save bar is visible\n so the bar doesn't cover the last form field. */\n.tab-content.has-savebar {\n padding-bottom: 84px;\n}\n\n/* ===========================\n Sticky Save Bar\n =========================== */\n\n.save-bar {\n position: absolute;\n left: 16px;\n right: 96px; /* leaves room for the bottom-right chat launcher */\n bottom: 16px;\n z-index: 50;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 12px 16px 12px 20px;\n background: var(--mj-bg-surface-elevated, var(--test-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 25%, var(--test-border));\n border-radius: 14px;\n box-shadow: 0 12px 32px color-mix(in srgb, var(--mj-status-warning) 10%, rgba(0, 0, 0, 0.18));\n backdrop-filter: blur(8px);\n /* Hidden / off-screen state by default. The .save-bar--visible class\n animates it up into view. Using `transition` (not `animation`)\n guarantees the change runs every time isDirty toggles. */\n opacity: 0;\n transform: translateY(calc(100% + 32px));\n pointer-events: none;\n transition:\n transform 420ms cubic-bezier(0.16, 1, 0.3, 1),\n opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);\n will-change: transform, opacity;\n}\n\n.save-bar--visible {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .save-bar {\n transition: opacity 180ms ease-out;\n transform: none;\n }\n .save-bar--visible {\n transform: none;\n }\n}\n\n.save-bar-message {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.save-bar-dot {\n color: var(--mj-status-warning);\n font-size: 12px;\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n.save-bar-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.save-bar-text strong {\n font-size: 14px;\n color: var(--test-text);\n font-weight: 700;\n}\n\n.save-bar-fields {\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.save-bar-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.save-bar-btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 9px 16px;\n font-size: 13px;\n font-weight: 600;\n border-radius: 8px;\n border: 1px solid transparent;\n cursor: pointer;\n transition: var(--test-transition);\n font-family: inherit;\n}\n\n.save-bar-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.save-bar-btn--primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.save-bar-btn--primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n\n.save-bar-btn--primary:active:not(:disabled) {\n transform: translateY(0);\n}\n\n.save-bar-btn--ghost {\n background: transparent;\n color: var(--test-text-secondary);\n border-color: var(--test-border);\n}\n\n.save-bar-btn--ghost:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover, var(--mj-bg-surface-card));\n color: var(--test-text);\n border-color: var(--test-text-muted);\n}\n\n.save-bar-kbd {\n display: inline-flex;\n gap: 3px;\n margin-left: 6px;\n opacity: 0.85;\n}\n\n.save-bar-kbd kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 4px;\n font-size: 10px;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n background: color-mix(in srgb, white 18%, transparent);\n border: 1px solid color-mix(in srgb, white 35%, transparent);\n border-radius: 4px;\n color: inherit;\n}\n\n@media (max-width: 640px) {\n .save-bar {\n left: 8px;\n right: 8px;\n bottom: 88px; /* push above the chat launcher on small screens */\n flex-direction: column;\n align-items: stretch;\n padding: 12px;\n }\n .save-bar-actions {\n justify-content: space-between;\n }\n .save-bar-kbd {\n display: none;\n }\n}\n\n"] }]
2772
+ args: [{ standalone: false, selector: 'mj-test-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"test-form\">\n <!-- Header Section -->\n <div class=\"test-header\">\n <!-- Breadcrumb Navigation -->\n <nav class=\"breadcrumb\" aria-label=\"Breadcrumb\">\n <ol>\n <li>\n <a href=\"javascript:void(0)\" (click)=\"navigateToTestingDashboard()\">\n <i class=\"fas fa-vial\"></i>\n <span class=\"breadcrumb-text\">Testing</span>\n </a>\n </li>\n <li class=\"current\">\n <i class=\"fas fa-chevron-right separator\"></i>\n <i class=\"fas fa-flask\"></i>\n <span>{{ record.Name }}</span>\n </li>\n </ol>\n </nav>\n\n <div class=\"header-content\">\n <div class=\"header-left\">\n <div class=\"test-icon\" [style.background-color]=\"getStatusColor()\">\n <i class=\"fas fa-flask\"></i>\n </div>\n <div class=\"test-info\">\n <h1>\n {{ record.Name }}\n @if (isDirty) {\n <span class=\"dirty-indicator\" title=\"You have unsaved changes\" aria-label=\"Unsaved changes\">\u25CF</span>\n }\n </h1>\n <div class=\"test-meta\">\n <span class=\"status-badge\" [ngClass]=\"getStatusClass()\">\n <i class=\"fas\" [ngClass]=\"getStatusIcon()\"></i>\n {{ record.Status }}\n </span>\n @if (record.Type) {\n <span class=\"test-type\">\n <i class=\"fas fa-tag\"></i>\n {{ record.Type }}\n </span>\n }\n @if (isDirty) {\n <span class=\"unsaved-pill\" title=\"You have unsaved changes\">\n <i class=\"fas fa-circle\"></i> Unsaved\n </span>\n }\n </div>\n </div>\n </div>\n <div class=\"header-actions\">\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n <button mjButton (click)=\"runTest()\" variant=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test\n </button>\n <button mjButton (click)=\"refresh()\" [disabled]=\"isRefreshing\">\n <i class=\"fas\" [ngClass]=\"isRefreshing ? 'fa-sync fa-spin' : 'fa-sync'\"></i>\n {{ isRefreshing ? 'Refreshing...' : 'Refresh' }}\n </button>\n </div>\n </div>\n\n <!-- Test Description -->\n @if (record.Description) {\n <div class=\"test-description\">\n <p>{{ record.Description }}</p>\n </div>\n }\n\n <!-- Metrics Bar -->\n @if (testRunsLoaded && testRuns.length > 0) {\n <div class=\"metrics-bar\">\n <div class=\"metric-card\">\n <div class=\"metric-label\">Total Runs</div>\n <div class=\"metric-value\">{{ testRuns.length }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Pass Rate</div>\n <div class=\"metric-value\">{{ getPassRate().toFixed(1) }}%</div>\n <div class=\"metric-progress\">\n <div class=\"metric-progress-fill\" [style.width.%]=\"getPassRate()\"></div>\n </div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Cost</div>\n <div class=\"metric-value\">{{ formatCost(getAverageCost()) }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Duration</div>\n <div class=\"metric-value\">{{ formatDuration(getAverageDuration()) }}</div>\n </div>\n </div>\n }\n </div>\n\n <!-- Tabs -->\n <div class=\"tabs-container\">\n <div class=\"tabs\" role=\"tablist\">\n <button class=\"tab\"\n [class.active]=\"activeTab === 'overview'\"\n (click)=\"changeTab('overview')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'overview'\">\n <i class=\"fas fa-th-large\"></i>\n <span>Overview</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'config'\"\n (click)=\"changeTab('config')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'config'\">\n <i class=\"fas fa-sliders-h\"></i>\n <span>Configuration</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'runs'\"\n (click)=\"changeTab('runs')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'runs'\">\n <i class=\"fas fa-history\"></i>\n <span>Runs</span>\n @if (testRunsLoaded) {\n <span class=\"tab-badge\">{{ testRuns.length }}</span>\n }\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'suites'\"\n (click)=\"changeTab('suites')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'suites'\">\n <i class=\"fas fa-layer-group\"></i>\n <span>Test Suites</span>\n @if (suiteTestsLoaded) {\n <span class=\"tab-badge\">{{ suiteTests.length }}</span>\n }\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'analytics'\"\n (click)=\"changeTab('analytics')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'analytics'\">\n <i class=\"fas fa-chart-line\"></i>\n <span>Analytics</span>\n </button>\n </div>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tab-content\" [class.has-savebar]=\"isDirty\">\n <!-- Overview Tab -->\n @if (activeTab === 'overview') {\n <div class=\"overview-tab\">\n <!-- Basic Information -->\n <div class=\"info-section\">\n <h3><i class=\"fas fa-info-circle\"></i> Test Information</h3>\n <div class=\"info-grid\">\n <div class=\"info-item\">\n <div class=\"info-label\">Name</div>\n <div class=\"info-value\">{{ record.Name }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Type</div>\n <div class=\"info-value\">{{ record.Type || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Status</div>\n <div class=\"info-value\">\n <span class=\"status-badge-inline\" [ngClass]=\"getStatusClass()\">{{ record.Status }}</span>\n </div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Priority</div>\n <div class=\"info-value\">{{ record.Priority || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Duration</div>\n <div class=\"info-value\">{{ record.EstimatedDurationSeconds ? formatDuration(record.EstimatedDurationSeconds) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Cost</div>\n <div class=\"info-value\">{{ record.EstimatedCostUSD ? formatCost(record.EstimatedCostUSD) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Repeat Count</div>\n <div class=\"info-value\">{{ record.RepeatCount || 1 }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Max Execution Time</div>\n <div class=\"info-value\">{{ formatTimeout(record.MaxExecutionTimeMS) }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Created</div>\n <div class=\"info-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Updated</div>\n <div class=\"info-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n </div>\n </div>\n <!-- JSON Data Views -->\n <div class=\"json-section\">\n <h3><i class=\"fas fa-code\"></i> Test Definition</h3>\n <div class=\"json-tabs\">\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'input'\"\n (click)=\"setJsonView('input')\">\n <i class=\"fas fa-sign-in-alt\"></i>\n Input Definition\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'expected'\"\n (click)=\"setJsonView('expected')\">\n <i class=\"fas fa-check-double\"></i>\n Expected Outcomes\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'config'\"\n (click)=\"setJsonView('config')\">\n <i class=\"fas fa-cog\"></i>\n Configuration\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'tags'\"\n (click)=\"setJsonView('tags')\">\n <i class=\"fas fa-tags\"></i>\n Tags\n </button>\n </div>\n <div class=\"code-editor-container\">\n <mj-code-editor\n [value]=\"getJsonData()\"\n language=\"json\"\n [readonly]=\"true\"\n [toolbar]=\"jsonToolbar\"\n [lineWrapping]=\"true\">\n </mj-code-editor>\n </div>\n </div>\n </div>\n }\n\n <!-- Configuration Tab -->\n @if (activeTab === 'config') {\n <div class=\"config-tab\">\n <!-- Details -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-pen-to-square\"></i> Details</h3>\n <p class=\"edit-section-sub\">Edit any field \u2014 changes are saved with the bar at the bottom.</p>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-name\">Name <span class=\"required\">*</span></label>\n <input\n id=\"test-name\"\n type=\"text\"\n class=\"config-input\"\n placeholder=\"e.g., Agent Response Quality Test\"\n [(ngModel)]=\"record.Name\"\n />\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-description\">Description</label>\n <textarea\n id=\"test-description\"\n class=\"config-input config-textarea\"\n rows=\"3\"\n placeholder=\"What does this test evaluate?\"\n [ngModel]=\"record.Description\"\n (ngModelChange)=\"record.Description = $event || null\"\n ></textarea>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-status\">Status</label>\n <div class=\"select-wrapper\">\n <select\n id=\"test-status\"\n class=\"config-input\"\n [(ngModel)]=\"record.Status\"\n >\n @for (opt of statusOptions; track opt) {\n <option [ngValue]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-rubric\">Rubric</label>\n <select\n id=\"test-rubric\"\n class=\"config-input\"\n [ngModel]=\"record.RubricID\"\n (ngModelChange)=\"record.RubricID = $event || null\"\n >\n <option [ngValue]=\"null\">\u2014 None \u2014</option>\n @for (rubric of RubricOptions; track rubric.id) {\n <option [ngValue]=\"rubric.id\">{{ rubric.name }}</option>\n }\n </select>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-type\">Test Type</label>\n <select\n id=\"test-type\"\n class=\"config-input\"\n [(ngModel)]=\"record.TypeID\"\n >\n @for (t of testTypeOptions; track t.ID) {\n <option [ngValue]=\"t.ID\">{{ t.Name }}</option>\n }\n </select>\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-tags\">Tags</label>\n <div class=\"tag-editor\" (click)=\"tagInput.focus()\">\n @for (tag of tags; track tag) {\n <span class=\"tag-chip-editable\">\n {{ tag }}\n <button type=\"button\" class=\"tag-remove\" (click)=\"removeTag(tag); $event.stopPropagation()\" [attr.aria-label]=\"'Remove tag ' + tag\">\n <i class=\"fas fa-times\"></i>\n </button>\n </span>\n }\n <input\n #tagInput\n id=\"test-tags\"\n type=\"text\"\n class=\"tag-input-inline\"\n [placeholder]=\"tags.length === 0 ? 'Add tags, press Enter or comma...' : ''\"\n [(ngModel)]=\"tagDraft\"\n (keydown)=\"onTagInputKeydown($event)\"\n (blur)=\"addTagFromDraft()\"\n />\n </div>\n <span class=\"config-hint\">Press Enter or comma to add a tag. Backspace on empty input removes the last tag.</span>\n </div>\n </div>\n </section>\n\n <!-- Execution Settings -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-cogs\"></i> Execution Settings</h3>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field\">\n <label for=\"test-priority\">Priority</label>\n <input\n id=\"test-priority\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0 (lower runs first)\"\n [ngModel]=\"record.Priority\"\n (ngModelChange)=\"record.Priority = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Lower numbers run first.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-repeat\">Repeat Count</label>\n <input\n id=\"test-repeat\"\n type=\"number\"\n class=\"config-input\"\n min=\"1\"\n placeholder=\"1\"\n [ngModel]=\"record.RepeatCount\"\n (ngModelChange)=\"record.RepeatCount = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Number of times to repeat for statistical analysis.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-duration\">Estimated Duration (seconds)</label>\n <input\n id=\"test-duration\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0\"\n [ngModel]=\"record.EstimatedDurationSeconds\"\n (ngModelChange)=\"record.EstimatedDurationSeconds = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field\">\n <label for=\"test-cost\">Estimated Cost (USD)</label>\n <input\n id=\"test-cost\"\n type=\"number\"\n step=\"0.000001\"\n class=\"config-input\"\n placeholder=\"0.00\"\n [ngModel]=\"record.EstimatedCostUSD\"\n (ngModelChange)=\"record.EstimatedCostUSD = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-timeout\">Max Execution Time (ms)</label>\n <input\n id=\"test-timeout\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"Default: 300000 (5 min)\"\n [ngModel]=\"record.MaxExecutionTimeMS\"\n (ngModelChange)=\"record.MaxExecutionTimeMS = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Leave empty for the default 5 minute timeout. Currently: {{ formatTimeout(record.MaxExecutionTimeMS) }}</span>\n </div>\n </div>\n </section>\n\n <!-- JSON editors (preserved from original layout) -->\n <div class=\"config-section\">\n <h3><i class=\"fas fa-sign-in-alt\"></i> Input Definition (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.InputDefinition || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.InputDefinition = $event\">\n </mj-code-editor>\n </div>\n </div>\n <div class=\"config-section\">\n <h3><i class=\"fas fa-check-double\"></i> Expected Outcomes (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.ExpectedOutcomes || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.ExpectedOutcomes = $event\">\n </mj-code-editor>\n </div>\n </div>\n <div class=\"config-section\">\n <h3><i class=\"fas fa-cog\"></i> Configuration (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.Configuration || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.Configuration = $event\">\n </mj-code-editor>\n </div>\n </div>\n\n <!-- Metadata (read-only) -->\n <section class=\"meta-section\">\n <div class=\"meta-grid\">\n <div class=\"meta-item\">\n <div class=\"meta-label\">Created</div>\n <div class=\"meta-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Last updated</div>\n <div class=\"meta-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Test ID</div>\n <div class=\"meta-value meta-mono\">{{ record.ID }}</div>\n </div>\n </div>\n </section>\n </div>\n }\n\n <!-- Test Runs Tab -->\n @if (activeTab === 'runs') {\n <div class=\"runs-tab\">\n <!-- Loading State -->\n @if (loadingRuns) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3,4,5]; track i) {\n <div class=\"skeleton-card\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line wide\"></div>\n <div class=\"skeleton-line narrow\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n <!-- Runs List -->\n @if (!loadingRuns && testRuns.length > 0) {\n <div class=\"runs-list\">\n @for (run of testRuns; track run) {\n <div class=\"run-item\" (click)=\"openTestRun(run.ID)\">\n <div class=\"run-icon\" [style.background-color]=\"getRunStatusColor(run.Status)\">\n <i class=\"fas\"\n [class.fa-check]=\"run.Status === 'Passed'\"\n [class.fa-times]=\"run.Status === 'Failed'\"\n [class.fa-exclamation]=\"run.Status === 'Error'\"\n [class.fa-stopwatch]=\"run.Status === 'Timeout'\"\n [class.fa-spinner]=\"run.Status === 'Running'\"\n [class.fa-clock]=\"run.Status === 'Pending'\"></i>\n </div>\n <div class=\"run-content\">\n <div class=\"run-header\">\n <span class=\"run-id\">Run #{{ run.ID.substring(0, 8) }}</span>\n <span class=\"run-status\" [style.color]=\"getRunStatusColor(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-meta\">\n <span><i class=\"fas fa-calendar\"></i> {{ getRelativeTime(run.StartedAt) }}</span>\n @if (run.DurationSeconds) {\n <span><i class=\"fas fa-clock\"></i> {{ formatDuration(run.DurationSeconds) }}</span>\n }\n @if (run.CostUSD) {\n <span><i class=\"fas fa-dollar-sign\"></i> {{ formatCost(run.CostUSD) }}</span>\n }\n @if (run.TargetLogEntityID && run.TargetLogID) {\n <mj-entity-link-pill\n [entityName]=\"run.TargetLogEntity\"\n [recordId]=\"run.TargetLogID\">\n </mj-entity-link-pill>\n }\n </div>\n <!-- Evaluation indicators -->\n <div class=\"run-eval-stack\">\n <!-- Status indicator -->\n @if (evalPreferences.showExecution) {\n <span class=\"eval-pill status-pill\"\n [ngClass]=\"'status-' + run.Status.toLowerCase()\"\n [title]=\"getStatusTooltip(run.Status)\">\n <i class=\"fas\"\n [class.fa-check]=\"run.Status === 'Passed'\"\n [class.fa-times]=\"run.Status === 'Failed'\"\n [class.fa-exclamation]=\"run.Status === 'Error'\"\n [class.fa-hourglass-end]=\"run.Status === 'Timeout'\"\n [class.fa-forward]=\"run.Status === 'Skipped'\"\n [class.fa-spinner]=\"run.Status === 'Running'\"\n [class.fa-clock]=\"run.Status === 'Pending'\"></i>\n <span>{{ run.Status }}</span>\n </span>\n }\n <!-- Human feedback indicator -->\n @if (evalPreferences.showHuman) {\n @if (!getFeedbackForRun(run.ID)?.Rating) {\n <span class=\"eval-pill human-pill no-feedback\"\n title=\"Human Review: No rating submitted yet\">\n <i class=\"fas fa-user-slash\"></i>\n </span>\n }\n @if (getFeedbackForRun(run.ID)?.Rating; as rating) {\n <span class=\"eval-pill human-pill has-feedback\"\n [class.rating-low]=\"rating <= 4\"\n [class.rating-medium]=\"rating >= 5 && rating <= 6\"\n [class.rating-good]=\"rating >= 7 && rating <= 8\"\n [class.rating-excellent]=\"rating >= 9\"\n [title]=\"getHumanTooltip(rating, getFeedbackForRun(run.ID)?.CorrectionSummary || getFeedbackForRun(run.ID)?.Comments || null)\">\n <i class=\"fas fa-user\"></i>\n <span>{{ rating }}</span>\n </span>\n }\n }\n <!-- Auto score indicator -->\n @if (evalPreferences.showAuto) {\n @if (run.Score == null) {\n <span class=\"eval-pill auto-pill no-score\"\n title=\"Auto Score: No automated score available\">\n <i class=\"fas fa-robot\"></i>\n </span>\n }\n @if (run.Score != null) {\n <span class=\"eval-pill auto-pill has-score\"\n [class.score-low]=\"run.Score < 0.5\"\n [class.score-medium]=\"run.Score >= 0.5 && run.Score < 0.7\"\n [class.score-good]=\"run.Score >= 0.7 && run.Score < 0.85\"\n [class.score-excellent]=\"run.Score >= 0.85\"\n [title]=\"'Auto Score: ' + (run.Score * 100).toFixed(0) + '% automated evaluation'\">\n <i class=\"fas fa-robot\"></i>\n <span>{{ (run.Score * 100).toFixed(0) }}%</span>\n </span>\n }\n }\n </div>\n @if (getRunTags(run).length > 0) {\n <div class=\"run-tags\">\n @for (tag of getRunTags(run).slice(0, 3); track tag) {\n <span class=\"run-tag\">{{ tag }}</span>\n }\n @if (getRunTags(run).length > 3) {\n <span class=\"run-tag-more\">+{{ getRunTags(run).length - 3 }}</span>\n }\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (testRunsLoaded && !loadingRuns && testRuns.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-circle-play\"\n Title=\"No Test Runs Yet\"\n Message=\"Run this test to see execution history and results here.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n\n <!-- Test Suites Tab -->\n @if (activeTab === 'suites') {\n <div class=\"suites-tab\">\n <!-- Loading State -->\n @if (loadingSuites) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3]; track i) {\n <div class=\"skeleton-card\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line wide\"></div>\n <div class=\"skeleton-line narrow\"></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n <!-- Suites List -->\n @if (!loadingSuites && suiteTests.length > 0) {\n <div class=\"suites-list\">\n @for (suiteTest of suiteTests; track suiteTest) {\n <div class=\"suite-item\" (click)=\"openTestSuite(suiteTest.SuiteID)\">\n <div class=\"suite-icon\">\n <i class=\"fas fa-layer-group\"></i>\n </div>\n <div class=\"suite-content\">\n <div class=\"suite-name\">{{ suiteTest.Suite }}</div>\n <div class=\"suite-meta\">\n <span><i class=\"fas fa-sort-numeric-up\"></i> Sequence: {{ suiteTest.Sequence }}</span>\n @if (suiteTest.Status) {\n <span><i class=\"fas fa-info-circle\"></i> {{ suiteTest.Status }}</span>\n }\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (suiteTestsLoaded && !loadingSuites && suiteTests.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-open\"\n Title=\"Not Part of Any Suite\"\n Message=\"This test is not included in any test suites. Add it to a suite to run it with other tests.\" />\n }\n </div>\n }\n\n <!-- Analytics Tab -->\n @if (activeTab === 'analytics') {\n <div class=\"history-tab\">\n <!-- Loading State -->\n @if (loadingHistory) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading history...\"></mj-loading>\n </div>\n }\n <!-- History Content -->\n @if (!loadingHistory && historyLoaded) {\n <div class=\"history-content\">\n <!-- Filters -->\n <div class=\"history-filters\">\n <div class=\"time-range-filters\">\n <span class=\"filter-label\">Time Range:</span>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '7d'\"\n (click)=\"setHistoryTimeRange('7d')\">7 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '30d'\"\n (click)=\"setHistoryTimeRange('30d')\">30 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '90d'\"\n (click)=\"setHistoryTimeRange('90d')\">90 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === 'all'\"\n (click)=\"setHistoryTimeRange('all')\">All Time</button>\n </div>\n <div class=\"history-actions\">\n <button mjButton (click)=\"exportHistoryToCSV()\" [disabled]=\"historyData.length === 0\">\n <i class=\"fas fa-download\"></i> Export CSV\n </button>\n </div>\n </div>\n <!-- KPI Cards -->\n @if (historyData.length > 0) {\n <div class=\"history-kpi-cards\">\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-play-circle\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ getTotalRuns() }}</div>\n <div class=\"kpi-label\">Total Runs</div>\n </div>\n </div>\n @if (evalPreferences.showExecution) {\n <div class=\"kpi-card\">\n <div class=\"kpi-icon pass-rate\">\n <i class=\"fas fa-percentage\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">\n {{ getOverallPassRate().toFixed(1) }}%\n <i class=\"fas trend-icon\"\n [class.fa-arrow-up]=\"getPassRateTrend() === 'up'\"\n [class.fa-arrow-down]=\"getPassRateTrend() === 'down'\"\n [class.fa-minus]=\"getPassRateTrend() === 'stable'\"\n [class.trend-up]=\"getPassRateTrend() === 'up'\"\n [class.trend-down]=\"getPassRateTrend() === 'down'\"></i>\n </div>\n <div class=\"kpi-label\">Pass Rate</div>\n </div>\n </div>\n }\n @if (evalPreferences.showAuto) {\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-star\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ (getOverallAvgScore() * 100).toFixed(1) }}%</div>\n <div class=\"kpi-label\">Avg Score</div>\n </div>\n </div>\n }\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-clock\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ formatDuration(getOverallAvgDuration()) }}</div>\n <div class=\"kpi-label\">Avg Duration</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-dollar-sign\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ formatCost(getOverallAvgCost()) }}</div>\n <div class=\"kpi-label\">Avg Cost</div>\n </div>\n </div>\n </div>\n }\n <!-- Daily History Table -->\n @if (historyData.length > 0) {\n <div class=\"history-section\">\n <h3><i class=\"fas fa-calendar-alt\"></i> Daily Performance</h3>\n <div class=\"history-table-container\">\n <table class=\"history-table\">\n <thead>\n <tr>\n <th>Date</th>\n <th>Runs</th>\n @if (evalPreferences.showExecution) {\n <th>Passed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Failed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Pass Rate</th>\n }\n @if (evalPreferences.showAuto) {\n <th>Avg Score</th>\n }\n <th>Avg Duration</th>\n <th>Avg Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (day of historyData; track day) {\n <tr>\n <td class=\"date-cell\">{{ day.date | date:'mediumDate' }}</td>\n <td class=\"runs-cell\">{{ day.runCount }}</td>\n @if (evalPreferences.showExecution) {\n <td class=\"passed-cell\">{{ day.passCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"failed-cell\">{{ day.failCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"pass-rate-cell\">\n <div class=\"pass-rate-bar\">\n <div class=\"pass-rate-fill\" [style.width.%]=\"day.passRate\"></div>\n <span class=\"pass-rate-text\">{{ day.passRate.toFixed(1) }}%</span>\n </div>\n </td>\n }\n @if (evalPreferences.showAuto) {\n <td class=\"score-cell\">{{ (day.avgScore * 100).toFixed(1) }}%</td>\n }\n <td class=\"duration-cell\">{{ formatDuration(day.avgDuration) }}</td>\n <td class=\"cost-cell\">{{ formatCost(day.avgCost) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n <!-- Suite Performance -->\n @if (suitePerformance.length > 0) {\n <div class=\"history-section\">\n <h3><i class=\"fas fa-layer-group\"></i> Performance by Suite</h3>\n <div class=\"suite-performance-list\">\n @for (suite of suitePerformance; track suite) {\n <div class=\"suite-perf-card\" (click)=\"openSuiteFromHistory(suite.suiteId)\">\n <div class=\"suite-perf-header\">\n <div class=\"suite-perf-name\">{{ suite.suiteName }}</div>\n @if (suite.tags.length > 0) {\n <div class=\"suite-perf-tags\">\n @for (tag of suite.tags.slice(0, 3); track tag) {\n <span class=\"tag-mini\">{{ tag }}</span>\n }\n @if (suite.tags.length > 3) {\n <span class=\"tag-more\">+{{ suite.tags.length - 3 }}</span>\n }\n </div>\n }\n </div>\n <div class=\"suite-perf-stats\">\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ suite.totalRuns }}</span>\n <span class=\"stat-label\">Runs</span>\n </div>\n @if (evalPreferences.showExecution) {\n <div class=\"suite-stat\">\n <span class=\"stat-value pass-rate\" [class.high]=\"suite.passRate >= 80\" [class.low]=\"suite.passRate < 50\">\n {{ suite.passRate.toFixed(1) }}%\n </span>\n <span class=\"stat-label\">Pass Rate</span>\n </div>\n }\n @if (evalPreferences.showAuto) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ (suite.avgScore * 100).toFixed(1) }}%</span>\n <span class=\"stat-label\">Avg Score</span>\n </div>\n }\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ formatDuration(suite.avgDuration) }}</span>\n <span class=\"stat-label\">Avg Duration</span>\n </div>\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ formatCost(suite.avgCost) }}</span>\n <span class=\"stat-label\">Avg Cost</span>\n </div>\n @if (suite.lastRun) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ getRelativeTime(suite.lastRun) }}</span>\n <span class=\"stat-label\">Last Run</span>\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right suite-perf-arrow\"></i>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty State -->\n @if (historyData.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-chart-line\"\n Title=\"No History Available\"\n Message=\"Run this test to start building history and analytics data.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Keyboard Shortcuts Toggle Button -->\n <button class=\"shortcuts-toggle\" (click)=\"toggleShortcuts()\" [title]=\"showShortcuts ? 'Hide keyboard shortcuts' : 'Show keyboard shortcuts'\">\n <i class=\"fas fa-keyboard\"></i>\n </button>\n\n <!-- Keyboard Shortcuts Hint (Desktop Only) -->\n @if (showShortcuts) {\n <div class=\"keyboard-shortcuts\">\n <div class=\"shortcuts-header\">\n <i class=\"fas fa-keyboard\"></i>\n Shortcuts\n <button class=\"shortcuts-close\" (click)=\"toggleShortcuts()\" title=\"Hide shortcuts\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"shortcut-list\">\n <div class=\"shortcut-item\">\n <span>Save changes</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>S</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Refresh</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>R</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Run Test</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>Enter</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Switch Tabs</span>\n <span class=\"shortcut-keys\"><kbd>1</kbd>-<kbd>5</kbd></span>\n </div>\n </div>\n </div>\n }\n\n <!-- Slide Panel for Test Execution -->\n @if (testingDialogService.IsPanelOpen) {\n <mj-slide-panel\n Mode=\"slide\"\n [Title]=\"testingDialogService.PanelOptions?.testId ? 'Test Execution' : 'Run Test'\"\n [Resizable]=\"true\"\n (Closed)=\"OnPanelClosed()\">\n <app-test-run-dialog\n [PanelMode]=\"true\"\n [selectedTestId]=\"testingDialogService.PanelOptions?.testId ?? null\"\n [selectedSuiteId]=\"testingDialogService.PanelOptions?.suiteId ?? null\"\n [runMode]=\"testingDialogService.PanelOptions?.mode ?? 'test'\"\n (PanelClose)=\"OnPanelClosed()\">\n </app-test-run-dialog>\n </mj-slide-panel>\n }\n\n <!-- Sticky Save Bar \u2014 always in the DOM so CSS transitions can fire reliably.\n Visibility is controlled by the .save-bar--visible class, driven by isDirty. -->\n <div\n class=\"save-bar\"\n [class.save-bar--visible]=\"isDirty\"\n [attr.aria-hidden]=\"!isDirty\"\n role=\"region\"\n aria-label=\"Unsaved changes\"\n >\n <div class=\"save-bar-message\">\n <span class=\"save-bar-dot\" aria-hidden=\"true\">\u25CF</span>\n <span class=\"save-bar-text\">\n <strong>Unsaved changes</strong>\n <span class=\"save-bar-fields\">\n @if (dirtyFieldNames.length === 1) {\n {{ dirtyFieldNames[0] }} edited\n } @else if (dirtyFieldNames.length > 1) {\n {{ dirtyFieldNames.length }} fields edited\n }\n </span>\n </span>\n </div>\n <div class=\"save-bar-actions\">\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--primary\"\n (click)=\"saveChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Save changes (\u2318S)\"\n >\n @if (isSaving) {\n <i class=\"fas fa-spinner fa-spin\"></i> Saving\u2026\n } @else {\n <i class=\"fas fa-check\"></i> Save changes\n <span class=\"save-bar-kbd\"><kbd>\u2318</kbd><kbd>S</kbd></span>\n }\n </button>\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--ghost\"\n (click)=\"discardChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Discard changes\"\n >\n <i class=\"fas fa-undo\"></i> Discard\n </button>\n </div>\n </div>\n</div>\n", styles: ["/* ===========================\n Test Form - World-Class UX\n Premium Test Definition Styles\n =========================== */\n\n/* CSS Custom Properties for Theming - using :host for Angular encapsulation */\n:host {\n --test-primary: var(--mj-brand-primary);\n --test-primary-light: var(--mj-brand-primary);\n --test-primary-dark: var(--mj-brand-primary-hover);\n --test-success: var(--mj-status-success);\n --test-success-light: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n --test-error: var(--mj-status-error);\n --test-error-light: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n --test-warning: var(--mj-status-warning);\n --test-warning-light: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n --test-disabled: var(--mj-text-muted);\n --test-bg: var(--mj-bg-surface-card);\n --test-surface: var(--mj-bg-surface);\n --test-border: var(--mj-border-default);\n --test-text: var(--mj-text-primary);\n --test-text-secondary: var(--mj-text-muted);\n --test-text-muted: var(--mj-text-disabled);\n --test-radius-sm: 6px;\n --test-radius-md: 10px;\n --test-radius-lg: 16px;\n --test-shadow-sm: var(--mj-shadow-sm);\n --test-shadow-md: var(--mj-shadow-md);\n --test-shadow-lg: var(--mj-shadow-lg);\n --test-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: block;\n height: 100%;\n}\n\n/* Breadcrumb */\n.breadcrumb {\n margin-bottom: 16px;\n}\n\n.breadcrumb ol {\n display: flex;\n align-items: center;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n font-size: 13px;\n}\n\n.breadcrumb li {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.breadcrumb a {\n color: var(--test-primary);\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.15s;\n}\n\n.breadcrumb a:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n text-decoration: none;\n}\n\n.breadcrumb .separator {\n font-size: 10px;\n color: var(--test-text-muted);\n margin: 0 4px;\n}\n\n.breadcrumb .current {\n color: var(--test-text-secondary);\n font-weight: 500;\n}\n\n.breadcrumb-text {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Base Container */\n.test-form {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--test-bg);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ===========================\n Header Section\n =========================== */\n.test-header {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n padding: 20px;\n}\n\n.header-content {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n gap: 16px;\n}\n\n.header-left {\n display: flex;\n gap: 16px;\n flex: 1;\n min-width: 0;\n}\n\n/* Test Icon */\n.test-icon {\n width: 56px;\n height: 56px;\n border-radius: var(--test-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-inverse);\n font-size: 24px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-md);\n transition: var(--test-transition);\n}\n\n.test-icon:hover {\n transform: scale(1.05);\n}\n\n/* Test Info */\n.test-info {\n flex: 1;\n min-width: 0;\n}\n\n.test-info h1 {\n margin: 0 0 8px 0;\n font-size: clamp(18px, 4vw, 24px);\n font-weight: 700;\n color: var(--test-text);\n line-height: 1.2;\n word-wrap: break-word;\n}\n\n.test-meta {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border-radius: 20px;\n color: var(--mj-text-inverse);\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.status-badge.status-active { background: var(--mj-status-success); }\n.status-badge.status-disabled { background: var(--mj-text-secondary); }\n.status-badge.status-pending { background: var(--mj-status-warning); }\n\n.status-badge-inline {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n border-radius: 10px;\n color: var(--mj-text-inverse);\n font-size: 11px;\n font-weight: 600;\n}\n\n.status-badge-inline.status-active { background: var(--test-success); }\n.status-badge-inline.status-disabled { background: var(--test-disabled); }\n.status-badge-inline.status-pending { background: var(--test-warning); }\n\n.test-type {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: var(--test-text-secondary);\n padding: 4px 10px;\n background: var(--test-bg);\n border-radius: var(--test-radius-sm);\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.header-actions button {\n white-space: nowrap;\n}\n\n/* Test Description */\n.test-description {\n padding: 16px;\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--test-radius-md);\n margin-bottom: 16px;\n border: 1px solid var(--test-border);\n}\n\n.test-description p {\n margin: 0;\n color: var(--test-text-secondary);\n line-height: 1.6;\n font-size: 14px;\n}\n\n/* ===========================\n Metrics Bar\n =========================== */\n.metrics-bar {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: 12px;\n}\n\n.metric-card {\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 14px;\n text-align: center;\n transition: var(--test-transition);\n}\n\n.metric-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.metric-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n margin-bottom: 6px;\n}\n\n.metric-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n/* Progress bar in metric card */\n.metric-progress {\n margin-top: 8px;\n height: 4px;\n background: var(--test-border);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.metric-progress-fill {\n height: 100%;\n background: var(--mj-status-success);\n border-radius: 2px;\n transition: width 0.5s ease-out;\n}\n\n/* ===========================\n Tabs Navigation\n =========================== */\n.tabs-container {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tabs {\n display: flex;\n padding: 0 20px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.tabs::-webkit-scrollbar {\n display: none;\n}\n\n.tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 18px;\n border: none;\n background: transparent;\n border-bottom: 3px solid transparent;\n color: var(--test-text-secondary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n white-space: nowrap;\n}\n\n.tab:hover {\n color: var(--test-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n}\n\n.tab.active {\n color: var(--test-primary);\n border-bottom-color: var(--test-primary);\n font-weight: 600;\n}\n\n.tab i {\n font-size: 15px;\n}\n\n.tab-badge {\n background: var(--test-border);\n color: var(--test-text-secondary);\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.tab.active .tab-badge {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n color: var(--test-primary);\n}\n\n.tab-shortcut {\n font-size: 10px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n margin-left: 4px;\n}\n\n/* ===========================\n Tab Content Area\n =========================== */\n.tab-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n scroll-behavior: smooth;\n}\n\n/* ===========================\n Overview Tab\n =========================== */\n.overview-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: fadeIn 0.3s ease-out;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Info Section */\n.info-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.info-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.info-section h3 i {\n color: var(--test-primary);\n}\n\n.info-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n}\n\n.info-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.info-label {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.info-value {\n font-size: 14px;\n color: var(--test-text);\n word-wrap: break-word;\n font-weight: 500;\n}\n\n/* JSON Section */\n.json-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-section h3 {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.json-section h3 i {\n color: var(--test-primary);\n}\n\n.json-tabs {\n display: flex;\n gap: 4px;\n margin-bottom: 16px;\n background: var(--test-bg);\n border-radius: var(--test-radius-md);\n padding: 4px;\n flex-wrap: wrap;\n}\n\n.json-tab {\n flex: 1;\n min-width: 100px;\n padding: 10px 14px;\n border: none;\n background: transparent;\n color: var(--test-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n border-radius: var(--test-radius-sm);\n transition: var(--test-transition);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n\n.json-tab:hover {\n color: var(--test-text);\n background: var(--mj-bg-overlay);\n}\n\n.json-tab.active {\n background: var(--test-surface);\n color: var(--test-primary);\n font-weight: 600;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-tab i {\n font-size: 12px;\n}\n\n.code-editor-container {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n max-height: 400px;\n}\n\n/* ===========================\n Configuration Tab\n =========================== */\n.config-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: fadeIn 0.3s ease-out;\n}\n\n.config-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.config-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.config-section h3 i {\n color: var(--test-primary);\n}\n\n.config-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n gap: 20px;\n}\n\n.config-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.config-item.full-width {\n grid-column: 1 / -1;\n}\n\n.config-item label {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.config-input {\n padding: 10px 14px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n font-size: 14px;\n transition: var(--test-transition);\n background: var(--test-surface);\n}\n\n.config-input:focus {\n outline: none;\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.config-input::placeholder {\n color: var(--test-text-muted);\n}\n\n.config-hint {\n font-size: 11px;\n color: var(--test-text-muted);\n margin-top: 4px;\n}\n\n.config-editor-container {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n}\n\n.config-editor-container.small {\n min-height: 100px;\n max-height: 150px;\n}\n\n/* ===========================\n Runs Tab\n =========================== */\n.runs-tab,\n.suites-tab {\n animation: fadeIn 0.3s ease-out;\n}\n\n/* Loading States & Skeletons */\n.loading-state {\n padding: 0;\n}\n\n.skeleton-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.skeleton-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n border: 1px solid var(--test-border);\n}\n\n.skeleton-icon {\n width: 44px;\n height: 44px;\n border-radius: var(--test-radius-md);\n background: linear-gradient(90deg, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n flex-shrink: 0;\n}\n\n.skeleton-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.skeleton-line {\n height: 14px;\n border-radius: 4px;\n background: linear-gradient(90deg, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skeleton-line.wide { width: 70%; }\n.skeleton-line.narrow { width: 40%; }\n\n@keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Runs List */\n.runs-list,\n.suites-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.run-item,\n.suite-item {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.run-item:hover,\n.suite-item:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n box-shadow: var(--test-shadow-sm);\n}\n\n.run-icon,\n.suite-icon {\n width: 44px;\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--test-radius-md);\n color: var(--mj-text-inverse);\n font-size: 18px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-sm);\n}\n\n.suite-icon {\n background: var(--mj-status-warning);\n}\n\n.run-content,\n.suite-content {\n flex: 1;\n min-width: 0;\n}\n\n.run-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 4px;\n}\n\n.run-id {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n}\n\n.run-status {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.suite-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 4px;\n}\n\n.run-meta,\n.suite-meta {\n display: flex;\n gap: 16px;\n font-size: 12px;\n color: var(--test-text-secondary);\n flex-wrap: wrap;\n}\n\n.run-meta span,\n.suite-meta span {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.run-meta span i,\n.suite-meta span i {\n color: var(--test-text-muted);\n font-size: 11px;\n}\n\n.run-item > i.fa-chevron-right,\n.suite-item > i.fa-chevron-right {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.run-item:hover > i.fa-chevron-right,\n.suite-item:hover > i.fa-chevron-right {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n/* Run Evaluation Stack */\n.run-eval-stack {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n/* Evaluation Pills */\n.eval-pill {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.eval-pill i {\n font-size: 10px;\n}\n\n/* Status pill colors */\n.eval-pill.status-pill.status-passed {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.status-pill.status-failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.status-pill.status-error {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-timeout {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-skipped,\n.eval-pill.status-pill.status-pending {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.eval-pill.status-pill.status-running {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Human feedback pills */\n.eval-pill.human-pill.no-feedback {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.human-pill.has-feedback.rating-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.human-pill.has-feedback.rating-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.human-pill.has-feedback.rating-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.human-pill.has-feedback.rating-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n/* Auto score pills */\n.eval-pill.auto-pill.no-score {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.auto-pill.has-score.score-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.auto-pill.has-score.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.auto-pill.has-score.score-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.auto-pill.has-score.score-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n/* Run Tags */\n.run-tags {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n.run-tag {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n.run-tag-more {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n/* ===========================\n Keyboard Shortcuts Popup\n =========================== */\n/* Keyboard shortcuts toggle button */\n.shortcuts-toggle {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n box-shadow: var(--test-shadow-md);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--test-text-secondary);\n font-size: 14px;\n z-index: 99;\n transition: var(--test-transition);\n opacity: 0.7;\n}\n\n.shortcuts-toggle:hover {\n opacity: 1;\n transform: scale(1.1);\n color: var(--test-primary);\n border-color: var(--test-primary);\n}\n\n.keyboard-shortcuts {\n position: fixed;\n bottom: 20px;\n right: 20px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 12px 16px;\n box-shadow: var(--test-shadow-lg);\n font-size: 12px;\n color: var(--test-text-secondary);\n z-index: 100;\n max-width: 260px;\n}\n\n.shortcuts-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 10px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--test-border);\n font-weight: 600;\n color: var(--test-text);\n}\n\n.shortcuts-close {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--test-text-muted);\n font-size: 12px;\n padding: 2px 4px;\n border-radius: 4px;\n transition: var(--test-transition);\n}\n\n.shortcuts-close:hover {\n color: var(--test-text);\n background: var(--test-border);\n}\n\n.shortcut-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.shortcut-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.shortcut-keys {\n display: flex;\n gap: 4px;\n}\n\n.shortcut-keys kbd {\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: 4px;\n padding: 2px 6px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 11px;\n color: var(--test-text);\n}\n\n/* ===========================\n Responsive Design - Tablet\n =========================== */\n@media (max-width: 1024px) {\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .info-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .config-grid {\n grid-template-columns: 1fr;\n }\n\n .keyboard-shortcuts, .shortcuts-toggle {\n display: none;\n }\n}\n\n/* ===========================\n Responsive Design - Mobile\n =========================== */\n@media (max-width: 768px) {\n .test-form {\n height: auto;\n min-height: 100%;\n }\n\n .test-header {\n padding: 16px;\n }\n\n .header-content {\n flex-direction: column;\n gap: 16px;\n }\n\n .header-left {\n width: 100%;\n }\n\n .test-icon {\n width: 48px;\n height: 48px;\n font-size: 20px;\n }\n\n .test-info h1 {\n font-size: 18px;\n }\n\n .test-meta {\n gap: 8px;\n }\n\n .status-badge {\n padding: 4px 10px;\n font-size: 11px;\n }\n\n .header-actions {\n width: 100%;\n justify-content: stretch;\n }\n\n .header-actions button {\n flex: 1;\n }\n\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n }\n\n .metric-card {\n padding: 12px;\n }\n\n .metric-value {\n font-size: 16px;\n }\n\n .tabs {\n padding: 0 12px;\n }\n\n .tab {\n padding: 12px 14px;\n font-size: 13px;\n gap: 6px;\n }\n\n .tab i {\n font-size: 14px;\n }\n\n .tab-shortcut {\n display: none;\n }\n\n .tab-content {\n padding: 16px;\n }\n\n .info-section,\n .json-section,\n .config-section {\n padding: 18px;\n }\n\n .info-grid {\n grid-template-columns: 1fr;\n }\n\n .json-tabs {\n flex-direction: column;\n }\n\n .json-tab {\n min-width: 0;\n justify-content: flex-start;\n }\n\n .run-item,\n .suite-item {\n padding: 14px;\n }\n\n .run-icon,\n .suite-icon {\n width: 40px;\n height: 40px;\n font-size: 16px;\n }\n\n .run-meta,\n .suite-meta {\n flex-direction: column;\n gap: 4px;\n }\n\n}\n\n/* ===========================\n Responsive Design - Small Mobile\n =========================== */\n@media (max-width: 480px) {\n .test-header {\n padding: 12px;\n }\n\n .header-left {\n gap: 12px;\n }\n\n .test-icon {\n width: 40px;\n height: 40px;\n font-size: 18px;\n border-radius: 8px;\n }\n\n .test-info h1 {\n font-size: 16px;\n }\n\n .metrics-bar {\n grid-template-columns: 1fr 1fr;\n }\n\n .tabs {\n padding: 0 8px;\n }\n\n .tab {\n padding: 10px 12px;\n font-size: 12px;\n }\n\n .tab-badge {\n display: none;\n }\n\n .tab-content {\n padding: 12px;\n }\n\n .run-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n}\n\n/* ===========================\n Touch Device Optimizations\n =========================== */\n@media (hover: none) and (pointer: coarse) {\n .tab,\n .json-tab,\n .run-item,\n .suite-item {\n -webkit-tap-highlight-color: transparent;\n }\n\n .run-item:active,\n .suite-item:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n transform: scale(0.98);\n }\n\n .tab:active,\n .json-tab:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n /* Larger touch targets */\n .tab {\n min-height: 48px;\n }\n\n .run-item,\n .suite-item {\n min-height: 64px;\n }\n}\n\n/* ===========================\n Reduced Motion\n =========================== */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n\n .skeleton-icon,\n .skeleton-line {\n animation: none;\n background: var(--mj-border-default);\n }\n}\n\n/* ===========================\n History Tab\n =========================== */\n.history-tab {\n animation: fadeIn 0.3s ease-out;\n}\n\n.history-content {\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* History Filters */\n.history-filters {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n}\n\n.time-range-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.filter-label {\n font-size: 13px;\n font-weight: 600;\n color: var(--test-text-secondary);\n margin-right: 4px;\n}\n\n.filter-btn {\n padding: 8px 16px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n color: var(--test-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.filter-btn:hover {\n border-color: var(--test-primary-light);\n color: var(--test-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n}\n\n.filter-btn.active {\n background: var(--test-primary);\n color: var(--mj-text-inverse);\n border-color: var(--test-primary);\n}\n\n.history-actions {\n display: flex;\n gap: 8px;\n}\n\n/* KPI Cards */\n.history-kpi-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 16px;\n}\n\n.kpi-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n transition: var(--test-transition);\n}\n\n.kpi-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.kpi-icon {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: var(--test-radius-md);\n color: var(--mj-text-inverse);\n font-size: 20px;\n flex-shrink: 0;\n}\n\n.kpi-icon.pass-rate {\n background: var(--mj-status-success);\n}\n\n.kpi-content {\n flex: 1;\n min-width: 0;\n}\n\n.kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.kpi-label {\n font-size: 12px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n}\n\n.trend-icon {\n font-size: 14px;\n}\n\n.trend-icon.trend-up {\n color: var(--test-success);\n}\n\n.trend-icon.trend-down {\n color: var(--test-error);\n}\n\n/* History Section */\n.history-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.history-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.history-section h3 i {\n color: var(--test-primary);\n}\n\n/* History Table */\n.history-table-container {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.history-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n}\n\n.history-table th,\n.history-table td {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--test-border);\n}\n\n.history-table th {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n}\n\n.history-table tbody tr:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 3%, transparent);\n}\n\n.date-cell {\n font-weight: 600;\n color: var(--test-text);\n}\n\n.passed-cell {\n color: var(--test-success);\n font-weight: 600;\n}\n\n.failed-cell {\n color: var(--test-error);\n font-weight: 600;\n}\n\n.pass-rate-cell {\n min-width: 120px;\n}\n\n.pass-rate-bar {\n position: relative;\n height: 24px;\n background: var(--test-bg);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.pass-rate-fill {\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: var(--mj-status-success);\n border-radius: 12px;\n transition: width 0.5s ease-out;\n}\n\n.pass-rate-text {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n font-size: 11px;\n font-weight: 700;\n color: var(--test-text);\n z-index: 1;\n}\n\n/* Suite Performance List */\n.suite-performance-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.suite-perf-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 18px;\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.suite-perf-card:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n}\n\n.suite-perf-header {\n flex: 1;\n min-width: 0;\n}\n\n.suite-perf-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 6px;\n}\n\n.suite-perf-tags {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.tag-mini {\n display: inline-flex;\n padding: 2px 8px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.tag-more {\n display: inline-flex;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.suite-perf-stats {\n display: flex;\n gap: 24px;\n flex-wrap: wrap;\n}\n\n.suite-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n}\n\n.suite-stat .stat-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n.suite-stat .stat-value.pass-rate.high {\n color: var(--test-success);\n}\n\n.suite-stat .stat-value.pass-rate.low {\n color: var(--test-error);\n}\n\n.suite-stat .stat-label {\n font-size: 10px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.suite-perf-arrow {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.suite-perf-card:hover .suite-perf-arrow {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n/* ===========================\n Print Styles\n =========================== */\n@media print {\n .test-form {\n background: var(--mj-bg-surface);\n height: auto;\n }\n\n .header-actions,\n .tabs-container,\n .keyboard-shortcuts {\n display: none !important;\n }\n\n .tab-content {\n overflow: visible;\n padding: 0;\n }\n\n .info-section,\n .json-section,\n .config-section {\n break-inside: avoid;\n box-shadow: none;\n border: 1px solid var(--mj-border-default);\n }\n}\n\n/* ===========================\n Editable Form Sections (Config tab)\n =========================== */\n\n.edit-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.edit-section-header {\n margin: 0 0 18px 0;\n}\n\n.edit-section-header h3 {\n margin: 0 0 4px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.edit-section-header h3 i {\n color: var(--test-primary);\n}\n\n.edit-section-sub {\n margin: 0;\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.edit-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 18px;\n}\n\n@media (max-width: 720px) {\n .edit-grid {\n grid-template-columns: 1fr;\n }\n}\n\n.edit-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.edit-field--full {\n grid-column: 1 / -1;\n}\n\n.edit-field label {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.edit-field .required {\n color: var(--mj-status-error);\n margin-left: 2px;\n}\n\n.config-textarea {\n resize: vertical;\n min-height: 72px;\n font-family: inherit;\n line-height: 1.5;\n}\n\n.select-wrapper {\n position: relative;\n}\n\n.select-wrapper select.config-input {\n padding-right: 36px;\n appearance: none;\n background-color: var(--test-surface);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2364748b' d='M1.41 0L6 4.59 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 14px center;\n background-size: 10px 7px;\n}\n\n/* Tag editor (chips + inline input) */\n.tag-editor {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 10px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n transition: var(--test-transition);\n cursor: text;\n min-height: 42px;\n align-items: center;\n}\n\n.tag-editor:focus-within {\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tag-chip-editable {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 4px 3px 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary-hover);\n border-radius: 999px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n}\n\n.tag-chip-editable .tag-remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 10px;\n transition: background 0.15s;\n}\n\n.tag-chip-editable .tag-remove:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n color: var(--mj-brand-primary-hover);\n}\n\n.tag-input-inline {\n flex: 1;\n min-width: 140px;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--test-text);\n padding: 4px 2px;\n}\n\n.tag-input-inline::placeholder {\n color: var(--test-text-muted);\n}\n\n/* Read-only metadata block at bottom of Config tab */\n.meta-section {\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--test-radius-md);\n padding: 14px 18px;\n border: 1px solid var(--test-border);\n}\n\n.meta-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 14px;\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.meta-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.meta-value {\n font-size: 12px;\n color: var(--test-text-secondary);\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.meta-mono {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n font-size: 11px;\n}\n\n/* Dirty indicator next to the title */\n.dirty-indicator {\n display: inline-block;\n margin-left: 8px;\n font-size: 14px;\n color: var(--mj-status-warning);\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n vertical-align: middle;\n}\n\n@keyframes pulseDot {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.85); }\n}\n\n.unsaved-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 30%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.unsaved-pill i {\n font-size: 6px;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n/* Pad the bottom of the tab content when the save bar is visible\n so the bar doesn't cover the last form field. */\n.tab-content.has-savebar {\n padding-bottom: 84px;\n}\n\n/* ===========================\n Sticky Save Bar\n =========================== */\n\n.save-bar {\n position: absolute;\n left: 16px;\n right: 96px; /* leaves room for the bottom-right chat launcher */\n bottom: 16px;\n z-index: 50;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 12px 16px 12px 20px;\n background: var(--mj-bg-surface-elevated, var(--test-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 25%, var(--test-border));\n border-radius: 14px;\n box-shadow: 0 12px 32px color-mix(in srgb, var(--mj-status-warning) 10%, rgba(0, 0, 0, 0.18));\n backdrop-filter: blur(8px);\n /* Hidden / off-screen state by default. The .save-bar--visible class\n animates it up into view. Using `transition` (not `animation`)\n guarantees the change runs every time isDirty toggles. */\n opacity: 0;\n transform: translateY(calc(100% + 32px));\n pointer-events: none;\n transition:\n transform 420ms cubic-bezier(0.16, 1, 0.3, 1),\n opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);\n will-change: transform, opacity;\n}\n\n.save-bar--visible {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .save-bar {\n transition: opacity 180ms ease-out;\n transform: none;\n }\n .save-bar--visible {\n transform: none;\n }\n}\n\n.save-bar-message {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.save-bar-dot {\n color: var(--mj-status-warning);\n font-size: 12px;\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n.save-bar-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.save-bar-text strong {\n font-size: 14px;\n color: var(--test-text);\n font-weight: 700;\n}\n\n.save-bar-fields {\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.save-bar-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.save-bar-btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 9px 16px;\n font-size: 13px;\n font-weight: 600;\n border-radius: 8px;\n border: 1px solid transparent;\n cursor: pointer;\n transition: var(--test-transition);\n font-family: inherit;\n}\n\n.save-bar-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.save-bar-btn--primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.save-bar-btn--primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n\n.save-bar-btn--primary:active:not(:disabled) {\n transform: translateY(0);\n}\n\n.save-bar-btn--ghost {\n background: transparent;\n color: var(--test-text-secondary);\n border-color: var(--test-border);\n}\n\n.save-bar-btn--ghost:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover, var(--mj-bg-surface-card));\n color: var(--test-text);\n border-color: var(--test-text-muted);\n}\n\n.save-bar-kbd {\n display: inline-flex;\n gap: 3px;\n margin-left: 6px;\n opacity: 0.85;\n}\n\n.save-bar-kbd kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 4px;\n font-size: 10px;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n background: color-mix(in srgb, white 18%, transparent);\n border: 1px solid color-mix(in srgb, white 35%, transparent);\n border-radius: 4px;\n color: inherit;\n}\n\n@media (max-width: 640px) {\n .save-bar {\n left: 8px;\n right: 8px;\n bottom: 88px; /* push above the chat launcher on small screens */\n flex-direction: column;\n align-items: stretch;\n padding: 12px;\n }\n .save-bar-actions {\n justify-content: space-between;\n }\n .save-bar-kbd {\n display: none;\n }\n}\n\n"] }]
2352
2773
  }], null, { handleKeyboardShortcut: [{
2353
2774
  type: HostListener,
2354
2775
  args: ['document:keydown', ['$event']]
@@ -2356,5 +2777,5 @@ export { MJTestFormComponentExtended };
2356
2777
  type: HostListener,
2357
2778
  args: ['window:beforeunload', ['$event']]
2358
2779
  }] }); })();
2359
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJTestFormComponentExtended, { className: "MJTestFormComponentExtended", filePath: "src/lib/custom/Tests/test-form.component.ts", lineNumber: 63 }); })();
2780
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJTestFormComponentExtended, { className: "MJTestFormComponentExtended", filePath: "src/lib/custom/Tests/test-form.component.ts", lineNumber: 64 }); })();
2360
2781
  //# sourceMappingURL=test-form.component.js.map