@memberjunction/ng-dashboards 5.11.0 → 5.13.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/dist/AI/components/agents/agent-configuration.component.d.ts +34 -2
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +586 -223
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +2 -2
- package/dist/AI/components/agents/agent-filter-panel.component.d.ts +8 -0
- package/dist/AI/components/agents/agent-filter-panel.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-filter-panel.component.js +85 -52
- package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -1
- package/dist/AI/components/charts/performance-heatmap.component.d.ts +1 -0
- package/dist/AI/components/charts/performance-heatmap.component.d.ts.map +1 -1
- package/dist/AI/components/charts/performance-heatmap.component.js +27 -5
- package/dist/AI/components/charts/performance-heatmap.component.js.map +1 -1
- package/dist/AI/components/charts/time-series-chart.component.d.ts +5 -0
- package/dist/AI/components/charts/time-series-chart.component.d.ts.map +1 -1
- package/dist/AI/components/charts/time-series-chart.component.js +23 -8
- package/dist/AI/components/charts/time-series-chart.component.js.map +1 -1
- package/dist/AI/components/execution-monitoring.component.js +2 -2
- package/dist/AI/components/execution-monitoring.component.js.map +1 -1
- package/dist/AI/components/models/model-management.component.js +2 -2
- package/dist/AI/components/prompts/model-prompt-priority-matrix.component.js +2 -2
- package/dist/AI/components/prompts/prompt-filter-panel.component.js +2 -2
- package/dist/AI/components/prompts/prompt-management.component.js +3 -3
- package/dist/AI/components/prompts/prompt-management.component.js.map +1 -1
- package/dist/AI/components/prompts/prompt-version-control.component.js +2 -2
- package/dist/AI/components/requests/agent-requests-resource.component.d.ts +83 -0
- package/dist/AI/components/requests/agent-requests-resource.component.d.ts.map +1 -0
- package/dist/AI/components/requests/agent-requests-resource.component.js +547 -0
- package/dist/AI/components/requests/agent-requests-resource.component.js.map +1 -0
- package/dist/AI/components/system/system-config-filter-panel.component.js +2 -2
- package/dist/AI/components/system/system-configuration.component.js +2 -2
- package/dist/AI/components/widgets/kpi-card.component.js +7 -7
- package/dist/AI/components/widgets/kpi-card.component.js.map +1 -1
- package/dist/AI/components/widgets/live-execution-widget.component.d.ts.map +1 -1
- package/dist/AI/components/widgets/live-execution-widget.component.js +6 -6
- package/dist/AI/components/widgets/live-execution-widget.component.js.map +1 -1
- package/dist/AI/index.d.ts +1 -0
- package/dist/AI/index.d.ts.map +1 -1
- package/dist/AI/index.js +2 -0
- package/dist/AI/index.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +3 -3
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-create-dialog.component.js +3 -3
- package/dist/APIKeys/api-key-create-dialog.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-list.component.js +3 -3
- package/dist/APIKeys/api-key-list.component.js.map +1 -1
- package/dist/APIKeys/api-keys-resource.component.js +1 -1
- package/dist/APIKeys/api-keys-resource.component.js.map +1 -1
- package/dist/APIKeys/api-scopes-panel.component.js +2 -2
- package/dist/APIKeys/api-usage-panel.component.js +2 -2
- package/dist/Actions/components/actions-overview.component.js +2 -2
- package/dist/Actions/components/execution-monitoring.component.js +2 -2
- package/dist/Actions/components/explorer/action-breadcrumb.component.js +2 -2
- package/dist/Actions/components/explorer/action-card.component.js +2 -2
- package/dist/Actions/components/explorer/action-explorer.component.js +2 -2
- package/dist/Actions/components/explorer/action-list-item.component.js +2 -2
- package/dist/Actions/components/explorer/action-toolbar.component.js +2 -2
- package/dist/Actions/components/explorer/action-tree-panel.component.js +2 -2
- package/dist/Actions/components/explorer/new-action-panel.component.js +2 -2
- package/dist/Actions/components/explorer/new-action-panel.component.js.map +1 -1
- package/dist/Actions/components/explorer/new-category-panel.component.js +2 -2
- package/dist/Actions/components/explorer/new-category-panel.component.js.map +1 -1
- package/dist/Communication/communication-dashboard.component.js +2 -2
- package/dist/Communication/communication-logs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-logs-resource.component.js +3 -3
- package/dist/Communication/communication-logs-resource.component.js.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-monitor-resource.component.js +5 -5
- package/dist/Communication/communication-monitor-resource.component.js.map +1 -1
- package/dist/Communication/communication-providers-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-providers-resource.component.js +3 -3
- package/dist/Communication/communication-providers-resource.component.js.map +1 -1
- package/dist/Communication/communication-runs-resource.component.d.ts.map +1 -1
- package/dist/Communication/communication-runs-resource.component.js +3 -3
- package/dist/Communication/communication-runs-resource.component.js.map +1 -1
- package/dist/Communication/communication-templates-resource.component.js +2 -2
- package/dist/Communication/communication-templates-resource.component.js.map +1 -1
- package/dist/ComponentStudio/component-studio-dashboard.component.js +2 -2
- package/dist/ComponentStudio/components/ai-assistant/ai-assistant-panel.component.js +2 -2
- package/dist/ComponentStudio/components/artifact-load-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/artifact-selection-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/browser/component-browser.component.js +2 -2
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js +2 -2
- package/dist/ComponentStudio/components/editors/code-editor-panel.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js +2 -2
- package/dist/ComponentStudio/components/editors/data-requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js +2 -2
- package/dist/ComponentStudio/components/editors/requirements-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/editors/spec-editor.component.js +2 -2
- package/dist/ComponentStudio/components/editors/spec-editor.component.js.map +1 -1
- package/dist/ComponentStudio/components/new-component-dialog/new-component-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/save-version-dialog/save-version-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/text-import-dialog.component.js +2 -2
- package/dist/ComponentStudio/components/text-import-dialog.component.js.map +1 -1
- package/dist/ComponentStudio/components/workspace/component-preview.component.js +2 -2
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.js +2 -2
- package/dist/ComponentStudio/components/workspace/editor-tabs.component.js.map +1 -1
- package/dist/Credentials/components/credentials-audit-resource.component.js +9 -9
- package/dist/Credentials/components/credentials-audit-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-categories-resource.component.js +11 -3
- package/dist/Credentials/components/credentials-categories-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +2 -2
- package/dist/Credentials/components/credentials-overview-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-overview-resource.component.js +12 -11
- package/dist/Credentials/components/credentials-overview-resource.component.js.map +1 -1
- package/dist/Credentials/components/credentials-types-resource.component.js +9 -9
- package/dist/Credentials/components/credentials-types-resource.component.js.map +1 -1
- package/dist/Credentials/credentials-dashboard.component.js +2 -2
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +2 -2
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +2 -2
- package/dist/DataExplorer/components/filter-dialog/filter-dialog.component.js +2 -2
- package/dist/DataExplorer/components/navigation-panel/navigation-panel.component.js +2 -2
- package/dist/DataExplorer/components/view-selector/view-selector.component.js +2 -2
- package/dist/DataExplorer/data-explorer-dashboard.component.js +4 -4
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Home/home-dashboard.component.js +2 -2
- package/dist/Integration/components/activity/activity.component.d.ts +1 -1
- package/dist/Integration/components/activity/activity.component.d.ts.map +1 -1
- package/dist/Integration/components/activity/activity.component.js +5 -5
- package/dist/Integration/components/activity/activity.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +31 -2
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +753 -412
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js +3 -3
- package/dist/Integration/components/mapping-workspace/mapping-workspace.component.js.map +1 -1
- package/dist/Integration/components/overview/overview.component.d.ts +0 -1
- package/dist/Integration/components/overview/overview.component.d.ts.map +1 -1
- package/dist/Integration/components/overview/overview.component.js +3 -6
- package/dist/Integration/components/overview/overview.component.js.map +1 -1
- package/dist/Integration/components/pipelines/pipelines.component.js +3 -3
- package/dist/Integration/components/pipelines/pipelines.component.js.map +1 -1
- package/dist/Integration/components/schedules/schedules.component.d.ts +20 -0
- package/dist/Integration/components/schedules/schedules.component.d.ts.map +1 -1
- package/dist/Integration/components/schedules/schedules.component.js +97 -5
- package/dist/Integration/components/schedules/schedules.component.js.map +1 -1
- package/dist/Integration/components/visual-editor/visual-editor.component.js +2 -2
- package/dist/Integration/components/widgets/integration-card.component.d.ts.map +1 -1
- package/dist/Integration/components/widgets/integration-card.component.js +5 -1
- package/dist/Integration/components/widgets/integration-card.component.js.map +1 -1
- package/dist/Integration/components/widgets/run-history-panel.component.js +2 -2
- package/dist/Integration/components/widgets/run-history-panel.component.js.map +1 -1
- package/dist/Integration/integration.module.d.ts +2 -1
- package/dist/Integration/integration.module.d.ts.map +1 -1
- package/dist/Integration/integration.module.js +7 -3
- package/dist/Integration/integration.module.js.map +1 -1
- package/dist/Integration/services/integration-data.service.d.ts +27 -2
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
- package/dist/Integration/services/integration-data.service.js +107 -4
- package/dist/Integration/services/integration-data.service.js.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-browse-resource.component.js +25 -24
- package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-categories-resource.component.js +2 -2
- package/dist/Lists/components/lists-categories-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-my-lists-resource.component.js +26 -25
- package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +2 -2
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/Lists/components/venn-diagram/venn-diagram.component.js +3 -3
- package/dist/Lists/components/venn-diagram/venn-diagram.component.js.map +1 -1
- package/dist/MCP/components/mcp-connection-dialog.component.js +2 -2
- package/dist/MCP/components/mcp-log-detail-panel.component.js +2 -2
- package/dist/MCP/components/mcp-log-detail-panel.component.js.map +1 -1
- package/dist/MCP/components/mcp-server-dialog.component.js +2 -2
- package/dist/MCP/components/mcp-test-tool-dialog.component.js +2 -2
- package/dist/MCP/components/mcp-test-tool-dialog.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +2 -2
- package/dist/MCP/mcp-filter-panel.component.js +2 -2
- package/dist/QueryBrowser/query-browser-resource.component.js +7 -7
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/Scheduling/components/index.d.ts +0 -1
- package/dist/Scheduling/components/index.d.ts.map +1 -1
- package/dist/Scheduling/components/index.js +0 -1
- package/dist/Scheduling/components/index.js.map +1 -1
- package/dist/Scheduling/components/scheduling-activity.component.js +2 -2
- package/dist/Scheduling/components/scheduling-jobs.component.d.ts +6 -9
- package/dist/Scheduling/components/scheduling-jobs.component.d.ts.map +1 -1
- package/dist/Scheduling/components/scheduling-jobs.component.js +118 -110
- package/dist/Scheduling/components/scheduling-jobs.component.js.map +1 -1
- package/dist/Scheduling/components/scheduling-overview.component.js +3 -3
- package/dist/Scheduling/components/scheduling-overview.component.js.map +1 -1
- package/dist/Scheduling/scheduling-dashboard.component.js +2 -2
- package/dist/SystemDiagnostics/system-diagnostics.component.js +4 -4
- package/dist/SystemDiagnostics/system-diagnostics.component.js.map +1 -1
- package/dist/Testing/components/testing-analytics.component.js +2 -2
- package/dist/Testing/components/testing-analytics.component.js.map +1 -1
- package/dist/Testing/components/testing-dashboard-tab.component.js +4 -4
- package/dist/Testing/components/testing-dashboard-tab.component.js.map +1 -1
- package/dist/Testing/components/testing-explorer.component.js +2 -2
- package/dist/Testing/components/testing-explorer.component.js.map +1 -1
- package/dist/Testing/components/testing-review.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-review.component.js +5 -5
- package/dist/Testing/components/testing-review.component.js.map +1 -1
- package/dist/Testing/components/testing-runs.component.js +2 -2
- package/dist/Testing/components/testing-runs.component.js.map +1 -1
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.js +2 -2
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.js.map +1 -1
- package/dist/Testing/components/widgets/suite-tree.component.js +4 -4
- package/dist/Testing/components/widgets/suite-tree.component.js.map +1 -1
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js +2 -2
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -1
- package/dist/Testing/testing-dashboard.component.js +2 -2
- package/dist/VersionHistory/components/diff-resource.component.js +2 -2
- package/dist/VersionHistory/components/graph-resource.component.js +2 -2
- package/dist/VersionHistory/components/labels-resource.component.js +3 -3
- package/dist/VersionHistory/components/labels-resource.component.js.map +1 -1
- package/dist/VersionHistory/components/restore-resource.component.js +3 -3
- package/dist/VersionHistory/components/restore-resource.component.js.map +1 -1
- package/dist/__tests__/integration-data-service.test.js +1 -0
- package/dist/__tests__/integration-data-service.test.js.map +1 -1
- package/dist/module.d.ts +52 -49
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +25 -6
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -1
- package/dist/public-api.js.map +1 -1
- package/package.json +42 -40
- package/dist/Scheduling/components/job-slideout.component.d.ts +0 -45
- package/dist/Scheduling/components/job-slideout.component.d.ts.map +0 -1
- package/dist/Scheduling/components/job-slideout.component.js +0 -459
- package/dist/Scheduling/components/job-slideout.component.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing-dashboard-tab.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-dashboard-tab.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, OnInit, OnDestroy, Input, Output, EventEmitter,\n ChangeDetectorRef, ChangeDetectionStrategy\n} from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\nimport { takeUntil, map } from 'rxjs/operators';\nimport { CompositeKey } from '@memberjunction/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport {\n TestingInstrumentationService,\n TestingDashboardKPIs,\n TestRunSummary,\n SuiteHierarchyNode\n} from '../services/testing-instrumentation.service';\nimport { KPICardData } from '../../AI/components/widgets/kpi-card.component';\nimport { TestEngineBase } from '@memberjunction/testing-engine-base';\n\n/** Status type union matching TestRunSummary */\ntype TestRunStatus = 'Passed' | 'Failed' | 'Skipped' | 'Error' | 'Running' | 'Timeout';\n\n/** Alert item for the alerts section */\ninterface TestAlert {\n id: string;\n testName: string;\n reason: 'regression' | 'low-score';\n score: number;\n status: TestRunStatus;\n runDateTime: Date;\n}\n\n@Component({\n standalone: false,\n selector: 'app-testing-dashboard-tab',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <!-- Full-page loading state -->\n @if (IsLoading) {\n <div class=\"full-page-loading\">\n <mj-loading text=\"Loading Testing Dashboard...\"></mj-loading>\n </div>\n } @else {\n <div class=\"dashboard-container\">\n\n <!-- Page Header -->\n <div class=\"page-header\">\n <h2 class=\"page-title\">\n <i class=\"fa-solid fa-gauge-high\"></i>\n Testing Dashboard\n </h2>\n <button class=\"refresh-btn\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fa-solid fa-refresh\" [class.spinning]=\"IsLoading\"></i>\n Refresh\n </button>\n </div>\n\n <!-- KPI Row -->\n <div class=\"kpi-row\">\n @for (kpi of KpiCards; track kpi.title) {\n <app-kpi-card [data]=\"kpi\"></app-kpi-card>\n }\n </div>\n\n <!-- Live Activity Section -->\n <div class=\"live-activity-card\">\n <div class=\"section-header\">\n @if (RunningTests.length > 0) {\n <span class=\"live-dot\"></span>\n <h3>{{ RunningTests.length }} test{{ RunningTests.length === 1 ? '' : 's' }} running</h3>\n } @else {\n <i class=\"fa-solid fa-circle-pause live-idle-icon\"></i>\n <h3>No tests currently running</h3>\n }\n </div>\n @if (RunningTests.length > 0) {\n <div class=\"running-tests-list\">\n @for (run of RunningTests; track run.id) {\n <div class=\"running-test-item\">\n <div class=\"running-test-name\">{{ run.testName }}</div>\n <div class=\"running-test-meta\">\n <span class=\"running-elapsed\">{{ FormatDuration(run.duration) }}</span>\n <span class=\"running-progress-bar\">\n <span class=\"running-progress-fill\"></span>\n </span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Lower Content: 2-column layout -->\n <div class=\"lower-grid\">\n\n <!-- Left Column: Recent Runs -->\n <div class=\"card recent-runs-card\">\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-history\"></i> Recent Runs</h3>\n </div>\n <div class=\"runs-list\">\n @for (run of RecentRuns; track run.id) {\n <div class=\"run-row\" (click)=\"OnOpenRun(run)\">\n <div class=\"run-row-left\">\n <span class=\"run-row-name\">{{ run.testName }}</span>\n <span class=\"run-row-time\">{{ FormatTimestamp(run.runDateTime) }}</span>\n </div>\n <div class=\"run-row-right\">\n <app-test-status-badge [status]=\"run.status\"></app-test-status-badge>\n <app-score-indicator [score]=\"run.score\" [showBar]=\"true\"></app-score-indicator>\n <span class=\"run-row-duration\">{{ FormatDuration(run.duration) }}</span>\n <app-cost-display [cost]=\"run.cost\"></app-cost-display>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">No completed test runs found</div>\n }\n </div>\n </div>\n\n <!-- Right Column: Suite Health + Alerts -->\n <div class=\"right-column\">\n\n <!-- Suite Health -->\n <div class=\"card suite-health-card\">\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-heartbeat\"></i> Suite Health</h3>\n </div>\n <div class=\"suite-health-list\">\n @for (suite of SortedSuites; track suite.id) {\n <div class=\"suite-health-row\">\n <div class=\"suite-health-info\">\n <span class=\"suite-health-name\">{{ suite.name }}</span>\n <span class=\"suite-health-count\">{{ suite.testCount }} tests</span>\n </div>\n <div class=\"suite-health-bar-wrapper\">\n <div class=\"suite-health-bar\">\n <div\n class=\"suite-health-bar-fill\"\n [style.width.%]=\"suite.passRate\"\n [style.background]=\"GetPassRateColor(suite.passRate)\">\n </div>\n </div>\n <span class=\"suite-health-pct\" [style.color]=\"GetPassRateColor(suite.passRate)\">\n {{ suite.passRate | number:'1.0-0' }}%\n </span>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">No test suites found</div>\n }\n </div>\n </div>\n\n <!-- Alerts -->\n <div class=\"card alerts-card\">\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-triangle-exclamation\"></i> Alerts</h3>\n </div>\n <div class=\"alerts-list\">\n @for (alert of Alerts; track alert.id) {\n <div class=\"alert-row\" (click)=\"OnOpenAlert(alert)\">\n <i class=\"fa-solid\"\n [class.fa-arrow-trend-down]=\"alert.reason === 'regression'\"\n [class.fa-circle-exclamation]=\"alert.reason === 'low-score'\"\n [style.color]=\"alert.reason === 'regression' ? '#ef4444' : '#f59e0b'\">\n </i>\n <div class=\"alert-info\">\n <span class=\"alert-name\">{{ alert.testName }}</span>\n <span class=\"alert-reason\">\n @if (alert.reason === 'regression') {\n Regression detected\n } @else {\n Score below 0.5 ({{ alert.score | number:'1.2-2' }})\n }\n </span>\n </div>\n <app-test-status-badge [status]=\"alert.status\"></app-test-status-badge>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-check-circle\" style=\"color: #22c55e; margin-right: 6px;\"></i>\n No alerts - all tests healthy\n </div>\n }\n </div>\n </div>\n\n </div>\n </div>\n </div>\n }\n `,\n styles: [`\n /* ===== Layout ===== */\n .full-page-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: #f8f9fa;\n }\n\n .dashboard-container {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n /* ===== Page Header ===== */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n\n .page-title {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: #1e293b;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .page-title i { color: #6366f1; }\n\n .refresh-btn {\n background: #6366f1;\n color: white;\n border: none;\n padding: 8px 18px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.2s ease;\n }\n\n .refresh-btn:hover:not(:disabled) { background: #4f46e5; }\n .refresh-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n .refresh-btn i.spinning { animation: spin 1s linear infinite; }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n /* ===== KPI Row ===== */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n /* ===== Card Base ===== */\n .card {\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n overflow: hidden;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-bottom: 1px solid #f1f5f9;\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #334155;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header h3 i { color: #6366f1; font-size: 13px; }\n\n /* ===== Live Activity ===== */\n .live-activity-card {\n background: white;\n border-radius: 12px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.08);\n margin-bottom: 24px;\n overflow: hidden;\n }\n\n .live-dot {\n width: 10px;\n height: 10px;\n background: #22c55e;\n border-radius: 50%;\n display: inline-block;\n animation: pulse-dot 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse-dot {\n 0%, 100% { box-shadow: 0 0 0 0 rgba(34,197,94,0.5); }\n 50% { box-shadow: 0 0 0 6px rgba(34,197,94,0); }\n }\n\n .live-idle-icon { color: #9ca3af; font-size: 14px; }\n\n .running-tests-list {\n padding: 0 20px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .running-test-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n background: #f0fdf4;\n border-radius: 8px;\n border: 1px solid #bbf7d0;\n }\n\n .running-test-name {\n font-size: 13px;\n font-weight: 500;\n color: #166534;\n }\n\n .running-test-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .running-elapsed {\n font-size: 12px;\n color: #15803d;\n font-weight: 500;\n }\n\n .running-progress-bar {\n width: 60px;\n height: 4px;\n background: #dcfce7;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .running-progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: linear-gradient(90deg, #22c55e, #86efac);\n animation: progress-slide 1.2s ease-in-out infinite;\n border-radius: 2px;\n }\n\n @keyframes progress-slide {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(100%); }\n }\n\n /* ===== Lower Grid ===== */\n .lower-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n }\n\n .right-column {\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n /* ===== Recent Runs ===== */\n .runs-list {\n max-height: 520px;\n overflow-y: auto;\n }\n\n .run-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 20px;\n border-bottom: 1px solid #f8fafc;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .run-row:hover { background: #f8fafc; }\n\n .run-row-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .run-row-name {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .run-row-time {\n font-size: 11px;\n color: #94a3b8;\n }\n\n .run-row-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .run-row-duration {\n font-size: 11px;\n color: #64748b;\n min-width: 44px;\n text-align: right;\n }\n\n /* ===== Suite Health ===== */\n .suite-health-list {\n padding: 8px 0;\n max-height: 260px;\n overflow-y: auto;\n }\n\n .suite-health-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 20px;\n }\n\n .suite-health-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .suite-health-name {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .suite-health-count {\n font-size: 11px;\n color: #94a3b8;\n }\n\n .suite-health-bar-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n\n .suite-health-bar {\n width: 80px;\n height: 6px;\n background: #f1f5f9;\n border-radius: 3px;\n overflow: hidden;\n }\n\n .suite-health-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .suite-health-pct {\n font-size: 12px;\n font-weight: 600;\n min-width: 36px;\n text-align: right;\n }\n\n /* ===== Alerts ===== */\n .alerts-list {\n max-height: 220px;\n overflow-y: auto;\n }\n\n .alert-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 20px;\n border-bottom: 1px solid #f8fafc;\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .alert-row:hover { background: #fef2f2; }\n\n .alert-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .alert-name {\n font-size: 13px;\n font-weight: 500;\n color: #1e293b;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .alert-reason {\n font-size: 11px;\n color: #94a3b8;\n }\n\n /* ===== Empty State ===== */\n .empty-state {\n padding: 32px 20px;\n text-align: center;\n color: #94a3b8;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n /* ===== Responsive ===== */\n @media (max-width: 900px) {\n .lower-grid {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 640px) {\n .dashboard-container { padding: 16px; }\n .kpi-row { grid-template-columns: 1fr; }\n }\n `]\n})\nexport class TestingDashboardTabComponent implements OnInit, OnDestroy {\n\n @Input() initialState: Record<string, unknown> | null = null;\n @Output() stateChange = new EventEmitter<Record<string, unknown>>();\n\n private destroy$ = new Subject<void>();\n\n /** Resolved data for the template */\n IsLoading = false;\n KpiCards: KPICardData[] = [];\n RunningTests: TestRunSummary[] = [];\n RecentRuns: TestRunSummary[] = [];\n SortedSuites: SuiteHierarchyNode[] = [];\n Alerts: TestAlert[] = [];\n\n constructor(\n private instrumentationService: TestingInstrumentationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n async ngOnInit(): Promise<void> {\n await TestEngineBase.Instance.Config(false);\n this.subscribeToStreams();\n this.instrumentationService.refresh();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ---------------------------------------------------------------------------\n // Public methods (PascalCase)\n // ---------------------------------------------------------------------------\n\n OnRefresh(): void {\n this.instrumentationService.refresh();\n }\n\n OnOpenRun(run: TestRunSummary): void {\n SharedService.Instance.OpenEntityRecord(\n 'MJ: Test Runs',\n CompositeKey.FromID(run.id)\n );\n }\n\n OnOpenAlert(alert: TestAlert): void {\n SharedService.Instance.OpenEntityRecord(\n 'MJ: Test Runs',\n CompositeKey.FromID(alert.id)\n );\n }\n\n FormatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const totalSeconds = Math.floor(ms / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n }\n\n FormatTimestamp(date: Date): string {\n if (!date) return '';\n const d = date instanceof Date ? date : new Date(date);\n const now = new Date();\n const diffMs = now.getTime() - d.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n if (diffMins < 1) return 'Just now';\n if (diffMins < 60) return `${diffMins}m ago`;\n const diffHours = Math.floor(diffMins / 60);\n if (diffHours < 24) return `${diffHours}h ago`;\n const diffDays = Math.floor(diffHours / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n return d.toLocaleDateString();\n }\n\n FormatCost(cost: number): string {\n return `$${cost.toFixed(4)}`;\n }\n\n GetPassRateColor(rate: number): string {\n if (rate >= 90) return '#22c55e';\n if (rate >= 70) return '#f59e0b';\n return '#ef4444';\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers (camelCase)\n // ---------------------------------------------------------------------------\n\n private subscribeToStreams(): void {\n this.subscribeLoading();\n this.subscribeKpis();\n this.subscribeTestRuns();\n this.subscribeSuites();\n }\n\n private subscribeLoading(): void {\n this.instrumentationService.isLoading$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(loading => {\n this.IsLoading = loading;\n this.cdr.markForCheck();\n });\n }\n\n private subscribeKpis(): void {\n this.instrumentationService.kpis$.pipe(\n takeUntil(this.destroy$),\n map(kpis => this.buildKpiCards(kpis))\n ).subscribe(cards => {\n this.KpiCards = cards;\n this.cdr.markForCheck();\n });\n }\n\n private subscribeTestRuns(): void {\n this.instrumentationService.testRuns$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(runs => {\n this.RunningTests = runs.filter(r => r.status === 'Running');\n this.RecentRuns = runs\n .filter(r => r.status !== 'Running')\n .slice(0, 15);\n this.Alerts = this.buildAlerts(runs);\n this.cdr.markForCheck();\n });\n }\n\n private subscribeSuites(): void {\n this.instrumentationService.suiteHierarchy$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(suites => {\n this.SortedSuites = this.flattenAndSort(suites);\n this.cdr.markForCheck();\n });\n }\n\n private buildKpiCards(kpis: TestingDashboardKPIs): KPICardData[] {\n const trendDir = kpis.passRateTrend > 0\n ? 'up' as const\n : kpis.passRateTrend < 0\n ? 'down' as const\n : 'stable' as const;\n\n return [\n {\n title: 'Active Tests',\n value: kpis.totalTestsActive,\n icon: 'fa-vial',\n color: 'primary',\n subtitle: `${kpis.totalTestRuns} runs this period`\n },\n {\n title: 'Pass Rate',\n value: `${kpis.passRateThisMonth.toFixed(1)}%`,\n icon: 'fa-check-circle',\n color: kpis.passRateThisMonth >= 90 ? 'success' : kpis.passRateThisMonth >= 75 ? 'warning' : 'danger',\n trend: kpis.passRateTrend !== 0 ? {\n direction: trendDir,\n percentage: Math.abs(Math.round(kpis.passRateTrend * 10) / 10),\n period: 'vs previous period'\n } : undefined\n },\n {\n title: 'Total Cost',\n value: `$${kpis.totalCostThisMonth.toFixed(2)}`,\n icon: 'fa-dollar-sign',\n color: 'warning',\n subtitle: 'This period'\n },\n {\n title: 'Avg Duration',\n value: this.FormatDuration(kpis.averageDuration),\n icon: 'fa-clock',\n color: 'info',\n subtitle: 'Per test run'\n },\n {\n title: 'Pending Review',\n value: kpis.testsPendingReview,\n icon: 'fa-clipboard-check',\n color: kpis.testsPendingReview > 10 ? 'warning' : 'success',\n subtitle: 'Tests need feedback'\n }\n ];\n }\n\n private buildAlerts(runs: TestRunSummary[]): TestAlert[] {\n const alerts: TestAlert[] = [];\n\n // Group runs by test name to detect regressions\n const byTest = new Map<string, TestRunSummary[]>();\n for (const run of runs) {\n const existing = byTest.get(run.testName);\n if (existing) {\n existing.push(run);\n } else {\n byTest.set(run.testName, [run]);\n }\n }\n\n this.detectRegressions(byTest, alerts);\n this.detectLowScores(runs, alerts);\n\n return alerts.slice(0, 20);\n }\n\n /** Find tests that previously passed but now fail */\n private detectRegressions(\n byTest: Map<string, TestRunSummary[]>,\n alerts: TestAlert[]\n ): void {\n const seenIds = new Set<string>();\n\n byTest.forEach((testRuns, testName) => {\n if (testRuns.length < 2) return;\n\n // Sort by date descending\n const sorted = [...testRuns].sort(\n (a, b) => b.runDateTime.getTime() - a.runDateTime.getTime()\n );\n\n const latest = sorted[0];\n const previous = sorted[1];\n\n if (\n (latest.status === 'Failed' || latest.status === 'Error') &&\n previous.status === 'Passed' &&\n !seenIds.has(latest.id)\n ) {\n seenIds.add(latest.id);\n alerts.push({\n id: latest.id,\n testName,\n reason: 'regression',\n score: latest.score,\n status: latest.status,\n runDateTime: latest.runDateTime\n });\n }\n });\n }\n\n /** Flag tests with scores below 0.5 */\n private detectLowScores(runs: TestRunSummary[], alerts: TestAlert[]): void {\n const alertIds = new Set(alerts.map(a => a.id));\n\n for (const run of runs) {\n if (run.score < 0.5 && run.status !== 'Running' && !alertIds.has(run.id)) {\n alerts.push({\n id: run.id,\n testName: run.testName,\n reason: 'low-score',\n score: run.score,\n status: run.status,\n runDateTime: run.runDateTime\n });\n }\n }\n }\n\n /** Flatten suite hierarchy and sort by worst health first */\n private flattenAndSort(nodes: SuiteHierarchyNode[]): SuiteHierarchyNode[] {\n const flat: SuiteHierarchyNode[] = [];\n this.collectNodes(nodes, flat);\n return flat.sort((a, b) => a.passRate - b.passRate);\n }\n\n private collectNodes(nodes: SuiteHierarchyNode[], out: SuiteHierarchyNode[]): void {\n for (const node of nodes) {\n out.push(node);\n if (node.children.length > 0) {\n this.collectNodes(node.children, out);\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"testing-dashboard-tab.component.js","sourceRoot":"","sources":["../../../src/Testing/components/testing-dashboard-tab.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Component, OnInit, OnDestroy, Input, Output, EventEmitter,\n ChangeDetectorRef, ChangeDetectionStrategy\n} from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\nimport { takeUntil, map } from 'rxjs/operators';\nimport { CompositeKey } from '@memberjunction/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport {\n TestingInstrumentationService,\n TestingDashboardKPIs,\n TestRunSummary,\n SuiteHierarchyNode\n} from '../services/testing-instrumentation.service';\nimport { KPICardData } from '../../AI/components/widgets/kpi-card.component';\nimport { TestEngineBase } from '@memberjunction/testing-engine-base';\n\n/** Status type union matching TestRunSummary */\ntype TestRunStatus = 'Passed' | 'Failed' | 'Skipped' | 'Error' | 'Running' | 'Timeout';\n\n/** Alert item for the alerts section */\ninterface TestAlert {\n id: string;\n testName: string;\n reason: 'regression' | 'low-score';\n score: number;\n status: TestRunStatus;\n runDateTime: Date;\n}\n\n@Component({\n standalone: false,\n selector: 'app-testing-dashboard-tab',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <!-- Full-page loading state -->\n @if (IsLoading) {\n <div class=\"full-page-loading\">\n <mj-loading text=\"Loading Testing Dashboard...\"></mj-loading>\n </div>\n } @else {\n <div class=\"dashboard-container\">\n\n <!-- Page Header -->\n <div class=\"page-header\">\n <h2 class=\"page-title\">\n <i class=\"fa-solid fa-gauge-high\"></i>\n Testing Dashboard\n </h2>\n <button class=\"refresh-btn\" (click)=\"OnRefresh()\" [disabled]=\"IsLoading\">\n <i class=\"fa-solid fa-refresh\" [class.spinning]=\"IsLoading\"></i>\n Refresh\n </button>\n </div>\n\n <!-- KPI Row -->\n <div class=\"kpi-row\">\n @for (kpi of KpiCards; track kpi.title) {\n <app-kpi-card [data]=\"kpi\"></app-kpi-card>\n }\n </div>\n\n <!-- Live Activity Section -->\n <div class=\"live-activity-card\">\n <div class=\"section-header\">\n @if (RunningTests.length > 0) {\n <span class=\"live-dot\"></span>\n <h3>{{ RunningTests.length }} test{{ RunningTests.length === 1 ? '' : 's' }} running</h3>\n } @else {\n <i class=\"fa-solid fa-circle-pause live-idle-icon\"></i>\n <h3>No tests currently running</h3>\n }\n </div>\n @if (RunningTests.length > 0) {\n <div class=\"running-tests-list\">\n @for (run of RunningTests; track run.id) {\n <div class=\"running-test-item\">\n <div class=\"running-test-name\">{{ run.testName }}</div>\n <div class=\"running-test-meta\">\n <span class=\"running-elapsed\">{{ FormatDuration(run.duration) }}</span>\n <span class=\"running-progress-bar\">\n <span class=\"running-progress-fill\"></span>\n </span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Lower Content: 2-column layout -->\n <div class=\"lower-grid\">\n\n <!-- Left Column: Recent Runs -->\n <div class=\"card recent-runs-card\">\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-history\"></i> Recent Runs</h3>\n </div>\n <div class=\"runs-list\">\n @for (run of RecentRuns; track run.id) {\n <div class=\"run-row\" (click)=\"OnOpenRun(run)\">\n <div class=\"run-row-left\">\n <span class=\"run-row-name\">{{ run.testName }}</span>\n <span class=\"run-row-time\">{{ FormatTimestamp(run.runDateTime) }}</span>\n </div>\n <div class=\"run-row-right\">\n <app-test-status-badge [status]=\"run.status\"></app-test-status-badge>\n <app-score-indicator [score]=\"run.score\" [showBar]=\"true\"></app-score-indicator>\n <span class=\"run-row-duration\">{{ FormatDuration(run.duration) }}</span>\n <app-cost-display [cost]=\"run.cost\"></app-cost-display>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">No completed test runs found</div>\n }\n </div>\n </div>\n\n <!-- Right Column: Suite Health + Alerts -->\n <div class=\"right-column\">\n\n <!-- Suite Health -->\n <div class=\"card suite-health-card\">\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-heartbeat\"></i> Suite Health</h3>\n </div>\n <div class=\"suite-health-list\">\n @for (suite of SortedSuites; track suite.id) {\n <div class=\"suite-health-row\">\n <div class=\"suite-health-info\">\n <span class=\"suite-health-name\">{{ suite.name }}</span>\n <span class=\"suite-health-count\">{{ suite.testCount }} tests</span>\n </div>\n <div class=\"suite-health-bar-wrapper\">\n <div class=\"suite-health-bar\">\n <div\n class=\"suite-health-bar-fill\"\n [style.width.%]=\"suite.passRate\"\n [style.background]=\"GetPassRateColor(suite.passRate)\">\n </div>\n </div>\n <span class=\"suite-health-pct\" [style.color]=\"GetPassRateColor(suite.passRate)\">\n {{ suite.passRate | number:'1.0-0' }}%\n </span>\n </div>\n </div>\n } @empty {\n <div class=\"empty-state\">No test suites found</div>\n }\n </div>\n </div>\n\n <!-- Alerts -->\n <div class=\"card alerts-card\">\n <div class=\"section-header\">\n <h3><i class=\"fa-solid fa-triangle-exclamation\"></i> Alerts</h3>\n </div>\n <div class=\"alerts-list\">\n @for (alert of Alerts; track alert.id) {\n <div class=\"alert-row\" (click)=\"OnOpenAlert(alert)\">\n <i class=\"fa-solid\"\n [class.fa-arrow-trend-down]=\"alert.reason === 'regression'\"\n [class.fa-circle-exclamation]=\"alert.reason === 'low-score'\"\n [style.color]=\"alert.reason === 'regression' ? '#ef4444' : '#f59e0b'\">\n </i>\n <div class=\"alert-info\">\n <span class=\"alert-name\">{{ alert.testName }}</span>\n <span class=\"alert-reason\">\n @if (alert.reason === 'regression') {\n Regression detected\n } @else {\n Score below 0.5 ({{ alert.score | number:'1.2-2' }})\n }\n </span>\n </div>\n <app-test-status-badge [status]=\"alert.status\"></app-test-status-badge>\n </div>\n } @empty {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-check-circle\" style=\"color: var(--mj-status-success); margin-right: 6px;\"></i>\n No alerts - all tests healthy\n </div>\n }\n </div>\n </div>\n\n </div>\n </div>\n </div>\n }\n `,\n styles: [`\n /* ===== Layout ===== */\n .full-page-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: var(--mj-bg-surface-card);\n }\n\n .dashboard-container {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n }\n\n /* ===== Page Header ===== */\n .page-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 24px;\n }\n\n .page-title {\n margin: 0;\n font-size: 22px;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .page-title i { color: var(--mj-brand-primary); }\n\n .refresh-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border: none;\n padding: 8px 18px;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 13px;\n font-weight: 500;\n transition: background 0.2s ease;\n }\n\n .refresh-btn:hover:not(:disabled) { background: var(--mj-brand-primary-hover); }\n .refresh-btn:disabled { opacity: 0.6; cursor: not-allowed; }\n .refresh-btn i.spinning { animation: spin 1s linear infinite; }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n /* ===== KPI Row ===== */\n .kpi-row {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n margin-bottom: 24px;\n }\n\n /* ===== Card Base ===== */\n .card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n }\n\n .section-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .section-header h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .section-header h3 i { color: var(--mj-brand-primary); font-size: 13px; }\n\n /* ===== Live Activity ===== */\n .live-activity-card {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: var(--mj-shadow-sm);\n margin-bottom: 24px;\n overflow: hidden;\n }\n\n .live-dot {\n width: 10px;\n height: 10px;\n background: var(--mj-status-success);\n border-radius: 50%;\n display: inline-block;\n animation: pulse-dot 1.5s ease-in-out infinite;\n }\n\n @keyframes pulse-dot {\n 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mj-status-success) 50%, transparent); }\n 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--mj-status-success) 0%, transparent); }\n }\n\n .live-idle-icon { color: var(--mj-text-disabled); font-size: 14px; }\n\n .running-tests-list {\n padding: 0 20px 16px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n }\n\n .running-test-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 14px;\n background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n border-radius: 8px;\n border: 1px solid color-mix(in srgb, var(--mj-status-success) 25%, var(--mj-bg-surface));\n }\n\n .running-test-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-status-success);\n }\n\n .running-test-meta {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .running-elapsed {\n font-size: 12px;\n color: var(--mj-status-success);\n font-weight: 500;\n }\n\n .running-progress-bar {\n width: 60px;\n height: 4px;\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n border-radius: 2px;\n overflow: hidden;\n }\n\n .running-progress-fill {\n display: block;\n width: 100%;\n height: 100%;\n background: var(--mj-status-success);\n animation: progress-slide 1.2s ease-in-out infinite;\n border-radius: 2px;\n }\n\n @keyframes progress-slide {\n 0% { transform: translateX(-100%); }\n 100% { transform: translateX(100%); }\n }\n\n /* ===== Lower Grid ===== */\n .lower-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 24px;\n }\n\n .right-column {\n display: flex;\n flex-direction: column;\n gap: 24px;\n }\n\n /* ===== Recent Runs ===== */\n .runs-list {\n max-height: 520px;\n overflow-y: auto;\n }\n\n .run-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-card);\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .run-row:hover { background: var(--mj-bg-surface-card); }\n\n .run-row-left {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .run-row-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .run-row-time {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n .run-row-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .run-row-duration {\n font-size: 11px;\n color: var(--mj-text-muted);\n min-width: 44px;\n text-align: right;\n }\n\n /* ===== Suite Health ===== */\n .suite-health-list {\n padding: 8px 0;\n max-height: 260px;\n overflow-y: auto;\n }\n\n .suite-health-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 20px;\n }\n\n .suite-health-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .suite-health-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .suite-health-count {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n .suite-health-bar-wrapper {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n }\n\n .suite-health-bar {\n width: 80px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n }\n\n .suite-health-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s ease;\n }\n\n .suite-health-pct {\n font-size: 12px;\n font-weight: 600;\n min-width: 36px;\n text-align: right;\n }\n\n /* ===== Alerts ===== */\n .alerts-list {\n max-height: 220px;\n overflow-y: auto;\n }\n\n .alert-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 20px;\n border-bottom: 1px solid var(--mj-bg-surface-card);\n cursor: pointer;\n transition: background 0.15s ease;\n }\n\n .alert-row:hover { background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface)); }\n\n .alert-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n flex: 1;\n min-width: 0;\n }\n\n .alert-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .alert-reason {\n font-size: 11px;\n color: var(--mj-text-disabled);\n }\n\n /* ===== Empty State ===== */\n .empty-state {\n padding: 32px 20px;\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n /* ===== Responsive ===== */\n @media (max-width: 900px) {\n .lower-grid {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 640px) {\n .dashboard-container { padding: 16px; }\n .kpi-row { grid-template-columns: 1fr; }\n }\n `]\n})\nexport class TestingDashboardTabComponent implements OnInit, OnDestroy {\n\n @Input() initialState: Record<string, unknown> | null = null;\n @Output() stateChange = new EventEmitter<Record<string, unknown>>();\n\n private destroy$ = new Subject<void>();\n\n /** Resolved data for the template */\n IsLoading = false;\n KpiCards: KPICardData[] = [];\n RunningTests: TestRunSummary[] = [];\n RecentRuns: TestRunSummary[] = [];\n SortedSuites: SuiteHierarchyNode[] = [];\n Alerts: TestAlert[] = [];\n\n constructor(\n private instrumentationService: TestingInstrumentationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n // ---------------------------------------------------------------------------\n // Lifecycle\n // ---------------------------------------------------------------------------\n\n async ngOnInit(): Promise<void> {\n await TestEngineBase.Instance.Config(false);\n this.subscribeToStreams();\n this.instrumentationService.refresh();\n }\n\n ngOnDestroy(): void {\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n // ---------------------------------------------------------------------------\n // Public methods (PascalCase)\n // ---------------------------------------------------------------------------\n\n OnRefresh(): void {\n this.instrumentationService.refresh();\n }\n\n OnOpenRun(run: TestRunSummary): void {\n SharedService.Instance.OpenEntityRecord(\n 'MJ: Test Runs',\n CompositeKey.FromID(run.id)\n );\n }\n\n OnOpenAlert(alert: TestAlert): void {\n SharedService.Instance.OpenEntityRecord(\n 'MJ: Test Runs',\n CompositeKey.FromID(alert.id)\n );\n }\n\n FormatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const totalSeconds = Math.floor(ms / 1000);\n const minutes = Math.floor(totalSeconds / 60);\n const seconds = totalSeconds % 60;\n if (minutes > 0) return `${minutes}m ${seconds}s`;\n return `${seconds}s`;\n }\n\n FormatTimestamp(date: Date): string {\n if (!date) return '';\n const d = date instanceof Date ? date : new Date(date);\n const now = new Date();\n const diffMs = now.getTime() - d.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n if (diffMins < 1) return 'Just now';\n if (diffMins < 60) return `${diffMins}m ago`;\n const diffHours = Math.floor(diffMins / 60);\n if (diffHours < 24) return `${diffHours}h ago`;\n const diffDays = Math.floor(diffHours / 24);\n if (diffDays < 7) return `${diffDays}d ago`;\n return d.toLocaleDateString();\n }\n\n FormatCost(cost: number): string {\n return `$${cost.toFixed(4)}`;\n }\n\n GetPassRateColor(rate: number): string {\n if (rate >= 90) return '#22c55e';\n if (rate >= 70) return '#f59e0b';\n return '#ef4444';\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers (camelCase)\n // ---------------------------------------------------------------------------\n\n private subscribeToStreams(): void {\n this.subscribeLoading();\n this.subscribeKpis();\n this.subscribeTestRuns();\n this.subscribeSuites();\n }\n\n private subscribeLoading(): void {\n this.instrumentationService.isLoading$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(loading => {\n this.IsLoading = loading;\n this.cdr.markForCheck();\n });\n }\n\n private subscribeKpis(): void {\n this.instrumentationService.kpis$.pipe(\n takeUntil(this.destroy$),\n map(kpis => this.buildKpiCards(kpis))\n ).subscribe(cards => {\n this.KpiCards = cards;\n this.cdr.markForCheck();\n });\n }\n\n private subscribeTestRuns(): void {\n this.instrumentationService.testRuns$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(runs => {\n this.RunningTests = runs.filter(r => r.status === 'Running');\n this.RecentRuns = runs\n .filter(r => r.status !== 'Running')\n .slice(0, 15);\n this.Alerts = this.buildAlerts(runs);\n this.cdr.markForCheck();\n });\n }\n\n private subscribeSuites(): void {\n this.instrumentationService.suiteHierarchy$.pipe(\n takeUntil(this.destroy$)\n ).subscribe(suites => {\n this.SortedSuites = this.flattenAndSort(suites);\n this.cdr.markForCheck();\n });\n }\n\n private buildKpiCards(kpis: TestingDashboardKPIs): KPICardData[] {\n const trendDir = kpis.passRateTrend > 0\n ? 'up' as const\n : kpis.passRateTrend < 0\n ? 'down' as const\n : 'stable' as const;\n\n return [\n {\n title: 'Active Tests',\n value: kpis.totalTestsActive,\n icon: 'fa-vial',\n color: 'primary',\n subtitle: `${kpis.totalTestRuns} runs this period`\n },\n {\n title: 'Pass Rate',\n value: `${kpis.passRateThisMonth.toFixed(1)}%`,\n icon: 'fa-check-circle',\n color: kpis.passRateThisMonth >= 90 ? 'success' : kpis.passRateThisMonth >= 75 ? 'warning' : 'danger',\n trend: kpis.passRateTrend !== 0 ? {\n direction: trendDir,\n percentage: Math.abs(Math.round(kpis.passRateTrend * 10) / 10),\n period: 'vs previous period'\n } : undefined\n },\n {\n title: 'Total Cost',\n value: `$${kpis.totalCostThisMonth.toFixed(2)}`,\n icon: 'fa-dollar-sign',\n color: 'warning',\n subtitle: 'This period'\n },\n {\n title: 'Avg Duration',\n value: this.FormatDuration(kpis.averageDuration),\n icon: 'fa-clock',\n color: 'info',\n subtitle: 'Per test run'\n },\n {\n title: 'Pending Review',\n value: kpis.testsPendingReview,\n icon: 'fa-clipboard-check',\n color: kpis.testsPendingReview > 10 ? 'warning' : 'success',\n subtitle: 'Tests need feedback'\n }\n ];\n }\n\n private buildAlerts(runs: TestRunSummary[]): TestAlert[] {\n const alerts: TestAlert[] = [];\n\n // Group runs by test name to detect regressions\n const byTest = new Map<string, TestRunSummary[]>();\n for (const run of runs) {\n const existing = byTest.get(run.testName);\n if (existing) {\n existing.push(run);\n } else {\n byTest.set(run.testName, [run]);\n }\n }\n\n this.detectRegressions(byTest, alerts);\n this.detectLowScores(runs, alerts);\n\n return alerts.slice(0, 20);\n }\n\n /** Find tests that previously passed but now fail */\n private detectRegressions(\n byTest: Map<string, TestRunSummary[]>,\n alerts: TestAlert[]\n ): void {\n const seenIds = new Set<string>();\n\n byTest.forEach((testRuns, testName) => {\n if (testRuns.length < 2) return;\n\n // Sort by date descending\n const sorted = [...testRuns].sort(\n (a, b) => b.runDateTime.getTime() - a.runDateTime.getTime()\n );\n\n const latest = sorted[0];\n const previous = sorted[1];\n\n if (\n (latest.status === 'Failed' || latest.status === 'Error') &&\n previous.status === 'Passed' &&\n !seenIds.has(latest.id)\n ) {\n seenIds.add(latest.id);\n alerts.push({\n id: latest.id,\n testName,\n reason: 'regression',\n score: latest.score,\n status: latest.status,\n runDateTime: latest.runDateTime\n });\n }\n });\n }\n\n /** Flag tests with scores below 0.5 */\n private detectLowScores(runs: TestRunSummary[], alerts: TestAlert[]): void {\n const alertIds = new Set(alerts.map(a => a.id));\n\n for (const run of runs) {\n if (run.score < 0.5 && run.status !== 'Running' && !alertIds.has(run.id)) {\n alerts.push({\n id: run.id,\n testName: run.testName,\n reason: 'low-score',\n score: run.score,\n status: run.status,\n runDateTime: run.runDateTime\n });\n }\n }\n }\n\n /** Flatten suite hierarchy and sort by worst health first */\n private flattenAndSort(nodes: SuiteHierarchyNode[]): SuiteHierarchyNode[] {\n const flat: SuiteHierarchyNode[] = [];\n this.collectNodes(nodes, flat);\n return flat.sort((a, b) => a.passRate - b.passRate);\n }\n\n private collectNodes(nodes: SuiteHierarchyNode[], out: SuiteHierarchyNode[]): void {\n for (const node of nodes) {\n out.push(node);\n if (node.children.length > 0) {\n this.collectNodes(node.children, out);\n }\n }\n }\n}\n"]}
|
|
@@ -1662,7 +1662,7 @@ export class TestingExplorerComponent {
|
|
|
1662
1662
|
i0.ɵɵclassProp("open", ctx.SlideoutOpen);
|
|
1663
1663
|
i0.ɵɵadvance(2);
|
|
1664
1664
|
i0.ɵɵconditional(ctx.SlideoutOpen ? 7 : -1);
|
|
1665
|
-
} }, dependencies: [i3.NgTemplateOutlet, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.DefaultValueAccessor, i4.NumberValueAccessor, i4.SelectControlValueAccessor, i4.NgControlStatus, i4.MinValidator, i4.NgModel, i5.LoadingComponent, i6.HighlightSearchPipe], styles: ["\n\n\n\n\n [_nghost-%COMP%] {\n display: block;\n height: 100%;\n width: 100%;\n }\n\n \n\n .explorer-loading[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: #f5f6fa;\n }\n\n \n\n .explorer-layout[_ngcontent-%COMP%] {\n display: flex;\n height: 100%;\n background: #f5f6fa;\n overflow: hidden;\n }\n\n \n\n\n\n .sidebar[_ngcontent-%COMP%] {\n width: 280px;\n min-width: 280px;\n background: #fff;\n border-right: 1px solid #e8ecef;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: width 0.2s ease, min-width 0.2s ease;\n }\n\n .sidebar.collapsed[_ngcontent-%COMP%] {\n width: 48px;\n min-width: 48px;\n }\n\n .sidebar-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px;\n border-bottom: 1px solid #e8ecef;\n }\n\n .sidebar-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .sidebar.collapsed[_ngcontent-%COMP%] .sidebar-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n display: none;\n }\n\n .sidebar-toggle[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n color: #636e72;\n cursor: pointer;\n font-size: 11px;\n transition: all 0.15s ease;\n }\n\n .sidebar-toggle[_ngcontent-%COMP%]:hover {\n background: #f5f6fa;\n color: #2d3436;\n }\n\n .sidebar-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n }\n\n .sidebar-section[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n }\n\n .sidebar-section-title[_ngcontent-%COMP%] {\n padding: 8px 16px 4px;\n font-size: 10px;\n font-weight: 700;\n color: #b2bec3;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .sidebar-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 13px;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .sidebar-item[_ngcontent-%COMP%]:hover {\n background: #f5f6fa;\n color: #2d3436;\n }\n\n .sidebar-item.active[_ngcontent-%COMP%] {\n background: #e0f2f1;\n color: #00897b;\n font-weight: 600;\n }\n\n .sidebar-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n }\n\n .sidebar-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:not(.sidebar-count) {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .sidebar-count[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #b2bec3;\n font-weight: 500;\n flex-shrink: 0;\n }\n\n .sidebar-item.active[_ngcontent-%COMP%] .sidebar-count[_ngcontent-%COMP%] {\n color: #00897b;\n }\n\n .sidebar-empty[_ngcontent-%COMP%] {\n padding: 8px 16px;\n font-size: 12px;\n color: #b2bec3;\n font-style: italic;\n }\n\n \n\n .suite-tree-item[_ngcontent-%COMP%] {\n gap: 6px;\n }\n\n .tree-toggle[_ngcontent-%COMP%] {\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n color: #b2bec3;\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n border-radius: 4px;\n padding: 0;\n }\n\n .tree-toggle[_ngcontent-%COMP%]:hover {\n background: #e8ecef;\n color: #636e72;\n }\n\n .tree-name[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n \n\n\n\n .main-content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n \n\n .toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n padding: 16px 24px;\n background: #fff;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .toolbar-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: #f5f6fa;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n min-width: 240px;\n max-width: 360px;\n flex: 1;\n transition: border-color 0.2s ease;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%]:focus-within {\n border-color: #00897b;\n background: #fff;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #b2bec3;\n font-size: 13px;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: #2d3436;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder {\n color: #b2bec3;\n }\n\n .clear-search[_ngcontent-%COMP%] {\n border: none;\n background: transparent;\n color: #b2bec3;\n cursor: pointer;\n padding: 2px 4px;\n border-radius: 4px;\n }\n\n .clear-search[_ngcontent-%COMP%]:hover {\n color: #636e72;\n background: #e8ecef;\n }\n\n .status-chips[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n }\n\n .chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n background: #f5f6fa;\n border: 1px solid transparent;\n border-radius: 16px;\n font-size: 12px;\n font-weight: 600;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .chip[_ngcontent-%COMP%]:hover {\n background: #e8ecef;\n }\n\n .chip.active[_ngcontent-%COMP%] {\n color: #fff;\n }\n\n .chip.active[data-status=\"active\"][_ngcontent-%COMP%] {\n background: #2e7d32;\n border-color: #2e7d32;\n }\n\n .chip.active[data-status=\"pending\"][_ngcontent-%COMP%] {\n background: #e65100;\n border-color: #e65100;\n }\n\n .chip.active[data-status=\"disabled\"][_ngcontent-%COMP%] {\n background: #636e72;\n border-color: #636e72;\n }\n\n .toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .result-count[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #b2bec3;\n font-weight: 500;\n white-space: nowrap;\n }\n\n .view-toggle[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n overflow: hidden;\n }\n\n .view-btn[_ngcontent-%COMP%] {\n width: 34px;\n height: 34px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #fff;\n border: none;\n color: #b2bec3;\n cursor: pointer;\n font-size: 13px;\n transition: all 0.15s ease;\n }\n\n .view-btn[_ngcontent-%COMP%]:not(:last-child) {\n border-right: 1px solid #e8ecef;\n }\n\n .view-btn[_ngcontent-%COMP%]:hover {\n background: #f5f6fa;\n color: #636e72;\n }\n\n .view-btn.active[_ngcontent-%COMP%] {\n background: #00897b;\n color: #fff;\n }\n\n \n\n .btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n }\n\n .btn-primary[_ngcontent-%COMP%] {\n background: #00897b;\n color: #fff;\n }\n\n .btn-primary[_ngcontent-%COMP%]:hover {\n background: #00796b;\n transform: translateY(-1px);\n box-shadow: 0 2px 8px rgba(0, 137, 123, 0.3);\n }\n\n .btn-secondary[_ngcontent-%COMP%] {\n background: #fff;\n color: #636e72;\n border: 1px solid #e8ecef;\n }\n\n .btn-secondary[_ngcontent-%COMP%]:hover {\n background: #f5f6fa;\n }\n\n .btn-sm[_ngcontent-%COMP%] {\n padding: 6px 12px;\n font-size: 12px;\n }\n\n \n\n .toggle-bar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 24px;\n background: #fafafa;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .toggle-group[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n overflow: hidden;\n }\n\n .toggle-btn[_ngcontent-%COMP%] {\n padding: 6px 16px;\n background: #fff;\n border: none;\n font-size: 12px;\n font-weight: 600;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .toggle-btn[_ngcontent-%COMP%]:not(:last-child) {\n border-right: 1px solid #e8ecef;\n }\n\n .toggle-btn.active[_ngcontent-%COMP%] {\n background: #00897b;\n color: #fff;\n }\n\n .toggle-btn[_ngcontent-%COMP%]:hover:not(.active) {\n background: #f5f6fa;\n }\n\n .sort-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n }\n\n .sort-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: transparent;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: #636e72;\n cursor: pointer;\n }\n\n .sort-btn[_ngcontent-%COMP%]:hover {\n background: #f5f6fa;\n }\n\n .sort-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n }\n\n \n\n .content-area[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .content-section[_ngcontent-%COMP%] {\n margin-bottom: 32px;\n }\n\n .section-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 0 16px 0;\n font-size: 16px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #00897b;\n font-size: 14px;\n }\n\n .section-count[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: #b2bec3;\n background: #f5f6fa;\n padding: 2px 8px;\n border-radius: 10px;\n }\n\n \n\n .card-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));\n gap: 16px;\n }\n\n \n\n .suite-card[_ngcontent-%COMP%], \n .test-card[_ngcontent-%COMP%] {\n background: #fff;\n border: 1px solid #e8ecef;\n border-radius: 10px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .suite-card[_ngcontent-%COMP%]:hover, \n .test-card[_ngcontent-%COMP%]:hover {\n border-color: #b2dfdb;\n box-shadow: 0 4px 16px rgba(0, 137, 123, 0.1);\n }\n\n .card-header[_ngcontent-%COMP%] {\n padding: 16px 16px 12px;\n }\n\n .card-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n }\n\n .card-icon[_ngcontent-%COMP%] {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .suite-icon[_ngcontent-%COMP%] {\n color: #00897b;\n }\n\n .test-icon[_ngcontent-%COMP%] {\n color: #6366f1;\n }\n\n .card-name[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 14px;\n font-weight: 700;\n color: #2d3436;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .status-badge[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n flex-shrink: 0;\n }\n\n .status-badge[data-status=\"active\"][_ngcontent-%COMP%] {\n background: #e8f5e9;\n color: #2e7d32;\n }\n\n .status-badge[data-status=\"pending\"][_ngcontent-%COMP%] {\n background: #fff3e0;\n color: #e65100;\n }\n\n .status-badge[data-status=\"disabled\"][_ngcontent-%COMP%] {\n background: #f5f6fa;\n color: #636e72;\n }\n\n .card-subtitle[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #636e72;\n margin-bottom: 6px;\n }\n\n .dot-sep[_ngcontent-%COMP%] {\n display: inline-block;\n width: 3px;\n height: 3px;\n background: #b2bec3;\n border-radius: 50%;\n vertical-align: middle;\n margin: 0 6px;\n }\n\n .card-description[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 12px;\n color: #636e72;\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta-row[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 8px;\n }\n\n .meta-item[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #636e72;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .meta-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n color: #b2bec3;\n }\n\n .card-tags[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 8px;\n }\n\n .tag[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n background: #f5f6fa;\n color: #636e72;\n border-radius: 4px;\n }\n\n .tag-more[_ngcontent-%COMP%] {\n background: #e8ecef;\n color: #b2bec3;\n }\n\n \n\n .card-stats[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1px;\n background: #e8ecef;\n border-top: 1px solid #e8ecef;\n }\n\n .stat[_ngcontent-%COMP%] {\n background: #fafafa;\n padding: 10px 12px;\n text-align: center;\n }\n\n .stat-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 10px;\n font-weight: 600;\n color: #b2bec3;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n }\n\n .stat-value[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .stat-value.good[_ngcontent-%COMP%] { color: #2e7d32; }\n .stat-value.warn[_ngcontent-%COMP%] { color: #e65100; }\n .stat-value.bad[_ngcontent-%COMP%] { color: #c62828; }\n\n .status-text[data-status=\"passed\"][_ngcontent-%COMP%] { color: #2e7d32; }\n .status-text[data-status=\"failed\"][_ngcontent-%COMP%] { color: #c62828; }\n .status-text[data-status=\"error\"][_ngcontent-%COMP%] { color: #e65100; }\n .status-text[data-status=\"running\"][_ngcontent-%COMP%] { color: #1565c0; }\n .status-text[data-status=\"pending\"][_ngcontent-%COMP%] { color: #e65100; }\n .status-text[data-status=\"skipped\"][_ngcontent-%COMP%] { color: #636e72; }\n\n \n\n .card-tests-preview[_ngcontent-%COMP%] {\n padding: 10px 16px;\n border-top: 1px solid #e8ecef;\n }\n\n .preview-test-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n font-size: 12px;\n }\n\n .preview-dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .preview-dot[data-status=\"passed\"][_ngcontent-%COMP%] { background: #2e7d32; }\n .preview-dot[data-status=\"failed\"][_ngcontent-%COMP%] { background: #c62828; }\n .preview-dot[data-status=\"error\"][_ngcontent-%COMP%] { background: #e65100; }\n .preview-dot[data-status=\"running\"][_ngcontent-%COMP%] { background: #1565c0; }\n .preview-dot[data-status=\"\"][_ngcontent-%COMP%] { background: #b2bec3; }\n\n .preview-test-name[_ngcontent-%COMP%] {\n flex: 1;\n color: #2d3436;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .preview-score[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 11px;\n min-width: 32px;\n text-align: right;\n }\n\n .preview-score.good[_ngcontent-%COMP%] { color: #2e7d32; }\n .preview-score.warn[_ngcontent-%COMP%] { color: #e65100; }\n .preview-score.bad[_ngcontent-%COMP%] { color: #c62828; }\n\n .preview-bar[_ngcontent-%COMP%] {\n width: 48px;\n height: 4px;\n background: #e8ecef;\n border-radius: 2px;\n overflow: hidden;\n flex-shrink: 0;\n }\n\n .preview-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .good-bg[_ngcontent-%COMP%] { background: #2e7d32; }\n .warn-bg[_ngcontent-%COMP%] { background: #e65100; }\n .bad-bg[_ngcontent-%COMP%] { background: #c62828; }\n\n .preview-status[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n min-width: 44px;\n text-align: right;\n }\n\n .preview-status[data-status=\"passed\"][_ngcontent-%COMP%] { color: #2e7d32; }\n .preview-status[data-status=\"failed\"][_ngcontent-%COMP%] { color: #c62828; }\n .preview-status[data-status=\"error\"][_ngcontent-%COMP%] { color: #e65100; }\n .preview-status[data-status=\"\"][_ngcontent-%COMP%] { color: #b2bec3; }\n\n .preview-more[_ngcontent-%COMP%] {\n padding: 4px 0 0;\n font-size: 11px;\n color: #b2bec3;\n font-style: italic;\n }\n\n \n\n .card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid #e8ecef;\n background: #fafafa;\n }\n\n \n\n .empty-state[_ngcontent-%COMP%] {\n padding: 80px 40px;\n text-align: center;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: #b2bec3;\n margin-bottom: 16px;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 16px;\n color: #636e72;\n margin: 0 0 8px 0;\n }\n\n .empty-hint[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #b2bec3;\n }\n\n \n\n\n\n .slideout-backdrop[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 999;\n animation: _ngcontent-%COMP%_fadeInBackdrop 0.2s ease;\n }\n\n @keyframes _ngcontent-%COMP%_fadeInBackdrop {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .slideout-panel[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n right: -100%;\n height: 100vh;\n background: #fff;\n box-shadow: -4px 0 20px rgba(0, 0, 0, 0.15);\n z-index: 1000;\n transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .slideout-panel.open[_ngcontent-%COMP%] {\n right: 0;\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n width: 5px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s;\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%]:hover {\n background: rgba(0, 137, 123, 0.3);\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%]:active {\n background: rgba(0, 137, 123, 0.5);\n }\n\n .slideout-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n\n .slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .slideout-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .slideout-title-icon[_ngcontent-%COMP%] {\n font-size: 18px;\n color: #00897b;\n }\n\n .slideout-title-text[_ngcontent-%COMP%] {\n font-size: 18px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .slideout-close-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n color: #636e72;\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n }\n\n .slideout-close-btn[_ngcontent-%COMP%]:hover {\n background: #f5f6fa;\n color: #2d3436;\n border-color: #ccc;\n }\n\n \n\n .slideout-type-toggle[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n padding: 16px 24px;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: #f5f6fa;\n border: 1px solid #e8ecef;\n font-size: 13px;\n font-weight: 600;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%]:first-child {\n border-radius: 8px 0 0 8px;\n border-right: none;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%]:last-child {\n border-radius: 0 8px 8px 0;\n }\n\n .type-toggle-btn.active[_ngcontent-%COMP%] {\n background: #00897b;\n border-color: #00897b;\n color: #fff;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%]:hover:not(.active) {\n background: #e8ecef;\n }\n\n \n\n .slideout-error[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 16px 24px 0;\n padding: 12px 16px;\n background: #fff5f5;\n border: 1px solid #fed7d7;\n border-radius: 8px;\n color: #c53030;\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .slideout-error[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n \n\n .slideout-body[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n }\n\n .form-section[_ngcontent-%COMP%] {\n margin-bottom: 24px;\n }\n\n .form-section-title[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: #b2bec3;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n margin-bottom: 14px;\n padding-bottom: 8px;\n border-bottom: 1px solid #f0f0f0;\n }\n\n .form-group[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n }\n\n .form-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: #2d3436;\n margin-bottom: 6px;\n }\n\n .form-required[_ngcontent-%COMP%] {\n color: #e53e3e;\n }\n\n .form-input[_ngcontent-%COMP%], \n .form-textarea[_ngcontent-%COMP%] {\n width: 100%;\n padding: 10px 14px;\n background: #fff;\n border: 1px solid #e8ecef;\n border-radius: 8px;\n font-size: 13px;\n color: #2d3436;\n transition: border-color 0.2s ease;\n outline: none;\n box-sizing: border-box;\n font-family: inherit;\n }\n\n .form-input[_ngcontent-%COMP%]:focus, \n .form-textarea[_ngcontent-%COMP%]:focus {\n border-color: #00897b;\n box-shadow: 0 0 0 3px rgba(0, 137, 123, 0.1);\n }\n\n .form-input[_ngcontent-%COMP%]::placeholder, \n .form-textarea[_ngcontent-%COMP%]::placeholder {\n color: #b2bec3;\n }\n\n .form-textarea[_ngcontent-%COMP%] {\n resize: vertical;\n min-height: 80px;\n }\n\n .form-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .form-hint[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: #b2bec3;\n margin-top: 4px;\n }\n\n \n\n .slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px;\n border-top: 1px solid #e8ecef;\n background: #fafafa;\n flex-shrink: 0;\n }\n\n .slideout-footer[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n min-width: 100px;\n justify-content: center;\n }\n\n .slideout-footer[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n }\n\n \n\n mark.search-highlight {\n background: #fff9c4;\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: 700;\n }\n\n \n\n\n\n @media (max-width: 1200px) {\n .card-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 900px) {\n .sidebar[_ngcontent-%COMP%] {\n display: none;\n }\n\n .toolbar[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n }\n\n .toolbar-left[_ngcontent-%COMP%], .toolbar-right[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n justify-content: center;\n }\n }\n\n @media (max-width: 600px) {\n .content-area[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .card-stats[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .slideout-panel[_ngcontent-%COMP%] {\n width: 100% !important;\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%] {\n display: none;\n }\n\n .form-row[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }"], changeDetection: 0 });
|
|
1665
|
+
} }, dependencies: [i3.NgTemplateOutlet, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.DefaultValueAccessor, i4.NumberValueAccessor, i4.SelectControlValueAccessor, i4.NgControlStatus, i4.MinValidator, i4.NgModel, i5.LoadingComponent, i6.HighlightSearchPipe], styles: ["\n\n\n\n\n [_nghost-%COMP%] {\n display: block;\n height: 100%;\n width: 100%;\n }\n\n \n\n .explorer-loading[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: var(--mj-bg-surface-sunken);\n }\n\n \n\n .explorer-layout[_ngcontent-%COMP%] {\n display: flex;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n }\n\n \n\n\n\n .sidebar[_ngcontent-%COMP%] {\n width: 280px;\n min-width: 280px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: width 0.2s ease, min-width 0.2s ease;\n }\n\n .sidebar.collapsed[_ngcontent-%COMP%] {\n width: 48px;\n min-width: 48px;\n }\n\n .sidebar-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .sidebar.collapsed[_ngcontent-%COMP%] .sidebar-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n display: none;\n }\n\n .sidebar-toggle[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\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: 6px;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 11px;\n transition: all 0.15s ease;\n }\n\n .sidebar-toggle[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n }\n\n .sidebar-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n }\n\n .sidebar-section[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n }\n\n .sidebar-section-title[_ngcontent-%COMP%] {\n padding: 8px 16px 4px;\n font-size: 10px;\n font-weight: 700;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .sidebar-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 13px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .sidebar-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n }\n\n .sidebar-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n }\n\n .sidebar-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n }\n\n .sidebar-item[_ngcontent-%COMP%] span[_ngcontent-%COMP%]:not(.sidebar-count) {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .sidebar-count[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n flex-shrink: 0;\n }\n\n .sidebar-item.active[_ngcontent-%COMP%] .sidebar-count[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n }\n\n .sidebar-empty[_ngcontent-%COMP%] {\n padding: 8px 16px;\n font-size: 12px;\n color: var(--mj-text-disabled);\n font-style: italic;\n }\n\n \n\n .suite-tree-item[_ngcontent-%COMP%] {\n gap: 6px;\n }\n\n .tree-toggle[_ngcontent-%COMP%] {\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n color: var(--mj-text-disabled);\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n border-radius: 4px;\n padding: 0;\n }\n\n .tree-toggle[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n }\n\n .tree-name[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n \n\n\n\n .main-content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n \n\n .toolbar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n padding: 16px 24px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .toolbar-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n min-width: 240px;\n max-width: 360px;\n flex: 1;\n transition: border-color 0.2s ease;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%]:focus-within {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--mj-text-primary);\n }\n\n .toolbar-search-box[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .clear-search[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover {\n color: var(--mj-text-muted);\n background: var(--mj-border-default);\n }\n\n .status-chips[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n }\n\n .chip[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid transparent;\n border-radius: 16px;\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 .chip[_ngcontent-%COMP%]:hover {\n background: var(--mj-border-default);\n }\n\n .chip.active[_ngcontent-%COMP%] {\n color: var(--mj-text-inverse);\n }\n\n .chip.active[data-status=\"active\"][_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n }\n\n .chip.active[data-status=\"pending\"][_ngcontent-%COMP%] {\n background: var(--mj-status-warning);\n border-color: var(--mj-status-warning);\n }\n\n .chip.active[data-status=\"disabled\"][_ngcontent-%COMP%] {\n background: var(--mj-text-muted);\n border-color: var(--mj-text-muted);\n }\n\n .toolbar-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .result-count[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n white-space: nowrap;\n }\n\n .view-toggle[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n }\n\n .view-btn[_ngcontent-%COMP%] {\n width: 34px;\n height: 34px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border: none;\n color: var(--mj-text-disabled);\n cursor: pointer;\n font-size: 13px;\n transition: all 0.15s ease;\n }\n\n .view-btn[_ngcontent-%COMP%]:not(:last-child) {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .view-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n }\n\n .view-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n \n\n .btn[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n }\n\n .btn-primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .btn-primary[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary-hover);\n transform: translateY(-1px);\n box-shadow: var(--mj-shadow-md);\n }\n\n .btn-secondary[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n color: var(--mj-text-muted);\n border: 1px solid var(--mj-border-default);\n }\n\n .btn-secondary[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n }\n\n .btn-sm[_ngcontent-%COMP%] {\n padding: 6px 12px;\n font-size: 12px;\n }\n\n \n\n .toggle-bar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 24px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .toggle-group[_ngcontent-%COMP%] {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n }\n\n .toggle-btn[_ngcontent-%COMP%] {\n padding: 6px 16px;\n background: var(--mj-bg-surface);\n border: none;\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[_ngcontent-%COMP%]:not(:last-child) {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .toggle-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .toggle-btn[_ngcontent-%COMP%]:hover:not(.active) {\n background: var(--mj-bg-surface-sunken);\n }\n\n .sort-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n }\n\n .sort-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n cursor: pointer;\n }\n\n .sort-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n }\n\n .sort-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n }\n\n \n\n .content-area[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .content-section[_ngcontent-%COMP%] {\n margin-bottom: 32px;\n }\n\n .section-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 0 16px 0;\n font-size: 16px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .section-title[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 14px;\n }\n\n .section-count[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n }\n\n \n\n .card-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));\n gap: 16px;\n }\n\n \n\n .suite-card[_ngcontent-%COMP%], \n .test-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .suite-card[_ngcontent-%COMP%]:hover, \n .test-card[_ngcontent-%COMP%]:hover {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-md);\n }\n\n .card-header[_ngcontent-%COMP%] {\n padding: 16px 16px 12px;\n }\n\n .card-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n }\n\n .card-icon[_ngcontent-%COMP%] {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .suite-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n }\n\n .test-icon[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n }\n\n .card-name[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 14px;\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .status-badge[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n flex-shrink: 0;\n }\n\n .status-badge[data-status=\"active\"][_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .status-badge[data-status=\"pending\"][_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n .status-badge[data-status=\"disabled\"][_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n }\n\n .card-subtitle[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n }\n\n .dot-sep[_ngcontent-%COMP%] {\n display: inline-block;\n width: 3px;\n height: 3px;\n background: var(--mj-text-disabled);\n border-radius: 50%;\n vertical-align: middle;\n margin: 0 6px;\n }\n\n .card-description[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 12px;\n color: var(--mj-text-muted);\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta-row[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 8px;\n }\n\n .meta-item[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .meta-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--mj-text-disabled);\n }\n\n .card-tags[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 8px;\n }\n\n .tag[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n border-radius: 4px;\n }\n\n .tag-more[_ngcontent-%COMP%] {\n background: var(--mj-border-default);\n color: var(--mj-text-disabled);\n }\n\n \n\n .card-stats[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1px;\n background: var(--mj-border-default);\n border-top: 1px solid var(--mj-border-default);\n }\n\n .stat[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n padding: 10px 12px;\n text-align: center;\n }\n\n .stat-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n }\n\n .stat-value[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .stat-value.good[_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .stat-value.warn[_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .stat-value.bad[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n\n .status-text[data-status=\"passed\"][_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .status-text[data-status=\"failed\"][_ngcontent-%COMP%] { color: var(--mj-status-error); }\n .status-text[data-status=\"error\"][_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .status-text[data-status=\"running\"][_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n .status-text[data-status=\"pending\"][_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .status-text[data-status=\"skipped\"][_ngcontent-%COMP%] { color: var(--mj-text-muted); }\n\n \n\n .card-tests-preview[_ngcontent-%COMP%] {\n padding: 10px 16px;\n border-top: 1px solid var(--mj-border-default);\n }\n\n .preview-test-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n font-size: 12px;\n }\n\n .preview-dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .preview-dot[data-status=\"passed\"][_ngcontent-%COMP%] { background: var(--mj-status-success); }\n .preview-dot[data-status=\"failed\"][_ngcontent-%COMP%] { background: var(--mj-status-error); }\n .preview-dot[data-status=\"error\"][_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n .preview-dot[data-status=\"running\"][_ngcontent-%COMP%] { background: var(--mj-brand-primary); }\n .preview-dot[data-status=\"\"][_ngcontent-%COMP%] { background: var(--mj-text-disabled); }\n\n .preview-test-name[_ngcontent-%COMP%] {\n flex: 1;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .preview-score[_ngcontent-%COMP%] {\n font-weight: 600;\n font-size: 11px;\n min-width: 32px;\n text-align: right;\n }\n\n .preview-score.good[_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .preview-score.warn[_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .preview-score.bad[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n\n .preview-bar[_ngcontent-%COMP%] {\n width: 48px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n flex-shrink: 0;\n }\n\n .preview-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .good-bg[_ngcontent-%COMP%] { background: var(--mj-status-success); }\n .warn-bg[_ngcontent-%COMP%] { background: var(--mj-status-warning); }\n .bad-bg[_ngcontent-%COMP%] { background: var(--mj-status-error); }\n\n .preview-status[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n min-width: 44px;\n text-align: right;\n }\n\n .preview-status[data-status=\"passed\"][_ngcontent-%COMP%] { color: var(--mj-status-success); }\n .preview-status[data-status=\"failed\"][_ngcontent-%COMP%] { color: var(--mj-status-error); }\n .preview-status[data-status=\"error\"][_ngcontent-%COMP%] { color: var(--mj-status-warning); }\n .preview-status[data-status=\"\"][_ngcontent-%COMP%] { color: var(--mj-text-disabled); }\n\n .preview-more[_ngcontent-%COMP%] {\n padding: 4px 0 0;\n font-size: 11px;\n color: var(--mj-text-disabled);\n font-style: italic;\n }\n\n \n\n .card-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n }\n\n \n\n .empty-state[_ngcontent-%COMP%] {\n padding: 80px 40px;\n text-align: center;\n }\n\n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-text-disabled);\n margin-bottom: 16px;\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 16px;\n color: var(--mj-text-muted);\n margin: 0 0 8px 0;\n }\n\n .empty-hint[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-disabled);\n }\n\n \n\n\n\n .slideout-backdrop[_ngcontent-%COMP%] {\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 animation: _ngcontent-%COMP%_fadeInBackdrop 0.2s ease;\n }\n\n @keyframes _ngcontent-%COMP%_fadeInBackdrop {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .slideout-panel[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n right: -100%;\n height: 100vh;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-lg);\n z-index: 1000;\n transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .slideout-panel.open[_ngcontent-%COMP%] {\n right: 0;\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n width: 5px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s;\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%]:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 50%, transparent);\n }\n\n .slideout-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n\n .slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .slideout-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .slideout-title-icon[_ngcontent-%COMP%] {\n font-size: 18px;\n color: var(--mj-brand-primary);\n }\n\n .slideout-title-text[_ngcontent-%COMP%] {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .slideout-close-btn[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\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: 6px;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n }\n\n .slideout-close-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n }\n\n \n\n .slideout-type-toggle[_ngcontent-%COMP%] {\n display: flex;\n gap: 0;\n padding: 16px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%]:first-child {\n border-radius: 8px 0 0 8px;\n border-right: none;\n }\n\n .type-toggle-btn[_ngcontent-%COMP%]:last-child {\n border-radius: 0 8px 8px 0;\n }\n\n .type-toggle-btn.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .type-toggle-btn[_ngcontent-%COMP%]:hover:not(.active) {\n background: var(--mj-border-default);\n }\n\n \n\n .slideout-error[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 16px 24px 0;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n border-radius: 8px;\n color: var(--mj-status-error);\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .slideout-error[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n \n\n .slideout-body[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n }\n\n .form-section[_ngcontent-%COMP%] {\n margin-bottom: 24px;\n }\n\n .form-section-title[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.8px;\n margin-bottom: 14px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .form-group[_ngcontent-%COMP%] {\n margin-bottom: 16px;\n }\n\n .form-label[_ngcontent-%COMP%] {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 6px;\n }\n\n .form-required[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .form-input[_ngcontent-%COMP%], \n .form-textarea[_ngcontent-%COMP%] {\n width: 100%;\n padding: 10px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: border-color 0.2s ease;\n outline: none;\n box-sizing: border-box;\n font-family: inherit;\n }\n\n .form-input[_ngcontent-%COMP%]:focus, \n .form-textarea[_ngcontent-%COMP%]:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n .form-input[_ngcontent-%COMP%]::placeholder, \n .form-textarea[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .form-textarea[_ngcontent-%COMP%] {\n resize: vertical;\n min-height: 80px;\n }\n\n .form-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .form-hint[_ngcontent-%COMP%] {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 4px;\n }\n\n \n\n .slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n }\n\n .slideout-footer[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n min-width: 100px;\n justify-content: center;\n }\n\n .slideout-footer[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n }\n\n \n\n mark.search-highlight {\n background: color-mix(in srgb, var(--mj-status-warning) 20%, var(--mj-bg-surface));\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: 700;\n }\n\n \n\n\n\n @media (max-width: 1200px) {\n .card-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 900px) {\n .sidebar[_ngcontent-%COMP%] {\n display: none;\n }\n\n .toolbar[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n }\n\n .toolbar-left[_ngcontent-%COMP%], .toolbar-right[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n justify-content: center;\n }\n }\n\n @media (max-width: 600px) {\n .content-area[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .card-stats[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .slideout-panel[_ngcontent-%COMP%] {\n width: 100% !important;\n }\n\n .slideout-resize-handle[_ngcontent-%COMP%] {\n display: none;\n }\n\n .form-row[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n }"], changeDetection: 0 });
|
|
1666
1666
|
}
|
|
1667
1667
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingExplorerComponent, [{
|
|
1668
1668
|
type: Component,
|
|
@@ -2217,7 +2217,7 @@ export class TestingExplorerComponent {
|
|
|
2217
2217
|
</div>
|
|
2218
2218
|
}
|
|
2219
2219
|
</div>
|
|
2220
|
-
`, styles: ["\n /* ==========================================\n Testing Explorer Component\n ========================================== */\n\n :host {\n display: block;\n height: 100%;\n width: 100%;\n }\n\n /* Loading */\n .explorer-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: #f5f6fa;\n }\n\n /* Layout */\n .explorer-layout {\n display: flex;\n height: 100%;\n background: #f5f6fa;\n overflow: hidden;\n }\n\n /* ==========================================\n Sidebar\n ========================================== */\n .sidebar {\n width: 280px;\n min-width: 280px;\n background: #fff;\n border-right: 1px solid #e8ecef;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: width 0.2s ease, min-width 0.2s ease;\n }\n\n .sidebar.collapsed {\n width: 48px;\n min-width: 48px;\n }\n\n .sidebar-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px;\n border-bottom: 1px solid #e8ecef;\n }\n\n .sidebar-header h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .sidebar.collapsed .sidebar-header h3 {\n display: none;\n }\n\n .sidebar-toggle {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n color: #636e72;\n cursor: pointer;\n font-size: 11px;\n transition: all 0.15s ease;\n }\n\n .sidebar-toggle:hover {\n background: #f5f6fa;\n color: #2d3436;\n }\n\n .sidebar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n }\n\n .sidebar-section {\n margin-bottom: 8px;\n }\n\n .sidebar-section-title {\n padding: 8px 16px 4px;\n font-size: 10px;\n font-weight: 700;\n color: #b2bec3;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .sidebar-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 13px;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .sidebar-item:hover {\n background: #f5f6fa;\n color: #2d3436;\n }\n\n .sidebar-item.active {\n background: #e0f2f1;\n color: #00897b;\n font-weight: 600;\n }\n\n .sidebar-item i {\n font-size: 12px;\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n }\n\n .sidebar-item span:not(.sidebar-count) {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .sidebar-count {\n font-size: 11px;\n color: #b2bec3;\n font-weight: 500;\n flex-shrink: 0;\n }\n\n .sidebar-item.active .sidebar-count {\n color: #00897b;\n }\n\n .sidebar-empty {\n padding: 8px 16px;\n font-size: 12px;\n color: #b2bec3;\n font-style: italic;\n }\n\n /* Tree nodes */\n .suite-tree-item {\n gap: 6px;\n }\n\n .tree-toggle {\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n color: #b2bec3;\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n border-radius: 4px;\n padding: 0;\n }\n\n .tree-toggle:hover {\n background: #e8ecef;\n color: #636e72;\n }\n\n .tree-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* ==========================================\n Main Content\n ========================================== */\n .main-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* Toolbar */\n .toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n padding: 16px 24px;\n background: #fff;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .toolbar-left {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .toolbar-search-box {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: #f5f6fa;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n min-width: 240px;\n max-width: 360px;\n flex: 1;\n transition: border-color 0.2s ease;\n }\n\n .toolbar-search-box:focus-within {\n border-color: #00897b;\n background: #fff;\n }\n\n .toolbar-search-box i {\n color: #b2bec3;\n font-size: 13px;\n }\n\n .toolbar-search-box input {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: #2d3436;\n }\n\n .toolbar-search-box input::placeholder {\n color: #b2bec3;\n }\n\n .clear-search {\n border: none;\n background: transparent;\n color: #b2bec3;\n cursor: pointer;\n padding: 2px 4px;\n border-radius: 4px;\n }\n\n .clear-search:hover {\n color: #636e72;\n background: #e8ecef;\n }\n\n .status-chips {\n display: flex;\n gap: 6px;\n }\n\n .chip {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n background: #f5f6fa;\n border: 1px solid transparent;\n border-radius: 16px;\n font-size: 12px;\n font-weight: 600;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .chip:hover {\n background: #e8ecef;\n }\n\n .chip.active {\n color: #fff;\n }\n\n .chip.active[data-status=\"active\"] {\n background: #2e7d32;\n border-color: #2e7d32;\n }\n\n .chip.active[data-status=\"pending\"] {\n background: #e65100;\n border-color: #e65100;\n }\n\n .chip.active[data-status=\"disabled\"] {\n background: #636e72;\n border-color: #636e72;\n }\n\n .toolbar-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n }\n\n .result-count {\n font-size: 12px;\n color: #b2bec3;\n font-weight: 500;\n white-space: nowrap;\n }\n\n .view-toggle {\n display: flex;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n overflow: hidden;\n }\n\n .view-btn {\n width: 34px;\n height: 34px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #fff;\n border: none;\n color: #b2bec3;\n cursor: pointer;\n font-size: 13px;\n transition: all 0.15s ease;\n }\n\n .view-btn:not(:last-child) {\n border-right: 1px solid #e8ecef;\n }\n\n .view-btn:hover {\n background: #f5f6fa;\n color: #636e72;\n }\n\n .view-btn.active {\n background: #00897b;\n color: #fff;\n }\n\n /* Buttons */\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n }\n\n .btn-primary {\n background: #00897b;\n color: #fff;\n }\n\n .btn-primary:hover {\n background: #00796b;\n transform: translateY(-1px);\n box-shadow: 0 2px 8px rgba(0, 137, 123, 0.3);\n }\n\n .btn-secondary {\n background: #fff;\n color: #636e72;\n border: 1px solid #e8ecef;\n }\n\n .btn-secondary:hover {\n background: #f5f6fa;\n }\n\n .btn-sm {\n padding: 6px 12px;\n font-size: 12px;\n }\n\n /* Toggle Bar */\n .toggle-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 24px;\n background: #fafafa;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .toggle-group {\n display: flex;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n overflow: hidden;\n }\n\n .toggle-btn {\n padding: 6px 16px;\n background: #fff;\n border: none;\n font-size: 12px;\n font-weight: 600;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .toggle-btn:not(:last-child) {\n border-right: 1px solid #e8ecef;\n }\n\n .toggle-btn.active {\n background: #00897b;\n color: #fff;\n }\n\n .toggle-btn:hover:not(.active) {\n background: #f5f6fa;\n }\n\n .sort-indicator {\n display: flex;\n align-items: center;\n }\n\n .sort-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: transparent;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: #636e72;\n cursor: pointer;\n }\n\n .sort-btn:hover {\n background: #f5f6fa;\n }\n\n .sort-btn i {\n font-size: 11px;\n }\n\n /* Content Area */\n .content-area {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .content-section {\n margin-bottom: 32px;\n }\n\n .section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 0 16px 0;\n font-size: 16px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .section-title i {\n color: #00897b;\n font-size: 14px;\n }\n\n .section-count {\n font-size: 12px;\n font-weight: 600;\n color: #b2bec3;\n background: #f5f6fa;\n padding: 2px 8px;\n border-radius: 10px;\n }\n\n /* Card Grid */\n .card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));\n gap: 16px;\n }\n\n /* Suite Card */\n .suite-card,\n .test-card {\n background: #fff;\n border: 1px solid #e8ecef;\n border-radius: 10px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .suite-card:hover,\n .test-card:hover {\n border-color: #b2dfdb;\n box-shadow: 0 4px 16px rgba(0, 137, 123, 0.1);\n }\n\n .card-header {\n padding: 16px 16px 12px;\n }\n\n .card-title-row {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n }\n\n .card-icon {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .suite-icon {\n color: #00897b;\n }\n\n .test-icon {\n color: #6366f1;\n }\n\n .card-name {\n flex: 1;\n font-size: 14px;\n font-weight: 700;\n color: #2d3436;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .status-badge {\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n flex-shrink: 0;\n }\n\n .status-badge[data-status=\"active\"] {\n background: #e8f5e9;\n color: #2e7d32;\n }\n\n .status-badge[data-status=\"pending\"] {\n background: #fff3e0;\n color: #e65100;\n }\n\n .status-badge[data-status=\"disabled\"] {\n background: #f5f6fa;\n color: #636e72;\n }\n\n .card-subtitle {\n font-size: 12px;\n color: #636e72;\n margin-bottom: 6px;\n }\n\n .dot-sep {\n display: inline-block;\n width: 3px;\n height: 3px;\n background: #b2bec3;\n border-radius: 50%;\n vertical-align: middle;\n margin: 0 6px;\n }\n\n .card-description {\n margin: 0;\n font-size: 12px;\n color: #636e72;\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta-row {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 8px;\n }\n\n .meta-item {\n font-size: 11px;\n color: #636e72;\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .meta-item i {\n font-size: 10px;\n color: #b2bec3;\n }\n\n .card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 8px;\n }\n\n .tag {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n background: #f5f6fa;\n color: #636e72;\n border-radius: 4px;\n }\n\n .tag-more {\n background: #e8ecef;\n color: #b2bec3;\n }\n\n /* Card Stats */\n .card-stats {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1px;\n background: #e8ecef;\n border-top: 1px solid #e8ecef;\n }\n\n .stat {\n background: #fafafa;\n padding: 10px 12px;\n text-align: center;\n }\n\n .stat-label {\n display: block;\n font-size: 10px;\n font-weight: 600;\n color: #b2bec3;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n }\n\n .stat-value {\n font-size: 14px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .stat-value.good { color: #2e7d32; }\n .stat-value.warn { color: #e65100; }\n .stat-value.bad { color: #c62828; }\n\n .status-text[data-status=\"passed\"] { color: #2e7d32; }\n .status-text[data-status=\"failed\"] { color: #c62828; }\n .status-text[data-status=\"error\"] { color: #e65100; }\n .status-text[data-status=\"running\"] { color: #1565c0; }\n .status-text[data-status=\"pending\"] { color: #e65100; }\n .status-text[data-status=\"skipped\"] { color: #636e72; }\n\n /* Suite Tests Preview */\n .card-tests-preview {\n padding: 10px 16px;\n border-top: 1px solid #e8ecef;\n }\n\n .preview-test-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n font-size: 12px;\n }\n\n .preview-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .preview-dot[data-status=\"passed\"] { background: #2e7d32; }\n .preview-dot[data-status=\"failed\"] { background: #c62828; }\n .preview-dot[data-status=\"error\"] { background: #e65100; }\n .preview-dot[data-status=\"running\"] { background: #1565c0; }\n .preview-dot[data-status=\"\"] { background: #b2bec3; }\n\n .preview-test-name {\n flex: 1;\n color: #2d3436;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .preview-score {\n font-weight: 600;\n font-size: 11px;\n min-width: 32px;\n text-align: right;\n }\n\n .preview-score.good { color: #2e7d32; }\n .preview-score.warn { color: #e65100; }\n .preview-score.bad { color: #c62828; }\n\n .preview-bar {\n width: 48px;\n height: 4px;\n background: #e8ecef;\n border-radius: 2px;\n overflow: hidden;\n flex-shrink: 0;\n }\n\n .preview-bar-fill {\n height: 100%;\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .good-bg { background: #2e7d32; }\n .warn-bg { background: #e65100; }\n .bad-bg { background: #c62828; }\n\n .preview-status {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n min-width: 44px;\n text-align: right;\n }\n\n .preview-status[data-status=\"passed\"] { color: #2e7d32; }\n .preview-status[data-status=\"failed\"] { color: #c62828; }\n .preview-status[data-status=\"error\"] { color: #e65100; }\n .preview-status[data-status=\"\"] { color: #b2bec3; }\n\n .preview-more {\n padding: 4px 0 0;\n font-size: 11px;\n color: #b2bec3;\n font-style: italic;\n }\n\n /* Card Actions */\n .card-actions {\n display: flex;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid #e8ecef;\n background: #fafafa;\n }\n\n /* Empty State */\n .empty-state {\n padding: 80px 40px;\n text-align: center;\n }\n\n .empty-state i {\n font-size: 48px;\n color: #b2bec3;\n margin-bottom: 16px;\n }\n\n .empty-state p {\n font-size: 16px;\n color: #636e72;\n margin: 0 0 8px 0;\n }\n\n .empty-hint {\n font-size: 13px;\n color: #b2bec3;\n }\n\n /* ==========================================\n Slideout Panel\n ========================================== */\n .slideout-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.3);\n z-index: 999;\n animation: fadeInBackdrop 0.2s ease;\n }\n\n @keyframes fadeInBackdrop {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .slideout-panel {\n position: fixed;\n top: 0;\n right: -100%;\n height: 100vh;\n background: #fff;\n box-shadow: -4px 0 20px rgba(0, 0, 0, 0.15);\n z-index: 1000;\n transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .slideout-panel.open {\n right: 0;\n }\n\n .slideout-resize-handle {\n position: absolute;\n top: 0;\n left: 0;\n width: 5px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s;\n }\n\n .slideout-resize-handle:hover {\n background: rgba(0, 137, 123, 0.3);\n }\n\n .slideout-resize-handle:active {\n background: rgba(0, 137, 123, 0.5);\n }\n\n .slideout-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n\n .slideout-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .slideout-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .slideout-title-icon {\n font-size: 18px;\n color: #00897b;\n }\n\n .slideout-title-text {\n font-size: 18px;\n font-weight: 700;\n color: #2d3436;\n }\n\n .slideout-close-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 1px solid #e8ecef;\n border-radius: 6px;\n color: #636e72;\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n }\n\n .slideout-close-btn:hover {\n background: #f5f6fa;\n color: #2d3436;\n border-color: #ccc;\n }\n\n /* Type Toggle */\n .slideout-type-toggle {\n display: flex;\n gap: 0;\n padding: 16px 24px;\n border-bottom: 1px solid #e8ecef;\n flex-shrink: 0;\n }\n\n .type-toggle-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: #f5f6fa;\n border: 1px solid #e8ecef;\n font-size: 13px;\n font-weight: 600;\n color: #636e72;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .type-toggle-btn:first-child {\n border-radius: 8px 0 0 8px;\n border-right: none;\n }\n\n .type-toggle-btn:last-child {\n border-radius: 0 8px 8px 0;\n }\n\n .type-toggle-btn.active {\n background: #00897b;\n border-color: #00897b;\n color: #fff;\n }\n\n .type-toggle-btn:hover:not(.active) {\n background: #e8ecef;\n }\n\n /* Error Banner */\n .slideout-error {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 16px 24px 0;\n padding: 12px 16px;\n background: #fff5f5;\n border: 1px solid #fed7d7;\n border-radius: 8px;\n color: #c53030;\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .slideout-error i {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n /* Slideout Body */\n .slideout-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n }\n\n .form-section {\n margin-bottom: 24px;\n }\n\n .form-section-title {\n font-size: 12px;\n font-weight: 700;\n color: #b2bec3;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n margin-bottom: 14px;\n padding-bottom: 8px;\n border-bottom: 1px solid #f0f0f0;\n }\n\n .form-group {\n margin-bottom: 16px;\n }\n\n .form-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: #2d3436;\n margin-bottom: 6px;\n }\n\n .form-required {\n color: #e53e3e;\n }\n\n .form-input,\n .form-textarea {\n width: 100%;\n padding: 10px 14px;\n background: #fff;\n border: 1px solid #e8ecef;\n border-radius: 8px;\n font-size: 13px;\n color: #2d3436;\n transition: border-color 0.2s ease;\n outline: none;\n box-sizing: border-box;\n font-family: inherit;\n }\n\n .form-input:focus,\n .form-textarea:focus {\n border-color: #00897b;\n box-shadow: 0 0 0 3px rgba(0, 137, 123, 0.1);\n }\n\n .form-input::placeholder,\n .form-textarea::placeholder {\n color: #b2bec3;\n }\n\n .form-textarea {\n resize: vertical;\n min-height: 80px;\n }\n\n .form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .form-hint {\n display: block;\n font-size: 11px;\n color: #b2bec3;\n margin-top: 4px;\n }\n\n /* Slideout Footer */\n .slideout-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px;\n border-top: 1px solid #e8ecef;\n background: #fafafa;\n flex-shrink: 0;\n }\n\n .slideout-footer .btn {\n min-width: 100px;\n justify-content: center;\n }\n\n .slideout-footer .btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n }\n\n /* Search Highlight */\n ::ng-deep mark.search-highlight {\n background: #fff9c4;\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: 700;\n }\n\n /* ==========================================\n Responsive\n ========================================== */\n @media (max-width: 1200px) {\n .card-grid {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 900px) {\n .sidebar {\n display: none;\n }\n\n .toolbar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .toolbar-left, .toolbar-right {\n flex-wrap: wrap;\n justify-content: center;\n }\n }\n\n @media (max-width: 600px) {\n .content-area {\n padding: 16px;\n }\n\n .card-stats {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .slideout-panel {\n width: 100% !important;\n }\n\n .slideout-resize-handle {\n display: none;\n }\n\n .form-row {\n grid-template-columns: 1fr;\n }\n }\n "] }]
|
|
2220
|
+
`, styles: ["\n /* ==========================================\n Testing Explorer Component\n ========================================== */\n\n :host {\n display: block;\n height: 100%;\n width: 100%;\n }\n\n /* Loading */\n .explorer-loading {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n min-height: 400px;\n background: var(--mj-bg-surface-sunken);\n }\n\n /* Layout */\n .explorer-layout {\n display: flex;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n }\n\n /* ==========================================\n Sidebar\n ========================================== */\n .sidebar {\n width: 280px;\n min-width: 280px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n transition: width 0.2s ease, min-width 0.2s ease;\n }\n\n .sidebar.collapsed {\n width: 48px;\n min-width: 48px;\n }\n\n .sidebar-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .sidebar-header h3 {\n margin: 0;\n font-size: 15px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .sidebar.collapsed .sidebar-header h3 {\n display: none;\n }\n\n .sidebar-toggle {\n width: 28px;\n height: 28px;\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: 6px;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 11px;\n transition: all 0.15s ease;\n }\n\n .sidebar-toggle:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n }\n\n .sidebar-content {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n }\n\n .sidebar-section {\n margin-bottom: 8px;\n }\n\n .sidebar-section-title {\n padding: 8px 16px 4px;\n font-size: 10px;\n font-weight: 700;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .sidebar-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 16px;\n font-size: 13px;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n overflow: hidden;\n }\n\n .sidebar-item:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n }\n\n .sidebar-item.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n }\n\n .sidebar-item i {\n font-size: 12px;\n width: 16px;\n text-align: center;\n flex-shrink: 0;\n }\n\n .sidebar-item span:not(.sidebar-count) {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .sidebar-count {\n font-size: 11px;\n color: var(--mj-text-disabled);\n font-weight: 500;\n flex-shrink: 0;\n }\n\n .sidebar-item.active .sidebar-count {\n color: var(--mj-brand-primary);\n }\n\n .sidebar-empty {\n padding: 8px 16px;\n font-size: 12px;\n color: var(--mj-text-disabled);\n font-style: italic;\n }\n\n /* Tree nodes */\n .suite-tree-item {\n gap: 6px;\n }\n\n .tree-toggle {\n width: 18px;\n height: 18px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n color: var(--mj-text-disabled);\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n border-radius: 4px;\n padding: 0;\n }\n\n .tree-toggle:hover {\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n }\n\n .tree-name {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n /* ==========================================\n Main Content\n ========================================== */\n .main-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n /* Toolbar */\n .toolbar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n padding: 16px 24px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .toolbar-left {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .toolbar-search-box {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n min-width: 240px;\n max-width: 360px;\n flex: 1;\n transition: border-color 0.2s ease;\n }\n\n .toolbar-search-box:focus-within {\n border-color: var(--mj-brand-primary);\n background: var(--mj-bg-surface);\n }\n\n .toolbar-search-box i {\n color: var(--mj-text-disabled);\n font-size: 13px;\n }\n\n .toolbar-search-box input {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--mj-text-primary);\n }\n\n .toolbar-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 .status-chips {\n display: flex;\n gap: 6px;\n }\n\n .chip {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid transparent;\n border-radius: 16px;\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 .chip:hover {\n background: var(--mj-border-default);\n }\n\n .chip.active {\n color: var(--mj-text-inverse);\n }\n\n .chip.active[data-status=\"active\"] {\n background: var(--mj-status-success);\n border-color: var(--mj-status-success);\n }\n\n .chip.active[data-status=\"pending\"] {\n background: var(--mj-status-warning);\n border-color: var(--mj-status-warning);\n }\n\n .chip.active[data-status=\"disabled\"] {\n background: var(--mj-text-muted);\n border-color: var(--mj-text-muted);\n }\n\n .toolbar-right {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\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 .view-toggle {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n }\n\n .view-btn {\n width: 34px;\n height: 34px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface);\n border: none;\n color: var(--mj-text-disabled);\n cursor: pointer;\n font-size: 13px;\n transition: all 0.15s ease;\n }\n\n .view-btn:not(:last-child) {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .view-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n }\n\n .view-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n /* Buttons */\n .btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 9px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n }\n\n .btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .btn-primary:hover {\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-muted);\n border: 1px solid var(--mj-border-default);\n }\n\n .btn-secondary:hover {\n background: var(--mj-bg-surface-sunken);\n }\n\n .btn-sm {\n padding: 6px 12px;\n font-size: 12px;\n }\n\n /* Toggle Bar */\n .toggle-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 10px 24px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .toggle-group {\n display: flex;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n overflow: hidden;\n }\n\n .toggle-btn {\n padding: 6px 16px;\n background: var(--mj-bg-surface);\n border: none;\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:not(:last-child) {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .toggle-btn.active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .toggle-btn:hover:not(.active) {\n background: var(--mj-bg-surface-sunken);\n }\n\n .sort-indicator {\n display: flex;\n align-items: center;\n }\n\n .sort-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n background: transparent;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 12px;\n font-weight: 500;\n color: var(--mj-text-muted);\n cursor: pointer;\n }\n\n .sort-btn:hover {\n background: var(--mj-bg-surface-sunken);\n }\n\n .sort-btn i {\n font-size: 11px;\n }\n\n /* Content Area */\n .content-area {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n }\n\n .content-section {\n margin-bottom: 32px;\n }\n\n .section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0 0 16px 0;\n font-size: 16px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .section-title i {\n color: var(--mj-brand-primary);\n font-size: 14px;\n }\n\n .section-count {\n font-size: 12px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n }\n\n /* Card Grid */\n .card-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(380px, 1fr));\n gap: 16px;\n }\n\n /* Suite Card */\n .suite-card,\n .test-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n overflow: hidden;\n transition: all 0.2s ease;\n }\n\n .suite-card:hover,\n .test-card:hover {\n border-color: color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-md);\n }\n\n .card-header {\n padding: 16px 16px 12px;\n }\n\n .card-title-row {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n }\n\n .card-icon {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n .suite-icon {\n color: var(--mj-brand-primary);\n }\n\n .test-icon {\n color: var(--mj-brand-primary);\n }\n\n .card-name {\n flex: 1;\n font-size: 14px;\n font-weight: 700;\n color: var(--mj-text-primary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .status-badge {\n font-size: 10px;\n font-weight: 700;\n padding: 3px 8px;\n border-radius: 4px;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n flex-shrink: 0;\n }\n\n .status-badge[data-status=\"active\"] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .status-badge[data-status=\"pending\"] {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n }\n\n .status-badge[data-status=\"disabled\"] {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n }\n\n .card-subtitle {\n font-size: 12px;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n }\n\n .dot-sep {\n display: inline-block;\n width: 3px;\n height: 3px;\n background: var(--mj-text-disabled);\n border-radius: 50%;\n vertical-align: middle;\n margin: 0 6px;\n }\n\n .card-description {\n margin: 0;\n font-size: 12px;\n color: var(--mj-text-muted);\n line-height: 1.5;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n\n .card-meta-row {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-top: 8px;\n }\n\n .meta-item {\n font-size: 11px;\n color: var(--mj-text-muted);\n display: flex;\n align-items: center;\n gap: 4px;\n }\n\n .meta-item i {\n font-size: 10px;\n color: var(--mj-text-disabled);\n }\n\n .card-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n margin-top: 8px;\n }\n\n .tag {\n font-size: 10px;\n font-weight: 600;\n padding: 2px 8px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n border-radius: 4px;\n }\n\n .tag-more {\n background: var(--mj-border-default);\n color: var(--mj-text-disabled);\n }\n\n /* Card Stats */\n .card-stats {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 1px;\n background: var(--mj-border-default);\n border-top: 1px solid var(--mj-border-default);\n }\n\n .stat {\n background: var(--mj-bg-surface-card);\n padding: 10px 12px;\n text-align: center;\n }\n\n .stat-label {\n display: block;\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n margin-bottom: 2px;\n }\n\n .stat-value {\n font-size: 14px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .stat-value.good { color: var(--mj-status-success); }\n .stat-value.warn { color: var(--mj-status-warning); }\n .stat-value.bad { color: var(--mj-status-error); }\n\n .status-text[data-status=\"passed\"] { color: var(--mj-status-success); }\n .status-text[data-status=\"failed\"] { color: var(--mj-status-error); }\n .status-text[data-status=\"error\"] { color: var(--mj-status-warning); }\n .status-text[data-status=\"running\"] { color: var(--mj-brand-primary); }\n .status-text[data-status=\"pending\"] { color: var(--mj-status-warning); }\n .status-text[data-status=\"skipped\"] { color: var(--mj-text-muted); }\n\n /* Suite Tests Preview */\n .card-tests-preview {\n padding: 10px 16px;\n border-top: 1px solid var(--mj-border-default);\n }\n\n .preview-test-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n font-size: 12px;\n }\n\n .preview-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n\n .preview-dot[data-status=\"passed\"] { background: var(--mj-status-success); }\n .preview-dot[data-status=\"failed\"] { background: var(--mj-status-error); }\n .preview-dot[data-status=\"error\"] { background: var(--mj-status-warning); }\n .preview-dot[data-status=\"running\"] { background: var(--mj-brand-primary); }\n .preview-dot[data-status=\"\"] { background: var(--mj-text-disabled); }\n\n .preview-test-name {\n flex: 1;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .preview-score {\n font-weight: 600;\n font-size: 11px;\n min-width: 32px;\n text-align: right;\n }\n\n .preview-score.good { color: var(--mj-status-success); }\n .preview-score.warn { color: var(--mj-status-warning); }\n .preview-score.bad { color: var(--mj-status-error); }\n\n .preview-bar {\n width: 48px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n flex-shrink: 0;\n }\n\n .preview-bar-fill {\n height: 100%;\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .good-bg { background: var(--mj-status-success); }\n .warn-bg { background: var(--mj-status-warning); }\n .bad-bg { background: var(--mj-status-error); }\n\n .preview-status {\n font-size: 10px;\n font-weight: 600;\n text-transform: uppercase;\n min-width: 44px;\n text-align: right;\n }\n\n .preview-status[data-status=\"passed\"] { color: var(--mj-status-success); }\n .preview-status[data-status=\"failed\"] { color: var(--mj-status-error); }\n .preview-status[data-status=\"error\"] { color: var(--mj-status-warning); }\n .preview-status[data-status=\"\"] { color: var(--mj-text-disabled); }\n\n .preview-more {\n padding: 4px 0 0;\n font-size: 11px;\n color: var(--mj-text-disabled);\n font-style: italic;\n }\n\n /* Card Actions */\n .card-actions {\n display: flex;\n gap: 8px;\n padding: 12px 16px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n }\n\n /* Empty State */\n .empty-state {\n padding: 80px 40px;\n text-align: center;\n }\n\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-disabled);\n margin-bottom: 16px;\n }\n\n .empty-state 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 /* ==========================================\n Slideout Panel\n ========================================== */\n .slideout-backdrop {\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 animation: fadeInBackdrop 0.2s ease;\n }\n\n @keyframes fadeInBackdrop {\n from { opacity: 0; }\n to { opacity: 1; }\n }\n\n .slideout-panel {\n position: fixed;\n top: 0;\n right: -100%;\n height: 100vh;\n background: var(--mj-bg-surface);\n box-shadow: var(--mj-shadow-lg);\n z-index: 1000;\n transition: right 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .slideout-panel.open {\n right: 0;\n }\n\n .slideout-resize-handle {\n position: absolute;\n top: 0;\n left: 0;\n width: 5px;\n height: 100%;\n cursor: col-resize;\n background: transparent;\n z-index: 10;\n transition: background 0.2s;\n }\n\n .slideout-resize-handle:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n }\n\n .slideout-resize-handle:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 50%, transparent);\n }\n\n .slideout-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n }\n\n .slideout-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .slideout-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n\n .slideout-title-icon {\n font-size: 18px;\n color: var(--mj-brand-primary);\n }\n\n .slideout-title-text {\n font-size: 18px;\n font-weight: 700;\n color: var(--mj-text-primary);\n }\n\n .slideout-close-btn {\n width: 32px;\n height: 32px;\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: 6px;\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 14px;\n transition: all 0.15s ease;\n }\n\n .slideout-close-btn:hover {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n }\n\n /* Type Toggle */\n .slideout-type-toggle {\n display: flex;\n gap: 0;\n padding: 16px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n }\n\n .type-toggle-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px 16px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.15s ease;\n }\n\n .type-toggle-btn:first-child {\n border-radius: 8px 0 0 8px;\n border-right: none;\n }\n\n .type-toggle-btn:last-child {\n border-radius: 0 8px 8px 0;\n }\n\n .type-toggle-btn.active {\n background: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n }\n\n .type-toggle-btn:hover:not(.active) {\n background: var(--mj-border-default);\n }\n\n /* Error Banner */\n .slideout-error {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 16px 24px 0;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-bg-surface));\n border-radius: 8px;\n color: var(--mj-status-error);\n font-size: 13px;\n flex-shrink: 0;\n }\n\n .slideout-error i {\n font-size: 14px;\n flex-shrink: 0;\n }\n\n /* Slideout Body */\n .slideout-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px 24px;\n }\n\n .form-section {\n margin-bottom: 24px;\n }\n\n .form-section-title {\n font-size: 12px;\n font-weight: 700;\n color: var(--mj-text-disabled);\n text-transform: uppercase;\n letter-spacing: 0.8px;\n margin-bottom: 14px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .form-group {\n margin-bottom: 16px;\n }\n\n .form-label {\n display: block;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-primary);\n margin-bottom: 6px;\n }\n\n .form-required {\n color: var(--mj-status-error);\n }\n\n .form-input,\n .form-textarea {\n width: 100%;\n padding: 10px 14px;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n font-size: 13px;\n color: var(--mj-text-primary);\n transition: border-color 0.2s ease;\n outline: none;\n box-sizing: border-box;\n font-family: inherit;\n }\n\n .form-input:focus,\n .form-textarea:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n }\n\n .form-input::placeholder,\n .form-textarea::placeholder {\n color: var(--mj-text-disabled);\n }\n\n .form-textarea {\n resize: vertical;\n min-height: 80px;\n }\n\n .form-row {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 12px;\n }\n\n .form-hint {\n display: block;\n font-size: 11px;\n color: var(--mj-text-disabled);\n margin-top: 4px;\n }\n\n /* Slideout Footer */\n .slideout-footer {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n flex-shrink: 0;\n }\n\n .slideout-footer .btn {\n min-width: 100px;\n justify-content: center;\n }\n\n .slideout-footer .btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none;\n box-shadow: none;\n }\n\n /* Search Highlight */\n ::ng-deep mark.search-highlight {\n background: color-mix(in srgb, var(--mj-status-warning) 20%, var(--mj-bg-surface));\n color: inherit;\n padding: 1px 2px;\n border-radius: 2px;\n font-weight: 700;\n }\n\n /* ==========================================\n Responsive\n ========================================== */\n @media (max-width: 1200px) {\n .card-grid {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 900px) {\n .sidebar {\n display: none;\n }\n\n .toolbar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .toolbar-left, .toolbar-right {\n flex-wrap: wrap;\n justify-content: center;\n }\n }\n\n @media (max-width: 600px) {\n .content-area {\n padding: 16px;\n }\n\n .card-stats {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .slideout-panel {\n width: 100% !important;\n }\n\n .slideout-resize-handle {\n display: none;\n }\n\n .form-row {\n grid-template-columns: 1fr;\n }\n }\n "] }]
|
|
2221
2221
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ViewContainerRef }, { type: i1.TestingDialogService }, { type: i2.TestingInstrumentationService }], null); })();
|
|
2222
2222
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingExplorerComponent, { className: "TestingExplorerComponent", filePath: "src/Testing/components/testing-explorer.component.ts", lineNumber: 1805 }); })();
|
|
2223
2223
|
//# sourceMappingURL=testing-explorer.component.js.map
|