import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil, map } from 'rxjs/operators';
import { CompositeKey } from '@memberjunction/core';
import { SharedService } from '@memberjunction/ng-shared';
import { TestEngineBase } from '@memberjunction/testing-engine-base';
import * as i0 from "@angular/core";
import * as i1 from "../services/testing-instrumentation.service";
import * as i2 from "@memberjunction/ng-testing";
import * as i3 from "@angular/common";
import * as i4 from "@memberjunction/ng-ui-components";
import * as i5 from "@memberjunction/ng-shared-generic";
import * as i6 from "../../AI/components/widgets/kpi-card.component";
const _forTrack0 = ($index, $item) => $item.title;
const _forTrack1 = ($index, $item) => $item.id;
function TestingDashboardTabComponent_Conditional_0_ng_container_0_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementContainer(0);
} }
function TestingDashboardTabComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵtemplate(0, TestingDashboardTabComponent_Conditional_0_ng_container_0_Template, 1, 0, "ng-container", 1);
} if (rf & 2) {
    i0.ɵɵnextContext();
    const content_r1 = i0.ɵɵreference(3);
    i0.ɵɵproperty("ngTemplateOutlet", content_r1);
} }
function TestingDashboardTabComponent_Conditional_1_ng_container_5_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementContainer(0);
} }
function TestingDashboardTabComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    const _r2 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "mj-page-layout")(1, "mj-page-header", 2)(2, "div", 3)(3, "mj-refresh-button", 4);
    i0.ɵɵlistener("Clicked", function TestingDashboardTabComponent_Conditional_1_Template_mj_refresh_button_Clicked_3_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnRefresh()); });
    i0.ɵɵelementEnd()()();
    i0.ɵɵelementStart(4, "mj-page-body");
    i0.ɵɵtemplate(5, TestingDashboardTabComponent_Conditional_1_ng_container_5_Template, 1, 0, "ng-container", 1);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext();
    const content_r1 = i0.ɵɵreference(3);
    i0.ɵɵadvance(3);
    i0.ɵɵproperty("Loading", ctx_r2.IsLoading);
    i0.ɵɵadvance(2);
    i0.ɵɵproperty("ngTemplateOutlet", content_r1);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_0_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 5);
    i0.ɵɵelement(1, "mj-loading", 7);
    i0.ɵɵelementEnd();
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_3_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "app-kpi-card", 9);
} if (rf & 2) {
    const kpi_r4 = ctx.$implicit;
    i0.ɵɵproperty("data", kpi_r4);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_6_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "span", 30);
    i0.ɵɵelementStart(1, "h3");
    i0.ɵɵtext(2);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate2("", ctx_r2.RunningTests.length, " test", ctx_r2.RunningTests.length === 1 ? "" : "s", " running");
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_7_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "i", 31);
    i0.ɵɵelementStart(1, "h3");
    i0.ɵɵtext(2, "No tests currently running");
    i0.ɵɵelementEnd();
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_8_For_2_Template(rf, ctx) { if (rf & 1) {
    const _r5 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 33);
    i0.ɵɵlistener("click", function TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_8_For_2_Template_div_click_0_listener() { const run_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r2 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r2.OnViewRunningTest(run_r6)); });
    i0.ɵɵelementStart(1, "div", 34)(2, "div", 35);
    i0.ɵɵtext(3);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "div", 36);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(6, "div", 37)(7, "span", 38);
    i0.ɵɵtext(8);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "span", 39);
    i0.ɵɵelement(10, "span", 40);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(11, "span", 41);
    i0.ɵɵtext(12);
    i0.ɵɵelementEnd()()();
} if (rf & 2) {
    const run_r6 = ctx.$implicit;
    const ctx_r2 = i0.ɵɵnextContext(4);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(run_r6.testName);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(ctx_r2.GetActiveRunStep(run_r6.testId));
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(ctx_r2.FormatDuration(run_r6.duration));
    i0.ɵɵadvance(2);
    i0.ɵɵstyleProp("width", ctx_r2.GetActiveRunProgress(run_r6.testId), "%");
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1("", ctx_r2.GetActiveRunProgress(run_r6.testId), "%");
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_8_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 12);
    i0.ɵɵrepeaterCreate(1, TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_8_For_2_Template, 13, 6, "div", 32, _forTrack1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance();
    i0.ɵɵrepeater(ctx_r2.RunningTests);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_17_Template(rf, ctx) { if (rf & 1) {
    const _r7 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 42);
    i0.ɵɵlistener("click", function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_17_Template_div_click_0_listener() { const run_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OnOpenRun(run_r8)); });
    i0.ɵɵelementStart(1, "div", 43)(2, "span", 44);
    i0.ɵɵtext(3);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "span", 45);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(6, "div", 46);
    i0.ɵɵelement(7, "app-test-status-badge", 47)(8, "app-score-indicator", 48);
    i0.ɵɵelementStart(9, "span", 49);
    i0.ɵɵtext(10);
    i0.ɵɵelementEnd();
    i0.ɵɵelement(11, "app-cost-display", 50);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const run_r8 = ctx.$implicit;
    const ctx_r2 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(run_r8.testName);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(ctx_r2.FormatTimestamp(run_r8.runDateTime));
    i0.ɵɵadvance(2);
    i0.ɵɵproperty("status", run_r8.status);
    i0.ɵɵadvance();
    i0.ɵɵproperty("score", run_r8.score)("showBar", true);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(ctx_r2.FormatDuration(run_r8.duration));
    i0.ɵɵadvance();
    i0.ɵɵproperty("cost", run_r8.cost);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_ForEmpty_18_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "mj-empty-state", 18);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_27_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 23)(1, "div", 51)(2, "span", 52);
    i0.ɵɵtext(3);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "span", 53);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(6, "div", 54)(7, "div", 55);
    i0.ɵɵelement(8, "div", 56);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "span", 57);
    i0.ɵɵtext(10);
    i0.ɵɵpipe(11, "number");
    i0.ɵɵelementEnd()()();
} if (rf & 2) {
    const suite_r9 = ctx.$implicit;
    const ctx_r2 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(suite_r9.name);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1("", suite_r9.testCount, " tests");
    i0.ɵɵadvance(3);
    i0.ɵɵstyleProp("width", suite_r9.passRate, "%")("background", ctx_r2.GetPassRateColor(suite_r9.passRate));
    i0.ɵɵadvance();
    i0.ɵɵstyleProp("color", ctx_r2.GetPassRateColor(suite_r9.passRate));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(11, 9, suite_r9.passRate, "1.0-0"), "% ");
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_ForEmpty_28_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "mj-empty-state", 24);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Conditional_6_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵtext(0, " Regression detected ");
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Conditional_7_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵtext(0);
    i0.ɵɵpipe(1, "number");
} if (rf & 2) {
    const alert_r11 = i0.ɵɵnextContext().$implicit;
    i0.ɵɵtextInterpolate1(" Score below 0.5 (", i0.ɵɵpipeBind2(1, 1, alert_r11.score, "1.2-2"), ") ");
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Template(rf, ctx) { if (rf & 1) {
    const _r10 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 58);
    i0.ɵɵlistener("click", function TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Template_div_click_0_listener() { const alert_r11 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OnOpenAlert(alert_r11)); });
    i0.ɵɵelement(1, "i", 59);
    i0.ɵɵelementStart(2, "div", 60)(3, "span", 61);
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "span", 62);
    i0.ɵɵconditionalCreate(6, TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Conditional_6_Template, 1, 0)(7, TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Conditional_7_Template, 2, 4);
    i0.ɵɵelementEnd()();
    i0.ɵɵelement(8, "app-test-status-badge", 47);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const alert_r11 = ctx.$implicit;
    i0.ɵɵadvance();
    i0.ɵɵstyleProp("color", alert_r11.reason === "regression" ? "#ef4444" : "#f59e0b");
    i0.ɵɵclassProp("fa-arrow-trend-down", alert_r11.reason === "regression")("fa-circle-exclamation", alert_r11.reason === "low-score");
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(alert_r11.testName);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(alert_r11.reason === "regression" ? 6 : 7);
    i0.ɵɵadvance(2);
    i0.ɵɵproperty("status", alert_r11.status);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_ForEmpty_37_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "mj-empty-state", 29);
} }
function TestingDashboardTabComponent_ng_template_2_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 6)(1, "div", 8);
    i0.ɵɵrepeaterCreate(2, TestingDashboardTabComponent_ng_template_2_Conditional_1_For_3_Template, 1, 1, "app-kpi-card", 9, _forTrack0);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "div", 10)(5, "div", 11);
    i0.ɵɵconditionalCreate(6, TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_6_Template, 3, 2)(7, TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_7_Template, 3, 0);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(8, TestingDashboardTabComponent_ng_template_2_Conditional_1_Conditional_8_Template, 3, 0, "div", 12);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "div", 13)(10, "div", 14)(11, "div", 11)(12, "h3");
    i0.ɵɵelement(13, "i", 15);
    i0.ɵɵtext(14, " Recent Runs");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(15, "div", 16);
    i0.ɵɵrepeaterCreate(16, TestingDashboardTabComponent_ng_template_2_Conditional_1_For_17_Template, 12, 7, "div", 17, _forTrack1, false, TestingDashboardTabComponent_ng_template_2_Conditional_1_ForEmpty_18_Template, 1, 0, "mj-empty-state", 18);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(19, "div", 19)(20, "div", 20)(21, "div", 11)(22, "h3");
    i0.ɵɵelement(23, "i", 21);
    i0.ɵɵtext(24, " Suite Health");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(25, "div", 22);
    i0.ɵɵrepeaterCreate(26, TestingDashboardTabComponent_ng_template_2_Conditional_1_For_27_Template, 12, 12, "div", 23, _forTrack1, false, TestingDashboardTabComponent_ng_template_2_Conditional_1_ForEmpty_28_Template, 1, 0, "mj-empty-state", 24);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(29, "div", 25)(30, "div", 11)(31, "h3");
    i0.ɵɵelement(32, "i", 26);
    i0.ɵɵtext(33, " Alerts");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(34, "div", 27);
    i0.ɵɵrepeaterCreate(35, TestingDashboardTabComponent_ng_template_2_Conditional_1_For_36_Template, 9, 9, "div", 28, _forTrack1, false, TestingDashboardTabComponent_ng_template_2_Conditional_1_ForEmpty_37_Template, 1, 0, "mj-empty-state", 29);
    i0.ɵɵelementEnd()()()()();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance(2);
    i0.ɵɵrepeater(ctx_r2.KpiCards);
    i0.ɵɵadvance(4);
    i0.ɵɵconditional(ctx_r2.RunningTests.length > 0 ? 6 : 7);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r2.RunningTests.length > 0 ? 8 : -1);
    i0.ɵɵadvance(8);
    i0.ɵɵrepeater(ctx_r2.RecentRuns);
    i0.ɵɵadvance(10);
    i0.ɵɵrepeater(ctx_r2.SortedSuites);
    i0.ɵɵadvance(9);
    i0.ɵɵrepeater(ctx_r2.Alerts);
} }
function TestingDashboardTabComponent_ng_template_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵconditionalCreate(0, TestingDashboardTabComponent_ng_template_2_Conditional_0_Template, 2, 0, "div", 5)(1, TestingDashboardTabComponent_ng_template_2_Conditional_1_Template, 38, 5, "div", 6);
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext();
    i0.ɵɵconditional(ctx_r2.IsLoading ? 0 : 1);
} }
export class TestingDashboardTabComponent {
    instrumentationService;
    executionService;
    testingDialogService;
    cdr;
    initialState = null;
    /** When true, the inner bespoke .page-header is hidden — the parent shell owns the chrome. */
    HideToolbar = false;
    stateChange = new EventEmitter();
    destroy$ = new Subject();
    /** Resolved data for the template */
    IsLoading = false;
    KpiCards = [];
    RunningTests = [];
    RecentRuns = [];
    SortedSuites = [];
    Alerts = [];
    activeRunsMap = new Map();
    constructor(instrumentationService, executionService, testingDialogService, cdr) {
        this.instrumentationService = instrumentationService;
        this.executionService = executionService;
        this.testingDialogService = testingDialogService;
        this.cdr = cdr;
    }
    // ---------------------------------------------------------------------------
    // Lifecycle
    // ---------------------------------------------------------------------------
    async ngOnInit() {
        await TestEngineBase.Instance.Config(false);
        this.subscribeToStreams();
        this.instrumentationService.refresh();
    }
    ngOnDestroy() {
        this.destroy$.next();
        this.destroy$.complete();
    }
    // ---------------------------------------------------------------------------
    // Public methods (PascalCase)
    // ---------------------------------------------------------------------------
    OnRefresh() {
        this.instrumentationService.refresh();
    }
    OnViewRunningTest(run) {
        this.testingDialogService.OpenTestMonitor(run.testId);
    }
    GetActiveRunProgress(testId) {
        return this.activeRunsMap.get(testId)?.Progress ?? 0;
    }
    GetActiveRunStep(testId) {
        const step = this.activeRunsMap.get(testId)?.CurrentStep ?? '';
        // Hide raw internal step identifiers (e.g. "driver_log", "loading_test")
        if (step.includes('_'))
            return '';
        return step;
    }
    OnOpenRun(run) {
        SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(run.id));
    }
    OnOpenAlert(alert) {
        SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(alert.id));
    }
    FormatDuration(ms) {
        if (ms < 1000)
            return `${ms}ms`;
        const totalSeconds = Math.floor(ms / 1000);
        const minutes = Math.floor(totalSeconds / 60);
        const seconds = totalSeconds % 60;
        if (minutes > 0)
            return `${minutes}m ${seconds}s`;
        return `${seconds}s`;
    }
    FormatTimestamp(date) {
        if (!date)
            return '';
        const d = date instanceof Date ? date : new Date(date);
        const now = new Date();
        const diffMs = now.getTime() - d.getTime();
        const diffMins = Math.floor(diffMs / 60000);
        if (diffMins < 1)
            return 'Just now';
        if (diffMins < 60)
            return `${diffMins}m ago`;
        const diffHours = Math.floor(diffMins / 60);
        if (diffHours < 24)
            return `${diffHours}h ago`;
        const diffDays = Math.floor(diffHours / 24);
        if (diffDays < 7)
            return `${diffDays}d ago`;
        return d.toLocaleDateString();
    }
    FormatCost(cost) {
        return `$${cost.toFixed(4)}`;
    }
    GetPassRateColor(rate) {
        if (rate >= 90)
            return '#22c55e';
        if (rate >= 70)
            return '#f59e0b';
        return '#ef4444';
    }
    // ---------------------------------------------------------------------------
    // Private helpers (camelCase)
    // ---------------------------------------------------------------------------
    subscribeToStreams() {
        this.subscribeLoading();
        this.subscribeKpis();
        this.subscribeTestRuns();
        this.subscribeSuites();
        this.subscribeActiveRuns();
    }
    subscribeActiveRuns() {
        this.executionService.ActiveRuns$.pipe(takeUntil(this.destroy$)).subscribe(runs => {
            this.activeRunsMap.clear();
            for (const run of runs) {
                this.activeRunsMap.set(run.TestId, run);
            }
            this.cdr.markForCheck();
        });
    }
    subscribeLoading() {
        this.instrumentationService.isLoading$.pipe(takeUntil(this.destroy$)).subscribe(loading => {
            this.IsLoading = loading;
            this.cdr.markForCheck();
        });
    }
    subscribeKpis() {
        this.instrumentationService.kpis$.pipe(takeUntil(this.destroy$), map(kpis => this.buildKpiCards(kpis))).subscribe(cards => {
            this.KpiCards = cards;
            this.cdr.markForCheck();
        });
    }
    subscribeTestRuns() {
        this.instrumentationService.testRuns$.pipe(takeUntil(this.destroy$)).subscribe(runs => {
            this.RunningTests = runs.filter(r => r.status === 'Running');
            this.RecentRuns = runs
                .filter(r => r.status !== 'Running')
                .slice(0, 15);
            this.Alerts = this.buildAlerts(runs);
            this.cdr.markForCheck();
        });
    }
    subscribeSuites() {
        this.instrumentationService.suiteHierarchy$.pipe(takeUntil(this.destroy$)).subscribe(suites => {
            this.SortedSuites = this.flattenAndSort(suites);
            this.cdr.markForCheck();
        });
    }
    buildKpiCards(kpis) {
        const trendDir = kpis.passRateTrend > 0
            ? 'up'
            : kpis.passRateTrend < 0
                ? 'down'
                : 'stable';
        return [
            {
                title: 'Active Tests',
                value: kpis.totalTestsActive,
                icon: 'fa-vial',
                color: 'primary',
                subtitle: `${kpis.totalTestRuns} runs this period`
            },
            {
                title: 'Pass Rate',
                value: `${kpis.passRateThisMonth.toFixed(1)}%`,
                icon: 'fa-check-circle',
                color: kpis.passRateThisMonth >= 90 ? 'success' : kpis.passRateThisMonth >= 75 ? 'warning' : 'danger',
                trend: kpis.passRateTrend !== 0 ? {
                    direction: trendDir,
                    percentage: Math.abs(Math.round(kpis.passRateTrend * 10) / 10),
                    period: 'vs previous period'
                } : undefined
            },
            {
                title: 'Total Cost',
                value: `$${kpis.totalCostThisMonth.toFixed(2)}`,
                icon: 'fa-dollar-sign',
                color: 'warning',
                subtitle: 'This period'
            },
            {
                title: 'Avg Duration',
                value: this.FormatDuration(kpis.averageDuration),
                icon: 'fa-clock',
                color: 'info',
                subtitle: 'Per test run'
            },
            {
                title: 'Pending Review',
                value: kpis.testsPendingReview,
                icon: 'fa-clipboard-check',
                color: kpis.testsPendingReview > 10 ? 'warning' : 'success',
                subtitle: 'Tests need feedback'
            }
        ];
    }
    buildAlerts(runs) {
        const alerts = [];
        // Group runs by test name to detect regressions
        const byTest = new Map();
        for (const run of runs) {
            const existing = byTest.get(run.testName);
            if (existing) {
                existing.push(run);
            }
            else {
                byTest.set(run.testName, [run]);
            }
        }
        this.detectRegressions(byTest, alerts);
        this.detectLowScores(runs, alerts);
        return alerts.slice(0, 20);
    }
    /** Find tests that previously passed but now fail */
    detectRegressions(byTest, alerts) {
        const seenIds = new Set();
        byTest.forEach((testRuns, testName) => {
            if (testRuns.length < 2)
                return;
            // Sort by date descending
            const sorted = [...testRuns].sort((a, b) => b.runDateTime.getTime() - a.runDateTime.getTime());
            const latest = sorted[0];
            const previous = sorted[1];
            if ((latest.status === 'Failed' || latest.status === 'Error') &&
                previous.status === 'Passed' &&
                !seenIds.has(latest.id)) {
                seenIds.add(latest.id);
                alerts.push({
                    id: latest.id,
                    testName,
                    reason: 'regression',
                    score: latest.score,
                    status: latest.status,
                    runDateTime: latest.runDateTime
                });
            }
        });
    }
    /** Flag tests with scores below 0.5 */
    detectLowScores(runs, alerts) {
        const alertIds = new Set(alerts.map(a => a.id));
        for (const run of runs) {
            if (run.score < 0.5 && run.status !== 'Running' && !alertIds.has(run.id)) {
                alerts.push({
                    id: run.id,
                    testName: run.testName,
                    reason: 'low-score',
                    score: run.score,
                    status: run.status,
                    runDateTime: run.runDateTime
                });
            }
        }
    }
    /** Flatten suite hierarchy and sort by worst health first */
    flattenAndSort(nodes) {
        const flat = [];
        this.collectNodes(nodes, flat);
        return flat.sort((a, b) => a.passRate - b.passRate);
    }
    collectNodes(nodes, out) {
        for (const node of nodes) {
            out.push(node);
            if (node.children.length > 0) {
                this.collectNodes(node.children, out);
            }
        }
    }
    static ɵfac = function TestingDashboardTabComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || TestingDashboardTabComponent)(i0.ɵɵdirectiveInject(i1.TestingInstrumentationService), i0.ɵɵdirectiveInject(i2.TestingExecutionService), i0.ɵɵdirectiveInject(i2.TestingDialogService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
    static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingDashboardTabComponent, selectors: [["app-testing-dashboard-tab"]], inputs: { initialState: "initialState", HideToolbar: "HideToolbar" }, outputs: { stateChange: "stateChange" }, standalone: false, decls: 4, vars: 1, consts: [["content", ""], [4, "ngTemplateOutlet"], ["Title", "Testing Overview", "Icon", "fa-solid fa-gauge-high", "Subtitle", "Test health, recent activity, and KPIs"], ["actions", ""], [3, "Clicked", "Loading"], [1, "full-page-loading"], [1, "dashboard-container"], ["text", "Loading Testing Dashboard..."], [1, "kpi-row"], [3, "data"], [1, "live-activity-card"], [1, "section-header"], [1, "running-tests-list"], [1, "lower-grid"], [1, "card", "recent-runs-card"], [1, "fa-solid", "fa-history"], [1, "runs-list"], [1, "run-row"], ["Size", "compact", "Icon", "fa-solid fa-flask-vial", "Title", "No completed test runs found"], [1, "right-column"], [1, "card", "suite-health-card"], [1, "fa-solid", "fa-heartbeat"], [1, "suite-health-list"], [1, "suite-health-row"], ["Size", "compact", "Icon", "fa-solid fa-layer-group", "Title", "No test suites found"], [1, "card", "alerts-card"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "alerts-list"], [1, "alert-row"], ["Variant", "success", "Size", "compact", "Title", "No alerts - all tests healthy"], [1, "live-dot"], [1, "fa-solid", "fa-circle-pause", "live-idle-icon"], [1, "running-test-item"], [1, "running-test-item", 3, "click"], [1, "running-test-info"], [1, "running-test-name"], [1, "running-test-step"], [1, "running-test-meta"], [1, "running-elapsed"], [1, "running-progress-bar"], [1, "running-progress-fill"], [1, "running-progress-pct"], [1, "run-row", 3, "click"], [1, "run-row-left"], [1, "run-row-name"], [1, "run-row-time"], [1, "run-row-right"], [3, "status"], [3, "score", "showBar"], [1, "run-row-duration"], [3, "cost"], [1, "suite-health-info"], [1, "suite-health-name"], [1, "suite-health-count"], [1, "suite-health-bar-wrapper"], [1, "suite-health-bar"], [1, "suite-health-bar-fill"], [1, "suite-health-pct"], [1, "alert-row", 3, "click"], [1, "fa-solid"], [1, "alert-info"], [1, "alert-name"], [1, "alert-reason"]], template: function TestingDashboardTabComponent_Template(rf, ctx) { if (rf & 1) {
            i0.ɵɵconditionalCreate(0, TestingDashboardTabComponent_Conditional_0_Template, 1, 1, "ng-container")(1, TestingDashboardTabComponent_Conditional_1_Template, 6, 2, "mj-page-layout");
            i0.ɵɵtemplate(2, TestingDashboardTabComponent_ng_template_2_Template, 2, 1, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
        } if (rf & 2) {
            i0.ɵɵconditional(ctx.HideToolbar ? 0 : 1);
        } }, dependencies: [i3.NgTemplateOutlet, i4.MJPageHeaderComponent, i4.MJPageLayoutComponent, i4.MJPageBodyComponent, i4.MJRefreshButtonComponent, i2.TestStatusBadgeComponent, i2.ScoreIndicatorComponent, i2.CostDisplayComponent, i4.MJEmptyStateComponent, i5.LoadingComponent, i6.KPICardComponent, i3.DecimalPipe], styles: ["\n\n    .full-page-loading[_ngcontent-%COMP%] {\n      display: flex;\n      justify-content: center;\n      align-items: center;\n      height: 100%;\n      min-height: 400px;\n      background: var(--mj-bg-surface-card);\n    }\n\n    .dashboard-container[_ngcontent-%COMP%] {\n      padding: 24px;\n      height: 100%;\n      overflow-y: auto;\n      background: var(--mj-bg-surface-card);\n    }\n\n    \n\n    .page-header[_ngcontent-%COMP%] {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      margin-bottom: 24px;\n    }\n\n    .page-title[_ngcontent-%COMP%] {\n      margin: 0;\n      font-size: 22px;\n      font-weight: 700;\n      color: var(--mj-text-primary);\n      display: flex;\n      align-items: center;\n      gap: 10px;\n    }\n\n    .page-title[_ngcontent-%COMP%]   i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n\n    .refresh-btn[_ngcontent-%COMP%] {\n      background: var(--mj-brand-primary);\n      color: var(--mj-text-inverse);\n      border: none;\n      padding: 8px 18px;\n      border-radius: 8px;\n      cursor: pointer;\n      display: flex;\n      align-items: center;\n      gap: 6px;\n      font-size: 13px;\n      font-weight: 500;\n      transition: background 0.2s ease;\n    }\n\n    .refresh-btn[_ngcontent-%COMP%]:hover:not(:disabled) { background: var(--mj-brand-primary-hover); }\n    .refresh-btn[_ngcontent-%COMP%]:disabled { opacity: 0.6; cursor: not-allowed; }\n    .refresh-btn[_ngcontent-%COMP%]   i.spinning[_ngcontent-%COMP%] { animation: _ngcontent-%COMP%_spin 1s linear infinite; }\n\n    @keyframes _ngcontent-%COMP%_spin {\n      from { transform: rotate(0deg); }\n      to { transform: rotate(360deg); }\n    }\n\n    \n\n    .kpi-row[_ngcontent-%COMP%] {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n      gap: 16px;\n      margin-bottom: 24px;\n    }\n\n    \n\n    .card[_ngcontent-%COMP%] {\n      background: var(--mj-bg-surface);\n      border-radius: 12px;\n      box-shadow: var(--mj-shadow-sm);\n      overflow: hidden;\n    }\n\n    .section-header[_ngcontent-%COMP%] {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      padding: 16px 20px;\n      border-bottom: 1px solid var(--mj-border-default);\n    }\n\n    .section-header[_ngcontent-%COMP%]   h3[_ngcontent-%COMP%] {\n      margin: 0;\n      font-size: 14px;\n      font-weight: 600;\n      color: var(--mj-text-secondary);\n      display: flex;\n      align-items: center;\n      gap: 8px;\n    }\n\n    .section-header[_ngcontent-%COMP%]   h3[_ngcontent-%COMP%]   i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); font-size: 13px; }\n\n    \n\n    .live-activity-card[_ngcontent-%COMP%] {\n      background: var(--mj-bg-surface);\n      border-radius: 12px;\n      box-shadow: var(--mj-shadow-sm);\n      margin-bottom: 24px;\n      overflow: hidden;\n    }\n\n    .live-dot[_ngcontent-%COMP%] {\n      width: 10px;\n      height: 10px;\n      background: var(--mj-status-success);\n      border-radius: 50%;\n      display: inline-block;\n      animation: _ngcontent-%COMP%_pulse-dot 1.5s ease-in-out infinite;\n    }\n\n    @keyframes _ngcontent-%COMP%_pulse-dot {\n      0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mj-status-success) 50%, transparent); }\n      50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--mj-status-success) 0%, transparent); }\n    }\n\n    .live-idle-icon[_ngcontent-%COMP%] { color: var(--mj-text-disabled); font-size: 14px; }\n\n    .running-tests-list[_ngcontent-%COMP%] {\n      padding: 0 20px 16px;\n      display: flex;\n      flex-direction: column;\n      gap: 10px;\n    }\n\n    .running-test-item[_ngcontent-%COMP%] {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      padding: 10px 14px;\n      background: color-mix(in srgb, var(--mj-status-success) 10%, var(--mj-bg-surface));\n      border-radius: 8px;\n      border: 1px solid color-mix(in srgb, var(--mj-status-success) 25%, var(--mj-bg-surface));\n      cursor: pointer;\n      transition: background 0.2s;\n    }\n\n    .running-test-item[_ngcontent-%COMP%]:hover {\n      background: color-mix(in srgb, var(--mj-status-success) 16%, var(--mj-bg-surface));\n    }\n\n    .running-test-info[_ngcontent-%COMP%] {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n    }\n\n    .running-test-name[_ngcontent-%COMP%] {\n      font-size: 13px;\n      font-weight: 500;\n      color: var(--mj-status-success);\n    }\n\n    .running-test-step[_ngcontent-%COMP%] {\n      font-size: 11px;\n      color: var(--mj-text-muted);\n    }\n\n    .running-test-meta[_ngcontent-%COMP%] {\n      display: flex;\n      align-items: center;\n      gap: 10px;\n    }\n\n    .running-elapsed[_ngcontent-%COMP%] {\n      font-size: 12px;\n      color: var(--mj-status-success);\n      font-weight: 500;\n    }\n\n    .running-progress-bar[_ngcontent-%COMP%] {\n      width: 80px;\n      height: 4px;\n      background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n      border-radius: 2px;\n      overflow: hidden;\n    }\n\n    .running-progress-fill[_ngcontent-%COMP%] {\n      display: block;\n      height: 100%;\n      background: var(--mj-status-success);\n      border-radius: 2px;\n      transition: width 0.5s ease;\n    }\n\n    .running-progress-pct[_ngcontent-%COMP%] {\n      font-size: 11px;\n      font-weight: 600;\n      color: var(--mj-status-success);\n      min-width: 30px;\n      text-align: right;\n    }\n\n    \n\n    .lower-grid[_ngcontent-%COMP%] {\n      display: grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 24px;\n    }\n\n    .right-column[_ngcontent-%COMP%] {\n      display: flex;\n      flex-direction: column;\n      gap: 24px;\n    }\n\n    \n\n    .runs-list[_ngcontent-%COMP%] {\n      max-height: 520px;\n      overflow-y: auto;\n    }\n\n    .run-row[_ngcontent-%COMP%] {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      padding: 10px 20px;\n      border-bottom: 1px solid var(--mj-bg-surface-card);\n      cursor: pointer;\n      transition: background 0.15s ease;\n    }\n\n    .run-row[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-card); }\n\n    .run-row-left[_ngcontent-%COMP%] {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n      flex: 1;\n      min-width: 0;\n    }\n\n    .run-row-name[_ngcontent-%COMP%] {\n      font-size: 13px;\n      font-weight: 500;\n      color: var(--mj-text-primary);\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    .run-row-time[_ngcontent-%COMP%] {\n      font-size: 11px;\n      color: var(--mj-text-disabled);\n    }\n\n    .run-row-right[_ngcontent-%COMP%] {\n      display: flex;\n      align-items: center;\n      gap: 12px;\n      flex-shrink: 0;\n    }\n\n    .run-row-duration[_ngcontent-%COMP%] {\n      font-size: 11px;\n      color: var(--mj-text-muted);\n      min-width: 44px;\n      text-align: right;\n    }\n\n    \n\n    .suite-health-list[_ngcontent-%COMP%] {\n      padding: 8px 0;\n      max-height: 260px;\n      overflow-y: auto;\n    }\n\n    .suite-health-row[_ngcontent-%COMP%] {\n      display: flex;\n      align-items: center;\n      justify-content: space-between;\n      padding: 8px 20px;\n    }\n\n    .suite-health-info[_ngcontent-%COMP%] {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n      flex: 1;\n      min-width: 0;\n    }\n\n    .suite-health-name[_ngcontent-%COMP%] {\n      font-size: 13px;\n      font-weight: 500;\n      color: var(--mj-text-primary);\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    .suite-health-count[_ngcontent-%COMP%] {\n      font-size: 11px;\n      color: var(--mj-text-disabled);\n    }\n\n    .suite-health-bar-wrapper[_ngcontent-%COMP%] {\n      display: flex;\n      align-items: center;\n      gap: 8px;\n      flex-shrink: 0;\n    }\n\n    .suite-health-bar[_ngcontent-%COMP%] {\n      width: 80px;\n      height: 6px;\n      background: var(--mj-bg-surface-sunken);\n      border-radius: 3px;\n      overflow: hidden;\n    }\n\n    .suite-health-bar-fill[_ngcontent-%COMP%] {\n      height: 100%;\n      border-radius: 3px;\n      transition: width 0.3s ease;\n    }\n\n    .suite-health-pct[_ngcontent-%COMP%] {\n      font-size: 12px;\n      font-weight: 600;\n      min-width: 36px;\n      text-align: right;\n    }\n\n    \n\n    .alerts-list[_ngcontent-%COMP%] {\n      max-height: 220px;\n      overflow-y: auto;\n    }\n\n    .alert-row[_ngcontent-%COMP%] {\n      display: flex;\n      align-items: center;\n      gap: 10px;\n      padding: 10px 20px;\n      border-bottom: 1px solid var(--mj-bg-surface-card);\n      cursor: pointer;\n      transition: background 0.15s ease;\n    }\n\n    .alert-row[_ngcontent-%COMP%]:hover { background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface)); }\n\n    .alert-info[_ngcontent-%COMP%] {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\n      flex: 1;\n      min-width: 0;\n    }\n\n    .alert-name[_ngcontent-%COMP%] {\n      font-size: 13px;\n      font-weight: 500;\n      color: var(--mj-text-primary);\n      white-space: nowrap;\n      overflow: hidden;\n      text-overflow: ellipsis;\n    }\n\n    .alert-reason[_ngcontent-%COMP%] {\n      font-size: 11px;\n      color: var(--mj-text-disabled);\n    }\n\n    \n\n    @media (max-width: 900px) {\n      .lower-grid[_ngcontent-%COMP%] {\n        grid-template-columns: 1fr;\n      }\n    }\n\n    @media (max-width: 640px) {\n      .dashboard-container[_ngcontent-%COMP%] { padding: 16px; }\n      .kpi-row[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n    }"], changeDetection: 0 });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingDashboardTabComponent, [{
        type: Component,
        args: [{ standalone: false, selector: 'app-testing-dashboard-tab', changeDetection: ChangeDetectionStrategy.OnPush, template: `
    @if (HideToolbar) {
      <ng-container *ngTemplateOutlet="content"></ng-container>
    } @else {
      <mj-page-layout>
        <mj-page-header
          Title="Testing Overview"
          Icon="fa-solid fa-gauge-high"
          Subtitle="Test health, recent activity, and KPIs">
          <div actions>
            <mj-refresh-button [Loading]="IsLoading" (Clicked)="OnRefresh()"></mj-refresh-button>
          </div>
        </mj-page-header>
        <mj-page-body>
          <ng-container *ngTemplateOutlet="content"></ng-container>
        </mj-page-body>
      </mj-page-layout>
    }

    <ng-template #content>
    <!-- Full-page loading state -->
    @if (IsLoading) {
      <div class="full-page-loading">
        <mj-loading text="Loading Testing Dashboard..."></mj-loading>
      </div>
    } @else {
      <div class="dashboard-container">

        <!-- KPI Row -->
        <div class="kpi-row">
          @for (kpi of KpiCards; track kpi.title) {
            <app-kpi-card [data]="kpi"></app-kpi-card>
          }
        </div>

        <!-- Live Activity Section -->
        <div class="live-activity-card">
          <div class="section-header">
            @if (RunningTests.length > 0) {
              <span class="live-dot"></span>
              <h3>{{ RunningTests.length }} test{{ RunningTests.length === 1 ? '' : 's' }} running</h3>
            } @else {
              <i class="fa-solid fa-circle-pause live-idle-icon"></i>
              <h3>No tests currently running</h3>
            }
          </div>
          @if (RunningTests.length > 0) {
            <div class="running-tests-list">
              @for (run of RunningTests; track run.id) {
                <div class="running-test-item" (click)="OnViewRunningTest(run)">
                  <div class="running-test-info">
                    <div class="running-test-name">{{ run.testName }}</div>
                    <div class="running-test-step">{{ GetActiveRunStep(run.testId) }}</div>
                  </div>
                  <div class="running-test-meta">
                    <span class="running-elapsed">{{ FormatDuration(run.duration) }}</span>
                    <span class="running-progress-bar">
                      <span class="running-progress-fill" [style.width.%]="GetActiveRunProgress(run.testId)"></span>
                    </span>
                    <span class="running-progress-pct">{{ GetActiveRunProgress(run.testId) }}%</span>
                  </div>
                </div>
              }
            </div>
          }
        </div>

        <!-- Lower Content: 2-column layout -->
        <div class="lower-grid">

          <!-- Left Column: Recent Runs -->
          <div class="card recent-runs-card">
            <div class="section-header">
              <h3><i class="fa-solid fa-history"></i> Recent Runs</h3>
            </div>
            <div class="runs-list">
              @for (run of RecentRuns; track run.id) {
                <div class="run-row" (click)="OnOpenRun(run)">
                  <div class="run-row-left">
                    <span class="run-row-name">{{ run.testName }}</span>
                    <span class="run-row-time">{{ FormatTimestamp(run.runDateTime) }}</span>
                  </div>
                  <div class="run-row-right">
                    <app-test-status-badge [status]="run.status"></app-test-status-badge>
                    <app-score-indicator [score]="run.score" [showBar]="true"></app-score-indicator>
                    <span class="run-row-duration">{{ FormatDuration(run.duration) }}</span>
                    <app-cost-display [cost]="run.cost"></app-cost-display>
                  </div>
                </div>
              } @empty {
                <mj-empty-state Size="compact" Icon="fa-solid fa-flask-vial"
                  Title="No completed test runs found" />
              }
            </div>
          </div>

          <!-- Right Column: Suite Health + Alerts -->
          <div class="right-column">

            <!-- Suite Health -->
            <div class="card suite-health-card">
              <div class="section-header">
                <h3><i class="fa-solid fa-heartbeat"></i> Suite Health</h3>
              </div>
              <div class="suite-health-list">
                @for (suite of SortedSuites; track suite.id) {
                  <div class="suite-health-row">
                    <div class="suite-health-info">
                      <span class="suite-health-name">{{ suite.name }}</span>
                      <span class="suite-health-count">{{ suite.testCount }} tests</span>
                    </div>
                    <div class="suite-health-bar-wrapper">
                      <div class="suite-health-bar">
                        <div
                          class="suite-health-bar-fill"
                          [style.width.%]="suite.passRate"
                          [style.background]="GetPassRateColor(suite.passRate)">
                        </div>
                      </div>
                      <span class="suite-health-pct" [style.color]="GetPassRateColor(suite.passRate)">
                        {{ suite.passRate | number:'1.0-0' }}%
                      </span>
                    </div>
                  </div>
                } @empty {
                  <mj-empty-state Size="compact" Icon="fa-solid fa-layer-group"
                    Title="No test suites found" />
                }
              </div>
            </div>

            <!-- Alerts -->
            <div class="card alerts-card">
              <div class="section-header">
                <h3><i class="fa-solid fa-triangle-exclamation"></i> Alerts</h3>
              </div>
              <div class="alerts-list">
                @for (alert of Alerts; track alert.id) {
                  <div class="alert-row" (click)="OnOpenAlert(alert)">
                    <i class="fa-solid"
                       [class.fa-arrow-trend-down]="alert.reason === 'regression'"
                       [class.fa-circle-exclamation]="alert.reason === 'low-score'"
                       [style.color]="alert.reason === 'regression' ? '#ef4444' : '#f59e0b'">
                    </i>
                    <div class="alert-info">
                      <span class="alert-name">{{ alert.testName }}</span>
                      <span class="alert-reason">
                        @if (alert.reason === 'regression') {
                          Regression detected
                        } @else {
                          Score below 0.5 ({{ alert.score | number:'1.2-2' }})
                        }
                      </span>
                    </div>
                    <app-test-status-badge [status]="alert.status"></app-test-status-badge>
                  </div>
                } @empty {
                  <mj-empty-state Variant="success" Size="compact"
                    Title="No alerts - all tests healthy" />
                }
              </div>
            </div>

          </div>
        </div>
      </div>
    }
    </ng-template>
  `, 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      cursor: pointer;\n      transition: background 0.2s;\n    }\n\n    .running-test-item:hover {\n      background: color-mix(in srgb, var(--mj-status-success) 16%, var(--mj-bg-surface));\n    }\n\n    .running-test-info {\n      display: flex;\n      flex-direction: column;\n      gap: 2px;\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-step {\n      font-size: 11px;\n      color: var(--mj-text-muted);\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: 80px;\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      height: 100%;\n      background: var(--mj-status-success);\n      border-radius: 2px;\n      transition: width 0.5s ease;\n    }\n\n    .running-progress-pct {\n      font-size: 11px;\n      font-weight: 600;\n      color: var(--mj-status-success);\n      min-width: 30px;\n      text-align: right;\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    /* ===== 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  "] }]
    }], () => [{ type: i1.TestingInstrumentationService }, { type: i2.TestingExecutionService }, { type: i2.TestingDialogService }, { type: i0.ChangeDetectorRef }], { initialState: [{
            type: Input
        }], HideToolbar: [{
            type: Input
        }], stateChange: [{
            type: Output
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingDashboardTabComponent, { className: "TestingDashboardTabComponent", filePath: "src/Testing/components/testing-dashboard-tab.component.ts", lineNumber: 588 }); })();
//# sourceMappingURL=testing-dashboard-tab.component.js.map