import { ChangeDetectorRef, Component, Input, ViewEncapsulation, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
import { MJButtonDirective } from '@memberjunction/ng-ui-components';
import { RunView } from '@memberjunction/core';
import { UUIDsEqual } from '@memberjunction/global';
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
import { primaryAuc } from '../predictive-studio.view-models';
import { humanizeCron } from '../production-distribution';
import { PSOperateDialogComponent } from './ps-operate-dialog.component';
import * as i0 from "@angular/core";
import * as i1 from "@memberjunction/ng-shared-generic";
import * as i2 from "@angular/common";
const _forTrack0 = ($index, $item) => $item.modelId;
const _forTrack1 = ($index, $item) => $item.id;
const _forTrack2 = ($index, $item) => $item.recordId;
const _forTrack3 = ($index, $item) => $item.ID;
function PSProductionComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 1)(1, "span", 2);
    i0.ɵɵelement(2, "i", 3);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "h3");
    i0.ɵɵtext(4, "No models in production yet");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "p");
    i0.ɵɵtext(6, " When you publish a trained model in the Model Registry it shows up here \u2014 ready to schedule, bind to a column, and run. Train a pipeline and promote the model to Published to get started. ");
    i0.ɵɵelementEnd()();
} }
function PSProductionComponent_Conditional_2_For_30_Template(rf, ctx) { if (rf & 1) {
    const _r2 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 32);
    i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_For_30_Template_div_click_0_listener() { const m_r3 = i0.ɵɵrestoreView(_r2).$implicit; const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.select(m_r3.modelId)); });
    i0.ɵɵelementStart(1, "div", 33);
    i0.ɵɵelement(2, "i");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "div", 34)(4, "div", 35);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(6, "div", 36);
    i0.ɵɵtext(7);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(8, "div", 37)(9, "span", 22);
    i0.ɵɵtext(10);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(11, "div", 38);
    i0.ɵɵtext(12);
    i0.ɵɵelementEnd()()();
} if (rf & 2) {
    const m_r3 = ctx.$implicit;
    const ctx_r3 = i0.ɵɵnextContext(2);
    i0.ɵɵclassProp("sel", m_r3.modelId === ctx_r3.selectedModelId);
    i0.ɵɵadvance();
    i0.ɵɵclassMap("st-" + m_r3.deployState);
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r3.deployIcon(m_r3.deployState));
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(m_r3.label);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate2("v", m_r3.version, " \u00B7 ", m_r3.algorithm);
    i0.ɵɵadvance(2);
    i0.ɵɵclassMap(ctx_r3.deployBadge(m_r3.deployState));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(ctx_r3.deployLabel(m_r3.deployState));
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(m_r3.holdoutMetric);
} }
function PSProductionComponent_Conditional_2_Conditional_55_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 27);
    i0.ɵɵelement(1, "i", 39);
    i0.ɵɵelementStart(2, "div", 40);
    i0.ɵɵtext(3, "This model is published but ");
    i0.ɵɵelementStart(4, "strong");
    i0.ɵɵtext(5, "not operating");
    i0.ɵɵelementEnd();
    i0.ɵɵtext(6, " yet \u2014 nothing scores with it. Click ");
    i0.ɵɵelementStart(7, "strong");
    i0.ɵɵtext(8, "Operate");
    i0.ɵɵelementEnd();
    i0.ɵɵtext(9, " above to run it now, schedule it to run regularly, or write predictions back to a column.");
    i0.ɵɵelementEnd()();
} }
function PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_For_3_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 42);
    i0.ɵɵelement(1, "i", 43);
    i0.ɵɵelementStart(2, "div", 18)(3, "div", 40)(4, "strong");
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(6);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(7, "div", 25);
    i0.ɵɵtext(8);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(9, "span", 22);
    i0.ɵɵtext(10);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const b_r5 = ctx.$implicit;
    const ctx_r3 = i0.ɵɵnextContext(4);
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(b_r5.targetEntity);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1(".", b_r5.targetColumn);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate2("", b_r5.mode, "", b_r5.schedulePhrase ? " \u00B7 " + b_r5.schedulePhrase : "");
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r3.modeBadge(b_r5.mode));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(b_r5.mode);
} }
function PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 41);
    i0.ɵɵtext(1, "Scoring bindings");
    i0.ɵɵelementEnd();
    i0.ɵɵrepeaterCreate(2, PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_For_3_Template, 11, 7, "div", 42, _forTrack1);
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(2);
    i0.ɵɵrepeater(ctx_r3.selectedBindings);
} }
function PSProductionComponent_Conditional_2_Conditional_56_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 29);
    i0.ɵɵtext(1, "Scheduled to run, writing generic output to the process run history (no write-back column).");
    i0.ɵɵelementEnd();
} }
function PSProductionComponent_Conditional_2_Conditional_56_Conditional_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "div", 44);
    i0.ɵɵelementStart(1, "div", 45)(2, "span", 25);
    i0.ɵɵtext(3, "Last scored");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "span", 40);
    i0.ɵɵtext(5);
    i0.ɵɵpipe(6, "date");
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate2("", i0.ɵɵpipeBind2(6, 2, ctx_r3.selected.lastScoredAt, "medium"), "", ctx_r3.selected.lastRowCount != null ? " \u00B7 " + ctx_r3.selected.lastRowCount + " rows" : "");
} }
function PSProductionComponent_Conditional_2_Conditional_56_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵconditionalCreate(0, PSProductionComponent_Conditional_2_Conditional_56_Conditional_0_Template, 4, 0)(1, PSProductionComponent_Conditional_2_Conditional_56_Conditional_1_Template, 2, 0, "div", 29);
    i0.ɵɵconditionalCreate(2, PSProductionComponent_Conditional_2_Conditional_56_Conditional_2_Template, 7, 5);
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext(2);
    i0.ɵɵconditional(ctx_r3.selectedBindings.length > 0 ? 0 : 1);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r3.selected.lastScoredAt ? 2 : -1);
} }
function PSProductionComponent_Conditional_2_Conditional_64_Conditional_3_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "mj-loading", 48);
} }
function PSProductionComponent_Conditional_2_Conditional_64_Conditional_4_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 29);
    i0.ɵɵtext(1, "No per-record predictions recorded for this run.");
    i0.ɵɵelementEnd();
} }
function PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Conditional_8_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "span", 52);
    i0.ɵɵtext(1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const d_r7 = i0.ɵɵnextContext().$implicit;
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1(" \u00B7 ", d_r7.class);
} }
function PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "tr")(1, "td", 50);
    i0.ɵɵtext(2);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "td")(4, "span", 22);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(6, "td", 51);
    i0.ɵɵtext(7);
    i0.ɵɵconditionalCreate(8, PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Conditional_8_Template, 2, 1, "span", 52);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "td", 29);
    i0.ɵɵtext(10);
    i0.ɵɵpipe(11, "date");
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const d_r7 = ctx.$implicit;
    const ctx_r3 = i0.ɵɵnextContext(4);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(d_r7.recordId);
    i0.ɵɵadvance(2);
    i0.ɵɵclassMap(ctx_r3.runBadge(d_r7.status));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(d_r7.status);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(d_r7.score);
    i0.ɵɵadvance();
    i0.ɵɵconditional(d_r7.class ? 8 : -1);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(d_r7.scoredAt ? i0.ɵɵpipeBind2(11, 7, d_r7.scoredAt, "short") : "\u2014");
} }
function PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "table", 49)(1, "thead")(2, "tr")(3, "th");
    i0.ɵɵtext(4, "Record");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "th");
    i0.ɵɵtext(6, "Status");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(7, "th");
    i0.ɵɵtext(8, "Prediction");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "th");
    i0.ɵɵtext(10, "Scored");
    i0.ɵɵelementEnd()()();
    i0.ɵɵelementStart(11, "tbody");
    i0.ɵɵrepeaterCreate(12, PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_For_13_Template, 12, 10, "tr", null, _forTrack2);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(12);
    i0.ɵɵrepeater(ctx_r3.runDetails);
} }
function PSProductionComponent_Conditional_2_Conditional_64_Template(rf, ctx) { if (rf & 1) {
    const _r6 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "button", 46);
    i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_64_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.closeRun()); });
    i0.ɵɵelement(1, "i", 47);
    i0.ɵɵtext(2, " All runs ");
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(3, PSProductionComponent_Conditional_2_Conditional_64_Conditional_3_Template, 1, 0, "mj-loading", 48)(4, PSProductionComponent_Conditional_2_Conditional_64_Conditional_4_Template, 2, 0, "div", 29)(5, PSProductionComponent_Conditional_2_Conditional_64_Conditional_5_Template, 14, 0, "table", 49);
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance(3);
    i0.ɵɵconditional(ctx_r3.loadingDetails ? 3 : ctx_r3.runDetails.length === 0 ? 4 : 5);
} }
function PSProductionComponent_Conditional_2_Conditional_65_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "mj-loading", 28);
} }
function PSProductionComponent_Conditional_2_Conditional_66_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 29);
    i0.ɵɵtext(1, "No runs recorded yet. Each time this model runs as a Record Process, its predictions are saved to the process run history \u2014 even without a scoring binding.");
    i0.ɵɵelementEnd();
} }
function PSProductionComponent_Conditional_2_Conditional_67_For_14_Conditional_14_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "span", 56);
    i0.ɵɵtext(1, "dry run");
    i0.ɵɵelementEnd();
} }
function PSProductionComponent_Conditional_2_Conditional_67_For_14_Template(rf, ctx) { if (rf & 1) {
    const _r8 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "tr", 54);
    i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Conditional_67_For_14_Template_tr_click_0_listener() { const r_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r3 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r3.openRun(r_r9.ID)); });
    i0.ɵɵelementStart(1, "td");
    i0.ɵɵtext(2);
    i0.ɵɵpipe(3, "date");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "td")(5, "span", 22);
    i0.ɵɵtext(6);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(7, "td", 51);
    i0.ɵɵtext(8);
    i0.ɵɵelementStart(9, "span", 52);
    i0.ɵɵtext(10);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(11, "td", 29);
    i0.ɵɵtext(12);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(13, "td", 55);
    i0.ɵɵconditionalCreate(14, PSProductionComponent_Conditional_2_Conditional_67_For_14_Conditional_14_Template, 2, 0, "span", 56);
    i0.ɵɵelement(15, "i", 57);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const r_r9 = ctx.$implicit;
    const ctx_r3 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, r_r9.StartTime || r_r9.CreatedAt, "short"));
    i0.ɵɵadvance(3);
    i0.ɵɵclassMap(ctx_r3.runBadge(r_r9.Status));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(r_r9.Status);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(r_r9.SuccessCount);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1("/", r_r9.TotalItemCount ?? 0);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(r_r9.ProcessName || "\u2014");
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(r_r9.DryRun ? 14 : -1);
} }
function PSProductionComponent_Conditional_2_Conditional_67_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "table", 30)(1, "thead")(2, "tr")(3, "th");
    i0.ɵɵtext(4, "When");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "th");
    i0.ɵɵtext(6, "Status");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(7, "th");
    i0.ɵɵtext(8, "Scored");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "th");
    i0.ɵɵtext(10, "Process");
    i0.ɵɵelementEnd();
    i0.ɵɵelement(11, "th");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(12, "tbody");
    i0.ɵɵrepeaterCreate(13, PSProductionComponent_Conditional_2_Conditional_67_For_14_Template, 16, 11, "tr", 53, _forTrack3);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance(13);
    i0.ɵɵrepeater(ctx_r3.runs);
} }
function PSProductionComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
    const _r1 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 4)(1, "div", 5)(2, "div", 6);
    i0.ɵɵtext(3, "Published");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(4, "div", 7);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(6, "div", 5)(7, "div", 6);
    i0.ɵɵtext(8, "Scheduled");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(9, "div", 7);
    i0.ɵɵtext(10);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(11, "div", 5)(12, "div", 6);
    i0.ɵɵtext(13, "Bound (write-back)");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(14, "div", 7);
    i0.ɵɵtext(15);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(16, "div", 5)(17, "div", 6);
    i0.ɵɵtext(18, "Idle");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(19, "div", 7);
    i0.ɵɵtext(20);
    i0.ɵɵelementEnd()()();
    i0.ɵɵelementStart(21, "div", 8)(22, "div", 9)(23, "div", 10)(24, "h3");
    i0.ɵɵtext(25, "Published models");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(26, "span", 11);
    i0.ɵɵtext(27);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(28, "div", 12);
    i0.ɵɵrepeaterCreate(29, PSProductionComponent_Conditional_2_For_30_Template, 13, 13, "div", 13, _forTrack0);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(31, "div", 14)(32, "div", 15)(33, "div", 16)(34, "div", 17);
    i0.ɵɵelement(35, "i");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(36, "div", 18)(37, "h2", 19);
    i0.ɵɵtext(38);
    i0.ɵɵelementStart(39, "span", 20);
    i0.ɵɵtext(40);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(41, "div", 21);
    i0.ɵɵtext(42);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(43, "span", 22);
    i0.ɵɵtext(44);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(45, "button", 23);
    i0.ɵɵlistener("click", function PSProductionComponent_Conditional_2_Template_button_click_45_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.operateOpen = true); });
    i0.ɵɵelement(46, "i", 24);
    i0.ɵɵtext(47, " Operate ");
    i0.ɵɵelementEnd()()();
    i0.ɵɵelementStart(48, "div", 15)(49, "div", 10)(50, "h3");
    i0.ɵɵtext(51, "Deployment");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(52, "span", 25);
    i0.ɵɵtext(53);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(54, "div", 26);
    i0.ɵɵconditionalCreate(55, PSProductionComponent_Conditional_2_Conditional_55_Template, 10, 0, "div", 27)(56, PSProductionComponent_Conditional_2_Conditional_56_Template, 3, 2);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(57, "div", 15)(58, "div", 10)(59, "h3");
    i0.ɵɵtext(60, "Run history");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(61, "span", 25);
    i0.ɵɵtext(62, "last 90 days \u00B7 newest first");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(63, "div", 26);
    i0.ɵɵconditionalCreate(64, PSProductionComponent_Conditional_2_Conditional_64_Template, 6, 1)(65, PSProductionComponent_Conditional_2_Conditional_65_Template, 1, 0, "mj-loading", 28)(66, PSProductionComponent_Conditional_2_Conditional_66_Template, 2, 0, "div", 29)(67, PSProductionComponent_Conditional_2_Conditional_67_Template, 15, 0, "table", 30);
    i0.ɵɵelementEnd()()()();
    i0.ɵɵelementStart(68, "ps-operate-dialog", 31);
    i0.ɵɵlistener("Close", function PSProductionComponent_Conditional_2_Template_ps_operate_dialog_Close_68_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.onOperateClose($event)); });
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r3 = i0.ɵɵnextContext();
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(ctx_r3.models.length);
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(ctx_r3.scheduledModelCount);
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(ctx_r3.boundModelCount);
    i0.ɵɵadvance(5);
    i0.ɵɵtextInterpolate(ctx_r3.idleModelCount);
    i0.ɵɵadvance(7);
    i0.ɵɵtextInterpolate(ctx_r3.models.length);
    i0.ɵɵadvance(2);
    i0.ɵɵrepeater(ctx_r3.models);
    i0.ɵɵadvance(5);
    i0.ɵɵclassMap("st-" + ctx_r3.selected.deployState);
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r3.deployIcon(ctx_r3.selected.deployState));
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate1("", ctx_r3.selected.label, " ");
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1("v", ctx_r3.selected.version);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate3("", ctx_r3.selected.algorithm, " \u00B7 ", ctx_r3.selected.problemType, " \u00B7 holdout ", ctx_r3.selected.holdoutMetric);
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r3.deployBadge(ctx_r3.selected.deployState));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(ctx_r3.deployLabel(ctx_r3.selected.deployState));
    i0.ɵɵadvance(9);
    i0.ɵɵtextInterpolate4("", ctx_r3.selected.processCount, " process", ctx_r3.selected.processCount === 1 ? "" : "es", " \u00B7 ", ctx_r3.selected.bindingCount, " binding", ctx_r3.selected.bindingCount === 1 ? "" : "s");
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r3.selected.deployState === "idle" ? 55 : 56);
    i0.ɵɵadvance(9);
    i0.ɵɵconditional(ctx_r3.selectedRunId ? 64 : ctx_r3.loadingRuns ? 65 : ctx_r3.runs.length === 0 ? 66 : 67);
    i0.ɵɵadvance(4);
    i0.ɵɵproperty("Visible", ctx_r3.operateOpen)("modelId", ctx_r3.selected.modelId)("modelLabel", ctx_r3.selected.label)("engine", ctx_r3.engine)("provider", ctx_r3.ProviderToUse)("currentUser", ctx_r3.ProviderToUse.CurrentUser);
} }
/**
 * Models in Production — the **model-centric** control tower. Every approved (Published) model appears
 * here whether it's idle, scheduled, or bound — a scoring binding is a *deployment marker*, not a
 * prerequisite to appear or to run. Reactive + DB-light: the published-model list and each model's
 * deployment state come straight from the `PredictiveStudioEngine` caches (a `BaseEngine` subclass) via
 * `ObserveProperty`, so it re-renders on any model/binding change with no polling; a model's **past runs**
 * are loaded on demand when selected (`MJ: Process Runs` grow unbounded — never bulk-cached).
 *
 * Entity-agnostic: model labels derive from the producing pipeline; binding targets come from the binding
 * rows. **Operate** (the rocket button) opens the {@link PSOperateDialogComponent} to run/schedule/bind the
 * model; clicking a run drills into its per-record predictions (`MJ: Process Run Details`).
 */
export class PSProductionComponent extends BaseAngularComponent {
    engine;
    cdr = inject(ChangeDetectorRef);
    modelsSub;
    models = [];
    selectedModelId = '';
    selectedBindings = [];
    runs = [];
    loadingRuns = false;
    /** Whether the "Operate this model" dialog is open for the selected model. */
    operateOpen = false;
    /** The run whose per-record predictions are drilled into (null = the run list). */
    selectedRunId = null;
    runDetails = [];
    loadingDetails = false;
    ngOnInit() {
        // Reactive: rebuild the published-model list on any MJ: ML Models change (save/delete/remote-invalidate).
        this.modelsSub = this.engine
            .ObserveProperty('_Models')
            .subscribe(() => {
            this.rebuildModels();
            this.cdr.detectChanges();
        });
    }
    ngOnDestroy() {
        this.modelsSub?.unsubscribe();
    }
    // ---- KPIs ----
    get scheduledModelCount() {
        return this.models.filter((m) => m.deployState === 'scheduled' || m.scheduledCount > 0).length;
    }
    get boundModelCount() {
        return this.models.filter((m) => m.bindingCount > 0).length;
    }
    get idleModelCount() {
        return this.models.filter((m) => m.deployState === 'idle').length;
    }
    // ---- reactive list build (DB-light, from caches) ----
    rebuildModels() {
        this.models = (this.engine?.PublishedModels ?? []).map((m) => this.toVM(m));
        if (!this.models.some((m) => UUIDsEqual(m.modelId, this.selectedModelId))) {
            const first = this.models[0]?.modelId ?? '';
            if (first) {
                this.select(first);
            }
            else {
                this.selectedModelId = '';
                this.selectedBindings = [];
                this.runs = [];
            }
        }
        else {
            this.selectedBindings = this.bindingsForModel(this.selectedModelId);
        }
    }
    toVM(m) {
        const bindings = this.engine.ScoringBindings.filter((b) => UUIDsEqual(b.MLModelID, m.ID));
        const processes = this.engine.RecordProcessesForModel(m.ID);
        const scheduledCount = processes.filter((p) => !!p.ScheduleEnabled).length
            + bindings.filter((b) => b.Mode === 'Scheduled').length;
        const lastScoredAt = bindings.reduce((acc, b) => (b.LastScoredAt && (!acc || b.LastScoredAt.getTime() > acc.getTime()) ? b.LastScoredAt : acc), null);
        const lastRowCount = bindings.find((b) => b.LastRowCount != null)?.LastRowCount ?? null;
        const auc = primaryAuc(m);
        return {
            modelId: m.ID,
            label: this.engine.ModelDisplayName(m),
            algorithm: this.engine.AlgorithmName(m.AlgorithmID),
            problemType: m.ProblemType ?? '—',
            version: m.Version,
            holdoutMetric: auc != null ? auc.toFixed(3) : '—',
            deployState: bindings.length > 0 ? 'bound' : scheduledCount > 0 ? 'scheduled' : 'idle',
            bindingCount: bindings.length,
            processCount: processes.length,
            scheduledCount,
            lastScoredAt,
            lastRowCount,
        };
    }
    bindingsForModel(modelId) {
        return this.engine.ScoringBindings
            .filter((b) => UUIDsEqual(b.MLModelID, modelId))
            .map((b) => this.toBindingVM(b));
    }
    toBindingVM(b) {
        const process = this.engine.RecordProcessByID(b.RecordProcessID);
        return {
            id: b.ID,
            targetEntity: this.resolveEntityName(b),
            targetColumn: b.TargetColumn ?? '(generic output)',
            mode: b.Mode,
            schedulePhrase: b.Mode === 'Scheduled' ? humanizeCron(process?.CronExpression ?? null) : null,
        };
    }
    resolveEntityName(b) {
        if (b.TargetEntity)
            return b.TargetEntity;
        if (b.TargetEntityID) {
            const entity = this.ProviderToUse.Entities.find((e) => UUIDsEqual(e.ID, b.TargetEntityID));
            if (entity)
                return entity.Name;
        }
        return '—';
    }
    // ---- selection + on-demand run history ----
    get selected() {
        return (this.models.find((m) => UUIDsEqual(m.modelId, this.selectedModelId)) ??
            this.models[0] ?? {
            modelId: '', label: '—', algorithm: '—', problemType: '—', version: 0, holdoutMetric: '—',
            deployState: 'idle', bindingCount: 0, processCount: 0, scheduledCount: 0, lastScoredAt: null, lastRowCount: null,
        });
    }
    /**
     * Close the Operate dialog. On a successful change the dialog already refreshed the engine
     * (Config(true)) — the reactive `_Models` subscription re-renders the deploy state — but the
     * on-demand run history + bindings of the SELECTED model are not part of that stream, so reload them
     * here so a just-created run / binding shows immediately.
     */
    onOperateClose(result) {
        this.operateOpen = false;
        if (result.changed && this.selectedModelId) {
            this.selectedBindings = this.bindingsForModel(this.selectedModelId);
            void this.loadRuns(this.selectedModelId);
        }
    }
    select(id) {
        this.selectedModelId = id;
        this.selectedBindings = this.bindingsForModel(id);
        this.closeRun();
        void this.loadRuns(id);
    }
    // ---- run drill-in: per-record predictions (on demand) ----
    /** Drill into a run's per-record predictions (`MJ: Process Run Details` `ResultPayload`). */
    openRun(runId) {
        this.selectedRunId = runId;
        void this.loadRunDetails(runId);
    }
    closeRun() {
        this.selectedRunId = null;
        this.runDetails = [];
    }
    async loadRunDetails(runId) {
        this.loadingDetails = true;
        this.runDetails = [];
        this.cdr.detectChanges();
        try {
            const rv = RunView.FromMetadataProvider(this.ProviderToUse);
            const r = await rv.RunView({
                EntityName: 'MJ: Process Run Details',
                ExtraFilter: `ProcessRunID='${runId}'`,
                OrderBy: '__mj_CreatedAt ASC',
                MaxRows: 200,
                ResultType: 'entity_object',
            }, this.ProviderToUse.CurrentUser);
            this.runDetails = r.Success ? (r.Results ?? []).map((d) => this.toDetailVM(d)) : [];
        }
        finally {
            this.loadingDetails = false;
            this.cdr.detectChanges();
        }
    }
    /** Project a Process Run Detail into a per-record prediction row, parsing the ML `ResultPayload`. */
    toDetailVM(d) {
        let score = '—';
        let cls = null;
        let scoredAt = d.CompletedAt ? d.CompletedAt.toISOString() : null;
        if (d.ResultPayload) {
            try {
                const p = JSON.parse(d.ResultPayload);
                if (typeof p.score === 'number') {
                    score = p.score.toFixed(4);
                }
                cls = p.class ?? null;
                scoredAt = p.scoredAt ?? scoredAt;
            }
            catch {
                /* leave defaults — a non-ML payload (or malformed) just shows '—' */
            }
        }
        return { recordId: d.RecordID ?? '—', status: d.Status, score, class: cls, scoredAt };
    }
    /** On-demand run history for the selected model (DB-light — only the selected model, capped). */
    async loadRuns(modelId) {
        this.loadingRuns = true;
        this.runs = [];
        this.cdr.detectChanges();
        try {
            this.runs = await this.engine.LoadRecentRunsForModel(modelId, this.ProviderToUse, this.ProviderToUse.CurrentUser, { sinceDays: 90, maxRows: 50 });
        }
        finally {
            this.loadingRuns = false;
            this.cdr.detectChanges();
        }
    }
    // ---- display helpers ----
    deployIcon(state) {
        switch (state) {
            case 'bound': return 'fa-solid fa-arrow-right-to-bracket';
            case 'scheduled': return 'fa-solid fa-clock';
            case 'idle': return 'fa-solid fa-circle-pause';
        }
    }
    deployLabel(state) {
        switch (state) {
            case 'bound': return 'Bound';
            case 'scheduled': return 'Scheduled';
            case 'idle': return 'Idle';
        }
    }
    deployBadge(state) {
        switch (state) {
            case 'bound': return 'green';
            case 'scheduled': return 'blue';
            case 'idle': return 'gray';
        }
    }
    modeBadge(mode) {
        return mode === 'Scheduled' ? 'blue' : mode === 'Materialized' ? 'green' : 'gray';
    }
    runBadge(status) {
        const s = (status || '').toLowerCase();
        if (s.includes('complete') || s.includes('success'))
            return 'green';
        if (s.includes('fail') || s.includes('error'))
            return 'red';
        if (s.includes('run') || s.includes('progress'))
            return 'blue';
        return 'gray';
    }
    static ɵfac = /*@__PURE__*/ (() => { let ɵPSProductionComponent_BaseFactory; return function PSProductionComponent_Factory(__ngFactoryType__) { return (ɵPSProductionComponent_BaseFactory || (ɵPSProductionComponent_BaseFactory = i0.ɵɵgetInheritedFactory(PSProductionComponent)))(__ngFactoryType__ || PSProductionComponent); }; })();
    static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSProductionComponent, selectors: [["ps-production"]], inputs: { engine: "engine" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 3, vars: 1, consts: [["data-testid", "ps-production-panel", 1, "ps-panel", "ps-production"], ["data-testid", "ps-production-empty", 1, "ps-empty"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-satellite-dish"], ["data-testid", "ps-production-kpis", 1, "prod-kpis"], [1, "ps-kpi"], [1, "label"], [1, "val"], [1, "prod-layout"], ["data-testid", "ps-production-list", 1, "ps-card", "mlist"], [1, "ps-card-head"], [1, "ps-badge", "gray"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-production-row", 1, "mrow", 3, "sel"], ["data-testid", "ps-production-detail", 1, "ps-col", "detail"], [1, "ps-card"], [1, "ps-card-body", "dh"], [1, "big-ico"], [2, "flex", "1"], ["data-testid", "ps-production-detail-name"], [1, "ps-tag", "ps-mono"], [1, "ps-muted", "ps-small", "sub"], [1, "ps-badge"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-production-operate", 3, "click"], [1, "fa-solid", "fa-rocket"], [1, "ps-muted", "ps-small"], [1, "ps-card-body"], [1, "ps-callout", "info"], ["text", "Loading runs...", "size", "small"], [1, "ps-small", "ps-muted"], [1, "runs-table"], [3, "Close", "Visible", "modelId", "modelLabel", "engine", "provider", "currentUser"], ["data-testid", "ps-production-row", 1, "mrow", 3, "click"], [1, "ico"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "meta"], [1, "ps-mono", "ps-small", "ps-muted"], [1, "fa-solid", "fa-circle-info"], [1, "ps-small"], [1, "ps-section-title"], [1, "bind-row"], [1, "fa-solid", "fa-arrow-right-to-bracket", "ps-muted"], [1, "ps-divider"], [1, "ps-row", 2, "justify-content", "space-between"], ["data-testid", "ps-production-run-back", 1, "run-back", 3, "click"], [1, "fa-solid", "fa-arrow-left"], ["text", "Loading predictions...", "size", "small"], ["data-testid", "ps-production-run-detail", 1, "runs-table"], [1, "ps-mono", "ps-small"], [1, "ps-mono"], [1, "ps-muted"], ["data-testid", "ps-production-run", 1, "run-row"], ["data-testid", "ps-production-run", 1, "run-row", 3, "click"], [1, "run-right"], [1, "ps-tag", "amber"], [1, "fa-solid", "fa-chevron-right", "ps-muted"]], template: function PSProductionComponent_Template(rf, ctx) { if (rf & 1) {
            i0.ɵɵelementStart(0, "div", 0);
            i0.ɵɵconditionalCreate(1, PSProductionComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSProductionComponent_Conditional_2_Template, 69, 29);
            i0.ɵɵelementEnd();
        } if (rf & 2) {
            i0.ɵɵadvance();
            i0.ɵɵconditional(ctx.models.length === 0 ? 1 : 2);
        } }, dependencies: [CommonModule, SharedGenericModule, i1.LoadingComponent, MJButtonDirective, PSOperateDialogComponent, i2.DatePipe], styles: ["/* ============================================================================\n   Predictive Studio \u2014 shared panel styles (design-token compliant)\n   Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n   Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n   so the class names are global within the PS dashboard subtree.\n   No hardcoded colors except the intentional categorical node/algorithm palette\n   (declared once as --ps-* custom props below) which must stay distinct across\n   light/dark to remain readable as diagram accents.\n   ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n   data-visualization colors that must remain distinct regardless of theme\n   (allowed exception #3 in the design-token rules). */\n.ps-panel {\n  --ps-node-source: #6b7280;\n  --ps-node-feature: #2050c8;\n  --ps-node-embedding: #7b3ff0;\n  --ps-node-target: #1f9d57;\n  --ps-node-algorithm: #d98213;\n  /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n  --ps-gauge-cost: var(--ps-node-algorithm);\n  --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n  display: block;\n  height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n  color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n  background: var(--mj-bg-surface);\n  border: 1px solid var(--mj-border-default);\n  border-radius: 10px;\n  box-shadow: var(--mj-shadow-sm);\n  overflow: hidden;\n}\n.ps-card-head {\n  display: flex; align-items: center; gap: 10px;\n  padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n  background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n  border-radius: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n  display: inline-flex; align-items: center; gap: 5px;\n  padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n  display: inline-flex; align-items: center; justify-content: center;\n  min-width: 92px; padding: 4px 10px; border-radius: 7px;\n  font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n  display: inline-block; padding: 2px 8px; border-radius: 6px;\n  background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n  font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n  color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n  border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n  width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n  display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\n\n/* gauge */\n.ps-gauge {\n  position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n  background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n  display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n  width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n  display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n  position: relative;\n  background:\n    linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n    linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n    var(--mj-bg-surface-card);\n  border: 1px solid var(--mj-border-default); border-radius: 10px;\n  min-height: 620px; overflow: hidden;\n}\n.ps-node {\n  position: absolute; width: 190px; background: var(--mj-bg-surface);\n  border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 10px;\n  box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n  display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n  border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n  width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n  color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n  border-color: var(--mj-brand-primary);\n  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n  position: absolute; width: 11px; height: 11px; border-radius: 50%;\n  background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n  top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n  display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n  border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n  font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n  border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n  padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n  border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n  display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n  color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n  width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n  border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n  box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n  display: flex; flex-direction: column; align-items: center; justify-content: center;\n  text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n  width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n  background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n  position: fixed; inset: 0; z-index: 1000;\n  background: var(--mj-bg-overlay);\n  display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n  width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n  border: 1px solid var(--mj-border-default); border-radius: 14px;\n  box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n  display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n  border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n  width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n  font-size: 16px; flex: 0 0 auto;\n  background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n  display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n  border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", "/* Models in Production \u2014 model-centric control tower (master/detail). All colors via --mj-* tokens. */\n\n.ps-production {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n  padding: 4px;\n}\n\n.prod-kpis {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 14px;\n}\n\n.prod-layout {\n  display: grid;\n  grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);\n  gap: 16px;\n  align-items: start;\n}\n@media (max-width: 900px) {\n  .prod-layout { grid-template-columns: 1fr; }\n}\n\n/* master list */\n.prod-layout .mlist .mrow {\n  display: flex;\n  align-items: center;\n  gap: 11px;\n  padding: 10px 10px;\n  border-radius: 9px;\n  cursor: pointer;\n}\n.prod-layout .mlist .mrow:hover { background: var(--mj-bg-surface-hover); }\n.prod-layout .mlist .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); }\n.prod-layout .mlist .mrow .ico {\n  width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;\n  color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.prod-layout .mlist .mrow .nm { font-weight: 640; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.prod-layout .mlist .mrow .ln2 { margin-top: 2px; }\n.prod-layout .mlist .mrow .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }\n\n/* deploy-state icon colors (master + detail) */\n.st-bound { background: var(--mj-status-success); }\n.st-scheduled { background: var(--mj-status-info); }\n.st-idle { background: var(--mj-text-disabled); }\n\n/* detail */\n.prod-layout .detail { display: flex; flex-direction: column; gap: 14px; }\n.prod-layout .detail .dh { display: flex; align-items: center; gap: 13px; }\n.prod-layout .detail .dh h2 { margin: 0; font-size: 17px; font-weight: 680; color: var(--mj-text-primary); }\n.prod-layout .detail .dh .sub { margin-top: 3px; }\n.prod-layout .detail .big-ico {\n  width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;\n  color: var(--mj-text-inverse); font-size: 16px; flex: 0 0 auto;\n}\n\n/* binding rows */\n.bind-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--mj-border-subtle); }\n.bind-row:last-child { border-bottom: none; }\n\n/* run history table */\n.runs-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.runs-table th {\n  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n  color: var(--mj-text-muted); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--mj-border-default);\n}\n.runs-table td { padding: 7px 8px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary); }\n.runs-table tr:last-child td { border-bottom: none; }\n.runs-table tr.run-row { cursor: pointer; }\n.runs-table tr.run-row:hover td { background: var(--mj-bg-surface-hover); }\n.runs-table .run-right { text-align: right; white-space: nowrap; }\n.runs-table .run-right i { margin-left: 8px; opacity: .5; }\n\n.run-back {\n  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;\n  background: none; border: none; color: var(--mj-text-link); font-size: 13px; padding: 0 0 10px;\n}\n\n/* empty state CTA spacing (shared .ps-empty) */\n.ps-production .ps-empty p { max-width: 520px; }\n"], encapsulation: 2 });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSProductionComponent, [{
        type: Component,
        args: [{ standalone: true, selector: 'ps-production', imports: [CommonModule, SharedGenericModule, MJButtonDirective, PSOperateDialogComponent], encapsulation: ViewEncapsulation.None, template: `
    <div class="ps-panel ps-production" data-testid="ps-production-panel">
      @if (models.length === 0) {
        <div class="ps-empty" data-testid="ps-production-empty">
          <span class="ps-empty-ico"><i class="fa-solid fa-satellite-dish"></i></span>
          <h3>No models in production yet</h3>
          <p>
            When you publish a trained model in the Model Registry it shows up here — ready to schedule,
            bind to a column, and run. Train a pipeline and promote the model to Published to get started.
          </p>
        </div>
      } @else {
        <!-- KPI strip -->
        <div class="prod-kpis" data-testid="ps-production-kpis">
          <div class="ps-kpi"><div class="label">Published</div><div class="val">{{ models.length }}</div></div>
          <div class="ps-kpi"><div class="label">Scheduled</div><div class="val">{{ scheduledModelCount }}</div></div>
          <div class="ps-kpi"><div class="label">Bound (write-back)</div><div class="val">{{ boundModelCount }}</div></div>
          <div class="ps-kpi"><div class="label">Idle</div><div class="val">{{ idleModelCount }}</div></div>
        </div>

        <div class="prod-layout">
          <!-- master list -->
          <div class="ps-card mlist" data-testid="ps-production-list">
            <div class="ps-card-head"><h3>Published models</h3><span class="ps-badge gray">{{ models.length }}</span></div>
            <div class="ps-card-body" style="padding:8px">
              @for (m of models; track m.modelId) {
                <div class="mrow" data-testid="ps-production-row" [class.sel]="m.modelId === selectedModelId" (click)="select(m.modelId)">
                  <div class="ico" [class]="'st-' + m.deployState"><i [class]="deployIcon(m.deployState)"></i></div>
                  <div style="flex:1;min-width:0">
                    <div class="nm">{{ m.label }}</div>
                    <div class="ln2 ps-muted ps-small">v{{ m.version }} · {{ m.algorithm }}</div>
                  </div>
                  <div class="meta">
                    <span class="ps-badge" [class]="deployBadge(m.deployState)">{{ deployLabel(m.deployState) }}</span>
                    <div class="ps-mono ps-small ps-muted">{{ m.holdoutMetric }}</div>
                  </div>
                </div>
              }
            </div>
          </div>

          <!-- detail -->
          <div class="ps-col detail" data-testid="ps-production-detail">
            <div class="ps-card">
              <div class="ps-card-body dh">
                <div class="big-ico" [class]="'st-' + selected.deployState"><i [class]="deployIcon(selected.deployState)"></i></div>
                <div style="flex:1">
                  <h2 data-testid="ps-production-detail-name">{{ selected.label }} <span class="ps-tag ps-mono">v{{ selected.version }}</span></h2>
                  <div class="ps-muted ps-small sub">{{ selected.algorithm }} · {{ selected.problemType }} · holdout {{ selected.holdoutMetric }}</div>
                </div>
                <span class="ps-badge" [class]="deployBadge(selected.deployState)">{{ deployLabel(selected.deployState) }}</span>
                <button mjButton variant="primary" size="sm" data-testid="ps-production-operate" (click)="operateOpen = true">
                  <i class="fa-solid fa-rocket"></i> Operate
                </button>
              </div>
            </div>

            <!-- deployment status -->
            <div class="ps-card">
              <div class="ps-card-head"><h3>Deployment</h3>
                <span class="ps-muted ps-small">{{ selected.processCount }} process{{ selected.processCount === 1 ? '' : 'es' }} · {{ selected.bindingCount }} binding{{ selected.bindingCount === 1 ? '' : 's' }}</span>
              </div>
              <div class="ps-card-body">
                @if (selected.deployState === 'idle') {
                  <div class="ps-callout info">
                    <i class="fa-solid fa-circle-info"></i>
                    <div class="ps-small">This model is published but <strong>not operating</strong> yet — nothing scores with it. Click <strong>Operate</strong> above to run it now, schedule it to run regularly, or write predictions back to a column.</div>
                  </div>
                } @else {
                  @if (selectedBindings.length > 0) {
                    <div class="ps-section-title">Scoring bindings</div>
                    @for (b of selectedBindings; track b.id) {
                      <div class="bind-row">
                        <i class="fa-solid fa-arrow-right-to-bracket ps-muted"></i>
                        <div style="flex:1">
                          <div class="ps-small"><strong>{{ b.targetEntity }}</strong>.{{ b.targetColumn }}</div>
                          <div class="ps-muted ps-small">{{ b.mode }}{{ b.schedulePhrase ? ' · ' + b.schedulePhrase : '' }}</div>
                        </div>
                        <span class="ps-badge" [class]="modeBadge(b.mode)">{{ b.mode }}</span>
                      </div>
                    }
                  } @else {
                    <div class="ps-small ps-muted">Scheduled to run, writing generic output to the process run history (no write-back column).</div>
                  }
                  @if (selected.lastScoredAt) {
                    <div class="ps-divider"></div>
                    <div class="ps-row" style="justify-content:space-between"><span class="ps-muted ps-small">Last scored</span><span class="ps-small">{{ selected.lastScoredAt | date:'medium' }}{{ selected.lastRowCount != null ? ' · ' + selected.lastRowCount + ' rows' : '' }}</span></div>
                  }
                }
              </div>
            </div>

            <!-- past runs (on demand) -->
            <div class="ps-card">
              <div class="ps-card-head"><h3>Run history</h3><span class="ps-muted ps-small">last 90 days · newest first</span></div>
              <div class="ps-card-body">
                @if (selectedRunId) {
                  <!-- run drill-in: per-record predictions from Process Run Details -->
                  <button class="run-back" data-testid="ps-production-run-back" (click)="closeRun()">
                    <i class="fa-solid fa-arrow-left"></i> All runs
                  </button>
                  @if (loadingDetails) {
                    <mj-loading text="Loading predictions..." size="small"></mj-loading>
                  } @else if (runDetails.length === 0) {
                    <div class="ps-small ps-muted">No per-record predictions recorded for this run.</div>
                  } @else {
                    <table class="runs-table" data-testid="ps-production-run-detail">
                      <thead><tr><th>Record</th><th>Status</th><th>Prediction</th><th>Scored</th></tr></thead>
                      <tbody>
                        @for (d of runDetails; track d.recordId) {
                          <tr>
                            <td class="ps-mono ps-small">{{ d.recordId }}</td>
                            <td><span class="ps-badge" [class]="runBadge(d.status)">{{ d.status }}</span></td>
                            <td class="ps-mono">{{ d.score }}@if (d.class) { <span class="ps-muted"> · {{ d.class }}</span> }</td>
                            <td class="ps-small ps-muted">{{ d.scoredAt ? (d.scoredAt | date:'short') : '—' }}</td>
                          </tr>
                        }
                      </tbody>
                    </table>
                  }
                } @else if (loadingRuns) {
                  <mj-loading text="Loading runs..." size="small"></mj-loading>
                } @else if (runs.length === 0) {
                  <div class="ps-small ps-muted">No runs recorded yet. Each time this model runs as a Record Process, its predictions are saved to the process run history — even without a scoring binding.</div>
                } @else {
                  <table class="runs-table">
                    <thead><tr><th>When</th><th>Status</th><th>Scored</th><th>Process</th><th></th></tr></thead>
                    <tbody>
                      @for (r of runs; track r.ID) {
                        <tr data-testid="ps-production-run" class="run-row" (click)="openRun(r.ID)">
                          <td>{{ (r.StartTime || r.CreatedAt) | date:'short' }}</td>
                          <td><span class="ps-badge" [class]="runBadge(r.Status)">{{ r.Status }}</span></td>
                          <td class="ps-mono">{{ r.SuccessCount }}<span class="ps-muted">/{{ r.TotalItemCount ?? 0 }}</span></td>
                          <td class="ps-small ps-muted">{{ r.ProcessName || '—' }}</td>
                          <td class="run-right">@if (r.DryRun) { <span class="ps-tag amber">dry run</span> }<i class="fa-solid fa-chevron-right ps-muted"></i></td>
                        </tr>
                      }
                    </tbody>
                  </table>
                }
              </div>
            </div>
          </div>
        </div>

        <ps-operate-dialog
          [Visible]="operateOpen"
          [modelId]="selected.modelId"
          [modelLabel]="selected.label"
          [engine]="engine"
          [provider]="ProviderToUse"
          [currentUser]="ProviderToUse.CurrentUser"
          (Close)="onOperateClose($event)">
        </ps-operate-dialog>
      }
    </div>
  `, styles: ["/* ============================================================================\n   Predictive Studio \u2014 shared panel styles (design-token compliant)\n   Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n   Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n   so the class names are global within the PS dashboard subtree.\n   No hardcoded colors except the intentional categorical node/algorithm palette\n   (declared once as --ps-* custom props below) which must stay distinct across\n   light/dark to remain readable as diagram accents.\n   ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n   data-visualization colors that must remain distinct regardless of theme\n   (allowed exception #3 in the design-token rules). */\n.ps-panel {\n  --ps-node-source: #6b7280;\n  --ps-node-feature: #2050c8;\n  --ps-node-embedding: #7b3ff0;\n  --ps-node-target: #1f9d57;\n  --ps-node-algorithm: #d98213;\n  /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n  --ps-gauge-cost: var(--ps-node-algorithm);\n  --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n  display: block;\n  height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n  font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n  color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n  background: var(--mj-bg-surface);\n  border: 1px solid var(--mj-border-default);\n  border-radius: 10px;\n  box-shadow: var(--mj-shadow-sm);\n  overflow: hidden;\n}\n.ps-card-head {\n  display: flex; align-items: center; gap: 10px;\n  padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n  background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n  border-radius: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n  display: inline-flex; align-items: center; gap: 5px;\n  padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n  display: inline-flex; align-items: center; justify-content: center;\n  min-width: 92px; padding: 4px 10px; border-radius: 7px;\n  font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n  display: inline-block; padding: 2px 8px; border-radius: 6px;\n  background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n  font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n  color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n  border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n  width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n  display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\n\n/* gauge */\n.ps-gauge {\n  position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n  background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n  display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n  width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n  display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n  position: relative;\n  background:\n    linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n    linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n    var(--mj-bg-surface-card);\n  border: 1px solid var(--mj-border-default); border-radius: 10px;\n  min-height: 620px; overflow: hidden;\n}\n.ps-node {\n  position: absolute; width: 190px; background: var(--mj-bg-surface);\n  border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 10px;\n  box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n  display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n  border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n  width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n  color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n  border-color: var(--mj-brand-primary);\n  box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n  position: absolute; width: 11px; height: 11px; border-radius: 50%;\n  background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n  top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n  display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n  border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n  font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n  border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n  padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n  border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n  display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n  color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n  width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n  border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n  box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n  display: flex; flex-direction: column; align-items: center; justify-content: center;\n  text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n  width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n  background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n  position: fixed; inset: 0; z-index: 1000;\n  background: var(--mj-bg-overlay);\n  display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n  width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n  border: 1px solid var(--mj-border-default); border-radius: 14px;\n  box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n  display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n  border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n  width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n  font-size: 16px; flex: 0 0 auto;\n  background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n  display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n  border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", "/* Models in Production \u2014 model-centric control tower (master/detail). All colors via --mj-* tokens. */\n\n.ps-production {\n  display: flex;\n  flex-direction: column;\n  gap: 16px;\n  padding: 4px;\n}\n\n.prod-kpis {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n  gap: 14px;\n}\n\n.prod-layout {\n  display: grid;\n  grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);\n  gap: 16px;\n  align-items: start;\n}\n@media (max-width: 900px) {\n  .prod-layout { grid-template-columns: 1fr; }\n}\n\n/* master list */\n.prod-layout .mlist .mrow {\n  display: flex;\n  align-items: center;\n  gap: 11px;\n  padding: 10px 10px;\n  border-radius: 9px;\n  cursor: pointer;\n}\n.prod-layout .mlist .mrow:hover { background: var(--mj-bg-surface-hover); }\n.prod-layout .mlist .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); }\n.prod-layout .mlist .mrow .ico {\n  width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center;\n  color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.prod-layout .mlist .mrow .nm { font-weight: 640; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.prod-layout .mlist .mrow .ln2 { margin-top: 2px; }\n.prod-layout .mlist .mrow .meta { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }\n\n/* deploy-state icon colors (master + detail) */\n.st-bound { background: var(--mj-status-success); }\n.st-scheduled { background: var(--mj-status-info); }\n.st-idle { background: var(--mj-text-disabled); }\n\n/* detail */\n.prod-layout .detail { display: flex; flex-direction: column; gap: 14px; }\n.prod-layout .detail .dh { display: flex; align-items: center; gap: 13px; }\n.prod-layout .detail .dh h2 { margin: 0; font-size: 17px; font-weight: 680; color: var(--mj-text-primary); }\n.prod-layout .detail .dh .sub { margin-top: 3px; }\n.prod-layout .detail .big-ico {\n  width: 40px; height: 40px; border-radius: 11px; display: grid; place-items: center;\n  color: var(--mj-text-inverse); font-size: 16px; flex: 0 0 auto;\n}\n\n/* binding rows */\n.bind-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid var(--mj-border-subtle); }\n.bind-row:last-child { border-bottom: none; }\n\n/* run history table */\n.runs-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.runs-table th {\n  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n  color: var(--mj-text-muted); font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--mj-border-default);\n}\n.runs-table td { padding: 7px 8px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary); }\n.runs-table tr:last-child td { border-bottom: none; }\n.runs-table tr.run-row { cursor: pointer; }\n.runs-table tr.run-row:hover td { background: var(--mj-bg-surface-hover); }\n.runs-table .run-right { text-align: right; white-space: nowrap; }\n.runs-table .run-right i { margin-left: 8px; opacity: .5; }\n\n.run-back {\n  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;\n  background: none; border: none; color: var(--mj-text-link); font-size: 13px; padding: 0 0 10px;\n}\n\n/* empty state CTA spacing (shared .ps-empty) */\n.ps-production .ps-empty p { max-width: 520px; }\n"] }]
    }], null, { engine: [{
            type: Input
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSProductionComponent, { className: "PSProductionComponent", filePath: "src/PredictiveStudio/components/ps-production.component.ts", lineNumber: 232 }); })();
//# sourceMappingURL=ps-production.component.js.map