import { ChangeDetectorRef, Component, EventEmitter, Input, Output, ViewEncapsulation, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RunView } from '@memberjunction/core';
import { UUIDsEqual } from '@memberjunction/global';
import { MJButtonDirective, MJDialogComponent } from '@memberjunction/ng-ui-components';
import { MJNotificationService } from '@memberjunction/ng-notifications';
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
import { PredictiveStudioCreateScoringProcessOperation, RecordProcessRunNowOperation, } from '@memberjunction/core-entities';
import { SchedulingModule } from '@memberjunction/ng-scheduling';
import { mapStateToCreateScoringInput, describeOperateMappingError, } from './ps-operate-dialog.mapping';
import * as i0 from "@angular/core";
import * as i1 from "@memberjunction/ng-scheduling";
const _forTrack0 = ($index, $item) => $item.ID;
function PSOperateDialogComponent_Conditional_22_For_4_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "option", 28);
    i0.ɵɵtext(1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const v_r3 = ctx.$implicit;
    i0.ɵɵproperty("value", v_r3.ID);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(v_r3.Name);
} }
function PSOperateDialogComponent_Conditional_22_Conditional_5_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 29);
    i0.ɵɵtext(1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1("No saved views for ", ctx_r1.state.targetEntityName, " yet \u2014 pick Everyone or a list.");
} }
function PSOperateDialogComponent_Conditional_22_Template(rf, ctx) { if (rf & 1) {
    const _r1 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "select", 26);
    i0.ɵɵlistener("change", function PSOperateDialogComponent_Conditional_22_Template_select_change_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.state.viewId = $event.target.value); });
    i0.ɵɵelementStart(1, "option", 27);
    i0.ɵɵtext(2, "Choose a view\u2026");
    i0.ɵɵelementEnd();
    i0.ɵɵrepeaterCreate(3, PSOperateDialogComponent_Conditional_22_For_4_Template, 2, 2, "option", 28, _forTrack0);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(5, PSOperateDialogComponent_Conditional_22_Conditional_5_Template, 2, 1, "div", 29);
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵproperty("value", ctx_r1.state.viewId || "");
    i0.ɵɵadvance(3);
    i0.ɵɵrepeater(ctx_r1.views);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r1.views.length === 0 ? 5 : -1);
} }
function PSOperateDialogComponent_Conditional_23_For_4_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "option", 28);
    i0.ɵɵtext(1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const l_r5 = ctx.$implicit;
    i0.ɵɵproperty("value", l_r5.ID);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(l_r5.Name);
} }
function PSOperateDialogComponent_Conditional_23_Conditional_5_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 29);
    i0.ɵɵtext(1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1("No lists for ", ctx_r1.state.targetEntityName, " yet \u2014 pick Everyone or a view.");
} }
function PSOperateDialogComponent_Conditional_23_Template(rf, ctx) { if (rf & 1) {
    const _r4 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "select", 30);
    i0.ɵɵlistener("change", function PSOperateDialogComponent_Conditional_23_Template_select_change_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.state.listId = $event.target.value); });
    i0.ɵɵelementStart(1, "option", 27);
    i0.ɵɵtext(2, "Choose a list\u2026");
    i0.ɵɵelementEnd();
    i0.ɵɵrepeaterCreate(3, PSOperateDialogComponent_Conditional_23_For_4_Template, 2, 2, "option", 28, _forTrack0);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(5, PSOperateDialogComponent_Conditional_23_Conditional_5_Template, 2, 1, "div", 29);
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵproperty("value", ctx_r1.state.listId || "");
    i0.ɵɵadvance(3);
    i0.ɵɵrepeater(ctx_r1.lists);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r1.lists.length === 0 ? 5 : -1);
} }
function PSOperateDialogComponent_Conditional_34_For_3_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "option", 28);
} if (rf & 2) {
    const f_r7 = ctx.$implicit;
    i0.ɵɵproperty("value", f_r7);
} }
function PSOperateDialogComponent_Conditional_34_Conditional_4_Template(rf, ctx) { if (rf & 1) {
    const _r8 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 33)(1, "button", 7);
    i0.ɵɵlistener("click", function PSOperateDialogComponent_Conditional_34_Conditional_4_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.state.valueKind = "score"); });
    i0.ɵɵtext(2, "Probability");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "button", 7);
    i0.ɵɵlistener("click", function PSOperateDialogComponent_Conditional_34_Conditional_4_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r8); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.state.valueKind = "class"); });
    i0.ɵɵtext(4, "Predicted class");
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance();
    i0.ɵɵclassProp("on", ctx_r1.state.valueKind === "score");
    i0.ɵɵadvance(2);
    i0.ɵɵclassProp("on", ctx_r1.state.valueKind === "class");
} }
function PSOperateDialogComponent_Conditional_34_Template(rf, ctx) { if (rf & 1) {
    const _r6 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "input", 31);
    i0.ɵɵlistener("input", function PSOperateDialogComponent_Conditional_34_Template_input_input_0_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.state.outputField = $event.target.value); });
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(1, "datalist", 32);
    i0.ɵɵrepeaterCreate(2, PSOperateDialogComponent_Conditional_34_For_3_Template, 1, 1, "option", 28, i0.ɵɵrepeaterTrackByIdentity);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(4, PSOperateDialogComponent_Conditional_34_Conditional_4_Template, 5, 4, "div", 33);
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵproperty("placeholder", i0.ɵɵinterpolate1("Column on ", ctx_r1.state.targetEntityName, " (e.g. RenewalProbability)"))("value", ctx_r1.state.outputField);
    i0.ɵɵadvance(2);
    i0.ɵɵrepeater(ctx_r1.entityFields);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r1.isClassification ? 4 : -1);
} }
function PSOperateDialogComponent_Conditional_46_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "span", 23);
    i0.ɵɵelement(1, "i", 34);
    i0.ɵɵtext(2);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r1 = i0.ɵɵnextContext();
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1(" ", ctx_r1.busyLabel);
} }
const RUN_RECORD_PROCESS_JOB_TYPE = 'Run Record Process';
/**
 * **Operate this model** — the dialog that turns a published model into a running one. It composes
 * three primitives, no bespoke transport:
 *  - **what** → the `PredictiveStudio.CreateScoringProcess` Remote Op (persists an on-demand
 *    `WorkType='ML Model'` Record Process; write-back when an output column is chosen, generic otherwise);
 *  - **now** → the generic `RecordProcess.RunNow` Remote Op on that process;
 *  - **when** → the generic `@memberjunction/ng-scheduling` dialog (a "Run Record Process" scheduled job).
 *
 * The target entity is FIXED to the model's training entity (the model's features only assemble against
 * that entity), so the knobs are **scope** (everyone / a saved view / a list) and **output** (generic /
 * write-back column). Pure submit→input mapping lives in `ps-operate-dialog.mapping.ts` (unit-tested).
 */
export class PSOperateDialogComponent extends BaseAngularComponent {
    modelId = '';
    modelLabel = '';
    engine;
    provider = null;
    currentUser = null;
    /** Opening the dialog (false → true) initializes the knobs + loads views/lists/fields for the model's entity. */
    set Visible(value) {
        const opening = value && !this._visible;
        this._visible = value;
        if (opening) {
            void this.init();
        }
    }
    get Visible() {
        return this._visible;
    }
    _visible = false;
    Close = new EventEmitter();
    cdr = inject(ChangeDetectorRef);
    notifications = inject(MJNotificationService);
    state = this.blankState();
    views = [];
    lists = [];
    entityFields = [];
    isClassification = false;
    busy = false;
    busyLabel = '';
    // scheduling sub-dialog
    scheduleOpen = false;
    jobTypeId = null;
    schedConfig = null;
    // ---- init / load (DB-light: a couple of small scoped RunViews on open) ----
    async init() {
        this.state = this.blankState();
        this.views = [];
        this.lists = [];
        this.entityFields = [];
        this.scheduleOpen = false;
        const model = this.engine?.ModelByID(this.modelId);
        this.isClassification = model?.ProblemType === 'classification';
        const pipeline = model ? this.engine.Pipelines.find((p) => UUIDsEqual(p.ID, model.PipelineID)) : undefined;
        const entity = pipeline?.TargetEntityID
            ? this.ProviderToUse.Entities.find((e) => UUIDsEqual(e.ID, pipeline.TargetEntityID))
            : undefined;
        this.state.targetEntityName = entity?.Name ?? '';
        this.entityFields = (entity?.Fields ?? []).map((f) => f.Name).sort((a, b) => a.localeCompare(b));
        await Promise.all([this.loadViews(entity?.ID), this.loadLists(entity?.ID), this.resolveJobType()]);
        this.cdr.detectChanges();
    }
    async loadViews(entityId) {
        if (!entityId)
            return;
        const rv = RunView.FromMetadataProvider(this.ProviderToUse);
        const r = await rv.RunView({ EntityName: 'MJ: User Views', ExtraFilter: `EntityID='${entityId}'`, OrderBy: 'Name', ResultType: 'entity_object' }, this.ProviderToUse.CurrentUser);
        this.views = r.Success ? (r.Results ?? []) : [];
    }
    async loadLists(entityId) {
        if (!entityId)
            return;
        const rv = RunView.FromMetadataProvider(this.ProviderToUse);
        const r = await rv.RunView({ EntityName: 'MJ: Lists', ExtraFilter: `EntityID='${entityId}'`, OrderBy: 'Name', ResultType: 'entity_object' }, this.ProviderToUse.CurrentUser);
        this.lists = r.Success ? (r.Results ?? []) : [];
    }
    /** Resolve the "Run Record Process" scheduled-job-type id the generic scheduler dialog needs. */
    async resolveJobType() {
        const rv = RunView.FromMetadataProvider(this.ProviderToUse);
        const r = await rv.RunView({ EntityName: 'MJ: Scheduled Job Types', ExtraFilter: `Name='${RUN_RECORD_PROCESS_JOB_TYPE}'`, ResultType: 'entity_object' }, this.ProviderToUse.CurrentUser);
        this.jobTypeId = r.Success ? (r.Results?.[0]?.ID ?? null) : null;
    }
    setScope(mode) {
        this.state.scopeMode = mode;
    }
    // ---- live summary ----
    get summary() {
        const entity = this.state.targetEntityName || 'the entity';
        const scope = this.state.scopeMode === 'all'
            ? `every record in <strong>${entity}</strong>`
            : this.state.scopeMode === 'view'
                ? `the <strong>${this.viewName() ?? 'selected'}</strong> view of ${entity}`
                : `the <strong>${this.listName() ?? 'selected'}</strong> list of ${entity}`;
        const output = this.state.outputMode === 'writeback'
            ? this.state.outputField.trim()
                ? `writes the ${this.state.valueKind === 'class' ? 'predicted class' : 'probability'} into <strong>${this.state.outputField.trim()}</strong>`
                : `writes back to a column you choose`
            : `records predictions in the run history (no column written)`;
        return `Scores ${scope} with <strong>${this.modelLabel}</strong> and ${output}.`;
    }
    viewName() {
        return this.views.find((v) => UUIDsEqual(v.ID, this.state.viewId ?? ''))?.Name ?? null;
    }
    listName() {
        return this.lists.find((l) => UUIDsEqual(l.ID, this.state.listId ?? ''))?.Name ?? null;
    }
    // ---- actions ----
    /** Create the scoring Record Process, then run it once immediately. */
    async onRunNow() {
        const rpId = await this.createProcess('Creating + running…');
        if (!rpId)
            return;
        try {
            const run = new RecordProcessRunNowOperation();
            const result = await run.Execute({ recordProcessID: rpId }, { provider: this.provider ?? undefined, user: this.currentUser ?? undefined });
            if (result.Success && result.Output) {
                this.notifications.CreateSimpleNotification(`Scored ${result.Output.success}/${result.Output.processed} record(s) — ${result.Output.status}.`, result.Output.status === 'Completed' ? 'success' : 'warning', 5000);
                await this.refreshAndClose();
            }
            else {
                this.notifications.CreateSimpleNotification(result.ErrorMessage || 'Run failed (the process was created and can be run again).', 'error', 6000);
            }
        }
        catch (e) {
            this.notifications.CreateSimpleNotification(`Run error: ${this.msg(e)}`, 'error', 6000);
        }
        finally {
            this.busy = false;
            this.cdr.detectChanges();
        }
    }
    /** Create the scoring Record Process, then hand it to the generic scheduler dialog. */
    async onSchedule() {
        if (!this.jobTypeId) {
            this.notifications.CreateSimpleNotification('Scheduling is unavailable — the "Run Record Process" job type was not found.', 'error', 6000);
            return;
        }
        const rpId = await this.createProcess('Creating process…');
        if (!rpId)
            return;
        this.busy = false;
        this.schedConfig = JSON.stringify({ RecordProcessID: rpId });
        this.scheduleOpen = true;
        this.cdr.detectChanges();
    }
    async onScheduleClose(result) {
        this.scheduleOpen = false;
        if (result?.Saved) {
            this.notifications.CreateSimpleNotification(`Scheduled "${this.modelLabel}" to score on a recurring cadence.`, 'success', 5000);
            await this.refreshAndClose();
        }
        else {
            // The on-demand Record Process still exists (it can be run or scheduled later) — just close.
            this.notifications.CreateSimpleNotification('Scoring process created (not scheduled). You can run or schedule it anytime.', 'info', 4500);
            await this.refreshAndClose();
        }
    }
    onCancel() {
        if (this.busy)
            return;
        this.Close.emit({ changed: false });
    }
    // ---- shared op plumbing ----
    /** Map knobs → input and create the scoring Record Process via the Remote Op. Returns its id, or null on failure. */
    async createProcess(label) {
        const mapped = mapStateToCreateScoringInput(this.state);
        if (!mapped.ok) {
            this.notifications.CreateSimpleNotification(describeOperateMappingError(mapped.error), 'warning', 4000);
            return null;
        }
        this.busy = true;
        this.busyLabel = label;
        this.cdr.detectChanges();
        try {
            const op = new PredictiveStudioCreateScoringProcessOperation();
            const result = await op.Execute(mapped.input, { provider: this.provider ?? undefined, user: this.currentUser ?? undefined });
            if (result.Success && result.Output?.recordProcessId) {
                return result.Output.recordProcessId;
            }
            this.notifications.CreateSimpleNotification(result.ErrorMessage || 'Could not create the scoring process.', 'error', 6000);
            this.busy = false;
            return null;
        }
        catch (e) {
            this.notifications.CreateSimpleNotification(`Could not create the scoring process: ${this.msg(e)}`, 'error', 6000);
            this.busy = false;
            return null;
        }
    }
    async refreshAndClose() {
        await this.engine.Config(true, this.currentUser ?? undefined, this.provider ?? undefined);
        this.busy = false;
        this.Close.emit({ changed: true });
    }
    msg(e) {
        return e instanceof Error ? e.message : String(e);
    }
    blankState() {
        return {
            modelId: this.modelId,
            targetEntityName: '',
            scopeMode: 'all',
            viewId: null,
            listId: null,
            outputMode: 'generic',
            outputField: '',
            valueKind: 'score',
        };
    }
    static ɵfac = /*@__PURE__*/ (() => { let ɵPSOperateDialogComponent_BaseFactory; return function PSOperateDialogComponent_Factory(__ngFactoryType__) { return (ɵPSOperateDialogComponent_BaseFactory || (ɵPSOperateDialogComponent_BaseFactory = i0.ɵɵgetInheritedFactory(PSOperateDialogComponent)))(__ngFactoryType__ || PSOperateDialogComponent); }; })();
    static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSOperateDialogComponent, selectors: [["ps-operate-dialog"]], inputs: { modelId: "modelId", modelLabel: "modelLabel", engine: "engine", provider: "provider", currentUser: "currentUser", Visible: "Visible" }, outputs: { Close: "Close" }, features: [i0.ɵɵInheritDefinitionFeature], decls: 50, vars: 26, consts: [[3, "Close", "Visible", "Title", "Width"], ["data-testid", "ps-operate-dialog", 1, "ps-operate"], [1, "ps-callout", "info", "op-target"], [1, "fa-solid", "fa-table"], [1, "ps-small"], [1, "ps-field"], ["data-testid", "ps-operate-scope", 1, "ps-seg"], [3, "click"], [1, "fa-solid", "fa-globe"], [1, "fa-solid", "fa-table-list"], [1, "fa-solid", "fa-list-check"], ["data-testid", "ps-operate-output", 1, "ps-seg"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "fa-solid", "fa-pen-to-square"], ["data-testid", "ps-operate-summary", 1, "ps-callout", "op-summary"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "ps-small", 3, "innerHTML"], [1, "op-footer"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-operate-run", 3, "click", "disabled"], [1, "fa-solid", "fa-play"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-operate-schedule", 3, "click", "disabled"], [1, "fa-solid", "fa-clock"], [1, "ps-spacer"], [1, "ps-small", "ps-muted"], ["mjButton", "", "variant", "flat", "size", "sm", 3, "click", "disabled"], [3, "Close", "Visible", "JobTypeID", "DefaultConfiguration", "HideJobType"], ["data-testid", "ps-operate-view", 1, "mj-input", 3, "change", "value"], ["value", "", "disabled", ""], [3, "value"], [1, "ps-small", "ps-muted", "op-hint"], ["data-testid", "ps-operate-list", 1, "mj-input", 3, "change", "value"], ["type", "text", "list", "ps-operate-fields", "data-testid", "ps-operate-column", 1, "mj-input", 3, "input", "placeholder", "value"], ["id", "ps-operate-fields"], [1, "ps-seg", "sm", 2, "margin-top", "8px"], [1, "fa-solid", "fa-spinner", "fa-spin"]], template: function PSOperateDialogComponent_Template(rf, ctx) { if (rf & 1) {
            i0.ɵɵelementStart(0, "mj-dialog", 0);
            i0.ɵɵlistener("Close", function PSOperateDialogComponent_Template_mj_dialog_Close_0_listener() { return ctx.onCancel(); });
            i0.ɵɵelementStart(1, "div", 1)(2, "div", 2);
            i0.ɵɵelement(3, "i", 3);
            i0.ɵɵelementStart(4, "div", 4);
            i0.ɵɵtext(5, " Scores ");
            i0.ɵɵelementStart(6, "strong");
            i0.ɵɵtext(7);
            i0.ɵɵelementEnd();
            i0.ɵɵtext(8, " \u2014 the entity this model was trained on. ");
            i0.ɵɵelementEnd()();
            i0.ɵɵelementStart(9, "div", 5)(10, "label");
            i0.ɵɵtext(11, "Which records?");
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(12, "div", 6)(13, "button", 7);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_13_listener() { return ctx.setScope("all"); });
            i0.ɵɵelement(14, "i", 8);
            i0.ɵɵtext(15, " Everyone");
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(16, "button", 7);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_16_listener() { return ctx.setScope("view"); });
            i0.ɵɵelement(17, "i", 9);
            i0.ɵɵtext(18, " A saved view");
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(19, "button", 7);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_19_listener() { return ctx.setScope("list"); });
            i0.ɵɵelement(20, "i", 10);
            i0.ɵɵtext(21, " A list");
            i0.ɵɵelementEnd()();
            i0.ɵɵconditionalCreate(22, PSOperateDialogComponent_Conditional_22_Template, 6, 2);
            i0.ɵɵconditionalCreate(23, PSOperateDialogComponent_Conditional_23_Template, 6, 2);
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(24, "div", 5)(25, "label");
            i0.ɵɵtext(26, "Where do predictions go?");
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(27, "div", 11)(28, "button", 7);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_28_listener() { return ctx.state.outputMode = "generic"; });
            i0.ɵɵelement(29, "i", 12);
            i0.ɵɵtext(30, " Run history only");
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(31, "button", 7);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_31_listener() { return ctx.state.outputMode = "writeback"; });
            i0.ɵɵelement(32, "i", 13);
            i0.ɵɵtext(33, " Write back to a column");
            i0.ɵɵelementEnd()();
            i0.ɵɵconditionalCreate(34, PSOperateDialogComponent_Conditional_34_Template, 5, 4);
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(35, "div", 14);
            i0.ɵɵelement(36, "i", 15)(37, "div", 16);
            i0.ɵɵelementEnd()();
            i0.ɵɵelementStart(38, "div", 17)(39, "button", 18);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_39_listener() { return ctx.onRunNow(); });
            i0.ɵɵelement(40, "i", 19);
            i0.ɵɵtext(41, " Run now ");
            i0.ɵɵelementEnd();
            i0.ɵɵelementStart(42, "button", 20);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_42_listener() { return ctx.onSchedule(); });
            i0.ɵɵelement(43, "i", 21);
            i0.ɵɵtext(44, " Schedule\u2026 ");
            i0.ɵɵelementEnd();
            i0.ɵɵelement(45, "span", 22);
            i0.ɵɵconditionalCreate(46, PSOperateDialogComponent_Conditional_46_Template, 3, 1, "span", 23);
            i0.ɵɵelementStart(47, "button", 24);
            i0.ɵɵlistener("click", function PSOperateDialogComponent_Template_button_click_47_listener() { return ctx.onCancel(); });
            i0.ɵɵtext(48, "Cancel");
            i0.ɵɵelementEnd()()();
            i0.ɵɵelementStart(49, "mj-scheduled-job-dialog", 25);
            i0.ɵɵlistener("Close", function PSOperateDialogComponent_Template_mj_scheduled_job_dialog_Close_49_listener($event) { return ctx.onScheduleClose($event); });
            i0.ɵɵelementEnd();
        } if (rf & 2) {
            i0.ɵɵproperty("Visible", ctx.Visible)("Title", "Operate \u00B7 " + ctx.modelLabel)("Width", 640);
            i0.ɵɵadvance(7);
            i0.ɵɵtextInterpolate(ctx.state.targetEntityName || "\u2014");
            i0.ɵɵadvance(6);
            i0.ɵɵclassProp("on", ctx.state.scopeMode === "all");
            i0.ɵɵadvance(3);
            i0.ɵɵclassProp("on", ctx.state.scopeMode === "view");
            i0.ɵɵadvance(3);
            i0.ɵɵclassProp("on", ctx.state.scopeMode === "list");
            i0.ɵɵadvance(3);
            i0.ɵɵconditional(ctx.state.scopeMode === "view" ? 22 : -1);
            i0.ɵɵadvance();
            i0.ɵɵconditional(ctx.state.scopeMode === "list" ? 23 : -1);
            i0.ɵɵadvance(5);
            i0.ɵɵclassProp("on", ctx.state.outputMode === "generic");
            i0.ɵɵadvance(3);
            i0.ɵɵclassProp("on", ctx.state.outputMode === "writeback");
            i0.ɵɵadvance(3);
            i0.ɵɵconditional(ctx.state.outputMode === "writeback" ? 34 : -1);
            i0.ɵɵadvance(3);
            i0.ɵɵproperty("innerHTML", ctx.summary, i0.ɵɵsanitizeHtml);
            i0.ɵɵadvance(2);
            i0.ɵɵproperty("disabled", ctx.busy);
            i0.ɵɵadvance(3);
            i0.ɵɵproperty("disabled", ctx.busy);
            i0.ɵɵadvance(4);
            i0.ɵɵconditional(ctx.busy ? 46 : -1);
            i0.ɵɵadvance();
            i0.ɵɵproperty("disabled", ctx.busy);
            i0.ɵɵadvance(2);
            i0.ɵɵproperty("Visible", ctx.scheduleOpen)("JobTypeID", ctx.jobTypeId)("DefaultConfiguration", ctx.schedConfig)("HideJobType", true);
        } }, dependencies: [CommonModule, MJButtonDirective, MJDialogComponent, SchedulingModule, i1.ScheduledJobDialogComponent], 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", "// \"Operate this model\" dialog \u2014 scope + output knobs, composed Remote Ops. Tokens only.\n\n.ps-operate {\n  display: flex;\n  flex-direction: column;\n  gap: 18px;\n  padding: 4px 2px;\n}\n\n.ps-operate .op-target {\n  margin: 0;\n}\n\n.ps-operate .op-hint {\n  margin-top: 6px;\n}\n\n.ps-operate .op-summary {\n  background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n  border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n  color: var(--mj-text-secondary);\n}\n.ps-operate .op-summary i {\n  color: var(--mj-brand-primary);\n}\n\n// smaller segmented control (value-kind toggle)\n.ps-operate .ps-seg.sm button {\n  font-size: 12px;\n  padding: 5px 10px;\n}\n\n.op-footer {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 14px 2px 2px;\n}\n"], encapsulation: 2 });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSOperateDialogComponent, [{
        type: Component,
        args: [{ standalone: true, selector: 'ps-operate-dialog', imports: [CommonModule, MJButtonDirective, MJDialogComponent, SchedulingModule], encapsulation: ViewEncapsulation.None, template: `
    <mj-dialog [Visible]="Visible" [Title]="'Operate · ' + modelLabel" [Width]="640" (Close)="onCancel()">
      <div class="ps-operate" data-testid="ps-operate-dialog">
        <!-- target entity (fixed to the model's training entity) -->
        <div class="ps-callout info op-target">
          <i class="fa-solid fa-table"></i>
          <div class="ps-small">
            Scores <strong>{{ state.targetEntityName || '—' }}</strong> — the entity this model was trained on.
          </div>
        </div>

        <!-- SCOPE -->
        <div class="ps-field">
          <label>Which records?</label>
          <div class="ps-seg" data-testid="ps-operate-scope">
            <button [class.on]="state.scopeMode === 'all'" (click)="setScope('all')"><i class="fa-solid fa-globe"></i> Everyone</button>
            <button [class.on]="state.scopeMode === 'view'" (click)="setScope('view')"><i class="fa-solid fa-table-list"></i> A saved view</button>
            <button [class.on]="state.scopeMode === 'list'" (click)="setScope('list')"><i class="fa-solid fa-list-check"></i> A list</button>
          </div>
          @if (state.scopeMode === 'view') {
            <select class="mj-input" data-testid="ps-operate-view" [value]="state.viewId || ''" (change)="state.viewId = $any($event.target).value">
              <option value="" disabled>Choose a view…</option>
              @for (v of views; track v.ID) { <option [value]="v.ID">{{ v.Name }}</option> }
            </select>
            @if (views.length === 0) { <div class="ps-small ps-muted op-hint">No saved views for {{ state.targetEntityName }} yet — pick Everyone or a list.</div> }
          }
          @if (state.scopeMode === 'list') {
            <select class="mj-input" data-testid="ps-operate-list" [value]="state.listId || ''" (change)="state.listId = $any($event.target).value">
              <option value="" disabled>Choose a list…</option>
              @for (l of lists; track l.ID) { <option [value]="l.ID">{{ l.Name }}</option> }
            </select>
            @if (lists.length === 0) { <div class="ps-small ps-muted op-hint">No lists for {{ state.targetEntityName }} yet — pick Everyone or a view.</div> }
          }
        </div>

        <!-- OUTPUT -->
        <div class="ps-field">
          <label>Where do predictions go?</label>
          <div class="ps-seg" data-testid="ps-operate-output">
            <button [class.on]="state.outputMode === 'generic'" (click)="state.outputMode = 'generic'"><i class="fa-solid fa-clock-rotate-left"></i> Run history only</button>
            <button [class.on]="state.outputMode === 'writeback'" (click)="state.outputMode = 'writeback'"><i class="fa-solid fa-pen-to-square"></i> Write back to a column</button>
          </div>
          @if (state.outputMode === 'writeback') {
            <input class="mj-input" type="text" list="ps-operate-fields" data-testid="ps-operate-column"
              placeholder="Column on {{ state.targetEntityName }} (e.g. RenewalProbability)"
              [value]="state.outputField" (input)="state.outputField = $any($event.target).value" />
            <datalist id="ps-operate-fields">@for (f of entityFields; track f) { <option [value]="f"></option> }</datalist>
            @if (isClassification) {
              <div class="ps-seg sm" style="margin-top:8px">
                <button [class.on]="state.valueKind === 'score'" (click)="state.valueKind = 'score'">Probability</button>
                <button [class.on]="state.valueKind === 'class'" (click)="state.valueKind = 'class'">Predicted class</button>
              </div>
            }
          }
        </div>

        <!-- live summary -->
        <div class="ps-callout op-summary" data-testid="ps-operate-summary">
          <i class="fa-solid fa-wand-magic-sparkles"></i>
          <div class="ps-small" [innerHTML]="summary"></div>
        </div>
      </div>

      <!-- footer (affirmative on the left, MJ convention) -->
      <div class="op-footer">
        <button mjButton variant="primary" size="sm" data-testid="ps-operate-run" [disabled]="busy" (click)="onRunNow()">
          <i class="fa-solid fa-play"></i> Run now
        </button>
        <button mjButton variant="secondary" size="sm" data-testid="ps-operate-schedule" [disabled]="busy" (click)="onSchedule()">
          <i class="fa-solid fa-clock"></i> Schedule…
        </button>
        <span class="ps-spacer"></span>
        @if (busy) { <span class="ps-small ps-muted"><i class="fa-solid fa-spinner fa-spin"></i> {{ busyLabel }}</span> }
        <button mjButton variant="flat" size="sm" [disabled]="busy" (click)="onCancel()">Cancel</button>
      </div>
    </mj-dialog>

    <!-- WHEN: the generic scheduling dialog, opened after the Record Process is created -->
    <mj-scheduled-job-dialog
      [Visible]="scheduleOpen"
      [JobTypeID]="jobTypeId"
      [DefaultConfiguration]="schedConfig"
      [HideJobType]="true"
      (Close)="onScheduleClose($event)">
    </mj-scheduled-job-dialog>
  `, 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", "// \"Operate this model\" dialog \u2014 scope + output knobs, composed Remote Ops. Tokens only.\n\n.ps-operate {\n  display: flex;\n  flex-direction: column;\n  gap: 18px;\n  padding: 4px 2px;\n}\n\n.ps-operate .op-target {\n  margin: 0;\n}\n\n.ps-operate .op-hint {\n  margin-top: 6px;\n}\n\n.ps-operate .op-summary {\n  background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n  border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n  color: var(--mj-text-secondary);\n}\n.ps-operate .op-summary i {\n  color: var(--mj-brand-primary);\n}\n\n// smaller segmented control (value-kind toggle)\n.ps-operate .ps-seg.sm button {\n  font-size: 12px;\n  padding: 5px 10px;\n}\n\n.op-footer {\n  display: flex;\n  align-items: center;\n  gap: 8px;\n  padding: 14px 2px 2px;\n}\n"] }]
    }], null, { modelId: [{
            type: Input
        }], modelLabel: [{
            type: Input
        }], engine: [{
            type: Input
        }], provider: [{
            type: Input
        }], currentUser: [{
            type: Input
        }], Visible: [{
            type: Input
        }], Close: [{
            type: Output
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSOperateDialogComponent, { className: "PSOperateDialogComponent", filePath: "src/PredictiveStudio/components/ps-operate-dialog.component.ts", lineNumber: 135 }); })();
//# sourceMappingURL=ps-operate-dialog.component.js.map