@memberjunction/ng-dashboards 5.38.0 → 5.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -7
- package/dist/AI/components/agents/agent-configuration.component.js +199 -198
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/analytics/ai-analytics-resource.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/ai-analytics-resource.component.js +20 -17
- package/dist/AI/components/analytics/ai-analytics-resource.component.js.map +1 -1
- package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts +15 -0
- package/dist/AI/components/analytics/cost-budget/cost-budget.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/cost-budget/cost-budget.component.js +166 -58
- package/dist/AI/components/analytics/cost-budget/cost-budget.component.js.map +1 -1
- package/dist/AI/components/analytics/executive-summary/executive-summary.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/executive-summary/executive-summary.component.js +2 -1
- package/dist/AI/components/analytics/executive-summary/executive-summary.component.js.map +1 -1
- package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts +1 -0
- package/dist/AI/components/analytics/model-performance/model-performance.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/model-performance/model-performance.component.js +55 -36
- package/dist/AI/components/analytics/model-performance/model-performance.component.js.map +1 -1
- package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts +9 -1
- package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js +158 -117
- package/dist/AI/components/analytics/prompt-runs/prompt-run-analysis.component.js.map +1 -1
- package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts +1 -0
- package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.d.ts.map +1 -1
- package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js +22 -8
- package/dist/AI/components/analytics/usage-patterns/usage-patterns.component.js.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts +217 -860
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.d.ts.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +1717 -7802
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.d.ts +56 -0
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.js +423 -0
- package/dist/AI/components/autotagging/components/classify-item-drilldown.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.d.ts +70 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.js +308 -0
- package/dist/AI/components/autotagging/components/classify-item-grid.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.d.ts +29 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.js +186 -0
- package/dist/AI/components/autotagging/components/classify-org-context-editor.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.d.ts +69 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.js +278 -0
- package/dist/AI/components/autotagging/components/classify-overview-analytics.component.js.map +1 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.d.ts +73 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.js +393 -0
- package/dist/AI/components/autotagging/components/classify-seed-taxonomy.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.d.ts +122 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.js +908 -0
- package/dist/AI/components/autotagging/dialogs/classify-setup-wizard.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.d.ts +87 -0
- package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.js +475 -0
- package/dist/AI/components/autotagging/dialogs/dry-run-preview.dialog.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.d.ts +29 -0
- package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.js +208 -0
- package/dist/AI/components/autotagging/dialogs/item-detail.dialog.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.d.ts +21 -0
- package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.js +70 -0
- package/dist/AI/components/autotagging/dialogs/no-content-type-warning.dialog.component.js.map +1 -0
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts +333 -0
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js +2125 -0
- package/dist/AI/components/autotagging/dialogs/source-type-form.dialog.component.js.map +1 -0
- package/dist/AI/components/autotagging/shared/classify.dryrun.d.ts +61 -0
- package/dist/AI/components/autotagging/shared/classify.dryrun.d.ts.map +1 -0
- package/dist/AI/components/autotagging/shared/classify.dryrun.js +78 -0
- package/dist/AI/components/autotagging/shared/classify.dryrun.js.map +1 -0
- package/dist/AI/components/autotagging/shared/classify.format.d.ts +58 -0
- package/dist/AI/components/autotagging/shared/classify.format.d.ts.map +1 -0
- package/dist/AI/components/autotagging/shared/classify.format.js +260 -0
- package/dist/AI/components/autotagging/shared/classify.format.js.map +1 -0
- package/dist/AI/components/autotagging/shared/classify.types.d.ts +319 -0
- package/dist/AI/components/autotagging/shared/classify.types.d.ts.map +1 -0
- package/dist/AI/components/autotagging/shared/classify.types.js +6 -0
- package/dist/AI/components/autotagging/shared/classify.types.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/health-tab.component.d.ts +103 -0
- package/dist/AI/components/autotagging/tabs/health-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/health-tab.component.js +571 -0
- package/dist/AI/components/autotagging/tabs/health-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts +77 -0
- package/dist/AI/components/autotagging/tabs/history-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/history-tab.component.js +519 -0
- package/dist/AI/components/autotagging/tabs/history-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/inbox-tab.component.d.ts +107 -0
- package/dist/AI/components/autotagging/tabs/inbox-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/inbox-tab.component.js +719 -0
- package/dist/AI/components/autotagging/tabs/inbox-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts +131 -0
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js +813 -0
- package/dist/AI/components/autotagging/tabs/pipeline-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts +177 -0
- package/dist/AI/components/autotagging/tabs/sources-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js +1465 -0
- package/dist/AI/components/autotagging/tabs/sources-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts +78 -0
- package/dist/AI/components/autotagging/tabs/tags-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/tags-tab.component.js +492 -0
- package/dist/AI/components/autotagging/tabs/tags-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.d.ts +397 -0
- package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.js +3490 -0
- package/dist/AI/components/autotagging/tabs/taxonomy-tab.component.js.map +1 -0
- package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts +56 -0
- package/dist/AI/components/autotagging/tabs/types-tab.component.d.ts.map +1 -0
- package/dist/AI/components/autotagging/tabs/types-tab.component.js +271 -0
- package/dist/AI/components/autotagging/tabs/types-tab.component.js.map +1 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts +3 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +306 -290
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +1 -1
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.js +209 -208
- package/dist/AI/components/models/model-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-management.component.js +130 -128
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/requests/agent-requests-resource.component.js +61 -61
- package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -1
- package/dist/AI/components/system/system-configuration.component.js +17 -17
- package/dist/AI/components/system/system-configuration.component.js.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.d.ts +1 -0
- package/dist/AI/components/tags/tags-resource.component.d.ts.map +1 -1
- package/dist/AI/components/tags/tags-resource.component.js +578 -538
- package/dist/AI/components/tags/tags-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +3 -0
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +331 -303
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.d.ts +5 -0
- package/dist/AI/services/ai-instrumentation.service.d.ts.map +1 -1
- package/dist/AI/services/ai-instrumentation.service.js +14 -2
- package/dist/AI/services/ai-instrumentation.service.js.map +1 -1
- package/dist/AI/services/cache-metrics.d.ts +50 -0
- package/dist/AI/services/cache-metrics.d.ts.map +1 -0
- package/dist/AI/services/cache-metrics.js +43 -0
- package/dist/AI/services/cache-metrics.js.map +1 -0
- package/dist/APIKeys/api-applications-panel.component.js +2 -2
- package/dist/APIKeys/api-key-create-dialog.component.js +2 -2
- package/dist/APIKeys/api-key-edit-panel.component.js +2 -2
- package/dist/APIKeys/api-keys-resource.component.js +132 -131
- package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
- package/dist/Actions/components/actions-overview.component.js +141 -141
- package/dist/Actions/components/actions-overview.component.js.map +1 -1
- package/dist/Actions/components/execution-monitoring.component.js +15 -15
- package/dist/Actions/components/execution-monitoring.component.js.map +1 -1
- package/dist/Actions/components/explorer/action-explorer.component.d.ts +0 -5
- package/dist/Actions/components/explorer/action-explorer.component.d.ts.map +1 -1
- package/dist/Actions/components/explorer/action-explorer.component.js +139 -212
- package/dist/Actions/components/explorer/action-explorer.component.js.map +1 -1
- package/dist/Admin/admin-data-schema.component.js +2 -2
- package/dist/Admin/admin-data-schema.component.js.map +1 -1
- package/dist/Admin/admin-dev-tools-resource.component.js +2 -2
- package/dist/Admin/admin-dev-tools-resource.component.js.map +1 -1
- package/dist/Admin/admin-identity-access.component.js +2 -2
- package/dist/Admin/admin-identity-access.component.js.map +1 -1
- package/dist/Admin/admin-monitoring.component.js +2 -2
- package/dist/Admin/admin-monitoring.component.js.map +1 -1
- package/dist/ApplicationRoles/application-roles-resource.component.js +54 -49
- package/dist/ApplicationRoles/application-roles-resource.component.js.map +1 -1
- package/dist/Communication/communication-logs-resource.component.d.ts +6 -0
- package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-logs-resource.component.js +72 -50
- package/dist/Communication/communication-logs-resource.component.js.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.js +103 -102
- package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
- package/dist/Communication/communication-providers-resource.component.js +52 -51
- package/dist/Communication/communication-providers-resource.component.js.map +1 -1
- package/dist/Communication/communication-runs-resource.component.js +39 -38
- package/dist/Communication/communication-runs-resource.component.js.map +1 -1
- package/dist/Communication/communication-templates-resource.component.d.ts +6 -0
- package/dist/Communication/communication-templates-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-templates-resource.component.js +92 -89
- package/dist/Communication/communication-templates-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-audit-resource.component.js +136 -135
- package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.js +155 -152
- package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +119 -118
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-overview-resource.component.js +129 -128
- package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.js +107 -106
- package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +31 -340
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +468 -1958
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-resource.component.js +10 -0
- package/dist/DataExplorer/data-explorer-resource.component.js.map +1 -1
- package/dist/DatabaseDesigner/components/database-designer-dashboard.component.js +1 -1
- package/dist/DatabaseDesigner/components/entity-list.component.js +115 -114
- package/dist/DatabaseDesigner/components/entity-list.component.js.map +1 -1
- package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts +5 -6
- package/dist/DatabaseDesigner/database-designer-dashboards.module.d.ts.map +1 -1
- package/dist/DatabaseDesigner/database-designer-dashboards.module.js +4 -5
- package/dist/DatabaseDesigner/database-designer-dashboards.module.js.map +1 -1
- package/dist/DevTools/app-state-inspector.component.js +18 -17
- package/dist/DevTools/app-state-inspector.component.js.map +1 -1
- package/dist/DevTools/class-registry.component.js +88 -85
- package/dist/DevTools/class-registry.component.js.map +1 -1
- package/dist/DevTools/event-monitor.component.js +155 -150
- package/dist/DevTools/event-monitor.component.js.map +1 -1
- package/dist/DevTools/graphql-console.component.js +245 -243
- package/dist/DevTools/graphql-console.component.js.map +1 -1
- package/dist/DevTools/layout-inspector.component.js +18 -17
- package/dist/DevTools/layout-inspector.component.js.map +1 -1
- package/dist/EntityAdmin/entity-admin-dashboard.component.js +20 -19
- package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.js +2 -2
- package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
- package/dist/Integration/components/activity/activity.component.js +236 -229
- package/dist/Integration/components/activity/activity.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +390 -389
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/components/overview/overview.component.js +2 -2
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +14 -11
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts +27 -2
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.d.ts.map +1 -1
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js +244 -119
- package/dist/KnowledgeHub/components/clusters/cluster-visualization-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js +293 -291
- package/dist/KnowledgeHub/components/config/knowledge-config-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js +62 -61
- package/dist/KnowledgeHub/components/scheduling/scheduling-resource.component.js.map +1 -1
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.d.ts +65 -0
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.js +176 -0
- package/dist/KnowledgeHub/components/visualize/record-drilldown/record-drilldown.component.js.map +1 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.d.ts +81 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.js +308 -0
- package/dist/KnowledgeHub/components/visualize/tag-cloud/tag-cloud.component.js.map +1 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.d.ts +85 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.d.ts.map +1 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.js +362 -0
- package/dist/KnowledgeHub/components/visualize/visualize-resource.component.js.map +1 -0
- package/dist/KnowledgeHub/index.d.ts +3 -0
- package/dist/KnowledgeHub/index.d.ts.map +1 -1
- package/dist/KnowledgeHub/index.js +3 -0
- package/dist/KnowledgeHub/index.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.d.ts +6 -2
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +525 -566
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-categories-resource.component.js +135 -134
- package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +199 -198
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/MCP/components/mcp-server-dialog.component.js +2 -2
- package/dist/MCP/mcp-dashboard.component.js +443 -438
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-activity.component.d.ts.map +1 -1
- package/dist/Scheduling/components/scheduling-activity.component.js +146 -147
- package/dist/Scheduling/components/scheduling-activity.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-jobs.component.js +76 -75
- package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-overview.component.js +97 -96
- package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
- package/dist/Scheduling/scheduling-dashboard.component.js +24 -22
- package/dist/Scheduling/scheduling-dashboard.component.js.map +1 -1
- package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts +2 -0
- package/dist/Scheduling/services/scheduling-instrumentation.service.d.ts.map +1 -1
- package/dist/Scheduling/services/scheduling-instrumentation.service.js +1 -0
- package/dist/Scheduling/services/scheduling-instrumentation.service.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab-resource.component.js +1 -1
- package/dist/Testing/components/testing-explorer.component.d.ts +14 -4
- package/dist/Testing/components/testing-explorer.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-explorer.component.js +436 -427
- package/dist/Testing/components/testing-explorer.component.js.map +1 -1
- package/dist/Testing/components/testing-runs-resource.component.js +1 -1
- package/dist/Testing/components/testing-runs.component.js +116 -115
- package/dist/Testing/components/testing-runs.component.js.map +1 -1
- package/dist/Testing/testing-dashboard.component.js +6 -7
- package/dist/Testing/testing-dashboard.component.js.map +1 -1
- package/dist/VersionHistory/components/labels-resource.component.js +173 -172
- package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
- package/dist/VersionHistory/components/restore-resource.component.d.ts +6 -0
- package/dist/VersionHistory/components/restore-resource.component.d.ts.map +1 -1
- package/dist/VersionHistory/components/restore-resource.component.js +116 -92
- package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
- package/dist/ai-dashboards.module.d.ts +57 -35
- package/dist/ai-dashboards.module.d.ts.map +1 -1
- package/dist/ai-dashboards.module.js +80 -1
- package/dist/ai-dashboards.module.js.map +1 -1
- package/dist/communication-dashboards.module.d.ts +1 -1
- package/dist/communication-dashboards.module.d.ts.map +1 -1
- package/dist/communication-dashboards.module.js +7 -1
- package/dist/communication-dashboards.module.js.map +1 -1
- package/dist/data-explorer-dashboards.module.d.ts +12 -14
- package/dist/data-explorer-dashboards.module.d.ts.map +1 -1
- package/dist/data-explorer-dashboards.module.js +5 -14
- package/dist/data-explorer-dashboards.module.js.map +1 -1
- package/dist/public-api.d.ts +3 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/dist/testing-dashboards.module.d.ts +4 -5
- package/dist/testing-dashboards.module.d.ts.map +1 -1
- package/dist/testing-dashboards.module.js +7 -5
- package/dist/testing-dashboards.module.js.map +1 -1
- package/package.json +57 -54
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.d.ts +0 -79
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.d.ts.map +0 -1
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js +0 -195
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js.map +0 -1
- package/dist/DataExplorer/components/view-selector/view-selector.component.d.ts +0 -226
- package/dist/DataExplorer/components/view-selector/view-selector.component.d.ts.map +0 -1
- package/dist/DataExplorer/components/view-selector/view-selector.component.js +0 -861
- package/dist/DataExplorer/components/view-selector/view-selector.component.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing-runs.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-runs.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n OnInit,\n OnDestroy,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ChangeDetectionStrategy,\n ViewContainerRef,\n HostListener\n} from '@angular/core';\nimport { Observable, Subject, BehaviorSubject, combineLatest } from 'rxjs';\nimport { takeUntil, map } from 'rxjs/operators';\nimport { CompositeKey } from '@memberjunction/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { TestingDialogService } from '@memberjunction/ng-testing';\nimport { EvaluationPreferences, DEFAULT_EVALUATION_PREFERENCES } from '@memberjunction/ng-testing';\nimport {\n TestingInstrumentationService,\n TestRunWithFeedbackSummary,\n EvaluationSummaryMetrics\n} from '../services/testing-instrumentation.service';\n\ntype StatusFilter = 'all' | 'running' | 'passed' | 'failed' | 'error';\ntype TimeRange = 'today' | 'week' | 'month' | '90days';\n\ninterface RunsFilterState {\n status: StatusFilter;\n timeRange: TimeRange;\n searchText: string;\n}\n\ninterface FilteredStats {\n totalRuns: number;\n passRate: number;\n avgDuration: number;\n totalCost: number;\n}\n\n@Component({\n standalone: false,\n selector: 'app-testing-runs',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (HideToolbar) {\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n } @else {\n <mj-page-layout>\n <mj-page-header\n Title=\"Test Runs\"\n Icon=\"fa-solid fa-list-check\"\n Subtitle=\"Test execution history and monitoring\">\n <div actions>\n <mj-refresh-button [Loading]=\"IsRefreshing\" (Clicked)=\"Refresh()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"StartNewTest()\">\n <i class=\"fa-solid fa-play\"></i> Run Test\n </button>\n </div>\n </mj-page-header>\n <mj-page-body>\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </mj-page-body>\n </mj-page-layout>\n }\n\n <ng-template #content>\n <div class=\"runs-container\" (keydown.escape)=\"CloseDetailPanel()\">\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"filter-left\">\n <div class=\"filter-chips\">\n @for (chip of StatusChips; track chip.value) {\n <button\n class=\"chip\"\n [class.active]=\"filterState.status === chip.value\"\n [attr.data-status]=\"chip.value\"\n (click)=\"SetStatusFilter(chip.value)\"\n >\n @if (chip.icon) {\n <i [class]=\"chip.icon\"></i>\n }\n {{ chip.label }}\n </button>\n }\n </div>\n <span class=\"result-count\">{{ (FilteredRuns$ | async)?.length ?? 0 }} results</span>\n </div>\n <div class=\"filter-right\">\n <select\n class=\"time-select\"\n [value]=\"filterState.timeRange\"\n (change)=\"OnTimeRangeChange($event)\"\n >\n <option value=\"today\">Today</option>\n <option value=\"week\">This Week</option>\n <option value=\"month\">This Month</option>\n <option value=\"90days\">Last 90 Days</option>\n </select>\n <div class=\"search-box\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n type=\"text\"\n placeholder=\"Search by test name...\"\n [value]=\"filterState.searchText\"\n (input)=\"OnSearchInput($event)\"\n />\n @if (filterState.searchText) {\n <button class=\"clear-search\" (click)=\"ClearSearch()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n </div>\n </div>\n\n <!-- Summary Stats Row -->\n @if (FilteredStats$ | async; as stats) {\n <div class=\"stats-row\">\n <div class=\"stat-card\">\n <div class=\"stat-icon total\"><i class=\"fa-solid fa-hashtag\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ stats.totalRuns }}</div>\n <div class=\"stat-label\">Total Runs</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon pass\"><i class=\"fa-solid fa-check-circle\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ FormatPercent(stats.passRate) }}</div>\n <div class=\"stat-label\">Pass Rate</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon duration\"><i class=\"fa-solid fa-clock\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ FormatDuration(stats.avgDuration) }}</div>\n <div class=\"stat-label\">Avg Duration</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon cost\"><i class=\"fa-solid fa-dollar-sign\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ FormatCostTotal(stats.totalCost) }}</div>\n <div class=\"stat-label\">Total Cost</div>\n </div>\n </div>\n </div>\n }\n\n <!-- Runs Table -->\n <div class=\"runs-table-wrapper\">\n <div class=\"table-header\">\n <div class=\"col col-name\">Test Name</div>\n <div class=\"col col-eval\">Evaluation</div>\n <div class=\"col col-score\">Score</div>\n <div class=\"col col-duration\">Duration</div>\n <div class=\"col col-cost\">Cost</div>\n <div class=\"col col-time\">Started</div>\n <div class=\"col col-actions\">Actions</div>\n </div>\n\n @if (instrumentationService.isLoading$ | async) {\n <div class=\"table-loading\">\n <mj-loading text=\"Loading test runs...\"></mj-loading>\n </div>\n } @else if ((FilteredRuns$ | async)?.length === 0) {\n <div class=\"table-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No test runs found</p>\n <span class=\"empty-hint\">Try adjusting your filters or run a new test.</span>\n </div>\n } @else {\n @for (run of FilteredRuns$ | async; track TrackByRunId($index, run)) {\n <div\n class=\"table-row\"\n [class.is-running]=\"run.status === 'Running'\"\n [class.is-selected]=\"SelectedRun?.id === run.id\"\n (click)=\"SelectRun(run)\"\n >\n <div class=\"col col-name\">\n <span class=\"test-name-link\" (click)=\"NavigateToTest(run, $event)\">\n {{ run.testName }}\n </span>\n </div>\n <div class=\"col col-eval\">\n <app-evaluation-badge\n [executionStatus]=\"run.status\"\n [originalStatus]=\"run.status\"\n [autoScore]=\"run.score\"\n [passedChecks]=\"run.passedChecks\"\n [failedChecks]=\"run.failedChecks\"\n [totalChecks]=\"run.totalChecks\"\n [humanRating]=\"run.humanRating\"\n [humanIsCorrect]=\"run.humanIsCorrect\"\n [hasHumanFeedback]=\"run.hasHumanFeedback\"\n [preferences]=\"EvalPreferences\"\n mode=\"compact\"\n ></app-evaluation-badge>\n </div>\n <div class=\"col col-score\">\n <app-score-indicator [score]=\"run.score\" [showBar]=\"true\"></app-score-indicator>\n </div>\n <div class=\"col col-duration\">{{ FormatDuration(run.duration) }}</div>\n <div class=\"col col-cost\">\n <app-cost-display [cost]=\"run.cost\"></app-cost-display>\n </div>\n <div class=\"col col-time\">{{ FormatRelativeTime(run.runDateTime) }}</div>\n <div class=\"col col-actions\">\n <button class=\"icon-btn\" title=\"View details\" (click)=\"SelectRun(run); $event.stopPropagation()\">\n <i class=\"fa-solid fa-eye\"></i>\n </button>\n <button class=\"icon-btn\" title=\"Re-run test\" (click)=\"RerunTest(run); $event.stopPropagation()\">\n <i class=\"fa-solid fa-rotate-right\"></i>\n </button>\n </div>\n </div>\n }\n }\n </div>\n\n <!-- Detail Panel (slide-in) -->\n @if (SelectedRun) {\n <div class=\"detail-overlay\" (click)=\"CloseDetailPanel()\"></div>\n <div class=\"detail-panel\">\n <div class=\"detail-panel-header\">\n <div class=\"detail-title-section\">\n <h3>{{ SelectedRun.testName }}</h3>\n <app-test-status-badge [status]=\"SelectedRun.status\"></app-test-status-badge>\n </div>\n <button class=\"close-btn\" (click)=\"CloseDetailPanel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"detail-panel-body\">\n <!-- Metrics row -->\n <div class=\"detail-metrics\">\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Score</span>\n <app-score-indicator [score]=\"SelectedRun.score\" [showBar]=\"true\"></app-score-indicator>\n </div>\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Duration</span>\n <span class=\"dm-value\">{{ FormatDuration(SelectedRun.duration) }}</span>\n </div>\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Cost</span>\n <app-cost-display [cost]=\"SelectedRun.cost\"></app-cost-display>\n </div>\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Started</span>\n <span class=\"dm-value\">{{ FormatDateTime(SelectedRun.runDateTime) }}</span>\n </div>\n </div>\n\n <!-- Evaluation Badge (expanded) -->\n <div class=\"detail-section\">\n <h4><i class=\"fa-solid fa-clipboard-check\"></i> Evaluation</h4>\n <app-evaluation-badge\n [executionStatus]=\"SelectedRun.status\"\n [originalStatus]=\"SelectedRun.status\"\n [autoScore]=\"SelectedRun.score\"\n [passedChecks]=\"SelectedRun.passedChecks\"\n [failedChecks]=\"SelectedRun.failedChecks\"\n [totalChecks]=\"SelectedRun.totalChecks\"\n [humanRating]=\"SelectedRun.humanRating\"\n [humanIsCorrect]=\"SelectedRun.humanIsCorrect\"\n [hasHumanFeedback]=\"SelectedRun.hasHumanFeedback\"\n [preferences]=\"EvalPreferences\"\n mode=\"expanded\"\n ></app-evaluation-badge>\n </div>\n\n <!-- Execution Context -->\n @if (SelectedRun.targetType) {\n <div class=\"detail-section\">\n <h4><i class=\"fa-solid fa-server\"></i> Execution Context</h4>\n <mj-execution-context></mj-execution-context>\n </div>\n }\n\n <!-- Inline Feedback Form -->\n <div class=\"detail-section feedback-section\">\n <h4><i class=\"fa-solid fa-comment-dots\"></i> Human Feedback</h4>\n @if (SelectedRun.hasHumanFeedback) {\n <div class=\"existing-feedback\">\n <span class=\"feedback-label\">Rating:</span>\n <span class=\"feedback-value\">{{ SelectedRun.humanRating }}/10</span>\n <span class=\"feedback-label\">Correct:</span>\n <span class=\"feedback-value\">\n @if (SelectedRun.humanIsCorrect === true) {\n <i class=\"fa-solid fa-check\" style=\"color: var(--mj-status-success)\"></i> Yes\n } @else if (SelectedRun.humanIsCorrect === false) {\n <i class=\"fa-solid fa-times\" style=\"color: var(--mj-status-error)\"></i> No\n } @else {\n --\n }\n </span>\n @if (SelectedRun.humanComments) {\n <div class=\"feedback-comments\">{{ SelectedRun.humanComments }}</div>\n }\n </div>\n } @else {\n <div class=\"feedback-form\">\n <div class=\"rating-row\">\n <label>Rating</label>\n <div class=\"rating-buttons\">\n @for (n of RatingValues; track n) {\n <button\n class=\"rating-btn\"\n [class.selected]=\"feedbackRating === n\"\n (click)=\"feedbackRating = n\"\n >\n {{ n }}\n </button>\n }\n </div>\n </div>\n <div class=\"correctness-row\">\n <label>Is the result correct?</label>\n <div class=\"toggle-group\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"feedbackIsCorrect === true\"\n (click)=\"feedbackIsCorrect = true\"\n >\n <i class=\"fa-solid fa-check\"></i> Yes\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"feedbackIsCorrect === false\"\n (click)=\"feedbackIsCorrect = false\"\n >\n <i class=\"fa-solid fa-times\"></i> No\n </button>\n </div>\n </div>\n <div class=\"comments-row\">\n <label>Comments</label>\n <textarea\n rows=\"3\"\n placeholder=\"Optional feedback comments...\"\n [value]=\"feedbackComments\"\n (input)=\"OnFeedbackCommentInput($event)\"\n ></textarea>\n </div>\n <button\n class=\"btn btn-primary submit-feedback-btn\"\n (click)=\"SubmitFeedback()\"\n [disabled]=\"IsSubmittingFeedback\"\n >\n <i class=\"fa-solid fa-paper-plane\"></i>\n {{ IsSubmittingFeedback ? 'Submitting...' : 'Submit Feedback' }}\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </ng-template>\n `,\n styles: [`\n /* ==========================================\n Testing Runs Component\n ========================================== */\n\n .runs-container {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n position: relative;\n }\n\n /* Page Header */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n }\n\n .header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-title i {\n font-size: 22px;\n color: var(--mj-brand-primary);\n }\n\n .header-title h2 {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .header-actions {\n display: flex;\n gap: 10px;\n }\n\n /* Buttons */\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n .btn-secondary {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n }\n\n .btn-secondary:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n }\n\n .spinning {\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n /* Filter Bar */\n .filter-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n margin-bottom: 20px;\n padding: 14px 20px;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .filter-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .filter-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: var(--mj-bg-surface-sunken);\n border: 2px solid transparent;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .chip:hover {\n background: var(--mj-border-default);\n }\n\n .chip.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n }\n\n .chip.active[data-status=\"running\"] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n }\n\n .chip.active[data-status=\"passed\"] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n }\n\n .chip.active[data-status=\"failed\"] {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n }\n\n .chip.active[data-status=\"error\"] {\n background: var(--mj-status-warning);\n border-color: var(--mj-status-warning);\n }\n\n .result-count {\n font-size: 12px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n white-space: nowrap;\n }\n\n .filter-right {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .time-select {\n padding: 8px 12px;\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n }\n\n .time-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n }\n\n .search-box {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n min-width: 220px;\n transition: border-color 0.2s ease;\n }\n\n .search-box:focus-within {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n }\n\n .search-box i {\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .search-box input {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--mj-text-secondary);\n }\n\n .search-box input::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .clear-search {\n border: none;\n background: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n padding: 2px 4px;\n border-radius: 4px;\n }\n\n .clear-search:hover {\n color: var(--mj-text-muted);\n background: var(--mj-border-default);\n }\n\n /* Stats Row */\n .stats-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 14px;\n margin-bottom: 20px;\n }\n\n .stat-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px 20px;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .stat-icon {\n width: 42px;\n height: 42px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n }\n\n .stat-icon.total { background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .stat-icon.pass { background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface)); color: var(--mj-status-success); }\n .stat-icon.duration { background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .stat-icon.cost { background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface)); color: var(--mj-status-warning); }\n\n .stat-body {\n flex: 1;\n }\n\n .stat-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.1;\n }\n\n .stat-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n }\n\n /* Runs Table */\n .runs-table-wrapper {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .table-header {\n display: grid;\n grid-template-columns: 2fr 140px 120px 100px 100px 120px 90px;\n gap: 12px;\n padding: 14px 24px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .table-row {\n display: grid;\n grid-template-columns: 2fr 140px 120px 100px 100px 120px 90px;\n gap: 12px;\n padding: 14px 24px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n cursor: pointer;\n transition: background 0.15s ease;\n align-items: center;\n }\n\n .table-row:last-child {\n border-bottom: none;\n }\n\n .table-row:hover {\n background: var(--mj-bg-surface-card);\n }\n\n .table-row.is-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n }\n\n .table-row.is-running {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, var(--mj-bg-surface));\n }\n\n .table-row.is-running .test-name-link::after {\n content: '';\n display: inline-block;\n width: 8px;\n height: 8px;\n background: var(--mj-brand-primary);\n border-radius: 50%;\n margin-left: 8px;\n animation: pulse-dot 1.5s infinite;\n }\n\n @keyframes pulse-dot {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n .col {\n display: flex;\n align-items: center;\n font-size: 13px;\n color: var(--mj-text-secondary);\n overflow: hidden;\n }\n\n .test-name-link {\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: pointer;\n text-decoration: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .test-name-link:hover {\n color: var(--mj-brand-primary);\n text-decoration: underline;\n }\n\n .col-actions {\n gap: 6px;\n justify-content: flex-end;\n }\n\n .icon-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 8px;\n font-size: 13px;\n transition: all 0.2s ease;\n }\n\n .icon-btn:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n /* Table states */\n .table-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 40px;\n }\n\n .table-empty {\n padding: 80px 40px;\n text-align: center;\n }\n\n .table-empty i {\n font-size: 48px;\n color: var(--mj-border-strong);\n margin-bottom: 16px;\n }\n\n .table-empty p {\n font-size: 16px;\n color: var(--mj-text-muted);\n margin: 0 0 8px 0;\n }\n\n .empty-hint {\n font-size: 13px;\n color: var(--mj-text-disabled);\n }\n\n /* Detail Panel Overlay */\n .detail-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n }\n\n /* Detail Panel */\n .detail-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 520px;\n max-width: 90vw;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-lg);\n z-index: 1000;\n display: flex;\n flex-direction: column;\n animation: slideIn 0.25s ease-out;\n }\n\n @keyframes slideIn {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n\n .detail-panel-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n }\n\n .detail-title-section {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .detail-title-section h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .close-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 14px;\n flex-shrink: 0;\n margin-left: 12px;\n transition: all 0.15s ease;\n }\n\n .close-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n }\n\n .detail-panel-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n }\n\n .detail-metrics {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n margin-bottom: 24px;\n }\n\n .detail-metric {\n padding: 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n border-left: 3px solid var(--mj-brand-primary);\n }\n\n .dm-label {\n display: block;\n font-size: 10px;\n font-weight: 700;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 6px;\n }\n\n .dm-value {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .detail-section {\n margin-bottom: 24px;\n }\n\n .detail-section h4 {\n font-size: 13px;\n font-weight: 700;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .detail-section h4 i {\n color: var(--mj-brand-primary);\n font-size: 14px;\n }\n\n /* Feedback Section */\n .existing-feedback {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 16px;\n align-items: center;\n padding: 14px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 8px;\n font-size: 13px;\n }\n\n .feedback-label {\n font-weight: 600;\n color: var(--mj-text-muted);\n }\n\n .feedback-value {\n color: var(--mj-text-primary);\n font-weight: 500;\n }\n\n .feedback-comments {\n width: 100%;\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid color-mix(in srgb, var(--mj-status-success) 25%, var(--mj-bg-surface));\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-style: italic;\n }\n\n .feedback-form {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .feedback-form label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n }\n\n .rating-buttons {\n display: flex;\n gap: 4px;\n }\n\n .rating-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 8px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .rating-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n }\n\n .rating-btn.selected {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .toggle-group {\n display: flex;\n gap: 8px;\n }\n\n .toggle-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 8px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .toggle-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n }\n\n .comments-row textarea {\n width: 100%;\n padding: 10px 12px;\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n color: var(--mj-text-secondary);\n resize: vertical;\n box-sizing: border-box;\n }\n\n .comments-row textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n }\n\n .comments-row textarea::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .submit-feedback-btn {\n align-self: flex-start;\n }\n\n /* Responsive */\n @media (max-width: 1400px) {\n .stats-row {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 1200px) {\n .filter-bar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .filter-left, .filter-right {\n justify-content: center;\n }\n\n .table-header, .table-row {\n grid-template-columns: 2fr 120px 100px 100px 80px;\n }\n\n .col-eval, .col-time {\n display: none;\n }\n }\n\n @media (max-width: 768px) {\n .stats-row {\n grid-template-columns: 1fr;\n }\n\n .detail-panel {\n width: 100vw;\n max-width: 100vw;\n }\n }\n\n @media (max-width: 480px) {\n .table-header, .table-row {\n grid-template-columns: 1fr 80px 60px;\n }\n\n .col-score, .col-duration {\n display: none;\n }\n\n .chip {\n min-height: 44px;\n padding: 8px 14px;\n }\n\n .table-row {\n min-height: 44px;\n }\n }\n `]\n})\nexport class TestingRunsComponent implements OnInit, OnDestroy {\n @Input() initialState: Record<string, unknown> | null = null;\n /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */\n @Input() HideToolbar = false;\n @Output() stateChange = new EventEmitter<Record<string, unknown>>();\n\n private destroy$ = new Subject<void>();\n private filterTrigger$ = new BehaviorSubject<void>(undefined);\n\n // Filter state\n filterState: RunsFilterState = {\n status: 'all',\n timeRange: 'month',\n searchText: ''\n };\n\n // Feedback form state\n feedbackRating = 5;\n feedbackIsCorrect: boolean | null = null;\n feedbackComments = '';\n IsSubmittingFeedback = false;\n IsRefreshing = false;\n\n SelectedRun: TestRunWithFeedbackSummary | null = null;\n\n EvalPreferences: EvaluationPreferences = { ...DEFAULT_EVALUATION_PREFERENCES, showAuto: true };\n\n // Observables\n FilteredRuns$!: Observable<TestRunWithFeedbackSummary[]>;\n FilteredStats$!: Observable<FilteredStats>;\n\n // Constants\n readonly StatusChips: Array<{ value: StatusFilter; label: string; icon: string }> = [\n { value: 'all', label: 'All', icon: '' },\n { value: 'running', label: 'Running', icon: 'fa-solid fa-spinner fa-spin' },\n { value: 'passed', label: 'Passed', icon: 'fa-solid fa-check' },\n { value: 'failed', label: 'Failed', icon: 'fa-solid fa-times' },\n { value: 'error', label: 'Error', icon: 'fa-solid fa-exclamation-triangle' }\n ];\n\n readonly RatingValues: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\n constructor(\n public instrumentationService: TestingInstrumentationService,\n private testingDialogService: TestingDialogService,\n private cdr: ChangeDetectorRef,\n private viewContainerRef: ViewContainerRef\n ) {}\n\n ngOnInit(): void {\n this.applyInitialState();\n this.updateServiceDateRange();\n this.setupObservables();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n @HostListener('document:keydown.escape')\n OnEscapeKey(): void {\n if (this.SelectedRun) {\n this.CloseDetailPanel();\n }\n }\n\n // ------- Public Methods -------\n\n SetStatusFilter(status: StatusFilter): void {\n this.filterState.status = status;\n this.filterTrigger$.next();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n OnTimeRangeChange(event: Event): void {\n const select = event.target as HTMLSelectElement;\n this.filterState.timeRange = select.value as TimeRange;\n this.updateServiceDateRange();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n OnSearchInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.filterState.searchText = input.value;\n this.filterTrigger$.next();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n ClearSearch(): void {\n this.filterState.searchText = '';\n this.filterTrigger$.next();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n Refresh(): void {\n this.IsRefreshing = true;\n this.instrumentationService.refresh();\n setTimeout(() => {\n this.IsRefreshing = false;\n this.cdr.markForCheck();\n }, 1000);\n }\n\n StartNewTest(): void {\n console.log('[TestingRuns] StartNewTest called, service IsPanelOpen before:', this.testingDialogService.IsPanelOpen);\n this.testingDialogService.OpenAsPanel();\n console.log('[TestingRuns] After OpenAsPanel, IsPanelOpen:', this.testingDialogService.IsPanelOpen);\n }\n\n SelectRun(run: TestRunWithFeedbackSummary): void {\n this.SelectedRun = run;\n this.resetFeedbackForm();\n this.cdr.markForCheck();\n }\n\n CloseDetailPanel(): void {\n this.SelectedRun = null;\n this.cdr.markForCheck();\n }\n\n NavigateToTest(run: TestRunWithFeedbackSummary, event: MouseEvent): void {\n event.stopPropagation();\n SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(run.id));\n }\n\n RerunTest(run: TestRunWithFeedbackSummary): void {\n if (!run.testId) return;\n this.testingDialogService.OpenTestPanel(run.testId);\n }\n\n async SubmitFeedback(): Promise<void> {\n if (!this.SelectedRun) return;\n\n this.IsSubmittingFeedback = true;\n this.cdr.markForCheck();\n\n const success = await this.instrumentationService.submitFeedback(\n this.SelectedRun.id,\n this.feedbackRating,\n this.feedbackIsCorrect === true,\n this.feedbackComments\n );\n\n this.IsSubmittingFeedback = false;\n\n if (success) {\n // Update the selected run to reflect feedback was submitted\n this.SelectedRun = {\n ...this.SelectedRun,\n hasHumanFeedback: true,\n humanRating: this.feedbackRating,\n humanIsCorrect: this.feedbackIsCorrect,\n humanComments: this.feedbackComments\n };\n }\n\n this.cdr.markForCheck();\n }\n\n OnFeedbackCommentInput(event: Event): void {\n const textarea = event.target as HTMLTextAreaElement;\n this.feedbackComments = textarea.value;\n }\n\n TrackByRunId(_index: number, run: TestRunWithFeedbackSummary): string {\n return run.id;\n }\n\n // ------- Formatting Helpers -------\n\n FormatDuration(milliseconds: number): string {\n if (milliseconds < 1000) return `${Math.round(milliseconds)}ms`;\n const seconds = Math.floor(milliseconds / 1000);\n const minutes = Math.floor(seconds / 60);\n if (minutes > 0) return `${minutes}m ${seconds % 60}s`;\n return `${seconds}s`;\n }\n\n FormatRelativeTime(date: Date): string {\n const now = Date.now();\n const then = date instanceof Date ? date.getTime() : new Date(date).getTime();\n const diffMs = now - then;\n const diffMinutes = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n\n if (diffMinutes < 1) return 'Just now';\n if (diffMinutes < 60) return `${diffMinutes}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n\n return this.formatShortDate(date);\n }\n\n FormatDateTime(date: Date): string {\n const d = date instanceof Date ? date : new Date(date);\n return d.toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit'\n });\n }\n\n FormatPercent(value: number): string {\n return `${Math.round(value)}%`;\n }\n\n FormatCostTotal(cost: number): string {\n if (cost < 0.01) return '$0.00';\n if (cost < 1) return `$${cost.toFixed(2)}`;\n return `$${cost.toFixed(2)}`;\n }\n\n // ------- Private Methods -------\n\n private setupObservables(): void {\n const data$ = this.instrumentationService.testRunsWithFeedback$.pipe(\n takeUntil(this.destroy$)\n );\n\n this.FilteredRuns$ = combineLatest([data$, this.filterTrigger$]).pipe(\n map(([runs]) => this.applyClientFilters(runs)),\n takeUntil(this.destroy$)\n );\n\n this.FilteredStats$ = this.FilteredRuns$.pipe(\n map(runs => this.computeStats(runs)),\n takeUntil(this.destroy$)\n );\n }\n\n private applyClientFilters(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n let filtered = runs;\n\n filtered = this.filterByStatus(filtered);\n filtered = this.filterBySearch(filtered);\n filtered = this.sortRunsWithRunningFirst(filtered);\n\n return filtered;\n }\n\n private filterByStatus(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n const status = this.filterState.status;\n if (status === 'all') return runs;\n\n const statusMap: Record<string, string[]> = {\n running: ['Running'],\n passed: ['Passed'],\n failed: ['Failed'],\n error: ['Error', 'Timeout']\n };\n\n const validStatuses = statusMap[status] ?? [];\n return runs.filter(r => validStatuses.includes(r.status));\n }\n\n private filterBySearch(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n const text = this.filterState.searchText.toLowerCase().trim();\n if (!text) return runs;\n return runs.filter(r => r.testName.toLowerCase().includes(text));\n }\n\n private sortRunsWithRunningFirst(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n return [...runs].sort((a, b) => {\n // Running tests at the top\n if (a.status === 'Running' && b.status !== 'Running') return -1;\n if (a.status !== 'Running' && b.status === 'Running') return 1;\n // Then by StartedAt DESC\n return b.runDateTime.getTime() - a.runDateTime.getTime();\n });\n }\n\n private computeStats(runs: TestRunWithFeedbackSummary[]): FilteredStats {\n const total = runs.length;\n if (total === 0) {\n return { totalRuns: 0, passRate: 0, avgDuration: 0, totalCost: 0 };\n }\n\n const passed = runs.filter(r => r.status === 'Passed').length;\n const passRate = (passed / total) * 100;\n\n const completedRuns = runs.filter(r => r.duration > 0 && r.status !== 'Running');\n const avgDuration = completedRuns.length > 0\n ? completedRuns.reduce((sum, r) => sum + r.duration, 0) / completedRuns.length\n : 0;\n\n const totalCost = runs.reduce((sum, r) => sum + r.cost, 0);\n\n return { totalRuns: total, passRate, avgDuration, totalCost };\n }\n\n private updateServiceDateRange(): void {\n const now = new Date();\n let startDate: Date;\n\n switch (this.filterState.timeRange) {\n case 'today':\n startDate = new Date(now);\n startDate.setHours(0, 0, 0, 0);\n break;\n case 'week':\n startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n break;\n case 'month':\n startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n break;\n case '90days':\n startDate = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n break;\n default:\n startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n break;\n }\n\n this.instrumentationService.setDateRange(startDate, now);\n }\n\n private applyInitialState(): void {\n if (!this.initialState) return;\n\n const state = this.initialState;\n if (state['status'] && typeof state['status'] === 'string') {\n this.filterState.status = state['status'] as StatusFilter;\n }\n if (state['timeRange'] && typeof state['timeRange'] === 'string') {\n this.filterState.timeRange = state['timeRange'] as TimeRange;\n }\n if (state['searchText'] && typeof state['searchText'] === 'string') {\n this.filterState.searchText = state['searchText'] as string;\n }\n }\n\n private emitStateChange(): void {\n this.stateChange.emit({\n status: this.filterState.status,\n timeRange: this.filterState.timeRange,\n searchText: this.filterState.searchText\n });\n }\n\n private resetFeedbackForm(): void {\n this.feedbackRating = 5;\n this.feedbackIsCorrect = null;\n this.feedbackComments = '';\n this.IsSubmittingFeedback = false;\n }\n\n private formatShortDate(date: Date): string {\n const d = date instanceof Date ? date : new Date(date);\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"testing-runs.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-runs.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n OnInit,\n OnDestroy,\n Input,\n Output,\n EventEmitter,\n ChangeDetectorRef,\n ChangeDetectionStrategy,\n ViewContainerRef,\n HostListener\n} from '@angular/core';\nimport { Observable, Subject, BehaviorSubject, combineLatest } from 'rxjs';\nimport { takeUntil, map } from 'rxjs/operators';\nimport { CompositeKey } from '@memberjunction/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { TestingDialogService } from '@memberjunction/ng-testing';\nimport { EvaluationPreferences, DEFAULT_EVALUATION_PREFERENCES } from '@memberjunction/ng-testing';\nimport {\n TestingInstrumentationService,\n TestRunWithFeedbackSummary,\n EvaluationSummaryMetrics\n} from '../services/testing-instrumentation.service';\n\ntype StatusFilter = 'all' | 'running' | 'passed' | 'failed' | 'error';\ntype TimeRange = 'today' | 'week' | 'month' | '90days';\n\ninterface RunsFilterState {\n status: StatusFilter;\n timeRange: TimeRange;\n searchText: string;\n}\n\ninterface FilteredStats {\n totalRuns: number;\n passRate: number;\n avgDuration: number;\n totalCost: number;\n}\n\n@Component({\n standalone: false,\n selector: 'app-testing-runs',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (HideToolbar) {\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n } @else {\n <mj-page-layout>\n <mj-page-header\n Title=\"Test Runs\"\n Icon=\"fa-solid fa-list-check\"\n Subtitle=\"Test execution history and monitoring\">\n <div actions>\n <mj-refresh-button [Loading]=\"IsRefreshing\" (Clicked)=\"Refresh()\"></mj-refresh-button>\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"StartNewTest()\">\n <i class=\"fa-solid fa-play\"></i> <span class=\"action-btn-label\">Run Test</span>\n </button>\n </div>\n </mj-page-header>\n <mj-page-body>\n <ng-container *ngTemplateOutlet=\"content\"></ng-container>\n </mj-page-body>\n </mj-page-layout>\n }\n\n <ng-template #content>\n <div class=\"runs-container\" (keydown.escape)=\"CloseDetailPanel()\">\n\n <!-- Filter Bar -->\n <div class=\"filter-bar\">\n <div class=\"filter-left\">\n <div class=\"filter-chips\">\n @for (chip of StatusChips; track chip.value) {\n <button\n class=\"chip\"\n [class.active]=\"filterState.status === chip.value\"\n [attr.data-status]=\"chip.value\"\n (click)=\"SetStatusFilter(chip.value)\"\n >\n @if (chip.icon) {\n <i [class]=\"chip.icon\"></i>\n }\n {{ chip.label }}\n </button>\n }\n </div>\n <span class=\"result-count\">{{ (FilteredRuns$ | async)?.length ?? 0 }} results</span>\n </div>\n <div class=\"filter-right\">\n <select\n class=\"time-select\"\n [value]=\"filterState.timeRange\"\n (change)=\"OnTimeRangeChange($event)\"\n >\n <option value=\"today\">Today</option>\n <option value=\"week\">This Week</option>\n <option value=\"month\">This Month</option>\n <option value=\"90days\">Last 90 Days</option>\n </select>\n <div class=\"search-box\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n type=\"text\"\n placeholder=\"Search by test name...\"\n [value]=\"filterState.searchText\"\n (input)=\"OnSearchInput($event)\"\n />\n @if (filterState.searchText) {\n <button class=\"clear-search\" (click)=\"ClearSearch()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n </div>\n </div>\n\n <!-- Summary Stats Row -->\n @if (FilteredStats$ | async; as stats) {\n <div class=\"stats-row\">\n <div class=\"stat-card\">\n <div class=\"stat-icon total\"><i class=\"fa-solid fa-hashtag\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ stats.totalRuns }}</div>\n <div class=\"stat-label\">Total Runs</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon pass\"><i class=\"fa-solid fa-check-circle\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ FormatPercent(stats.passRate) }}</div>\n <div class=\"stat-label\">Pass Rate</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon duration\"><i class=\"fa-solid fa-clock\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ FormatDuration(stats.avgDuration) }}</div>\n <div class=\"stat-label\">Avg Duration</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon cost\"><i class=\"fa-solid fa-dollar-sign\"></i></div>\n <div class=\"stat-body\">\n <div class=\"stat-value\">{{ FormatCostTotal(stats.totalCost) }}</div>\n <div class=\"stat-label\">Total Cost</div>\n </div>\n </div>\n </div>\n }\n\n <!-- Runs Table -->\n <div class=\"runs-table-wrapper\">\n <div class=\"table-header\">\n <div class=\"col col-name\">Test Name</div>\n <div class=\"col col-eval\">Evaluation</div>\n <div class=\"col col-score\">Score</div>\n <div class=\"col col-duration\">Duration</div>\n <div class=\"col col-cost\">Cost</div>\n <div class=\"col col-time\">Started</div>\n <div class=\"col col-actions\">Actions</div>\n </div>\n\n @if (instrumentationService.isLoading$ | async) {\n <div class=\"table-loading\">\n <mj-loading text=\"Loading test runs...\"></mj-loading>\n </div>\n } @else if ((FilteredRuns$ | async)?.length === 0) {\n <div class=\"table-empty\">\n <i class=\"fa-solid fa-inbox\"></i>\n <p>No test runs found</p>\n <span class=\"empty-hint\">Try adjusting your filters or run a new test.</span>\n </div>\n } @else {\n @for (run of FilteredRuns$ | async; track TrackByRunId($index, run)) {\n <div\n class=\"table-row\"\n [class.is-running]=\"run.status === 'Running'\"\n [class.is-selected]=\"SelectedRun?.id === run.id\"\n (click)=\"SelectRun(run)\"\n >\n <div class=\"col col-name\">\n <span class=\"test-name-link\" (click)=\"NavigateToTest(run, $event)\">\n {{ run.testName }}\n </span>\n </div>\n <div class=\"col col-eval\">\n <app-evaluation-badge\n [executionStatus]=\"run.status\"\n [originalStatus]=\"run.status\"\n [autoScore]=\"run.score\"\n [passedChecks]=\"run.passedChecks\"\n [failedChecks]=\"run.failedChecks\"\n [totalChecks]=\"run.totalChecks\"\n [humanRating]=\"run.humanRating\"\n [humanIsCorrect]=\"run.humanIsCorrect\"\n [hasHumanFeedback]=\"run.hasHumanFeedback\"\n [preferences]=\"EvalPreferences\"\n mode=\"compact\"\n ></app-evaluation-badge>\n </div>\n <div class=\"col col-score\">\n <app-score-indicator [score]=\"run.score\" [showBar]=\"true\"></app-score-indicator>\n </div>\n <div class=\"col col-duration\">{{ FormatDuration(run.duration) }}</div>\n <div class=\"col col-cost\">\n <app-cost-display [cost]=\"run.cost\"></app-cost-display>\n </div>\n <div class=\"col col-time\">{{ FormatRelativeTime(run.runDateTime) }}</div>\n <div class=\"col col-actions\">\n <button class=\"icon-btn\" title=\"View details\" (click)=\"SelectRun(run); $event.stopPropagation()\">\n <i class=\"fa-solid fa-eye\"></i>\n </button>\n <button class=\"icon-btn\" title=\"Re-run test\" (click)=\"RerunTest(run); $event.stopPropagation()\">\n <i class=\"fa-solid fa-rotate-right\"></i>\n </button>\n </div>\n </div>\n }\n }\n </div>\n\n <!-- Detail Panel (slide-in) -->\n @if (SelectedRun) {\n <div class=\"detail-overlay\" (click)=\"CloseDetailPanel()\"></div>\n <div class=\"detail-panel\">\n <div class=\"detail-panel-header\">\n <div class=\"detail-title-section\">\n <h3>{{ SelectedRun.testName }}</h3>\n <app-test-status-badge [status]=\"SelectedRun.status\"></app-test-status-badge>\n </div>\n <button class=\"close-btn\" (click)=\"CloseDetailPanel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"detail-panel-body\">\n <!-- Metrics row -->\n <div class=\"detail-metrics\">\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Score</span>\n <app-score-indicator [score]=\"SelectedRun.score\" [showBar]=\"true\"></app-score-indicator>\n </div>\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Duration</span>\n <span class=\"dm-value\">{{ FormatDuration(SelectedRun.duration) }}</span>\n </div>\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Cost</span>\n <app-cost-display [cost]=\"SelectedRun.cost\"></app-cost-display>\n </div>\n <div class=\"detail-metric\">\n <span class=\"dm-label\">Started</span>\n <span class=\"dm-value\">{{ FormatDateTime(SelectedRun.runDateTime) }}</span>\n </div>\n </div>\n\n <!-- Evaluation Badge (expanded) -->\n <div class=\"detail-section\">\n <h4><i class=\"fa-solid fa-clipboard-check\"></i> Evaluation</h4>\n <app-evaluation-badge\n [executionStatus]=\"SelectedRun.status\"\n [originalStatus]=\"SelectedRun.status\"\n [autoScore]=\"SelectedRun.score\"\n [passedChecks]=\"SelectedRun.passedChecks\"\n [failedChecks]=\"SelectedRun.failedChecks\"\n [totalChecks]=\"SelectedRun.totalChecks\"\n [humanRating]=\"SelectedRun.humanRating\"\n [humanIsCorrect]=\"SelectedRun.humanIsCorrect\"\n [hasHumanFeedback]=\"SelectedRun.hasHumanFeedback\"\n [preferences]=\"EvalPreferences\"\n mode=\"expanded\"\n ></app-evaluation-badge>\n </div>\n\n <!-- Execution Context -->\n @if (SelectedRun.targetType) {\n <div class=\"detail-section\">\n <h4><i class=\"fa-solid fa-server\"></i> Execution Context</h4>\n <mj-execution-context></mj-execution-context>\n </div>\n }\n\n <!-- Inline Feedback Form -->\n <div class=\"detail-section feedback-section\">\n <h4><i class=\"fa-solid fa-comment-dots\"></i> Human Feedback</h4>\n @if (SelectedRun.hasHumanFeedback) {\n <div class=\"existing-feedback\">\n <span class=\"feedback-label\">Rating:</span>\n <span class=\"feedback-value\">{{ SelectedRun.humanRating }}/10</span>\n <span class=\"feedback-label\">Correct:</span>\n <span class=\"feedback-value\">\n @if (SelectedRun.humanIsCorrect === true) {\n <i class=\"fa-solid fa-check\" style=\"color: var(--mj-status-success)\"></i> Yes\n } @else if (SelectedRun.humanIsCorrect === false) {\n <i class=\"fa-solid fa-times\" style=\"color: var(--mj-status-error)\"></i> No\n } @else {\n --\n }\n </span>\n @if (SelectedRun.humanComments) {\n <div class=\"feedback-comments\">{{ SelectedRun.humanComments }}</div>\n }\n </div>\n } @else {\n <div class=\"feedback-form\">\n <div class=\"rating-row\">\n <label>Rating</label>\n <div class=\"rating-buttons\">\n @for (n of RatingValues; track n) {\n <button\n class=\"rating-btn\"\n [class.selected]=\"feedbackRating === n\"\n (click)=\"feedbackRating = n\"\n >\n {{ n }}\n </button>\n }\n </div>\n </div>\n <div class=\"correctness-row\">\n <label>Is the result correct?</label>\n <div class=\"toggle-group\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"feedbackIsCorrect === true\"\n (click)=\"feedbackIsCorrect = true\"\n >\n <i class=\"fa-solid fa-check\"></i> Yes\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"feedbackIsCorrect === false\"\n (click)=\"feedbackIsCorrect = false\"\n >\n <i class=\"fa-solid fa-times\"></i> No\n </button>\n </div>\n </div>\n <div class=\"comments-row\">\n <label>Comments</label>\n <textarea\n rows=\"3\"\n placeholder=\"Optional feedback comments...\"\n [value]=\"feedbackComments\"\n (input)=\"OnFeedbackCommentInput($event)\"\n ></textarea>\n </div>\n <button\n class=\"btn btn-primary submit-feedback-btn\"\n (click)=\"SubmitFeedback()\"\n [disabled]=\"IsSubmittingFeedback\"\n >\n <i class=\"fa-solid fa-paper-plane\"></i>\n {{ IsSubmittingFeedback ? 'Submitting...' : 'Submit Feedback' }}\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n </ng-template>\n `,\n styles: [`\n /* ==========================================\n Testing Runs Component\n ========================================== */\n\n .runs-container {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n position: relative;\n }\n\n /* Page Header */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n }\n\n .header-title {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-title i {\n font-size: 22px;\n color: var(--mj-brand-primary);\n }\n\n .header-title h2 {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .header-actions {\n display: flex;\n gap: 10px;\n }\n\n /* Buttons */\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 10px 18px;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n\n .btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n .btn-secondary {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n }\n\n .btn-secondary:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n }\n\n .spinning {\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n /* Filter Bar */\n .filter-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n margin-bottom: 20px;\n padding: 14px 20px;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .filter-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .filter-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n }\n\n .chip {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: var(--mj-bg-surface-sunken);\n border: 2px solid transparent;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .chip:hover {\n background: var(--mj-border-default);\n }\n\n .chip.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n }\n\n .chip.active[data-status=\"running\"] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n }\n\n .chip.active[data-status=\"passed\"] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n }\n\n .chip.active[data-status=\"failed\"] {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n }\n\n .chip.active[data-status=\"error\"] {\n background: var(--mj-status-warning);\n border-color: var(--mj-status-warning);\n }\n\n .result-count {\n font-size: 12px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n white-space: nowrap;\n }\n\n .filter-right {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .time-select {\n padding: 8px 12px;\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface);\n cursor: pointer;\n }\n\n .time-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n }\n\n .search-box {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-card);\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n min-width: 220px;\n transition: border-color 0.2s ease;\n }\n\n .search-box:focus-within {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n }\n\n .search-box i {\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .search-box input {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--mj-text-secondary);\n }\n\n .search-box input::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .clear-search {\n border: none;\n background: transparent;\n color: var(--mj-text-disabled);\n cursor: pointer;\n padding: 2px 4px;\n border-radius: 4px;\n }\n\n .clear-search:hover {\n color: var(--mj-text-muted);\n background: var(--mj-border-default);\n }\n\n /* Stats Row */\n .stats-row {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 14px;\n margin-bottom: 20px;\n }\n\n .stat-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px 20px;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n }\n\n .stat-icon {\n width: 42px;\n height: 42px;\n border-radius: 10px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 16px;\n }\n\n .stat-icon.total { background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .stat-icon.pass { background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface)); color: var(--mj-status-success); }\n .stat-icon.duration { background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface)); color: var(--mj-brand-primary); }\n .stat-icon.cost { background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface)); color: var(--mj-status-warning); }\n\n .stat-body {\n flex: 1;\n }\n\n .stat-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n line-height: 1.1;\n }\n\n .stat-label {\n font-size: 11px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n }\n\n /* Runs Table */\n .runs-table-wrapper {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .table-header {\n display: grid;\n grid-template-columns: 2fr 140px 120px 100px 100px 120px 90px;\n gap: 12px;\n padding: 14px 24px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 11px;\n font-weight: 700;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .table-row {\n display: grid;\n grid-template-columns: 2fr 140px 120px 100px 100px 120px 90px;\n gap: 12px;\n padding: 14px 24px;\n border-bottom: 1px solid var(--mj-bg-surface-sunken);\n cursor: pointer;\n transition: background 0.15s ease;\n align-items: center;\n }\n\n .table-row:last-child {\n border-bottom: none;\n }\n\n .table-row:hover {\n background: var(--mj-bg-surface-card);\n }\n\n .table-row.is-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n border-left: 3px solid var(--mj-brand-primary);\n }\n\n .table-row.is-running {\n background: color-mix(in srgb, var(--mj-brand-primary) 4%, var(--mj-bg-surface));\n }\n\n .table-row.is-running .test-name-link::after {\n content: '';\n display: inline-block;\n width: 8px;\n height: 8px;\n background: var(--mj-brand-primary);\n border-radius: 50%;\n margin-left: 8px;\n animation: pulse-dot 1.5s infinite;\n }\n\n @keyframes pulse-dot {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.3; }\n }\n\n .col {\n display: flex;\n align-items: center;\n font-size: 13px;\n color: var(--mj-text-secondary);\n overflow: hidden;\n }\n\n .test-name-link {\n font-weight: 600;\n color: var(--mj-text-primary);\n cursor: pointer;\n text-decoration: none;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .test-name-link:hover {\n color: var(--mj-brand-primary);\n text-decoration: underline;\n }\n\n .col-actions {\n gap: 6px;\n justify-content: flex-end;\n }\n\n .icon-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: pointer;\n border-radius: 8px;\n font-size: 13px;\n transition: all 0.2s ease;\n }\n\n .icon-btn:hover {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n /* Table states */\n .table-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 40px;\n }\n\n .table-empty {\n padding: 80px 40px;\n text-align: center;\n }\n\n .table-empty i {\n font-size: 48px;\n color: var(--mj-border-strong);\n margin-bottom: 16px;\n }\n\n .table-empty p {\n font-size: 16px;\n color: var(--mj-text-muted);\n margin: 0 0 8px 0;\n }\n\n .empty-hint {\n font-size: 13px;\n color: var(--mj-text-disabled);\n }\n\n /* Detail Panel Overlay */\n .detail-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 999;\n }\n\n /* Detail Panel */\n .detail-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 520px;\n max-width: 90vw;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-lg);\n z-index: 1000;\n display: flex;\n flex-direction: column;\n animation: slideIn 0.25s ease-out;\n }\n\n @keyframes slideIn {\n from { transform: translateX(100%); }\n to { transform: translateX(0); }\n }\n\n .detail-panel-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n }\n\n .detail-title-section {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1;\n min-width: 0;\n }\n\n .detail-title-section h3 {\n margin: 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .close-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 14px;\n flex-shrink: 0;\n margin-left: 12px;\n transition: all 0.15s ease;\n }\n\n .close-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n }\n\n .detail-panel-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n }\n\n .detail-metrics {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n margin-bottom: 24px;\n }\n\n .detail-metric {\n padding: 12px;\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n border-left: 3px solid var(--mj-brand-primary);\n }\n\n .dm-label {\n display: block;\n font-size: 10px;\n font-weight: 700;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-bottom: 6px;\n }\n\n .dm-value {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n\n .detail-section {\n margin-bottom: 24px;\n }\n\n .detail-section h4 {\n font-size: 13px;\n font-weight: 700;\n color: var(--mj-text-secondary);\n margin: 0 0 12px 0;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .detail-section h4 i {\n color: var(--mj-brand-primary);\n font-size: 14px;\n }\n\n /* Feedback Section */\n .existing-feedback {\n display: flex;\n flex-wrap: wrap;\n gap: 8px 16px;\n align-items: center;\n padding: 14px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 8px;\n font-size: 13px;\n }\n\n .feedback-label {\n font-weight: 600;\n color: var(--mj-text-muted);\n }\n\n .feedback-value {\n color: var(--mj-text-primary);\n font-weight: 500;\n }\n\n .feedback-comments {\n width: 100%;\n margin-top: 8px;\n padding-top: 8px;\n border-top: 1px solid color-mix(in srgb, var(--mj-status-success) 25%, var(--mj-bg-surface));\n font-size: 13px;\n color: var(--mj-text-secondary);\n font-style: italic;\n }\n\n .feedback-form {\n display: flex;\n flex-direction: column;\n gap: 16px;\n }\n\n .feedback-form label {\n display: block;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n }\n\n .rating-buttons {\n display: flex;\n gap: 4px;\n }\n\n .rating-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 8px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .rating-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n }\n\n .rating-btn.selected {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .toggle-group {\n display: flex;\n gap: 8px;\n }\n\n .toggle-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 2px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n border-radius: 8px;\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .toggle-btn.active {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n }\n\n .comments-row textarea {\n width: 100%;\n padding: 10px 12px;\n border: 2px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n font-family: inherit;\n color: var(--mj-text-secondary);\n resize: vertical;\n box-sizing: border-box;\n }\n\n .comments-row textarea:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n }\n\n .comments-row textarea::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .submit-feedback-btn {\n align-self: flex-start;\n }\n\n /* Responsive */\n @media (max-width: 1400px) {\n .stats-row {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 1200px) {\n .filter-bar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .filter-left, .filter-right {\n justify-content: center;\n }\n\n .table-header, .table-row {\n grid-template-columns: 2fr 120px 100px 100px 80px;\n }\n\n .col-eval, .col-time {\n display: none;\n }\n }\n\n @media (max-width: 768px) {\n .stats-row {\n grid-template-columns: 1fr;\n }\n\n .detail-panel {\n width: 100vw;\n max-width: 100vw;\n }\n }\n\n @media (max-width: 480px) {\n .table-header, .table-row {\n grid-template-columns: 1fr 80px 60px;\n }\n\n .col-score, .col-duration {\n display: none;\n }\n\n .chip {\n min-height: 44px;\n padding: 8px 14px;\n }\n\n .table-row {\n min-height: 44px;\n }\n }\n `]\n})\nexport class TestingRunsComponent implements OnInit, OnDestroy {\n @Input() initialState: Record<string, unknown> | null = null;\n /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */\n @Input() HideToolbar = false;\n @Output() stateChange = new EventEmitter<Record<string, unknown>>();\n\n private destroy$ = new Subject<void>();\n private filterTrigger$ = new BehaviorSubject<void>(undefined);\n\n // Filter state\n filterState: RunsFilterState = {\n status: 'all',\n timeRange: 'month',\n searchText: ''\n };\n\n // Feedback form state\n feedbackRating = 5;\n feedbackIsCorrect: boolean | null = null;\n feedbackComments = '';\n IsSubmittingFeedback = false;\n IsRefreshing = false;\n\n SelectedRun: TestRunWithFeedbackSummary | null = null;\n\n EvalPreferences: EvaluationPreferences = { ...DEFAULT_EVALUATION_PREFERENCES, showAuto: true };\n\n // Observables\n FilteredRuns$!: Observable<TestRunWithFeedbackSummary[]>;\n FilteredStats$!: Observable<FilteredStats>;\n\n // Constants\n readonly StatusChips: Array<{ value: StatusFilter; label: string; icon: string }> = [\n { value: 'all', label: 'All', icon: '' },\n { value: 'running', label: 'Running', icon: 'fa-solid fa-spinner fa-spin' },\n { value: 'passed', label: 'Passed', icon: 'fa-solid fa-check' },\n { value: 'failed', label: 'Failed', icon: 'fa-solid fa-times' },\n { value: 'error', label: 'Error', icon: 'fa-solid fa-exclamation-triangle' }\n ];\n\n readonly RatingValues: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\n constructor(\n public instrumentationService: TestingInstrumentationService,\n private testingDialogService: TestingDialogService,\n private cdr: ChangeDetectorRef,\n private viewContainerRef: ViewContainerRef\n ) {}\n\n ngOnInit(): void {\n this.applyInitialState();\n this.updateServiceDateRange();\n this.setupObservables();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n @HostListener('document:keydown.escape')\n OnEscapeKey(): void {\n if (this.SelectedRun) {\n this.CloseDetailPanel();\n }\n }\n\n // ------- Public Methods -------\n\n SetStatusFilter(status: StatusFilter): void {\n this.filterState.status = status;\n this.filterTrigger$.next();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n OnTimeRangeChange(event: Event): void {\n const select = event.target as HTMLSelectElement;\n this.filterState.timeRange = select.value as TimeRange;\n this.updateServiceDateRange();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n OnSearchInput(event: Event): void {\n const input = event.target as HTMLInputElement;\n this.filterState.searchText = input.value;\n this.filterTrigger$.next();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n ClearSearch(): void {\n this.filterState.searchText = '';\n this.filterTrigger$.next();\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n Refresh(): void {\n this.IsRefreshing = true;\n this.instrumentationService.refresh();\n setTimeout(() => {\n this.IsRefreshing = false;\n this.cdr.markForCheck();\n }, 1000);\n }\n\n StartNewTest(): void {\n console.log('[TestingRuns] StartNewTest called, service IsPanelOpen before:', this.testingDialogService.IsPanelOpen);\n this.testingDialogService.OpenAsPanel();\n console.log('[TestingRuns] After OpenAsPanel, IsPanelOpen:', this.testingDialogService.IsPanelOpen);\n }\n\n SelectRun(run: TestRunWithFeedbackSummary): void {\n this.SelectedRun = run;\n this.resetFeedbackForm();\n this.cdr.markForCheck();\n }\n\n CloseDetailPanel(): void {\n this.SelectedRun = null;\n this.cdr.markForCheck();\n }\n\n NavigateToTest(run: TestRunWithFeedbackSummary, event: MouseEvent): void {\n event.stopPropagation();\n SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(run.id));\n }\n\n RerunTest(run: TestRunWithFeedbackSummary): void {\n if (!run.testId) return;\n this.testingDialogService.OpenTestPanel(run.testId);\n }\n\n async SubmitFeedback(): Promise<void> {\n if (!this.SelectedRun) return;\n\n this.IsSubmittingFeedback = true;\n this.cdr.markForCheck();\n\n const success = await this.instrumentationService.submitFeedback(\n this.SelectedRun.id,\n this.feedbackRating,\n this.feedbackIsCorrect === true,\n this.feedbackComments\n );\n\n this.IsSubmittingFeedback = false;\n\n if (success) {\n // Update the selected run to reflect feedback was submitted\n this.SelectedRun = {\n ...this.SelectedRun,\n hasHumanFeedback: true,\n humanRating: this.feedbackRating,\n humanIsCorrect: this.feedbackIsCorrect,\n humanComments: this.feedbackComments\n };\n }\n\n this.cdr.markForCheck();\n }\n\n OnFeedbackCommentInput(event: Event): void {\n const textarea = event.target as HTMLTextAreaElement;\n this.feedbackComments = textarea.value;\n }\n\n TrackByRunId(_index: number, run: TestRunWithFeedbackSummary): string {\n return run.id;\n }\n\n // ------- Formatting Helpers -------\n\n FormatDuration(milliseconds: number): string {\n if (milliseconds < 1000) return `${Math.round(milliseconds)}ms`;\n const seconds = Math.floor(milliseconds / 1000);\n const minutes = Math.floor(seconds / 60);\n if (minutes > 0) return `${minutes}m ${seconds % 60}s`;\n return `${seconds}s`;\n }\n\n FormatRelativeTime(date: Date): string {\n const now = Date.now();\n const then = date instanceof Date ? date.getTime() : new Date(date).getTime();\n const diffMs = now - then;\n const diffMinutes = Math.floor(diffMs / 60000);\n const diffHours = Math.floor(diffMs / 3600000);\n\n if (diffMinutes < 1) return 'Just now';\n if (diffMinutes < 60) return `${diffMinutes}m ago`;\n if (diffHours < 24) return `${diffHours}h ago`;\n\n return this.formatShortDate(date);\n }\n\n FormatDateTime(date: Date): string {\n const d = date instanceof Date ? date : new Date(date);\n return d.toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit'\n });\n }\n\n FormatPercent(value: number): string {\n return `${Math.round(value)}%`;\n }\n\n FormatCostTotal(cost: number): string {\n if (cost < 0.01) return '$0.00';\n if (cost < 1) return `$${cost.toFixed(2)}`;\n return `$${cost.toFixed(2)}`;\n }\n\n // ------- Private Methods -------\n\n private setupObservables(): void {\n const data$ = this.instrumentationService.testRunsWithFeedback$.pipe(\n takeUntil(this.destroy$)\n );\n\n this.FilteredRuns$ = combineLatest([data$, this.filterTrigger$]).pipe(\n map(([runs]) => this.applyClientFilters(runs)),\n takeUntil(this.destroy$)\n );\n\n this.FilteredStats$ = this.FilteredRuns$.pipe(\n map(runs => this.computeStats(runs)),\n takeUntil(this.destroy$)\n );\n }\n\n private applyClientFilters(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n let filtered = runs;\n\n filtered = this.filterByStatus(filtered);\n filtered = this.filterBySearch(filtered);\n filtered = this.sortRunsWithRunningFirst(filtered);\n\n return filtered;\n }\n\n private filterByStatus(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n const status = this.filterState.status;\n if (status === 'all') return runs;\n\n const statusMap: Record<string, string[]> = {\n running: ['Running'],\n passed: ['Passed'],\n failed: ['Failed'],\n error: ['Error', 'Timeout']\n };\n\n const validStatuses = statusMap[status] ?? [];\n return runs.filter(r => validStatuses.includes(r.status));\n }\n\n private filterBySearch(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n const text = this.filterState.searchText.toLowerCase().trim();\n if (!text) return runs;\n return runs.filter(r => r.testName.toLowerCase().includes(text));\n }\n\n private sortRunsWithRunningFirst(runs: TestRunWithFeedbackSummary[]): TestRunWithFeedbackSummary[] {\n return [...runs].sort((a, b) => {\n // Running tests at the top\n if (a.status === 'Running' && b.status !== 'Running') return -1;\n if (a.status !== 'Running' && b.status === 'Running') return 1;\n // Then by StartedAt DESC\n return b.runDateTime.getTime() - a.runDateTime.getTime();\n });\n }\n\n private computeStats(runs: TestRunWithFeedbackSummary[]): FilteredStats {\n const total = runs.length;\n if (total === 0) {\n return { totalRuns: 0, passRate: 0, avgDuration: 0, totalCost: 0 };\n }\n\n const passed = runs.filter(r => r.status === 'Passed').length;\n const passRate = (passed / total) * 100;\n\n const completedRuns = runs.filter(r => r.duration > 0 && r.status !== 'Running');\n const avgDuration = completedRuns.length > 0\n ? completedRuns.reduce((sum, r) => sum + r.duration, 0) / completedRuns.length\n : 0;\n\n const totalCost = runs.reduce((sum, r) => sum + r.cost, 0);\n\n return { totalRuns: total, passRate, avgDuration, totalCost };\n }\n\n private updateServiceDateRange(): void {\n const now = new Date();\n let startDate: Date;\n\n switch (this.filterState.timeRange) {\n case 'today':\n startDate = new Date(now);\n startDate.setHours(0, 0, 0, 0);\n break;\n case 'week':\n startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);\n break;\n case 'month':\n startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n break;\n case '90days':\n startDate = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);\n break;\n default:\n startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);\n break;\n }\n\n this.instrumentationService.setDateRange(startDate, now);\n }\n\n private applyInitialState(): void {\n if (!this.initialState) return;\n\n const state = this.initialState;\n if (state['status'] && typeof state['status'] === 'string') {\n this.filterState.status = state['status'] as StatusFilter;\n }\n if (state['timeRange'] && typeof state['timeRange'] === 'string') {\n this.filterState.timeRange = state['timeRange'] as TimeRange;\n }\n if (state['searchText'] && typeof state['searchText'] === 'string') {\n this.filterState.searchText = state['searchText'] as string;\n }\n }\n\n private emitStateChange(): void {\n this.stateChange.emit({\n status: this.filterState.status,\n timeRange: this.filterState.timeRange,\n searchText: this.filterState.searchText\n });\n }\n\n private resetFeedbackForm(): void {\n this.feedbackRating = 5;\n this.feedbackIsCorrect = null;\n this.feedbackComments = '';\n this.IsSubmittingFeedback = false;\n }\n\n private formatShortDate(date: Date): string {\n const d = date instanceof Date ? date : new Date(date);\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });\n }\n}\n"]}
|
|
@@ -13,12 +13,11 @@ import * as i0 from "@angular/core";
|
|
|
13
13
|
import * as i1 from "@memberjunction/ng-testing";
|
|
14
14
|
import * as i2 from "./services/testing-instrumentation.service";
|
|
15
15
|
import * as i3 from "@memberjunction/ng-ui-components";
|
|
16
|
-
import * as i4 from "@memberjunction/ng-
|
|
17
|
-
import * as i5 from "
|
|
18
|
-
import * as i6 from "./components/testing-
|
|
19
|
-
import * as i7 from "./components/testing-
|
|
20
|
-
import * as i8 from "./components/testing-
|
|
21
|
-
import * as i9 from "./components/testing-review.component";
|
|
16
|
+
import * as i4 from "@memberjunction/ng-shared-generic";
|
|
17
|
+
import * as i5 from "./components/testing-dashboard-tab.component";
|
|
18
|
+
import * as i6 from "./components/testing-runs.component";
|
|
19
|
+
import * as i7 from "./components/testing-analytics.component";
|
|
20
|
+
import * as i8 from "./components/testing-review.component";
|
|
22
21
|
function TestingDashboardComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
23
22
|
i0.ɵɵelement(0, "mj-stat-badge", 2);
|
|
24
23
|
} if (rf & 2) {
|
|
@@ -328,7 +327,7 @@ let TestingDashboardComponent = class TestingDashboardComponent extends BaseDash
|
|
|
328
327
|
i0.ɵɵconditional(!ctx.isLoading ? 10 : -1);
|
|
329
328
|
i0.ɵɵadvance();
|
|
330
329
|
i0.ɵɵconditional(ctx.testingDialogService.IsPanelOpen ? 11 : -1);
|
|
331
|
-
} }, dependencies: [i3.MJPageHeaderComponent, i3.MJPageLayoutComponent, i3.MJPageBodyComponent, i3.MJTabNavComponent, i3.MJStatBadgeComponent, i1.TestRunDialogComponent, i1.EvaluationModeToggleComponent,
|
|
330
|
+
} }, dependencies: [i3.MJPageHeaderComponent, i3.MJPageLayoutComponent, i3.MJPageBodyComponent, i3.MJTabNavComponent, i3.MJStatBadgeComponent, i1.TestRunDialogComponent, i1.EvaluationModeToggleComponent, i3.MjSlidePanelComponent, i4.LoadingComponent, i5.TestingDashboardTabComponent, i6.TestingRunsComponent, i7.TestingAnalyticsComponent, i8.TestingReviewComponent], styles: ["\n\n\n\n\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n background: var(--mj-bg-surface);\n backdrop-filter: blur(8px);\n}\n\n.loading-content[_ngcontent-%COMP%] {\n text-align: center;\n}\n\n.loading-spinner[_ngcontent-%COMP%] {\n display: inline-block;\n position: relative;\n width: 80px;\n height: 80px;\n}\n\n.spinner-ring[_ngcontent-%COMP%] {\n box-sizing: border-box;\n display: block;\n position: absolute;\n width: 64px;\n height: 64px;\n margin: 8px;\n border: 8px solid var(--mj-brand-primary);\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spinner-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;\n border-color: var(--mj-brand-primary) transparent transparent transparent;\n}\n\n.spinner-ring[_ngcontent-%COMP%]:nth-child(1) {\n animation-delay: -0.45s;\n}\n\n.spinner-ring[_ngcontent-%COMP%]:nth-child(2) {\n animation-delay: -0.3s;\n}\n\n.spinner-ring[_ngcontent-%COMP%]:nth-child(3) {\n animation-delay: -0.15s;\n}\n\n@keyframes _ngcontent-%COMP%_spinner-ring {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}\n\n.loading-text[_ngcontent-%COMP%] {\n margin-top: 20px;\n font-size: 16px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.dashboard-content[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n\n\n\n.active-runs-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 20px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n border-bottom: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, var(--mj-border-default));\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.active-runs-banner[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.active-runs-count[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n\n.active-runs-detail[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.active-runs-view-btn[_ngcontent-%COMP%] {\n padding: 4px 14px;\n border: 1px solid var(--mj-brand-primary);\n border-radius: 6px;\n background: transparent;\n color: var(--mj-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n white-space: nowrap;\n transition: background 0.2s;\n}\n\n.active-runs-view-btn[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, transparent);\n}\n\n\n\n@media (max-width: 768px) {\n .testing-dashboard-nav[_ngcontent-%COMP%] {\n padding: 0 8px;\n overflow-x: auto;\n }\n\n .nav-item[_ngcontent-%COMP%] {\n padding: 12px 16px;\n gap: 8px;\n }\n\n .nav-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 11px;\n }\n\n .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n\n .active-runs-banner[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n padding: 8px 12px;\n gap: 6px;\n }\n\n .active-runs-detail[_ngcontent-%COMP%] {\n width: 100%;\n order: 3;\n }\n}\n\n@media (max-width: 480px) {\n .nav-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n display: none;\n }\n\n .nav-item[_ngcontent-%COMP%] {\n padding: 12px 14px;\n }\n}"], changeDetection: 0 });
|
|
332
331
|
};
|
|
333
332
|
TestingDashboardComponent = __decorate([
|
|
334
333
|
RegisterClass(BaseDashboard, 'TestingDashboard')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing-dashboard.component.js","sourceRoot":"","sources":["../../src/Testing/testing-dashboard.component.ts","../../src/Testing/testing-dashboard.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, AfterViewInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { BaseDashboard } from '@memberjunction/ng-shared';\nimport { RegisterClass } from '@memberjunction/global';\nimport { Subject } from 'rxjs';\nimport { debounceTime, takeUntil } from 'rxjs/operators';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { TestingDialogService, TestingExecutionService, ActiveRun } from '@memberjunction/ng-testing';\nimport { TabConfig } from '@memberjunction/ng-ui-components';\nimport { TestingInstrumentationService } from './services/testing-instrumentation.service';\n\ninterface TestingDashboardState {\n activeTab: string;\n dashboardState: Record<string, unknown>;\n runsState: Record<string, unknown>;\n analyticsState: Record<string, unknown>;\n reviewState: Record<string, unknown>;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-testing-dashboard',\n templateUrl: './testing-dashboard.component.html',\n styleUrls: ['./testing-dashboard.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\n@RegisterClass(BaseDashboard, 'TestingDashboard')\nexport class TestingDashboardComponent extends BaseDashboard implements AfterViewInit, OnDestroy {\n\n public isLoading = false;\n public activeTab = 'dashboard';\n public selectedIndex = 0;\n\n // Active test runs from execution service\n public ActiveRuns: ActiveRun[] = [];\n\n // Component states\n public dashboardState: Record<string, unknown> | null = null;\n public runsState: Record<string, unknown> | null = null;\n public analyticsState: Record<string, unknown> | null = null;\n public reviewState: Record<string, unknown> | null = null;\n\n // Track visited tabs for lazy loading\n private visitedTabs = new Set<string>();\n\n // Navigation items\n public navigationItems: string[] = ['dashboard', 'runs', 'analytics', 'review'];\n\n public navigationConfig = [\n { text: 'Dashboard', icon: 'fa-solid fa-gauge-high', selected: false },\n { text: 'Runs', icon: 'fa-solid fa-play-circle', selected: false },\n { text: 'Analytics', icon: 'fa-solid fa-chart-bar', selected: false },\n { text: 'Review', icon: 'fa-solid fa-clipboard-check', selected: false }\n ];\n\n public get Tabs(): TabConfig[] {\n return [\n { key: 'dashboard', label: 'Dashboard', icon: 'fa-solid fa-gauge-high' },\n {\n key: 'runs',\n label: 'Runs',\n icon: 'fa-solid fa-play-circle',\n badge: this.ActiveRuns.length > 0 ? this.ActiveRuns.length : null,\n badgeVariant: this.ActiveRuns.length > 0 ? 'warning' : 'default'\n },\n { key: 'analytics', label: 'Analytics', icon: 'fa-solid fa-chart-bar' },\n { key: 'review', label: 'Review', icon: 'fa-solid fa-clipboard-check' }\n ];\n }\n\n private stateChangeSubject = new Subject<TestingDashboardState>();\n protected override destroy$ = new Subject<void>();\n\n constructor(\n private cdr: ChangeDetectorRef,\n public testingDialogService: TestingDialogService,\n private executionService: TestingExecutionService,\n private instrumentationService: TestingInstrumentationService\n ) {\n super();\n this.setupStateManagement();\n this.updateNavigationSelection();\n }\n\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n return 'Testing';\n }\n\n ngAfterViewInit(): void {\n this.visitedTabs.add(this.activeTab);\n this.updateNavigationSelection();\n this.emitStateChange();\n\n this.executionService.ActiveRuns$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(runs => {\n this.ActiveRuns = runs;\n this.cdr.markForCheck();\n });\n\n this.testingDialogService.PanelStateChanged$.pipe(\n takeUntil(this.destroy$)\n ).subscribe((isOpen) => {\n console.log('[TestingDashboard] PanelStateChanged$:', isOpen, 'IsPanelOpen:', this.testingDialogService.IsPanelOpen);\n this.cdr.detectChanges();\n });\n\n this.cdr.detectChanges();\n }\n\n ngOnDestroy(): void {\n super.ngOnDestroy();\n this.destroy$.next();\n this.destroy$.complete();\n this.stateChangeSubject.complete();\n }\n\n public onTabChange(tabId: string): void {\n this.activeTab = tabId;\n const index = this.navigationItems.indexOf(tabId);\n this.selectedIndex = index >= 0 ? index : 0;\n this.updateNavigationSelection();\n this.visitedTabs.add(tabId);\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n public hasVisited(tabId: string): boolean {\n return this.visitedTabs.has(tabId);\n }\n\n private setupStateManagement(): void {\n this.stateChangeSubject.pipe(\n debounceTime(50)\n ).subscribe(state => {\n this.UserStateChanged.emit(state);\n });\n }\n\n private emitStateChange(): void {\n const state: TestingDashboardState = {\n activeTab: this.activeTab,\n dashboardState: (this.dashboardState || {}) as Record<string, unknown>,\n runsState: (this.runsState || {}) as Record<string, unknown>,\n analyticsState: (this.analyticsState || {}) as Record<string, unknown>,\n reviewState: (this.reviewState || {}) as Record<string, unknown>\n };\n\n this.stateChangeSubject.next(state);\n }\n\n public onDashboardStateChange(state: Record<string, unknown>): void {\n this.dashboardState = state;\n this.emitStateChange();\n }\n\n public onRunsStateChange(state: Record<string, unknown>): void {\n this.runsState = state;\n this.emitStateChange();\n }\n\n public onAnalyticsStateChange(state: Record<string, unknown>): void {\n this.analyticsState = state;\n this.emitStateChange();\n }\n\n public onReviewStateChange(state: Record<string, unknown>): void {\n this.reviewState = state;\n this.emitStateChange();\n }\n\n public loadUserState(state: Partial<TestingDashboardState>): void {\n if (state.activeTab) {\n this.activeTab = state.activeTab;\n const index = this.navigationItems.indexOf(state.activeTab);\n this.selectedIndex = index >= 0 ? index : 0;\n this.visitedTabs.add(state.activeTab);\n this.updateNavigationSelection();\n }\n\n if (state.dashboardState) this.dashboardState = state.dashboardState;\n if (state.runsState) this.runsState = state.runsState;\n if (state.analyticsState) this.analyticsState = state.analyticsState;\n if (state.reviewState) this.reviewState = state.reviewState;\n\n this.cdr.markForCheck();\n }\n\n initDashboard(): void {\n try {\n this.isLoading = true;\n this.instrumentationService.Provider = this.ProviderToUse;\n } catch (error) {\n console.error('Error initializing Testing dashboard:', error);\n this.Error.emit(new Error('Failed to initialize Testing dashboard. Please try again.'));\n } finally {\n this.isLoading = false;\n }\n }\n\n loadData(): void {\n if (this.Config?.userState) {\n setTimeout(() => {\n if (this.Config?.userState) {\n this.loadUserState(this.Config.userState);\n }\n }, 0);\n }\n\n this.NotifyLoadComplete();\n }\n\n public getCurrentTabLabel(): string {\n const tabIndex = this.navigationItems.indexOf(this.activeTab);\n return tabIndex >= 0 ? this.navigationConfig[tabIndex].text : 'Testing Dashboard';\n }\n\n public OnPanelClosed(): void {\n this.testingDialogService.ClosePanel();\n this.cdr.markForCheck();\n }\n\n public OnViewActiveRun(run: ActiveRun): void {\n this.testingDialogService.OpenTestPanel(run.TestId);\n this.cdr.markForCheck();\n }\n\n public OnViewRunningTestFromTab(testId: string): void {\n this.testingDialogService.OpenTestPanel(testId);\n this.cdr.detectChanges();\n }\n\n private updateNavigationSelection(): void {\n this.navigationConfig.forEach((item, index) => {\n item.selected = this.navigationItems[index] === this.activeTab;\n });\n }\n}\n","<mj-page-layout>\n <mj-page-header\n Title=\"Testing\"\n Icon=\"fa-solid fa-vial-circle-check\"\n Subtitle=\"Evaluate AI agents, workflows, and system components\">\n <div meta>\n @if (ActiveRuns.length > 0) {\n <mj-stat-badge\n Icon=\"fa-solid fa-spinner fa-spin\"\n [Count]=\"ActiveRuns.length\"\n Label=\"running\"\n Variant=\"running\">\n </mj-stat-badge>\n }\n </div>\n <div actions>\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n </div>\n <div toolbar>\n <mj-tab-nav\n [Tabs]=\"Tabs\"\n [ActiveKey]=\"activeTab\"\n (TabChange)=\"onTabChange($any($event))\">\n </mj-tab-nav>\n </div>\n </mj-page-header>\n\n <mj-page-body [Flex]=\"true\">\n @if (isLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading Testing Dashboard...\" size=\"large\"></mj-loading>\n </div>\n }\n\n @if (!isLoading) {\n <!-- Active Runs Banner (in-body so it doesn't crowd the page-header) -->\n @if (ActiveRuns.length > 0) {\n <div class=\"active-runs-banner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span class=\"active-runs-count\">{{ ActiveRuns.length }} test{{ ActiveRuns.length === 1 ? '' : 's' }} running</span>\n <span class=\"active-runs-detail\">{{ ActiveRuns[0].TestName }} — {{ ActiveRuns[0].Progress }}%</span>\n <button class=\"active-runs-view-btn\" (click)=\"OnViewActiveRun(ActiveRuns[0])\">View</button>\n </div>\n }\n\n <div class=\"dashboard-content\">\n @if (activeTab === 'dashboard' && hasVisited('dashboard')) {\n <app-testing-dashboard-tab\n [initialState]=\"dashboardState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onDashboardStateChange($event)\"\n ></app-testing-dashboard-tab>\n }\n\n @if (activeTab === 'runs' && hasVisited('runs')) {\n <app-testing-runs\n [initialState]=\"runsState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onRunsStateChange($event)\"\n ></app-testing-runs>\n }\n\n @if (activeTab === 'analytics' && hasVisited('analytics')) {\n <app-testing-analytics\n [initialState]=\"analyticsState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onAnalyticsStateChange($event)\"\n ></app-testing-analytics>\n }\n\n @if (activeTab === 'review' && hasVisited('review')) {\n <app-testing-review\n [initialState]=\"reviewState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onReviewStateChange($event)\"\n ></app-testing-review>\n }\n </div>\n }\n </mj-page-body>\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</mj-page-layout>\n"]}
|
|
1
|
+
{"version":3,"file":"testing-dashboard.component.js","sourceRoot":"","sources":["../../src/Testing/testing-dashboard.component.ts","../../src/Testing/testing-dashboard.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, AfterViewInit, OnDestroy, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';\nimport { BaseDashboard } from '@memberjunction/ng-shared';\nimport { RegisterClass } from '@memberjunction/global';\nimport { Subject } from 'rxjs';\nimport { debounceTime, takeUntil } from 'rxjs/operators';\nimport { ResourceData } from '@memberjunction/core-entities';\nimport { TestingDialogService, TestingExecutionService, ActiveRun } from '@memberjunction/ng-testing';\nimport { TabConfig } from '@memberjunction/ng-ui-components';\nimport { TestingInstrumentationService } from './services/testing-instrumentation.service';\n\ninterface TestingDashboardState {\n activeTab: string;\n dashboardState: Record<string, unknown>;\n runsState: Record<string, unknown>;\n analyticsState: Record<string, unknown>;\n reviewState: Record<string, unknown>;\n}\n\n@Component({\n standalone: false,\n selector: 'mj-testing-dashboard',\n templateUrl: './testing-dashboard.component.html',\n styleUrls: ['./testing-dashboard.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\n@RegisterClass(BaseDashboard, 'TestingDashboard')\nexport class TestingDashboardComponent extends BaseDashboard implements AfterViewInit, OnDestroy {\n\n public isLoading = false;\n public activeTab = 'dashboard';\n public selectedIndex = 0;\n\n // Active test runs from execution service\n public ActiveRuns: ActiveRun[] = [];\n\n // Component states\n public dashboardState: Record<string, unknown> | null = null;\n public runsState: Record<string, unknown> | null = null;\n public analyticsState: Record<string, unknown> | null = null;\n public reviewState: Record<string, unknown> | null = null;\n\n // Track visited tabs for lazy loading\n private visitedTabs = new Set<string>();\n\n // Navigation items\n public navigationItems: string[] = ['dashboard', 'runs', 'analytics', 'review'];\n\n public navigationConfig = [\n { text: 'Dashboard', icon: 'fa-solid fa-gauge-high', selected: false },\n { text: 'Runs', icon: 'fa-solid fa-play-circle', selected: false },\n { text: 'Analytics', icon: 'fa-solid fa-chart-bar', selected: false },\n { text: 'Review', icon: 'fa-solid fa-clipboard-check', selected: false }\n ];\n\n public get Tabs(): TabConfig[] {\n return [\n { key: 'dashboard', label: 'Dashboard', icon: 'fa-solid fa-gauge-high' },\n {\n key: 'runs',\n label: 'Runs',\n icon: 'fa-solid fa-play-circle',\n badge: this.ActiveRuns.length > 0 ? this.ActiveRuns.length : null,\n badgeVariant: this.ActiveRuns.length > 0 ? 'warning' : 'default'\n },\n { key: 'analytics', label: 'Analytics', icon: 'fa-solid fa-chart-bar' },\n { key: 'review', label: 'Review', icon: 'fa-solid fa-clipboard-check' }\n ];\n }\n\n private stateChangeSubject = new Subject<TestingDashboardState>();\n protected override destroy$ = new Subject<void>();\n\n constructor(\n private cdr: ChangeDetectorRef,\n public testingDialogService: TestingDialogService,\n private executionService: TestingExecutionService,\n private instrumentationService: TestingInstrumentationService\n ) {\n super();\n this.setupStateManagement();\n this.updateNavigationSelection();\n }\n\n async GetResourceDisplayName(data: ResourceData): Promise<string> {\n return 'Testing';\n }\n\n ngAfterViewInit(): void {\n this.visitedTabs.add(this.activeTab);\n this.updateNavigationSelection();\n this.emitStateChange();\n\n this.executionService.ActiveRuns$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(runs => {\n this.ActiveRuns = runs;\n this.cdr.markForCheck();\n });\n\n this.testingDialogService.PanelStateChanged$.pipe(\n takeUntil(this.destroy$)\n ).subscribe((isOpen) => {\n console.log('[TestingDashboard] PanelStateChanged$:', isOpen, 'IsPanelOpen:', this.testingDialogService.IsPanelOpen);\n this.cdr.detectChanges();\n });\n\n this.cdr.detectChanges();\n }\n\n ngOnDestroy(): void {\n super.ngOnDestroy();\n this.destroy$.next();\n this.destroy$.complete();\n this.stateChangeSubject.complete();\n }\n\n public onTabChange(tabId: string): void {\n this.activeTab = tabId;\n const index = this.navigationItems.indexOf(tabId);\n this.selectedIndex = index >= 0 ? index : 0;\n this.updateNavigationSelection();\n this.visitedTabs.add(tabId);\n this.emitStateChange();\n this.cdr.markForCheck();\n }\n\n public hasVisited(tabId: string): boolean {\n return this.visitedTabs.has(tabId);\n }\n\n private setupStateManagement(): void {\n this.stateChangeSubject.pipe(\n debounceTime(50)\n ).subscribe(state => {\n this.UserStateChanged.emit(state);\n });\n }\n\n private emitStateChange(): void {\n const state: TestingDashboardState = {\n activeTab: this.activeTab,\n dashboardState: (this.dashboardState || {}) as Record<string, unknown>,\n runsState: (this.runsState || {}) as Record<string, unknown>,\n analyticsState: (this.analyticsState || {}) as Record<string, unknown>,\n reviewState: (this.reviewState || {}) as Record<string, unknown>\n };\n\n this.stateChangeSubject.next(state);\n }\n\n public onDashboardStateChange(state: Record<string, unknown>): void {\n this.dashboardState = state;\n this.emitStateChange();\n }\n\n public onRunsStateChange(state: Record<string, unknown>): void {\n this.runsState = state;\n this.emitStateChange();\n }\n\n public onAnalyticsStateChange(state: Record<string, unknown>): void {\n this.analyticsState = state;\n this.emitStateChange();\n }\n\n public onReviewStateChange(state: Record<string, unknown>): void {\n this.reviewState = state;\n this.emitStateChange();\n }\n\n public loadUserState(state: Partial<TestingDashboardState>): void {\n if (state.activeTab) {\n this.activeTab = state.activeTab;\n const index = this.navigationItems.indexOf(state.activeTab);\n this.selectedIndex = index >= 0 ? index : 0;\n this.visitedTabs.add(state.activeTab);\n this.updateNavigationSelection();\n }\n\n if (state.dashboardState) this.dashboardState = state.dashboardState;\n if (state.runsState) this.runsState = state.runsState;\n if (state.analyticsState) this.analyticsState = state.analyticsState;\n if (state.reviewState) this.reviewState = state.reviewState;\n\n this.cdr.markForCheck();\n }\n\n initDashboard(): void {\n try {\n this.isLoading = true;\n this.instrumentationService.Provider = this.ProviderToUse;\n } catch (error) {\n console.error('Error initializing Testing dashboard:', error);\n this.Error.emit(new Error('Failed to initialize Testing dashboard. Please try again.'));\n } finally {\n this.isLoading = false;\n }\n }\n\n loadData(): void {\n if (this.Config?.userState) {\n setTimeout(() => {\n if (this.Config?.userState) {\n this.loadUserState(this.Config.userState);\n }\n }, 0);\n }\n\n this.NotifyLoadComplete();\n }\n\n public getCurrentTabLabel(): string {\n const tabIndex = this.navigationItems.indexOf(this.activeTab);\n return tabIndex >= 0 ? this.navigationConfig[tabIndex].text : 'Testing Dashboard';\n }\n\n public OnPanelClosed(): void {\n this.testingDialogService.ClosePanel();\n this.cdr.markForCheck();\n }\n\n public OnViewActiveRun(run: ActiveRun): void {\n this.testingDialogService.OpenTestPanel(run.TestId);\n this.cdr.markForCheck();\n }\n\n public OnViewRunningTestFromTab(testId: string): void {\n this.testingDialogService.OpenTestPanel(testId);\n this.cdr.detectChanges();\n }\n\n private updateNavigationSelection(): void {\n this.navigationConfig.forEach((item, index) => {\n item.selected = this.navigationItems[index] === this.activeTab;\n });\n }\n}\n","<mj-page-layout>\n <mj-page-header\n Title=\"Testing\"\n Icon=\"fa-solid fa-vial-circle-check\"\n Subtitle=\"Evaluate AI agents, workflows, and system components\">\n <div meta>\n @if (ActiveRuns.length > 0) {\n <mj-stat-badge\n Icon=\"fa-solid fa-spinner fa-spin\"\n [Count]=\"ActiveRuns.length\"\n Label=\"running\"\n Variant=\"running\">\n </mj-stat-badge>\n }\n </div>\n <div actions>\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n </div>\n <div toolbar>\n <mj-tab-nav\n [Tabs]=\"Tabs\"\n [ActiveKey]=\"activeTab\"\n (TabChange)=\"onTabChange($any($event))\">\n </mj-tab-nav>\n </div>\n </mj-page-header>\n\n <mj-page-body [Flex]=\"true\">\n @if (isLoading) {\n <div class=\"loading-container\">\n <mj-loading text=\"Loading Testing Dashboard...\" size=\"large\"></mj-loading>\n </div>\n }\n\n @if (!isLoading) {\n <!-- Active Runs Banner (in-body so it doesn't crowd the page-header) -->\n @if (ActiveRuns.length > 0) {\n <div class=\"active-runs-banner\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span class=\"active-runs-count\">{{ ActiveRuns.length }} test{{ ActiveRuns.length === 1 ? '' : 's' }} running</span>\n <span class=\"active-runs-detail\">{{ ActiveRuns[0].TestName }} — {{ ActiveRuns[0].Progress }}%</span>\n <button class=\"active-runs-view-btn\" (click)=\"OnViewActiveRun(ActiveRuns[0])\">View</button>\n </div>\n }\n\n <div class=\"dashboard-content\">\n @if (activeTab === 'dashboard' && hasVisited('dashboard')) {\n <app-testing-dashboard-tab\n [initialState]=\"dashboardState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onDashboardStateChange($event)\"\n ></app-testing-dashboard-tab>\n }\n\n @if (activeTab === 'runs' && hasVisited('runs')) {\n <app-testing-runs\n [initialState]=\"runsState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onRunsStateChange($event)\"\n ></app-testing-runs>\n }\n\n @if (activeTab === 'analytics' && hasVisited('analytics')) {\n <app-testing-analytics\n [initialState]=\"analyticsState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onAnalyticsStateChange($event)\"\n ></app-testing-analytics>\n }\n\n @if (activeTab === 'review' && hasVisited('review')) {\n <app-testing-review\n [initialState]=\"reviewState\"\n [HideToolbar]=\"true\"\n (stateChange)=\"onReviewStateChange($event)\"\n ></app-testing-review>\n }\n </div>\n }\n </mj-page-body>\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</mj-page-layout>\n"]}
|