@masterteam/flowplus-workflow 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -10917,7 +10917,7 @@ class ContextPickerComponent {
|
|
|
10917
10917
|
return sourceGroupLabel(this.transloco, key);
|
|
10918
10918
|
}
|
|
10919
10919
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ContextPickerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10920
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ContextPickerComponent, isStandalone: true, selector: "fp-context-picker", outputs: { insert: "insert" }, ngImport: i0, template: "<div\r\n class=\"bg-(--p-content-background) border border-(--p-content-border-color) rounded-xl shadow-md w-[360px] max-h-[460px] flex flex-col\"\r\n role=\"dialog\"\r\n>\r\n <header class=\"p-2.5 border-b border-(--p-content-border-color)\">\r\n <mt-text-field\r\n [(ngModel)]=\"search\"\r\n [placeholder]=\"'flowplus.context.pickerPlaceholder' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n <div class=\"p-1.5 overflow-y-auto flex-1\">\r\n @if (groupedSources().length === 0) {\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.common.empty\" | transloco }}\r\n </div>\r\n }\r\n @for (group of groupedSources(); track group.key) {\r\n <section>\r\n <h4\r\n class=\"m-1.5 mt-2 text-[12px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ groupLabel(group.key) }}\r\n </h4>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-0.5\">\r\n @for (s of group.items; track s.path) {\r\n <li\r\n class=\"px-2 py-1.5 rounded-md flex items-center gap-1.5 cursor-pointer hover:bg-(--p-surface-100) focus:outline-none focus:bg-(--p-surface-100)\"\r\n tabindex=\"0\"\r\n (click)=\"insert.emit(s.path)\"\r\n (keydown.enter)=\"insert.emit(s.path)\"\r\n >\r\n <fp-context-pill [source]=\"s\" />\r\n <small\r\n class=\"text-(--p-text-muted-color) font-mono text-[11px] flex-1 truncate\"\r\n >{{ s.path }}</small\r\n >\r\n @if (s.isSecret) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(var(--fp-error))]/14 px-2 py-0.5 text-[10.5px] font-medium text-[rgb(var(--fp-error))]\"\r\n title=\"Secret value \u2014 masked\"\r\n >secret</span\r\n >\r\n }\r\n @if (s.isProducedBeforeCurrentStep === false) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(var(--fp-warning))]/18 px-2 py-0.5 text-[10.5px] font-medium text-[rgb(146_91_0)]\"\r\n [title]=\"'flowplus.context.availabilityWarning' | transloco\"\r\n >\r\n {{ \"flowplus.context.availabilityWarning\" | transloco }}\r\n </span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </section>\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
10920
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ContextPickerComponent, isStandalone: true, selector: "fp-context-picker", outputs: { insert: "insert" }, ngImport: i0, template: "<div\r\n class=\"bg-(--p-content-background) border border-(--p-content-border-color) rounded-xl shadow-md w-[360px] max-h-[460px] flex flex-col\"\r\n role=\"dialog\"\r\n>\r\n <header class=\"p-2.5 border-b border-(--p-content-border-color)\">\r\n <mt-text-field\r\n [(ngModel)]=\"search\"\r\n [placeholder]=\"'flowplus.context.pickerPlaceholder' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n <div class=\"p-1.5 overflow-y-auto flex-1\">\r\n @if (groupedSources().length === 0) {\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.common.empty\" | transloco }}\r\n </div>\r\n }\r\n @for (group of groupedSources(); track group.key) {\r\n <section>\r\n <h4\r\n class=\"m-1.5 mt-2 text-[12px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ groupLabel(group.key) }}\r\n </h4>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-0.5\">\r\n @for (s of group.items; track s.path) {\r\n <li\r\n class=\"px-2 py-1.5 rounded-md flex items-center gap-1.5 cursor-pointer hover:bg-(--p-surface-100) focus:outline-none focus:bg-(--p-surface-100)\"\r\n tabindex=\"0\"\r\n (click)=\"insert.emit(s.path)\"\r\n (keydown.enter)=\"insert.emit(s.path)\"\r\n >\r\n <fp-context-pill [source]=\"s\" />\r\n <small\r\n class=\"text-(--p-text-muted-color) font-mono text-[11px] flex-1 truncate\"\r\n >{{ s.path }}</small\r\n >\r\n @if (s.isSecret) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(var(--fp-error))]/14 px-2 py-0.5 text-[10.5px] font-medium text-[rgb(var(--fp-error))]\"\r\n title=\"Secret value \u2014 masked\"\r\n >secret</span\r\n >\r\n }\r\n @if (s.isProducedBeforeCurrentStep === false) {\r\n <span\r\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(var(--fp-warning))]/18 px-2 py-0.5 text-[10.5px] font-medium text-[rgb(146_91_0)]\"\r\n [title]=\"'flowplus.context.availabilityWarning' | transloco\"\r\n >\r\n {{ \"flowplus.context.availabilityWarning\" | transloco }}\r\n </span>\r\n }\r\n </li>\r\n }\r\n </ul>\r\n </section>\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: ContextPillComponent, selector: "fp-context-pill", inputs: ["source"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
10921
10921
|
}
|
|
10922
10922
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ContextPickerComponent, decorators: [{
|
|
10923
10923
|
type: Component,
|
|
@@ -11022,7 +11022,7 @@ class ConnectionInspectorComponent {
|
|
|
11022
11022
|
this.change.emit({ expressionText: next });
|
|
11023
11023
|
}
|
|
11024
11024
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ConnectionInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11025
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ConnectionInspectorComponent, isStandalone: true, selector: "fp-connection-inspector", inputs: { connection: { classPropertyName: "connection", publicName: "connection", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-number-field\r\n [ngModel]=\"connection().priority\"\r\n (ngModelChange)=\"patch({ priority: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.connection.priority' | transloco\"\r\n [min]=\"0\"\r\n/>\r\n\r\n<mt-text-field\r\n [ngModel]=\"connection().label?.['en']\"\r\n (ngModelChange)=\"setLabel($event)\"\r\n [label]=\"'flowplus.inspector.connection.labelEn' | transloco\"\r\n/>\r\n\r\n@if (sourceOutputOptions().length > 0) {\n <mt-select-field\n [ngModel]=\"sourceOutputValue()\"\n (ngModelChange)=\"setSourceOutputKey($event)\"\n label=\"Source output\"\n hint=\"Select one of the backend-provided route output keys for the source node.\"\n [options]=\"sourceOutputOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (sourceOutputMissing()) {\n <div\n class=\"rounded-md border border-[rgb(var(--fp-warning))]/30 bg-[rgb(var(--fp-warning))]/10 px-3 py-2 text-[12px] leading-5 text-[rgb(var(--fp-warning))]\"\n >\n The saved output key <code>{{ sourceOutputValue() }}</code> is no longer\n exposed by the backend catalog or current case/branch config.\n </div>\n }\n} @else {\n <mt-text-field\n [ngModel]=\"actionKeyString()\"\n (ngModelChange)=\"setActionsFromString($event)\"\n [label]=\"'flowplus.inspector.connection.selectedActions' | transloco\"\n [hint]=\"'flowplus.inspector.connection.selectedActionsHint' | transloco\"\n />\n}\n\r\n<div class=\"flex flex-col gap-1.5\">\r\n <mt-textarea-field\r\n [ngModel]=\"connection().expressionText\"\r\n (ngModelChange)=\"patch({ expressionText: $event })\"\r\n [label]=\"'flowplus.inspector.connection.expression' | transloco\"\r\n [rows]=\"3\"\r\n />\r\n <fp-context-pill-button (insert)=\"appendExpression($event)\" />\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
11025
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ConnectionInspectorComponent, isStandalone: true, selector: "fp-connection-inspector", inputs: { connection: { classPropertyName: "connection", publicName: "connection", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-number-field\r\n [ngModel]=\"connection().priority\"\r\n (ngModelChange)=\"patch({ priority: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.connection.priority' | transloco\"\r\n [min]=\"0\"\r\n/>\r\n\r\n<mt-text-field\r\n [ngModel]=\"connection().label?.['en']\"\r\n (ngModelChange)=\"setLabel($event)\"\r\n [label]=\"'flowplus.inspector.connection.labelEn' | transloco\"\r\n/>\r\n\r\n@if (sourceOutputOptions().length > 0) {\n <mt-select-field\n [ngModel]=\"sourceOutputValue()\"\n (ngModelChange)=\"setSourceOutputKey($event)\"\n label=\"Source output\"\n hint=\"Select one of the backend-provided route output keys for the source node.\"\n [options]=\"sourceOutputOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (sourceOutputMissing()) {\n <div\n class=\"rounded-md border border-[rgb(var(--fp-warning))]/30 bg-[rgb(var(--fp-warning))]/10 px-3 py-2 text-[12px] leading-5 text-[rgb(var(--fp-warning))]\"\n >\n The saved output key <code>{{ sourceOutputValue() }}</code> is no longer\n exposed by the backend catalog or current case/branch config.\n </div>\n }\n} @else {\n <mt-text-field\n [ngModel]=\"actionKeyString()\"\n (ngModelChange)=\"setActionsFromString($event)\"\n [label]=\"'flowplus.inspector.connection.selectedActions' | transloco\"\n [hint]=\"'flowplus.inspector.connection.selectedActionsHint' | transloco\"\n />\n}\n\r\n<div class=\"flex flex-col gap-1.5\">\r\n <mt-textarea-field\r\n [ngModel]=\"connection().expressionText\"\r\n (ngModelChange)=\"patch({ expressionText: $event })\"\r\n [label]=\"'flowplus.inspector.connection.expression' | transloco\"\r\n [rows]=\"3\"\r\n />\r\n <fp-context-pill-button (insert)=\"appendExpression($event)\" />\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ContextPillButtonComponent, selector: "fp-context-pill-button", outputs: ["insert"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
11026
11026
|
}
|
|
11027
11027
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ConnectionInspectorComponent, decorators: [{
|
|
11028
11028
|
type: Component,
|
|
@@ -11147,7 +11147,7 @@ class InspectorCommonComponent {
|
|
|
11147
11147
|
this.change.emit(partial);
|
|
11148
11148
|
}
|
|
11149
11149
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: InspectorCommonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11150
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: InspectorCommonComponent, isStandalone: true, selector: "fp-inspector-common", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-text-field\r\n [ngModel]=\"nameEn()\"\r\n (ngModelChange)=\"onName($event, 'en')\"\r\n [label]=\"'flowplus.inspector.fields.nameEn' | transloco\"\r\n [readonly]=\"step().isLocked\"\r\n/>\r\n\r\n<mt-text-field\r\n [ngModel]=\"nameAr()\"\r\n (ngModelChange)=\"onName($event, 'ar')\"\r\n [label]=\"'flowplus.inspector.fields.nameAr' | transloco\"\r\n [readonly]=\"step().isLocked\"\r\n [pInputs]=\"{ dir: 'rtl' }\"\r\n/>\r\n\r\n<mt-textarea-field\r\n [ngModel]=\"descEn()\"\r\n (ngModelChange)=\"onDesc($event, 'en')\"\r\n [label]=\"'flowplus.inspector.fields.descriptionEn' | transloco\"\r\n [rows]=\"2\"\r\n [readonly]=\"step().isLocked\"\r\n/>\r\n\r\n<mt-text-field\n [ngModel]=\"step().key\"\n [label]=\"'flowplus.inspector.fields.key' | transloco\"\n [readonly]=\"true\"\n hint=\"Technical backend node key. It is generated and managed by the workflow definition.\"\n/>\n\r\n<mt-text-field\r\n [ngModel]=\"step().type\"\r\n [label]=\"'flowplus.inspector.fields.type' | transloco\"\r\n [readonly]=\"true\"\r\n/>\r\n\r\n@if (step().type === \"UserInput\" || step().type === \"AppAction\") {\r\n <mt-number-field\r\n [ngModel]=\"step().sla ?? step().slaHours ?? null\"\r\n (ngModelChange)=\"patch({ sla: $event })\"\r\n [label]=\"'flowplus.inspector.fields.sla' | transloco\"\r\n [min]=\"0\"\r\n [readonly]=\"step().isLocked\"\r\n />\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
11150
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: InspectorCommonComponent, isStandalone: true, selector: "fp-inspector-common", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-text-field\r\n [ngModel]=\"nameEn()\"\r\n (ngModelChange)=\"onName($event, 'en')\"\r\n [label]=\"'flowplus.inspector.fields.nameEn' | transloco\"\r\n [readonly]=\"step().isLocked\"\r\n/>\r\n\r\n<mt-text-field\r\n [ngModel]=\"nameAr()\"\r\n (ngModelChange)=\"onName($event, 'ar')\"\r\n [label]=\"'flowplus.inspector.fields.nameAr' | transloco\"\r\n [readonly]=\"step().isLocked\"\r\n [pInputs]=\"{ dir: 'rtl' }\"\r\n/>\r\n\r\n<mt-textarea-field\r\n [ngModel]=\"descEn()\"\r\n (ngModelChange)=\"onDesc($event, 'en')\"\r\n [label]=\"'flowplus.inspector.fields.descriptionEn' | transloco\"\r\n [rows]=\"2\"\r\n [readonly]=\"step().isLocked\"\r\n/>\r\n\r\n<mt-text-field\n [ngModel]=\"step().key\"\n [label]=\"'flowplus.inspector.fields.key' | transloco\"\n [readonly]=\"true\"\n hint=\"Technical backend node key. It is generated and managed by the workflow definition.\"\n/>\n\r\n<mt-text-field\r\n [ngModel]=\"step().type\"\r\n [label]=\"'flowplus.inspector.fields.type' | transloco\"\r\n [readonly]=\"true\"\r\n/>\r\n\r\n@if (step().type === \"UserInput\" || step().type === \"AppAction\") {\r\n <mt-number-field\r\n [ngModel]=\"step().sla ?? step().slaHours ?? null\"\r\n (ngModelChange)=\"patch({ sla: $event })\"\r\n [label]=\"'flowplus.inspector.fields.sla' | transloco\"\r\n [min]=\"0\"\r\n [readonly]=\"step().isLocked\"\r\n />\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
11151
11151
|
}
|
|
11152
11152
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: InspectorCommonComponent, decorators: [{
|
|
11153
11153
|
type: Component,
|
|
@@ -11602,7 +11602,7 @@ class UserInputInspectorComponent {
|
|
|
11602
11602
|
return resolveTranslatable(v);
|
|
11603
11603
|
}
|
|
11604
11604
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: UserInputInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11605
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: UserInputInspectorComponent, isStandalone: true, selector: "fp-user-input-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-select-field\r\n [ngModel]=\"step().targetType\"\r\n (ngModelChange)=\"setActorType($event)\"\r\n [label]=\"'flowplus.inspector.tabs.actor' | transloco\"\r\n [options]=\"actorTypeOptions()\"\r\n optionValue=\"key\"\r\n optionLabel=\"label\"\r\n/>\r\n\r\n@switch (step().targetType) {\r\n @case (\"Group\") {\r\n @if (groupLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n } @else if (groupOptions().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.group' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.groupHint' | transloco\"\r\n [options]=\"groupOptions()\"\r\n optionValue=\"value\"\r\n optionLabel=\"label\"\r\n [filter]=\"true\"\r\n filterBy=\"label,value\"\r\n [showClear]=\"true\"\r\n [placeholder]=\"'flowplus.inspector.userInput.selectGroup' | transloco\"\r\n />\r\n } @else {\r\n <mt-text-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.groupKey' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.groupKeyHint' | transloco\"\r\n />\r\n @if (groupLoadFailed()) {\r\n <p class=\"text-[12px] leading-relaxed text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.userInput.groupLookupEmpty\" | transloco }}\r\n </p>\r\n }\r\n }\r\n }\r\n @case (\"Role\") {\r\n @if (roleLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n } @else if (roleOptions().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.role' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.roleHint' | transloco\"\r\n [options]=\"roleOptions()\"\r\n optionValue=\"value\"\r\n optionLabel=\"label\"\r\n [group]=\"roleOptionsGrouped()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n [filter]=\"true\"\r\n filterBy=\"label,value\"\r\n [showClear]=\"true\"\r\n [placeholder]=\"'flowplus.inspector.userInput.selectRole' | transloco\"\r\n />\r\n } @else {\r\n <mt-text-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.roleKey' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.roleKeyHint' | transloco\"\r\n />\r\n <p class=\"text-[12px] leading-relaxed text-(--p-text-muted-color)\">\r\n {{\r\n (workflowHasPrimaryResource()\r\n ? \"flowplus.inspector.userInput.roleLookupEmpty\"\r\n : \"flowplus.inspector.userInput.roleLookupNeedsResource\"\r\n ) | transloco\r\n }}\r\n </p>\r\n }\r\n }\r\n @case (\"User\") {\r\n <mt-user-search-field\r\n [ngModel]=\"selectedUser()\"\r\n (ngModelChange)=\"setSelectedUser($event)\"\r\n [label]=\"'flowplus.inspector.userInput.user' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.userHint' | transloco\"\r\n apiUrl=\"Identity/users\"\r\n />\r\n }\r\n @case (\"PropertyUser\") {\r\n <div class=\"flex gap-1.5 items-end\">\r\n <div class=\"flex-1\">\r\n <mt-text-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.propertyPath' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.propertyPathHint' | transloco\"\r\n />\r\n </div>\r\n <fp-context-pill-button (insert)=\"patch({ targetValue: $event })\" />\r\n </div>\r\n }\r\n @default {\r\n <p class=\"text-[12px] leading-relaxed text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.userInput.noTargetNeeded\" | transloco }}\r\n </p>\r\n }\r\n}\r\n\r\n@if (step().targetType === \"Group\") {\r\n <mt-select-field\r\n [ngModel]=\"step().groupSelection ?? defaultGroupSelection()\"\r\n (ngModelChange)=\"patch({ groupSelection: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.groupSelection' | transloco\"\r\n [options]=\"groupSelectionOptions()\"\r\n optionValue=\"key\"\r\n optionLabel=\"label\"\r\n />\r\n}\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.tabs.actions\" | transloco }}\r\n </label>\r\n <ul class=\"m-0 flex list-none flex-col gap-1.5 p-0\">\r\n @for (a of step().actions ?? []; track a.key) {\r\n <li class=\"flex items-center gap-1.5\">\r\n <div class=\"flex-1\">\r\n <mt-text-field\r\n [ngModel]=\"a.label?.['en'] ?? a.key\"\r\n (ngModelChange)=\"renameAction(a.key, $event)\"\r\n [label]=\"'flowplus.inspector.fields.nameEn' | transloco\"\r\n />\r\n </div>\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n [tooltip]=\"'Remove'\"\r\n (onClick)=\"removeAction(a.key)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"self-start\"\r\n [label]=\"'Action'\"\r\n (onClick)=\"addAction()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
11605
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: UserInputInspectorComponent, isStandalone: true, selector: "fp-user-input-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-select-field\r\n [ngModel]=\"step().targetType\"\r\n (ngModelChange)=\"setActorType($event)\"\r\n [label]=\"'flowplus.inspector.tabs.actor' | transloco\"\r\n [options]=\"actorTypeOptions()\"\r\n optionValue=\"key\"\r\n optionLabel=\"label\"\r\n/>\r\n\r\n@switch (step().targetType) {\r\n @case (\"Group\") {\r\n @if (groupLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n } @else if (groupOptions().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.group' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.groupHint' | transloco\"\r\n [options]=\"groupOptions()\"\r\n optionValue=\"value\"\r\n optionLabel=\"label\"\r\n [filter]=\"true\"\r\n filterBy=\"label,value\"\r\n [showClear]=\"true\"\r\n [placeholder]=\"'flowplus.inspector.userInput.selectGroup' | transloco\"\r\n />\r\n } @else {\r\n <mt-text-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.groupKey' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.groupKeyHint' | transloco\"\r\n />\r\n @if (groupLoadFailed()) {\r\n <p class=\"text-[12px] leading-relaxed text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.userInput.groupLookupEmpty\" | transloco }}\r\n </p>\r\n }\r\n }\r\n }\r\n @case (\"Role\") {\r\n @if (roleLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n } @else if (roleOptions().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.role' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.roleHint' | transloco\"\r\n [options]=\"roleOptions()\"\r\n optionValue=\"value\"\r\n optionLabel=\"label\"\r\n [group]=\"roleOptionsGrouped()\"\r\n optionGroupLabel=\"label\"\r\n optionGroupChildren=\"items\"\r\n [filter]=\"true\"\r\n filterBy=\"label,value\"\r\n [showClear]=\"true\"\r\n [placeholder]=\"'flowplus.inspector.userInput.selectRole' | transloco\"\r\n />\r\n } @else {\r\n <mt-text-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.roleKey' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.roleKeyHint' | transloco\"\r\n />\r\n <p class=\"text-[12px] leading-relaxed text-(--p-text-muted-color)\">\r\n {{\r\n (workflowHasPrimaryResource()\r\n ? \"flowplus.inspector.userInput.roleLookupEmpty\"\r\n : \"flowplus.inspector.userInput.roleLookupNeedsResource\"\r\n ) | transloco\r\n }}\r\n </p>\r\n }\r\n }\r\n @case (\"User\") {\r\n <mt-user-search-field\r\n [ngModel]=\"selectedUser()\"\r\n (ngModelChange)=\"setSelectedUser($event)\"\r\n [label]=\"'flowplus.inspector.userInput.user' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.userHint' | transloco\"\r\n apiUrl=\"Identity/users\"\r\n />\r\n }\r\n @case (\"PropertyUser\") {\r\n <div class=\"flex gap-1.5 items-end\">\r\n <div class=\"flex-1\">\r\n <mt-text-field\r\n [ngModel]=\"step().targetValue\"\r\n (ngModelChange)=\"patch({ targetValue: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.propertyPath' | transloco\"\r\n [hint]=\"'flowplus.inspector.userInput.propertyPathHint' | transloco\"\r\n />\r\n </div>\r\n <fp-context-pill-button (insert)=\"patch({ targetValue: $event })\" />\r\n </div>\r\n }\r\n @default {\r\n <p class=\"text-[12px] leading-relaxed text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.userInput.noTargetNeeded\" | transloco }}\r\n </p>\r\n }\r\n}\r\n\r\n@if (step().targetType === \"Group\") {\r\n <mt-select-field\r\n [ngModel]=\"step().groupSelection ?? defaultGroupSelection()\"\r\n (ngModelChange)=\"patch({ groupSelection: $event })\"\r\n [label]=\"'flowplus.inspector.userInput.groupSelection' | transloco\"\r\n [options]=\"groupSelectionOptions()\"\r\n optionValue=\"key\"\r\n optionLabel=\"label\"\r\n />\r\n}\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.tabs.actions\" | transloco }}\r\n </label>\r\n <ul class=\"m-0 flex list-none flex-col gap-1.5 p-0\">\r\n @for (a of step().actions ?? []; track a.key) {\r\n <li class=\"flex items-center gap-1.5\">\r\n <div class=\"flex-1\">\r\n <mt-text-field\r\n [ngModel]=\"a.label?.['en'] ?? a.key\"\r\n (ngModelChange)=\"renameAction(a.key, $event)\"\r\n [label]=\"'flowplus.inspector.fields.nameEn' | transloco\"\r\n />\r\n </div>\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n [tooltip]=\"'Remove'\"\r\n (onClick)=\"removeAction(a.key)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"self-start\"\r\n [label]=\"'Action'\"\r\n (onClick)=\"addAction()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: UserSearchField, selector: "mt-user-search-field", inputs: ["hint", "label", "placeholder", "class", "readonly", "required", "isMultiple", "apiUrl", "dataKey", "paramName", "context", "size"] }, { kind: "component", type: ContextPillButtonComponent, selector: "fp-context-pill-button", outputs: ["insert"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11606
11606
|
}
|
|
11607
11607
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: UserInputInspectorComponent, decorators: [{
|
|
11608
11608
|
type: Component,
|
|
@@ -11874,7 +11874,7 @@ class AutomatedInspectorComponent {
|
|
|
11874
11874
|
this.patchConfig({ body: `${this.config().body ?? ''}{{${path}}}` });
|
|
11875
11875
|
}
|
|
11876
11876
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AutomatedInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11877
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AutomatedInspectorComponent, isStandalone: true, selector: "fp-automated-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-5" }, ngImport: i0, template: "<mt-select-field\r\n [ngModel]=\"config().method ?? 'GET'\"\r\n (ngModelChange)=\"patchConfig({ method: $event })\"\r\n [label]=\"'flowplus.inspector.automated.method' | transloco\"\r\n [options]=\"methodOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >URL</label\r\n >\r\n <div class=\"flex gap-1.5 items-start\">\r\n <div\r\n class=\"flex-1 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"appendToUrl($event)\"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"config().url\"\r\n (ngModelChange)=\"patchConfig({ url: $event })\"\r\n />\r\n </div>\r\n <fp-context-pill-button (insert)=\"appendToUrl($event)\" />\r\n </div>\r\n</section>\r\n\r\n<mt-number-field\r\n [ngModel]=\"config().timeoutSeconds\"\r\n (ngModelChange)=\"patchConfig({ timeoutSeconds: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.automated.timeout' | transloco\"\r\n [min]=\"0\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.automated.headers\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (h of headers(); track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"h.key\"\r\n (ngModelChange)=\"updateHeader($index, 'key', $event)\"\r\n placeholder=\"key\"\r\n />\r\n <div\r\n class=\"min-w-0 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"\r\n updateHeader(\r\n $index,\r\n 'value',\r\n (h.value ?? '') + '{{' + $event + '}}'\r\n )\r\n \"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"h.value\"\r\n (ngModelChange)=\"updateHeader($index, 'value', $event)\"\r\n placeholder=\"value\"\r\n />\r\n </div>\r\n <fp-context-pill-button\r\n (insert)=\"\r\n updateHeader(\r\n $index,\r\n 'value',\r\n (h.value ?? '') + '{{' + $event + '}}'\r\n )\r\n \"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeHeader($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.automated.addHeader' | transloco\"\r\n (onClick)=\"addHeader()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.automated.query\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (q of query(); track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"q.key\"\r\n (ngModelChange)=\"updateQuery($index, 'key', $event)\"\r\n placeholder=\"key\"\r\n />\r\n <div\r\n class=\"min-w-0 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"\r\n updateQuery($index, 'value', (q.value ?? '') + '{{' + $event + '}}')\r\n \"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"q.value\"\r\n (ngModelChange)=\"updateQuery($index, 'value', $event)\"\r\n placeholder=\"value\"\r\n />\r\n </div>\r\n <fp-context-pill-button\r\n (insert)=\"\r\n updateQuery($index, 'value', (q.value ?? '') + '{{' + $event + '}}')\r\n \"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeQuery($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.automated.addQuery' | transloco\"\r\n (onClick)=\"addQuery()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <div\r\n class=\"rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"appendToBody($event)\"\r\n >\r\n <mt-textarea-field\r\n [ngModel]=\"config().body\"\r\n (ngModelChange)=\"patchConfig({ body: $event })\"\r\n [label]=\"'flowplus.inspector.automated.body' | transloco\"\r\n [rows]=\"4\"\r\n />\r\n </div>\r\n <div class=\"mt-1.5\">\r\n <fp-context-pill-button (insert)=\"appendToBody($event)\" />\r\n </div>\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.automated.outputs\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (o of outputs(); track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"o.source\"\r\n (ngModelChange)=\"updateOutput($index, 'source', $event)\"\r\n [placeholder]=\"\r\n 'flowplus.inspector.automated.responsePath' | transloco\r\n \"\r\n />\r\n <div\r\n class=\"min-w-0 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"updateOutput($index, 'targetPath', $event)\"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"o.targetPath\"\r\n (ngModelChange)=\"updateOutput($index, 'targetPath', $event)\"\r\n [placeholder]=\"\r\n 'flowplus.inspector.automated.contextTarget' | transloco\r\n \"\r\n />\r\n </div>\r\n <fp-context-pill-button\r\n (insert)=\"updateOutput($index, 'targetPath', $event)\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeOutput($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.automated.addOutput' | transloco\"\r\n (onClick)=\"addOutput()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
11877
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AutomatedInspectorComponent, isStandalone: true, selector: "fp-automated-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-5" }, ngImport: i0, template: "<mt-select-field\r\n [ngModel]=\"config().method ?? 'GET'\"\r\n (ngModelChange)=\"patchConfig({ method: $event })\"\r\n [label]=\"'flowplus.inspector.automated.method' | transloco\"\r\n [options]=\"methodOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >URL</label\r\n >\r\n <div class=\"flex gap-1.5 items-start\">\r\n <div\r\n class=\"flex-1 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"appendToUrl($event)\"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"config().url\"\r\n (ngModelChange)=\"patchConfig({ url: $event })\"\r\n />\r\n </div>\r\n <fp-context-pill-button (insert)=\"appendToUrl($event)\" />\r\n </div>\r\n</section>\r\n\r\n<mt-number-field\r\n [ngModel]=\"config().timeoutSeconds\"\r\n (ngModelChange)=\"patchConfig({ timeoutSeconds: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.automated.timeout' | transloco\"\r\n [min]=\"0\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.automated.headers\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (h of headers(); track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"h.key\"\r\n (ngModelChange)=\"updateHeader($index, 'key', $event)\"\r\n placeholder=\"key\"\r\n />\r\n <div\r\n class=\"min-w-0 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"\r\n updateHeader(\r\n $index,\r\n 'value',\r\n (h.value ?? '') + '{{' + $event + '}}'\r\n )\r\n \"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"h.value\"\r\n (ngModelChange)=\"updateHeader($index, 'value', $event)\"\r\n placeholder=\"value\"\r\n />\r\n </div>\r\n <fp-context-pill-button\r\n (insert)=\"\r\n updateHeader(\r\n $index,\r\n 'value',\r\n (h.value ?? '') + '{{' + $event + '}}'\r\n )\r\n \"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeHeader($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.automated.addHeader' | transloco\"\r\n (onClick)=\"addHeader()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.automated.query\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (q of query(); track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"q.key\"\r\n (ngModelChange)=\"updateQuery($index, 'key', $event)\"\r\n placeholder=\"key\"\r\n />\r\n <div\r\n class=\"min-w-0 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"\r\n updateQuery($index, 'value', (q.value ?? '') + '{{' + $event + '}}')\r\n \"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"q.value\"\r\n (ngModelChange)=\"updateQuery($index, 'value', $event)\"\r\n placeholder=\"value\"\r\n />\r\n </div>\r\n <fp-context-pill-button\r\n (insert)=\"\r\n updateQuery($index, 'value', (q.value ?? '') + '{{' + $event + '}}')\r\n \"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeQuery($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.automated.addQuery' | transloco\"\r\n (onClick)=\"addQuery()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <div\r\n class=\"rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"appendToBody($event)\"\r\n >\r\n <mt-textarea-field\r\n [ngModel]=\"config().body\"\r\n (ngModelChange)=\"patchConfig({ body: $event })\"\r\n [label]=\"'flowplus.inspector.automated.body' | transloco\"\r\n [rows]=\"4\"\r\n />\r\n </div>\r\n <div class=\"mt-1.5\">\r\n <fp-context-pill-button (insert)=\"appendToBody($event)\" />\r\n </div>\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.automated.outputs\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (o of outputs(); track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"o.source\"\r\n (ngModelChange)=\"updateOutput($index, 'source', $event)\"\r\n [placeholder]=\"\r\n 'flowplus.inspector.automated.responsePath' | transloco\r\n \"\r\n />\r\n <div\r\n class=\"min-w-0 rounded-md\"\r\n fpDropData\r\n [fpDropAutoInsert]=\"false\"\r\n (dataDrop)=\"updateOutput($index, 'targetPath', $event)\"\r\n >\r\n <mt-text-field\r\n [ngModel]=\"o.targetPath\"\r\n (ngModelChange)=\"updateOutput($index, 'targetPath', $event)\"\r\n [placeholder]=\"\r\n 'flowplus.inspector.automated.contextTarget' | transloco\r\n \"\r\n />\r\n </div>\r\n <fp-context-pill-button\r\n (insert)=\"updateOutput($index, 'targetPath', $event)\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeOutput($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.automated.addOutput' | transloco\"\r\n (onClick)=\"addOutput()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ContextPillButtonComponent, selector: "fp-context-pill-button", outputs: ["insert"] }, { kind: "directive", type: DropDataDirective, selector: "[fpDropData]", inputs: ["fpDropAutoInsert"], outputs: ["dataDrop"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
11878
11878
|
}
|
|
11879
11879
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AutomatedInspectorComponent, decorators: [{
|
|
11880
11880
|
type: Component,
|
|
@@ -11965,7 +11965,7 @@ class PluginInspectorComponent {
|
|
|
11965
11965
|
});
|
|
11966
11966
|
}
|
|
11967
11967
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PluginInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11968
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PluginInspectorComponent, isStandalone: true, selector: "fp-plugin-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-5" }, ngImport: i0, template: "@if (pluginsLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n} @else if (plugins().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"config().pluginId\"\r\n (ngModelChange)=\"patchConfig({ pluginId: $event })\"\r\n [label]=\"'flowplus.inspector.plugin.plugin' | transloco\"\r\n [options]=\"plugins()\"\r\n optionValue=\"pluginId\"\r\n optionLabel=\"pluginId\"\r\n [filter]=\"true\"\r\n filterBy=\"pluginId\"\r\n />\r\n} @else {\r\n <mt-text-field\r\n [ngModel]=\"config().pluginId\"\r\n (ngModelChange)=\"patchConfig({ pluginId: $event })\"\r\n [label]=\"'flowplus.inspector.plugin.plugin' | transloco\"\r\n />\r\n}\r\n\r\n<mt-toggle-field\r\n [ngModel]=\"config().waitForCompletion\"\r\n (ngModelChange)=\"patchConfig({ waitForCompletion: $event })\"\r\n [label]=\"'flowplus.inspector.plugin.waitForCompletion' | transloco\"\r\n labelPosition=\"end\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.plugin.inputMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (m of config().inputs ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_120px_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"m.field\"\r\n (ngModelChange)=\"updateInput($index, { field: $event })\"\r\n placeholder=\"field\"\r\n />\r\n <mt-select-field\r\n [ngModel]=\"m.source\"\r\n (ngModelChange)=\"updateInput($index, { source: $event })\"\r\n [options]=\"sourceOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"m.value\"\r\n (ngModelChange)=\"updateInput($index, { value: $event })\"\r\n placeholder=\"value\"\r\n />\r\n <fp-context-pill-button\r\n (insert)=\"updateInput($index, { source: 'Context', value: $event })\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeInput($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.plugin.addMapping' | transloco\"\r\n (onClick)=\"addInput()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.plugin.outputMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (o of config().outputs ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"o.field\"\r\n (ngModelChange)=\"updateOutput($index, { field: $event })\"\r\n placeholder=\"field\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"o.targetPath\"\r\n (ngModelChange)=\"updateOutput($index, { targetPath: $event })\"\r\n [placeholder]=\"'flowplus.inspector.plugin.targetPathHint' | transloco\"\r\n />\r\n <fp-context-pill-button\r\n (insert)=\"updateOutput($index, { targetPath: $event })\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeOutput($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.plugin.addOutput' | transloco\"\r\n (onClick)=\"addOutput()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
11968
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PluginInspectorComponent, isStandalone: true, selector: "fp-plugin-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-5" }, ngImport: i0, template: "@if (pluginsLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n} @else if (plugins().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"config().pluginId\"\r\n (ngModelChange)=\"patchConfig({ pluginId: $event })\"\r\n [label]=\"'flowplus.inspector.plugin.plugin' | transloco\"\r\n [options]=\"plugins()\"\r\n optionValue=\"pluginId\"\r\n optionLabel=\"pluginId\"\r\n [filter]=\"true\"\r\n filterBy=\"pluginId\"\r\n />\r\n} @else {\r\n <mt-text-field\r\n [ngModel]=\"config().pluginId\"\r\n (ngModelChange)=\"patchConfig({ pluginId: $event })\"\r\n [label]=\"'flowplus.inspector.plugin.plugin' | transloco\"\r\n />\r\n}\r\n\r\n<mt-toggle-field\r\n [ngModel]=\"config().waitForCompletion\"\r\n (ngModelChange)=\"patchConfig({ waitForCompletion: $event })\"\r\n [label]=\"'flowplus.inspector.plugin.waitForCompletion' | transloco\"\r\n labelPosition=\"end\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.plugin.inputMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (m of config().inputs ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_120px_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"m.field\"\r\n (ngModelChange)=\"updateInput($index, { field: $event })\"\r\n placeholder=\"field\"\r\n />\r\n <mt-select-field\r\n [ngModel]=\"m.source\"\r\n (ngModelChange)=\"updateInput($index, { source: $event })\"\r\n [options]=\"sourceOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"m.value\"\r\n (ngModelChange)=\"updateInput($index, { value: $event })\"\r\n placeholder=\"value\"\r\n />\r\n <fp-context-pill-button\r\n (insert)=\"updateInput($index, { source: 'Context', value: $event })\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeInput($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.plugin.addMapping' | transloco\"\r\n (onClick)=\"addInput()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.plugin.outputMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (o of config().outputs ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_auto_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"o.field\"\r\n (ngModelChange)=\"updateOutput($index, { field: $event })\"\r\n placeholder=\"field\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"o.targetPath\"\r\n (ngModelChange)=\"updateOutput($index, { targetPath: $event })\"\r\n [placeholder]=\"'flowplus.inspector.plugin.targetPathHint' | transloco\"\r\n />\r\n <fp-context-pill-button\r\n (insert)=\"updateOutput($index, { targetPath: $event })\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeOutput($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.plugin.addOutput' | transloco\"\r\n (onClick)=\"addOutput()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ContextPillButtonComponent, selector: "fp-context-pill-button", outputs: ["insert"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
11969
11969
|
}
|
|
11970
11970
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PluginInspectorComponent, decorators: [{
|
|
11971
11971
|
type: Component,
|
|
@@ -12093,7 +12093,7 @@ class AppActionInspectorComponent {
|
|
|
12093
12093
|
});
|
|
12094
12094
|
}
|
|
12095
12095
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AppActionInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12096
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AppActionInspectorComponent, isStandalone: true, selector: "fp-app-action-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "@if (appsLoading() || appsError()) {\r\n @if (appsLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n } @else {\r\n <mt-text-field\r\n [ngModel]=\"config().appCode\"\r\n (ngModelChange)=\"patchConfig({ appCode: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.app' | transloco\"\r\n [placeholder]=\"'flowplus.inspector.appAction.appFallback' | transloco\"\r\n />\r\n }\r\n} @else {\r\n <mt-select-field\r\n [ngModel]=\"config().appCode\"\r\n (ngModelChange)=\"onAppChange($event)\"\r\n [label]=\"'flowplus.inspector.appAction.app' | transloco\"\r\n [options]=\"apps()\"\r\n optionValue=\"appCode\"\r\n optionLabel=\"displayName\"\r\n [filter]=\"true\"\r\n filterBy=\"appCode,displayName\"\r\n [showClear]=\"true\"\r\n />\r\n}\r\n\r\n@if (actionsLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n} @else if (actions().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"config().actionKey\"\r\n (ngModelChange)=\"patchConfig({ actionKey: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.action' | transloco\"\r\n [options]=\"actions()\"\r\n optionValue=\"actionKey\"\r\n optionLabel=\"actionKey\"\r\n [filter]=\"true\"\r\n filterBy=\"actionKey,description\"\r\n />\r\n} @else {\r\n <mt-text-field\r\n [ngModel]=\"config().actionKey\"\r\n (ngModelChange)=\"patchConfig({ actionKey: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.action' | transloco\"\r\n />\r\n}\r\n\r\n<mt-select-field\r\n [ngModel]=\"config().failureBehavior\"\r\n (ngModelChange)=\"patchConfig({ failureBehavior: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.failure' | transloco\"\r\n [options]=\"failureOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"label\"\r\n [showClear]=\"true\"\r\n/>\r\n\r\n<details>\r\n <summary class=\"cursor-pointer text-[12px] text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.appAction.rawConfig\" | transloco }}\r\n </summary>\r\n <mt-textarea-field\r\n [ngModel]=\"configJson()\"\r\n (ngModelChange)=\"setConfigJson($event)\"\r\n [rows]=\"6\"\r\n />\r\n</details>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
12096
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AppActionInspectorComponent, isStandalone: true, selector: "fp-app-action-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "@if (appsLoading() || appsError()) {\r\n @if (appsLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n } @else {\r\n <mt-text-field\r\n [ngModel]=\"config().appCode\"\r\n (ngModelChange)=\"patchConfig({ appCode: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.app' | transloco\"\r\n [placeholder]=\"'flowplus.inspector.appAction.appFallback' | transloco\"\r\n />\r\n }\r\n} @else {\r\n <mt-select-field\r\n [ngModel]=\"config().appCode\"\r\n (ngModelChange)=\"onAppChange($event)\"\r\n [label]=\"'flowplus.inspector.appAction.app' | transloco\"\r\n [options]=\"apps()\"\r\n optionValue=\"appCode\"\r\n optionLabel=\"displayName\"\r\n [filter]=\"true\"\r\n filterBy=\"appCode,displayName\"\r\n [showClear]=\"true\"\r\n />\r\n}\r\n\r\n@if (actionsLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n} @else if (actions().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"config().actionKey\"\r\n (ngModelChange)=\"patchConfig({ actionKey: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.action' | transloco\"\r\n [options]=\"actions()\"\r\n optionValue=\"actionKey\"\r\n optionLabel=\"actionKey\"\r\n [filter]=\"true\"\r\n filterBy=\"actionKey,description\"\r\n />\r\n} @else {\r\n <mt-text-field\r\n [ngModel]=\"config().actionKey\"\r\n (ngModelChange)=\"patchConfig({ actionKey: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.action' | transloco\"\r\n />\r\n}\r\n\r\n<mt-select-field\r\n [ngModel]=\"config().failureBehavior\"\r\n (ngModelChange)=\"patchConfig({ failureBehavior: $event })\"\r\n [label]=\"'flowplus.inspector.appAction.failure' | transloco\"\r\n [options]=\"failureOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"label\"\r\n [showClear]=\"true\"\r\n/>\r\n\r\n<details>\r\n <summary class=\"cursor-pointer text-[12px] text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.appAction.rawConfig\" | transloco }}\r\n </summary>\r\n <mt-textarea-field\r\n [ngModel]=\"configJson()\"\r\n (ngModelChange)=\"setConfigJson($event)\"\r\n [rows]=\"6\"\r\n />\r\n</details>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
12097
12097
|
}
|
|
12098
12098
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AppActionInspectorComponent, decorators: [{
|
|
12099
12099
|
type: Component,
|
|
@@ -12172,7 +12172,7 @@ class SubworkflowInspectorComponent {
|
|
|
12172
12172
|
});
|
|
12173
12173
|
}
|
|
12174
12174
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SubworkflowInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12175
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: SubworkflowInspectorComponent, isStandalone: true, selector: "fp-subworkflow-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-5" }, ngImport: i0, template: "@if (candidatesLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n} @else if (candidates().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"config().targetWorkflowId\"\r\n (ngModelChange)=\"patchConfig({ targetWorkflowId: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.subworkflow.target' | transloco\"\r\n [options]=\"candidates()\"\r\n optionValue=\"id\"\r\n optionLabel=\"name.en\"\r\n [filter]=\"true\"\r\n filterBy=\"name.en,key\"\r\n />\r\n} @else {\r\n <mt-number-field\r\n [ngModel]=\"config().targetWorkflowId\"\r\n (ngModelChange)=\"patchConfig({ targetWorkflowId: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.subworkflow.target' | transloco\"\r\n [min]=\"0\"\r\n />\r\n}\r\n\r\n<mt-toggle-field\r\n [ngModel]=\"config().waitForCompletion\"\r\n (ngModelChange)=\"patchConfig({ waitForCompletion: $event })\"\r\n [label]=\"'flowplus.inspector.subworkflow.waitForCompletion' | transloco\"\r\n labelPosition=\"end\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.subworkflow.propertyMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (m of config().propertyMappings ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_120px_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"m.parentPath\"\r\n (ngModelChange)=\"updatePropMap($index, { parentPath: $event })\"\r\n placeholder=\"parent path\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"m.childPath\"\r\n (ngModelChange)=\"updatePropMap($index, { childPath: $event })\"\r\n placeholder=\"child path\"\r\n />\r\n <mt-select-field\r\n [ngModel]=\"m.direction\"\r\n (ngModelChange)=\"updatePropMap($index, { direction: $event })\"\r\n [options]=\"directionOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removePropMap($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.subworkflow.addMapping' | transloco\"\r\n (onClick)=\"addPropMap()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.subworkflow.contextMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (m of config().contextMappings ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_auto_1fr_120px_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"m.parentPath\"\r\n (ngModelChange)=\"updateCtxMap($index, { parentPath: $event })\"\r\n [placeholder]=\"\r\n 'flowplus.inspector.subworkflow.parentPath' | transloco\r\n \"\r\n />\r\n <fp-context-pill-button\r\n (insert)=\"updateCtxMap($index, { parentPath: $event })\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"m.childPath\"\r\n (ngModelChange)=\"updateCtxMap($index, { childPath: $event })\"\r\n [placeholder]=\"'flowplus.inspector.subworkflow.childPath' | transloco\"\r\n />\r\n <mt-select-field\r\n [ngModel]=\"m.direction\"\r\n (ngModelChange)=\"updateCtxMap($index, { direction: $event })\"\r\n [options]=\"directionOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeCtxMap($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.subworkflow.addContextMapping' | transloco\"\r\n (onClick)=\"addCtxMap()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
12175
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: SubworkflowInspectorComponent, isStandalone: true, selector: "fp-subworkflow-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-5" }, ngImport: i0, template: "@if (candidatesLoading()) {\r\n <div class=\"h-10 animate-pulse rounded-lg bg-(--p-surface-100)\"></div>\r\n} @else if (candidates().length > 0) {\r\n <mt-select-field\r\n [ngModel]=\"config().targetWorkflowId\"\r\n (ngModelChange)=\"patchConfig({ targetWorkflowId: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.subworkflow.target' | transloco\"\r\n [options]=\"candidates()\"\r\n optionValue=\"id\"\r\n optionLabel=\"name.en\"\r\n [filter]=\"true\"\r\n filterBy=\"name.en,key\"\r\n />\r\n} @else {\r\n <mt-number-field\r\n [ngModel]=\"config().targetWorkflowId\"\r\n (ngModelChange)=\"patchConfig({ targetWorkflowId: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.subworkflow.target' | transloco\"\r\n [min]=\"0\"\r\n />\r\n}\r\n\r\n<mt-toggle-field\r\n [ngModel]=\"config().waitForCompletion\"\r\n (ngModelChange)=\"patchConfig({ waitForCompletion: $event })\"\r\n [label]=\"'flowplus.inspector.subworkflow.waitForCompletion' | transloco\"\r\n labelPosition=\"end\"\r\n/>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.subworkflow.propertyMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (m of config().propertyMappings ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_1fr_120px_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"m.parentPath\"\r\n (ngModelChange)=\"updatePropMap($index, { parentPath: $event })\"\r\n placeholder=\"parent path\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"m.childPath\"\r\n (ngModelChange)=\"updatePropMap($index, { childPath: $event })\"\r\n placeholder=\"child path\"\r\n />\r\n <mt-select-field\r\n [ngModel]=\"m.direction\"\r\n (ngModelChange)=\"updatePropMap($index, { direction: $event })\"\r\n [options]=\"directionOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removePropMap($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.subworkflow.addMapping' | transloco\"\r\n (onClick)=\"addPropMap()\"\r\n />\r\n</section>\r\n\r\n<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.subworkflow.contextMappings\" | transloco }}\r\n </label>\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (m of config().contextMappings ?? []; track $index) {\r\n <li class=\"grid grid-cols-[1fr_auto_1fr_120px_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"m.parentPath\"\r\n (ngModelChange)=\"updateCtxMap($index, { parentPath: $event })\"\r\n [placeholder]=\"\r\n 'flowplus.inspector.subworkflow.parentPath' | transloco\r\n \"\r\n />\r\n <fp-context-pill-button\r\n (insert)=\"updateCtxMap($index, { parentPath: $event })\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"m.childPath\"\r\n (ngModelChange)=\"updateCtxMap($index, { childPath: $event })\"\r\n [placeholder]=\"'flowplus.inspector.subworkflow.childPath' | transloco\"\r\n />\r\n <mt-select-field\r\n [ngModel]=\"m.direction\"\r\n (ngModelChange)=\"updateCtxMap($index, { direction: $event })\"\r\n [options]=\"directionOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeCtxMap($index)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'flowplus.inspector.subworkflow.addContextMapping' | transloco\"\r\n (onClick)=\"addCtxMap()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ContextPillButtonComponent, selector: "fp-context-pill-button", outputs: ["insert"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
12176
12176
|
}
|
|
12177
12177
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: SubworkflowInspectorComponent, decorators: [{
|
|
12178
12178
|
type: Component,
|
|
@@ -12226,7 +12226,7 @@ class StartParallelInspectorComponent {
|
|
|
12226
12226
|
this.patchConfig({ branches: next });
|
|
12227
12227
|
}
|
|
12228
12228
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: StartParallelInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12229
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: StartParallelInspectorComponent, isStandalone: true, selector: "fp-start-parallel-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >Branches</label\r\n >\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (b of config().branches ?? []; track b.key) {\r\n <li class=\"grid grid-cols-[140px_1fr_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"b.key\"\r\n (ngModelChange)=\"renameBranch(b.key, $event)\"\r\n placeholder=\"key\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"b.label?.['en']\"\r\n (ngModelChange)=\"setBranchLabel(b.key, $event)\"\r\n placeholder=\"label\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeBranch(b.key)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'Branch'\"\r\n (onClick)=\"addBranch()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
12229
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: StartParallelInspectorComponent, isStandalone: true, selector: "fp-start-parallel-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<section>\r\n <label\r\n class=\"mb-1.5 block text-[11.5px] font-medium text-(--p-text-muted-color)\"\r\n >Branches</label\r\n >\r\n <ul class=\"list-none p-0 m-0 flex flex-col gap-1.5\">\r\n @for (b of config().branches ?? []; track b.key) {\r\n <li class=\"grid grid-cols-[140px_1fr_auto] gap-1.5 items-start\">\r\n <mt-text-field\r\n [ngModel]=\"b.key\"\r\n (ngModelChange)=\"renameBranch(b.key, $event)\"\r\n placeholder=\"key\"\r\n />\r\n <mt-text-field\r\n [ngModel]=\"b.label?.['en']\"\r\n (ngModelChange)=\"setBranchLabel(b.key, $event)\"\r\n placeholder=\"label\"\r\n />\r\n <mt-button\r\n variant=\"text\"\r\n severity=\"danger\"\r\n size=\"small\"\r\n icon=\"general.x-close\"\r\n (onClick)=\"removeBranch(b.key)\"\r\n />\r\n </li>\r\n }\r\n </ul>\r\n <mt-button\r\n icon=\"general.plus\"\r\n variant=\"outlined\"\r\n size=\"small\"\r\n class=\"mt-2\"\r\n [label]=\"'Branch'\"\r\n (onClick)=\"addBranch()\"\r\n />\r\n</section>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }] });
|
|
12230
12230
|
}
|
|
12231
12231
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: StartParallelInspectorComponent, decorators: [{
|
|
12232
12232
|
type: Component,
|
|
@@ -12268,7 +12268,7 @@ class JoinParallelInspectorComponent {
|
|
|
12268
12268
|
});
|
|
12269
12269
|
}
|
|
12270
12270
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: JoinParallelInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
12271
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: JoinParallelInspectorComponent, isStandalone: true, selector: "fp-join-parallel-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-select-field\r\n [ngModel]=\"config().policy\"\r\n (ngModelChange)=\"patchConfig({ policy: $event })\"\r\n [label]=\"'flowplus.inspector.join.policy' | transloco\"\r\n [options]=\"policyOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n/>\r\n\r\n@if (config().policy === \"Threshold\") {\r\n <mt-number-field\r\n [ngModel]=\"config().threshold\"\r\n (ngModelChange)=\"patchConfig({ threshold: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.join.threshold' | transloco\"\r\n [min]=\"0\"\r\n />\r\n}\r\n\r\n<mt-text-field\r\n [ngModel]=\"config().aggregation?.targetPath\"\r\n (ngModelChange)=\"setAggPath($event)\"\r\n [label]=\"'flowplus.inspector.join.aggregationTarget' | transloco\"\r\n/>\r\n\r\n<mt-select-field\r\n [ngModel]=\"config().aggregation?.strategy\"\r\n (ngModelChange)=\"setAggStrategy($event)\"\r\n [label]=\"'flowplus.inspector.join.aggregationStrategy' | transloco\"\r\n [options]=\"strategyOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n/>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
12271
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: JoinParallelInspectorComponent, isStandalone: true, selector: "fp-join-parallel-inspector", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<mt-select-field\r\n [ngModel]=\"config().policy\"\r\n (ngModelChange)=\"patchConfig({ policy: $event })\"\r\n [label]=\"'flowplus.inspector.join.policy' | transloco\"\r\n [options]=\"policyOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n/>\r\n\r\n@if (config().policy === \"Threshold\") {\r\n <mt-number-field\r\n [ngModel]=\"config().threshold\"\r\n (ngModelChange)=\"patchConfig({ threshold: +($event ?? 0) })\"\r\n [label]=\"'flowplus.inspector.join.threshold' | transloco\"\r\n [min]=\"0\"\r\n />\r\n}\r\n\r\n<mt-text-field\r\n [ngModel]=\"config().aggregation?.targetPath\"\r\n (ngModelChange)=\"setAggPath($event)\"\r\n [label]=\"'flowplus.inspector.join.aggregationTarget' | transloco\"\r\n/>\r\n\r\n<mt-select-field\r\n [ngModel]=\"config().aggregation?.strategy\"\r\n (ngModelChange)=\"setAggStrategy($event)\"\r\n [label]=\"'flowplus.inspector.join.aggregationStrategy' | transloco\"\r\n [options]=\"strategyOptions\"\r\n optionValue=\"key\"\r\n optionLabel=\"key\"\r\n/>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
12272
12272
|
}
|
|
12273
12273
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: JoinParallelInspectorComponent, decorators: [{
|
|
12274
12274
|
type: Component,
|
|
@@ -13807,7 +13807,7 @@ class AutomationSmartEditorComponent {
|
|
|
13807
13807
|
.subscribe((result) => this.subworkflowAutomations.set(result.items ?? []));
|
|
13808
13808
|
}
|
|
13809
13809
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AutomationSmartEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
13810
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AutomationSmartEditorComponent, isStandalone: true, selector: "fp-automation-smart-editor", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<div\n class=\"fp-scroll flex h-full min-h-0 flex-col overflow-y-auto\"\n fpDropData\n [fpDropAutoInsert]=\"false\"\n (dataDrop)=\"insertExpression($event)\"\n>\n <div class=\"space-y-4 px-5 py-5\">\n @if (helperError()) {\n <div\n class=\"rounded-lg border border-[rgb(var(--fp-warning))]/30 bg-[rgb(var(--fp-warning))]/10 px-3 py-2 text-[12px] leading-5 text-(--p-text-color)\"\n >\n {{ helperError() }}\n </div>\n }\n\n @if (sectionInMain(\"startConnection\") && trigger()) {\n <section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n >\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Start connection\n </h3>\n <div class=\"space-y-3 p-4\">\n @if (startConnection().key) {\n @if (startConnection().step) {\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-[12px] font-semibold text-(--p-text-muted-color)\">\n First node connected\n </div>\n <div class=\"truncate text-[14px] font-semibold text-(--p-text-color)\">\n {{ startConnection().label }}\n </div>\n <div class=\"flex flex-wrap items-center gap-2 text-[12px] text-(--p-text-muted-color)\">\n <span>Managed on canvas</span>\n <span\n class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\"\n >\n {{ startConnection().key }}\n </span>\n </div>\n </div>\n <div class=\"flex shrink-0 flex-wrap gap-2\">\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n severity=\"secondary\"\n label=\"Focus connected node\"\n (onClick)=\"focusStartConnection()\"\n />\n </div>\n </div>\n } @else {\n <div class=\"space-y-2\">\n <div class=\"text-[14px] font-semibold text-(--p-text-color)\">\n No first node connected\n </div>\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n The saved start connection points to a node key that is not on\n the canvas. Connect this trigger to the first node on the\n canvas.\n </p>\n <div class=\"flex flex-wrap items-center gap-2 text-[12px] text-(--p-text-muted-color)\">\n <span>Technical key</span>\n <span\n class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\"\n >\n {{ startConnection().key }}\n </span>\n <span>Managed on canvas</span>\n </div>\n </div>\n }\n } @else {\n <div class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-surface-50) p-3\">\n <div class=\"text-[14px] font-semibold text-(--p-text-color)\">\n No first node connected\n </div>\n <p class=\"m-0 mt-1 text-[12px] leading-5 text-(--p-text-muted-color)\">\n Connect this trigger to the first node on the canvas.\n </p>\n </div>\n }\n </div>\n </section>\n }\n\n @switch (editorType()) {\n @case (\"ManualTrigger\") {\n @if (sectionInMain(\"manualTrigger\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Manual run input</div>\n @if (hasTriggerPayloadSchema() && triggerPayloadSchema(); as schema) {\n <div class=\"rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Payload schema</div>\n <pre class=\"fp-ae-code\">{{ schemaText(schema) }}</pre>\n </div>\n }\n @if (triggerPayloadSample(); as sample) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Sample payload</div>\n <pre class=\"fp-ae-code\">{{ schemaText(sample) }}</pre>\n </div>\n }\n @if (!hasTriggerPayloadSchema() && !triggerPayloadSample()) {\n <p class=\"fp-ae-copy\">\n This manual trigger has no backend-provided input schema. It can still be connected to the first step on the canvas.\n </p>\n }\n </section>\n }\n }\n @case (\"WebhookTrigger\") {\n @if (sectionInMain(\"webhookSetup\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Webhook setup</div>\n @if (webhookSetup(); as setup) {\n <div class=\"flex gap-2\">\n <mt-text-field\n class=\"flex-1 font-mono\"\n [ngModel]=\"setup.webhookUrl ?? ''\"\n [readonly]=\"true\"\n label=\"Webhook URL\"\n hint=\"Backend-generated endpoint clients should call to start this trigger.\"\n />\n <mt-button class=\"self-end\" size=\"small\" variant=\"outlined\" label=\"Copy\" (onClick)=\"copyWebhookUrl()\" />\n </div>\n <div class=\"grid gap-2 md:grid-cols-2\">\n <div class=\"fp-ae-kv\">\n <span>Auth mode</span>\n <strong>{{ setup.authMode ?? \"Backend default\" }}</strong>\n </div>\n <div class=\"fp-ae-kv\">\n <span>Required headers</span>\n <strong>{{ (setup.requiredHeaders ?? []).join(\", \") || \"-\" }}</strong>\n </div>\n </div>\n @if (setup.hmacSigning) {\n <p class=\"fp-ae-copy\">{{ setup.hmacSigning }}</p>\n }\n @if (setup.sampleRequest) {\n <details class=\"mt-3 rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">\n Sample request\n </summary>\n <pre class=\"fp-ae-code\">{{ schemaText(setup.sampleRequest) }}</pre>\n </details>\n }\n } @else {\n <p class=\"fp-ae-copy\">\n Webhook setup is not available for this draft yet.\n </p>\n }\n </section>\n }\n @if (sectionInMain(\"authPolicy\") && hasAuthPolicy()) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Authentication policy</div>\n <div class=\"grid gap-3 md:grid-cols-3\">\n <mt-select-field\n [ngModel]=\"authPolicy()['mode'] ?? ''\"\n (ngModelChange)=\"onAuthFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Authentication mode required by the backend webhook policy.\"\n [options]=\"authModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n [ngModel]=\"authPolicy()['signatureHeaderName'] ?? ''\"\n (ngModelChange)=\"onAuthFieldChange('signatureHeaderName', $event)\"\n label=\"Signature header\"\n hint=\"Header name that carries the webhook signature when the policy requires signed requests.\"\n />\n <mt-text-field\n [ngModel]=\"authPolicy()['timestampHeaderName'] ?? ''\"\n (ngModelChange)=\"onAuthFieldChange('timestampHeaderName', $event)\"\n label=\"Timestamp header\"\n hint=\"Header name that carries the request timestamp for replay protection.\"\n />\n </div>\n </section>\n }\n }\n @case (\"FormSubmitTrigger\") {\n @if (sectionInMain(\"formBinding\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Form binding</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"selectedFormId() || formBinding()?.formId || ''\"\n (ngModelChange)=\"onFormChange($event)\"\n label=\"Form\"\n hint=\"Choose a backend form that will submit data into this trigger.\"\n [options]=\"formOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"selectedFormVersionId() || formBinding()?.formVersionId || ''\"\n (ngModelChange)=\"onFormVersionChange($event)\"\n label=\"Form version\"\n hint=\"Persist the exact backend formVersionId. Do not type or generate IDs manually.\"\n [options]=\"formVersionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <div class=\"mt-3 flex flex-wrap items-center gap-2\">\n <mt-button size=\"small\" severity=\"primary\" label=\"Save binding\" (onClick)=\"saveFormBinding()\" />\n @if (formBinding()) {\n <span class=\"rounded-lg bg-(--p-surface-100) px-2 py-1 font-mono text-[11px] text-(--p-text-muted-color)\">\n {{ formBinding()!.formVersionId }}\n </span>\n }\n </div>\n @if (formSchema(); as schema) {\n <details class=\"mt-3 rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">\n Form schema preview\n </summary>\n <pre class=\"fp-ae-code\">{{ schemaText(schema.schema) }}</pre>\n </details>\n }\n </section>\n }\n }\n @case (\"ScheduleTrigger\") {\n @if (sectionInMain(\"schedule\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Schedule</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['mode'] ?? 'cron'\"\n (ngModelChange)=\"onConfigFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Pick one schedule mode: cron, interval, or once. Backend validation requires exactly one mode.\"\n [options]=\"scheduleModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['timezone'] ?? 'UTC'\"\n (ngModelChange)=\"onConfigFieldChange('timezone', $event)\"\n label=\"Timezone\"\n hint=\"Timezone used to calculate the next fire time.\"\n [options]=\"timeZoneOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['cron'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('cron', $event)\"\n label=\"Cron\"\n hint=\"Cron expression used only when mode is cron, for example 0 9 * * *.\"\n />\n <mt-number-field\n [ngModel]=\"numberValue(config()['intervalSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('intervalSeconds', $event)\"\n label=\"Interval seconds\"\n hint=\"Repeat interval in seconds used only when mode is interval.\"\n [min]=\"0\"\n />\n <mt-date-field\n [ngModel]=\"config()['startDate'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('startDate', $event)\"\n label=\"Start date UTC\"\n hint=\"UTC date/time used for once schedules or as the first allowed run time.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n <mt-select-field\n [ngModel]=\"config()['misfirePolicy'] ?? 'SkipMissed'\"\n (ngModelChange)=\"onConfigFieldChange('misfirePolicy', $event)\"\n label=\"Misfire policy\"\n hint=\"Backend behavior when a scheduled run is missed while the automation is unavailable.\"\n [options]=\"misfirePolicyOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <div class=\"mt-3 flex gap-2\">\n <mt-button size=\"small\" variant=\"outlined\" label=\"Validate\" (onClick)=\"validateSchedule()\" />\n <mt-button size=\"small\" severity=\"primary\" label=\"Preview next fire\" (onClick)=\"previewSchedule()\" />\n </div>\n @if (schedulePreview(); as preview) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n <strong class=\"text-emerald-600\">Valid</strong>\n <span class=\"ms-2\">Next fire: {{ preview.nextFireAtUtc ?? \"-\" }}</span>\n </div>\n }\n </section>\n }\n }\n @case (\"SetFields\") {\n @if (sectionInMain(\"setFields\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Set fields</div>\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'fields', rows: fieldsRows(), keyLabel: 'Field', valueLabel: 'Value', addLabel: 'Add field' }\" />\n </section>\n }\n }\n @case (\"If\") {\n @if (sectionInMain(\"condition\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Condition</div>\n <div class=\"grid gap-3 md:grid-cols-[1fr_180px_1fr]\">\n <mt-text-field\n [ngModel]=\"fieldText('left')\"\n (focusin)=\"setExpressionTarget('config:left')\"\n (ngModelChange)=\"onConfigFieldChange('left', $event)\"\n label=\"Left\"\n hint=\"Left-side value or expression to compare.\"\n />\n <mt-select-field\n [ngModel]=\"config()['operator'] ?? 'equals'\"\n (ngModelChange)=\"onConfigFieldChange('operator', $event)\"\n label=\"Operator\"\n hint=\"Comparison operator used by the backend condition evaluator.\"\n [options]=\"ifOperatorOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n [ngModel]=\"fieldText('right')\"\n (focusin)=\"setExpressionTarget('config:right')\"\n (ngModelChange)=\"onConfigFieldChange('right', $event)\"\n label=\"Right\"\n hint=\"Right-side value or expression to compare against.\"\n />\n </div>\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Route outputs</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n </div>\n </div>\n </section>\n }\n }\n @case (\"HTTP\") {\n @if (sectionInMain(\"httpRequest\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">HTTP request</div>\n <div class=\"grid gap-3 md:grid-cols-[150px_1fr]\">\n <mt-select-field\n [ngModel]=\"config()['method'] ?? 'GET'\"\n (ngModelChange)=\"onConfigFieldChange('method', $event)\"\n label=\"Method\"\n hint=\"HTTP method used for the outbound request.\"\n [options]=\"httpMethodOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['url'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:url')\"\n (ngModelChange)=\"onConfigFieldChange('url', $event)\"\n label=\"URL\"\n hint=\"Target URL. Expressions are supported for dynamic hosts, paths, and query values.\"\n />\n </div>\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'headers', rows: headerRows(), keyLabel: 'Header', valueLabel: 'Value', addLabel: 'Add header' }\" />\n </div>\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'query', rows: queryRows(), keyLabel: 'Query param', valueLabel: 'Value', addLabel: 'Add query param' }\" />\n </div>\n @if (supportsConfigKey('bodyMode')) {\n <mt-select-field\n class=\"mt-3\"\n [ngModel]=\"config()['bodyMode'] ?? 'json'\"\n (ngModelChange)=\"onConfigFieldChange('bodyMode', $event)\"\n label=\"Body mode\"\n hint=\"Backend-supported request body serialization mode.\"\n [options]=\"httpBodyModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n }\n <mt-textarea-field\n class=\"mt-3 w-full font-mono\"\n [ngModel]=\"valueText(config()['body'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:body')\"\n (ngModelChange)=\"onConfigFieldChange('body', $event)\"\n label=\"Body\"\n hint=\"Request body sent by the HTTP node. Use JSON or expressions when the backend schema allows it.\"\n rows=\"6\"\n />\n @if (supportsConfigKey('timeoutSeconds') || supportsConfigKey('responseHandling')) {\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n @if (supportsConfigKey('timeoutSeconds')) {\n <mt-number-field\n [ngModel]=\"numberValue(config()['timeoutSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('timeoutSeconds', $event)\"\n label=\"Timeout seconds\"\n hint=\"Request timeout when exposed by the backend schema.\"\n [min]=\"0\"\n />\n }\n @if (supportsConfigKey('responseHandling')) {\n <mt-text-field\n [ngModel]=\"config()['responseHandling'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('responseHandling', $event)\"\n label=\"Response handling\"\n hint=\"Backend-supported response handling mode or expression.\"\n />\n }\n </div>\n }\n @if (sectionInMain(\"credentials\")) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Credential</div>\n <ng-container *ngTemplateOutlet=\"credentialSelector\" />\n </div>\n }\n </section>\n }\n }\n @case (\"Wait\") {\n @if (sectionInMain(\"wait\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Wait</div>\n <div class=\"grid gap-3 md:grid-cols-3\">\n <mt-select-field\n [ngModel]=\"config()['mode'] ?? 'duration'\"\n (ngModelChange)=\"onConfigFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Choose whether this wait uses a duration or a specific date/time.\"\n [options]=\"waitModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-number-field\n [ngModel]=\"numberValue(config()['durationSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('durationSeconds', $event)\"\n label=\"Duration seconds\"\n hint=\"How long execution should wait when mode is duration.\"\n [min]=\"0\"\n />\n <mt-date-field\n [ngModel]=\"config()['waitUntil'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('waitUntil', $event)\"\n label=\"Wait until\"\n hint=\"Date/time that resolves to when execution should resume.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n </div>\n @if (supportsConfigKey('resumePayloadSchema') || supportsConfigKey('resumePayload')) {\n <mt-textarea-field\n class=\"mt-3 w-full font-mono\"\n [ngModel]=\"valueText(config()['resumePayloadSchema'] ?? config()['resumePayload'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:resumePayload')\"\n (ngModelChange)=\"onConfigFieldChange('resumePayload', $event)\"\n label=\"Resume payload\"\n hint=\"Expected payload when the backend supports manual or external resume data.\"\n rows=\"5\"\n />\n }\n </section>\n }\n }\n @case (\"HumanApproval\") {\n @if (sectionInMain(\"approvalTask\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Approval task</div>\n @if (assignmentOptions()?.providerStatus && assignmentOptions()?.providerStatus !== \"Available\") {\n <p class=\"fp-ae-copy\">\n Assignment provider status: {{ assignmentOptions()?.providerStatus }}.\n The backend provider is the source of truth for available assignees.\n </p>\n }\n <div class=\"grid gap-3 xl:grid-cols-3\">\n <mt-text-field\n [ngModel]=\"config()['title'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:title')\"\n (ngModelChange)=\"onConfigFieldChange('title', $event)\"\n label=\"Approval title\"\n hint=\"Approval title shown to the assigned human approver.\"\n />\n <mt-select-field\n [ngModel]=\"selectedAssignmentKey()\"\n (ngModelChange)=\"onAssignmentOptionChange($event)\"\n label=\"Assignment\"\n hint=\"Backend-provided assignee, role, or group that can decide this approval.\"\n [options]=\"assignmentSelectOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (humanApprovalSupportsConfig('priority')) {\n <mt-text-field\n [ngModel]=\"config()['priority'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('priority', $event)\"\n label=\"Priority\"\n hint=\"Approval priority when supported by the backend schema.\"\n />\n }\n </div>\n <mt-textarea-field\n class=\"mt-3 w-full\"\n [ngModel]=\"config()['message'] ?? config()['instructions'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:message')\"\n (ngModelChange)=\"onConfigFieldChange('message', $event)\"\n label=\"Approval message\"\n hint=\"Decision instructions shown to the approver.\"\n rows=\"4\"\n />\n @if (humanApprovalSupportsConfig('dueDate') || humanApprovalSupportsConfig('dueInSeconds') || humanApprovalSupportsConfig('timeoutSeconds') || humanApprovalSupportsConfig('expiresAt') || humanApprovalSupportsConfig('commentsRequired') || humanApprovalSupportsConfig('allowReturn')) {\n <div class=\"mt-3 grid gap-3 xl:grid-cols-4\">\n @if (humanApprovalSupportsConfig('dueDate')) {\n <mt-date-field\n [ngModel]=\"config()['dueDate'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('dueDate', $event)\"\n label=\"Due date\"\n hint=\"Backend-supported approval due date.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n }\n @if (humanApprovalSupportsConfig('dueInSeconds')) {\n <mt-number-field\n [ngModel]=\"numberValue(config()['dueInSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('dueInSeconds', $event)\"\n label=\"Due in seconds\"\n hint=\"Relative approval due duration when supported by the backend schema.\"\n [min]=\"0\"\n />\n }\n @if (humanApprovalSupportsConfig('timeoutSeconds')) {\n <mt-number-field\n [ngModel]=\"numberValue(config()['timeoutSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('timeoutSeconds', $event)\"\n label=\"Timeout seconds\"\n hint=\"Approval timeout emitted according to backend approval runtime support.\"\n [min]=\"0\"\n />\n }\n @if (humanApprovalSupportsConfig('expiresAt')) {\n <mt-date-field\n [ngModel]=\"config()['expiresAt'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('expiresAt', $event)\"\n label=\"Expires at\"\n hint=\"Backend-supported approval expiry timestamp.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n }\n @if (humanApprovalSupportsConfig('commentsRequired')) {\n <div class=\"flex min-h-[4.25rem] items-end rounded-md border border-surface-200 bg-surface-0 px-3 py-2\">\n <mt-toggle-field\n size=\"small\"\n label=\"Comments required\"\n labelPosition=\"end\"\n [ngModel]=\"config()['commentsRequired'] === true\"\n (ngModelChange)=\"onConfigFieldChange('commentsRequired', $event === true)\"\n hint=\"Require approver comments when the backend supports this flag.\"\n />\n </div>\n }\n @if (humanApprovalSupportsConfig('allowReturn')) {\n <div class=\"flex min-h-[4.25rem] items-end rounded-md border border-surface-200 bg-surface-0 px-3 py-2\">\n <mt-toggle-field\n size=\"small\"\n label=\"Allow return for changes\"\n labelPosition=\"end\"\n [ngModel]=\"config()['allowReturn'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('allowReturn', $event === true)\"\n hint=\"Allow ReturnForChanges when the backend schema exposes this option.\"\n />\n </div>\n }\n </div>\n }\n <div class=\"mt-3 space-y-3\">\n <div class=\"fp-ae-section-title\">Decision options</div>\n <div class=\"overflow-hidden rounded-md border border-surface-200 bg-surface-0\">\n @if (selectedApprovalDecisionRows().length > 0) {\n <div class=\"hidden border-b border-surface-200 bg-surface-50 px-3 py-2 text-[12px] font-semibold text-(--p-text-muted-color) xl:grid xl:grid-cols-[1.1fr_1fr_1.2fr_80px_44px] xl:gap-3\">\n <span>Decision label</span>\n <span>Canonical value</span>\n <span>Route output key</span>\n <span>Routes</span>\n <span>Action</span>\n </div>\n @for (decision of selectedApprovalDecisionRows(); track decision.value) {\n <div class=\"grid gap-2 border-b border-surface-100 px-3 py-3 last:border-b-0 xl:grid-cols-[1.1fr_1fr_1.2fr_80px_44px] xl:items-center xl:gap-3\">\n <div class=\"min-w-0\">\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Decision label</div>\n <div class=\"truncate text-[13px] font-semibold text-(--p-text-color)\">{{ decision.label }}</div>\n </div>\n <div class=\"min-w-0\">\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Canonical value</div>\n <div class=\"truncate font-mono text-[12px] text-(--p-text-color)\">{{ decision.value }}</div>\n </div>\n <div class=\"min-w-0\">\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Route output key</div>\n <div class=\"truncate font-mono text-[12px] text-(--p-text-muted-color)\">{{ decision.routeOutputKey }}</div>\n </div>\n <div>\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Routes</div>\n <div class=\"text-[13px] text-(--p-text-color)\">{{ decision.routeCount }}</div>\n </div>\n <mt-button\n class=\"justify-self-start xl:justify-self-end\"\n size=\"small\"\n variant=\"outlined\"\n severity=\"danger\"\n icon=\"general.trash-01\"\n tooltip=\"Remove decision option\"\n (onClick)=\"removeApprovalDecision(decision.value)\"\n />\n </div>\n }\n } @else {\n <div class=\"px-3 py-4 text-[12px] text-(--p-text-muted-color)\">\n Add at least one backend-supported approval decision.\n </div>\n }\n </div>\n @if (approvalDecisionIssues().length > 0) {\n <div class=\"rounded-lg border border-[rgb(var(--fp-error))]/30 bg-[rgb(var(--fp-error))]/10 px-3 py-2 text-[12px] leading-5 text-[rgb(var(--fp-error))]\">\n @for (issue of approvalDecisionIssues(); track issue) {\n <div>{{ issue }}</div>\n }\n </div>\n }\n <div class=\"grid gap-2 md:grid-cols-[minmax(0,1fr)_auto]\">\n <mt-select-field\n [ngModel]=\"approvalDecisionToAdd()\"\n (ngModelChange)=\"approvalDecisionToAdd.set($event)\"\n label=\"Decision to add\"\n hint=\"Only backend-supported decisions with matching route outputs are available.\"\n [options]=\"addableApprovalDecisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n variant=\"outlined\"\n icon=\"general.plus\"\n label=\"Add decision option\"\n [disabled]=\"!approvalDecisionToAdd()\"\n (onClick)=\"addApprovalDecision()\"\n />\n </div>\n </div>\n @if (humanApprovalSupportsConfig('payload') || humanApprovalSupportsConfig('context') || humanApprovalSupportsConfig('metadata')) {\n <div class=\"mt-3 grid gap-3\">\n @if (humanApprovalSupportsConfig('payload')) {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(config()['payload'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:payload')\"\n (ngModelChange)=\"onConfigFieldChange('payload', $event)\"\n label=\"Payload\"\n hint=\"Approval task payload or context fields supported by backend schema.\"\n rows=\"5\"\n />\n }\n @if (humanApprovalSupportsConfig('context')) {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(config()['context'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:context')\"\n (ngModelChange)=\"onConfigFieldChange('context', $event)\"\n label=\"Context\"\n hint=\"Additional approval context supported by backend schema.\"\n rows=\"5\"\n />\n }\n @if (humanApprovalSupportsConfig('metadata')) {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(config()['metadata'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:metadata')\"\n (ngModelChange)=\"onConfigFieldChange('metadata', $event)\"\n label=\"Metadata\"\n hint=\"Additional approval metadata supported by the backend schema.\"\n rows=\"4\"\n />\n }\n </div>\n }\n <div class=\"mt-3 flex gap-2\">\n <mt-button size=\"small\" variant=\"outlined\" label=\"Validate assignment\" (onClick)=\"validateAssignment()\" />\n </div>\n @if (assignmentValidation(); as result) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n {{ result.isValid === false ? \"Assignment invalid\" : \"Assignment accepted by helper\" }}\n @for (issue of resultIssues(result); track issue) {\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">{{ issue }}</div>\n }\n </div>\n }\n @if (sectionInMain(\"approvalOutput\")) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Output data</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (field of approvalOutputFieldLabels; track field) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ field }}</span>\n }\n </div>\n <div class=\"mt-3 fp-ae-label mb-2\">Approval routes</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n </div>\n </div>\n }\n </section>\n }\n }\n @case (\"FlowPlusCommit\") {\n @if (sectionInMain(\"flowplusCommit\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">FlowPlus commit</div>\n <p class=\"fp-ae-copy\">\n This node is the explicit module-data write boundary. Approval does\n not commit data unless this node is reached.\n </p>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['targetModule'] ?? ''\"\n (ngModelChange)=\"onModuleChange($event)\"\n label=\"Module\"\n hint=\"Module whose records will be written by this explicit FlowPlus commit.\"\n [options]=\"moduleOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['operation'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('operation', $event)\"\n label=\"Operation\"\n hint=\"Write operation supported by the selected backend module schema.\"\n [options]=\"operationOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <mt-text-field\n class=\"mt-3 font-mono\"\n [ngModel]=\"config()['idempotencyKey'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:idempotencyKey')\"\n (ngModelChange)=\"onConfigFieldChange('idempotencyKey', $event)\"\n label=\"Idempotency key\"\n hint=\"Optional stable key used by the backend to prevent duplicate writes.\"\n />\n @if (selectedModuleFields().length) {\n <div class=\"mt-3 space-y-2\">\n <div class=\"fp-ae-section-title\">Module schema</div>\n <div class=\"grid gap-2 md:grid-cols-2\">\n @for (field of selectedModuleFields(); track field.key) {\n <div class=\"fp-ae-kv\">\n <span>{{ field.displayName ?? field.key }}</span>\n <strong>{{ field.viewType ?? \"Value\" }} @if (field.required) { * }</strong>\n </div>\n }\n </div>\n </div>\n }\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'mapping', rows: mappingRows(), keyLabel: 'Module field', valueLabel: 'Expression / value', addLabel: 'Add module field' }\" />\n </div>\n <div class=\"mt-3 flex gap-2\">\n <mt-button size=\"small\" severity=\"primary\" label=\"Validate mapping\" (onClick)=\"validateCommitMapping()\" />\n </div>\n @if (commitValidation(); as result) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n {{ result.isValid === false ? \"Mapping invalid\" : \"Mapping accepted by helper\" }}\n @for (issue of resultIssues(result); track issue) {\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">{{ issue }}</div>\n }\n </div>\n }\n @if (sectionInMain(\"credentials\")) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Credential</div>\n <ng-container *ngTemplateOutlet=\"credentialSelector\" />\n </div>\n }\n </section>\n }\n }\n @case (\"WebhookResponse\") {\n @if (sectionInMain(\"webhookResponse\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Webhook response</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-number-field\n [ngModel]=\"numberValue(config()['statusCode']) ?? 200\"\n (ngModelChange)=\"onConfigFieldChange('statusCode', $event)\"\n label=\"Status code\"\n hint=\"HTTP status code sent back by the webhook response node.\"\n [min]=\"100\"\n [max]=\"599\"\n />\n <mt-select-field\n [ngModel]=\"config()['responseMode'] ?? 'json'\"\n (ngModelChange)=\"onConfigFieldChange('responseMode', $event)\"\n label=\"Response mode\"\n hint=\"How the webhook response body should be serialized.\"\n [options]=\"responseModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'headers', rows: headerRows(), keyLabel: 'Header', valueLabel: 'Value', addLabel: 'Add header' }\" />\n </div>\n <mt-textarea-field\n class=\"mt-3 w-full font-mono\"\n [ngModel]=\"valueText(config()['body'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:body')\"\n (ngModelChange)=\"onConfigFieldChange('body', $event)\"\n label=\"Body\"\n hint=\"Body returned to the webhook caller. Expressions can use current execution data.\"\n rows=\"6\"\n />\n </section>\n }\n }\n @case (\"CallAutomation\") {\n @if (sectionInMain(\"callAutomation\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Subworkflow</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['targetAutomationId'] ?? ''\"\n (ngModelChange)=\"onSubworkflowTargetChange($event)\"\n label=\"Target automation\"\n hint=\"Backend-provided automation candidate. Tenant and recursion validation stay on the backend.\"\n [options]=\"subworkflowAutomationOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['revisionMode'] ?? 'Active'\"\n (ngModelChange)=\"onConfigFieldChange('revisionMode', $event)\"\n label=\"Revision mode\"\n hint=\"Choose which published or active child revision to call.\"\n [options]=\"revisionModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (config()['revisionMode'] === \"SpecificRevision\") {\n <mt-text-field\n [ngModel]=\"config()['specificRevisionId'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('specificRevisionId', $event)\"\n label=\"Specific revision id\"\n hint=\"Required only when revision mode is Specific revision.\"\n />\n }\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['waitForCompletion'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('waitForCompletion', $event === true)\"\n label=\"Wait for completion\"\n hint=\"When disabled, the parent continues after dispatch and backend records the child linkage.\"\n />\n </div>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['inputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:inputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('inputMappingJson', $event)\"\n label=\"Input mapping JSON\"\n hint=\"JSON object or expression map sent into the child automation input.\"\n rows=\"7\"\n />\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['outputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:outputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('outputMappingJson', $event)\"\n label=\"Output mapping JSON\"\n hint=\"JSON object or expression map for child output/status returned to the parent.\"\n rows=\"7\"\n />\n </div>\n </section>\n }\n }\n @case (\"Subworkflow\") {\n @if (sectionInMain(\"callAutomation\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Subworkflow</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['targetAutomationId'] ?? ''\"\n (ngModelChange)=\"onSubworkflowTargetChange($event)\"\n label=\"Target automation\"\n hint=\"Backend-provided automation candidate. Tenant and recursion validation stay on the backend.\"\n [options]=\"subworkflowAutomationOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['revisionMode'] ?? 'Active'\"\n (ngModelChange)=\"onConfigFieldChange('revisionMode', $event)\"\n label=\"Revision mode\"\n hint=\"Choose which published or active child revision to call.\"\n [options]=\"revisionModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (config()['revisionMode'] === \"SpecificRevision\") {\n <mt-text-field\n [ngModel]=\"config()['specificRevisionId'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('specificRevisionId', $event)\"\n label=\"Specific revision id\"\n hint=\"Required only when revision mode is Specific revision.\"\n />\n }\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['waitForCompletion'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('waitForCompletion', $event === true)\"\n label=\"Wait for completion\"\n hint=\"When disabled, the parent continues after dispatch and backend records the child linkage.\"\n />\n </div>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['inputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:inputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('inputMappingJson', $event)\"\n label=\"Input mapping JSON\"\n hint=\"JSON object or expression map sent into the child automation input.\"\n rows=\"7\"\n />\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['outputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:outputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('outputMappingJson', $event)\"\n label=\"Output mapping JSON\"\n hint=\"JSON object or expression map for child output/status returned to the parent.\"\n rows=\"7\"\n />\n </div>\n </section>\n }\n }\n @case (\"ParallelStart\") {\n @if (sectionInMain(\"parallelStart\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"flex items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\">\n <div class=\"fp-ae-section-title !m-0\">Parallel start</div>\n <mt-button size=\"small\" variant=\"outlined\" icon=\"general.plus\" label=\"Add branch\" (onClick)=\"addParallelBranch()\" />\n </div>\n <div class=\"space-y-3 p-4\">\n @for (branch of parallelBranches(); track branch.key; let i = $index) {\n <div class=\"rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"grid gap-3 md:grid-cols-[160px_1fr_1fr_auto]\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"branch.key\"\n (ngModelChange)=\"updateParallelBranch(i, 'key', $event)\"\n label=\"Branch key\"\n hint=\"Persisted route key. Do not use array index names.\"\n />\n <mt-text-field\n [ngModel]=\"branch.label\"\n (ngModelChange)=\"updateParallelBranch(i, 'label', $event)\"\n label=\"Label\"\n hint=\"Visual branch label. Changing it does not change existing routes.\"\n />\n <mt-text-field\n [ngModel]=\"branch.description\"\n (ngModelChange)=\"updateParallelBranch(i, 'description', $event)\"\n label=\"Description\"\n hint=\"Optional branch note for reviewers.\"\n />\n <div class=\"flex items-end gap-1\">\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-up\" tooltip=\"Move up\" (onClick)=\"moveParallelBranch(i, -1)\" />\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-down\" tooltip=\"Move down\" (onClick)=\"moveParallelBranch(i, 1)\" />\n <mt-button size=\"small\" variant=\"outlined\" severity=\"danger\" icon=\"general.trash-01\" tooltip=\"Remove branch\" (onClick)=\"removeParallelBranch(i)\" />\n </div>\n </div>\n <div class=\"mt-3 flex flex-wrap items-center gap-1.5 text-[11px]\">\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-50 px-2 font-mono font-semibold text-(--p-text-color)\">{{ branch.key }}</span>\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-0 px-2 font-medium text-(--p-text-muted-color)\">{{ branch.routeCount }} connected route{{ branch.routeCount === 1 ? \"\" : \"s\" }}</span>\n </div>\n </div>\n }\n @if (parallelBranches().length === 0) {\n <p class=\"fp-ae-copy\">Add at least two stable branch keys. Backend validation blocks publish until branches are valid.</p>\n }\n </div>\n </section>\n }\n }\n @case (\"ParallelJoin\") {\n @if (sectionInMain(\"parallelJoin\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Parallel join</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['joinPolicy'] ?? 'All'\"\n (ngModelChange)=\"onConfigFieldChange('joinPolicy', $event)\"\n label=\"Join policy\"\n hint=\"Backend policy used to decide when branch wait is complete.\"\n [options]=\"joinPolicyOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (config()['joinPolicy'] === \"Threshold\") {\n <mt-number-field\n [ngModel]=\"numberValue(config()['threshold'])\"\n (ngModelChange)=\"onConfigFieldChange('threshold', $event)\"\n label=\"Threshold\"\n hint=\"Minimum completed branch count required for Threshold policy.\"\n [min]=\"1\"\n />\n }\n <mt-select-field\n [ngModel]=\"config()['aggregationStrategy'] ?? 'MergeObjects'\"\n (ngModelChange)=\"onConfigFieldChange('aggregationStrategy', $event)\"\n label=\"Aggregation strategy\"\n hint=\"How backend joins branch outputs into the join node output.\"\n [options]=\"aggregationStrategyOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['outputTargetPath'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:outputTargetPath')\"\n (ngModelChange)=\"onConfigFieldChange('outputTargetPath', $event)\"\n label=\"Output target path\"\n hint=\"Optional context path where joined output should be written.\"\n />\n </div>\n </section>\n }\n }\n @case (\"Switch\") {\n @if (sectionInMain(\"switch\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Switch</div>\n <div class=\"grid gap-4\">\n <div class=\"grid items-end gap-3 md:grid-cols-[minmax(0,1fr)_minmax(280px,0.7fr)]\">\n <mt-select-field\n [ngModel]=\"config()['mode'] ?? 'value'\"\n (ngModelChange)=\"onConfigFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Backend evaluation mode: expression, rules, or source-value matching.\"\n [options]=\"switchModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-toggle-field\n class=\"self-end pb-1\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['firstMatch'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('firstMatch', $event === true)\"\n label=\"First match\"\n hint=\"Use the first matching case unless backend explicitly supports multi-match.\"\n />\n </div>\n @if (showSwitchSourceValue() || showSwitchExpression()) {\n <div class=\"grid gap-3 md:grid-cols-2\">\n @if (showSwitchSourceValue()) {\n <mt-text-field\n [class]=\"showSwitchExpression() ? 'font-mono' : 'font-mono md:col-span-2'\"\n [ngModel]=\"config()['sourceValue'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:sourceValue')\"\n (ngModelChange)=\"onConfigFieldChange('sourceValue', $event)\"\n label=\"Source value\"\n hint=\"Value or expression used for value matching.\"\n />\n }\n @if (showSwitchExpression()) {\n <mt-text-field\n [class]=\"showSwitchSourceValue() ? 'font-mono' : 'font-mono md:col-span-2'\"\n [ngModel]=\"config()['expression'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:expression')\"\n (ngModelChange)=\"onConfigFieldChange('expression', $event)\"\n label=\"Expression\"\n hint=\"Expression evaluated when mode is expression or rules.\"\n />\n }\n </div>\n }\n <div class=\"grid items-end gap-3 md:grid-cols-[minmax(0,1fr)_minmax(280px,0.7fr)]\">\n @if (showSwitchDefaultOutputKey()) {\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['defaultOutputKey'] ?? 'default'\"\n (ngModelChange)=\"onConfigFieldChange('defaultOutputKey', $event)\"\n label=\"Default output key\"\n hint=\"Stable default route key used when no case matches.\"\n />\n }\n <mt-toggle-field\n [class]=\"showSwitchDefaultOutputKey() ? 'self-end pb-1' : 'self-end pb-1 md:col-span-2'\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['includeDefaultOutput'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('includeDefaultOutput', $event === true)\"\n label=\"Include default output\"\n hint=\"Expose the default route output key in the canvas.\"\n />\n </div>\n </div>\n </section>\n <section class=\"fp-ae-panel\">\n <div class=\"flex items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\">\n <div class=\"fp-ae-section-title !m-0\">Switch cases</div>\n <mt-button size=\"small\" variant=\"outlined\" icon=\"general.plus\" label=\"Add case\" (onClick)=\"addSwitchCase()\" />\n </div>\n <div class=\"space-y-3 p-4\">\n @for (item of switchCases(); track item.key; let i = $index) {\n <div class=\"rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"grid gap-3 md:grid-cols-[160px_1fr_1fr_auto]\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"item.key\"\n (ngModelChange)=\"updateSwitchCase(i, 'key', $event)\"\n label=\"Case key\"\n hint=\"Persisted stable key. The route output becomes case_key.\"\n />\n <mt-text-field\n [ngModel]=\"item.label\"\n (ngModelChange)=\"updateSwitchCase(i, 'label', $event)\"\n label=\"Label\"\n hint=\"Visual case label. Routes stay attached to the case key.\"\n />\n <mt-text-field\n [ngModel]=\"item.value\"\n (focusin)=\"setExpressionTarget('config:cases:' + item.key + ':value')\"\n (ngModelChange)=\"updateSwitchCase(i, 'value', $event)\"\n label=\"Value\"\n hint=\"Expected value for value matching.\"\n />\n <div class=\"flex items-end gap-1\">\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-up\" tooltip=\"Move up\" (onClick)=\"moveSwitchCase(i, -1)\" />\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-down\" tooltip=\"Move down\" (onClick)=\"moveSwitchCase(i, 1)\" />\n <mt-button size=\"small\" variant=\"outlined\" severity=\"danger\" icon=\"general.trash-01\" tooltip=\"Remove case\" (onClick)=\"removeSwitchCase(i)\" />\n </div>\n </div>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"item.condition\"\n (focusin)=\"setExpressionTarget('config:cases:' + item.key + ':condition')\"\n (ngModelChange)=\"updateSwitchCase(i, 'condition', $event)\"\n label=\"Condition\"\n hint=\"Optional condition for rule matching.\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"item.expression\"\n (focusin)=\"setExpressionTarget('config:cases:' + item.key + ':expression')\"\n (ngModelChange)=\"updateSwitchCase(i, 'expression', $event)\"\n label=\"Case expression\"\n hint=\"Optional expression for this case.\"\n />\n </div>\n <div class=\"mt-3 flex flex-wrap items-center gap-1.5 text-[11px]\">\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-50 px-2 font-mono font-semibold text-(--p-text-color)\">{{ item.routeKey }}</span>\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-0 px-2 font-medium text-(--p-text-muted-color)\">{{ item.routeCount }} connected route{{ item.routeCount === 1 ? \"\" : \"s\" }}</span>\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-0 px-2 font-medium text-(--p-text-muted-color)\">Visual order {{ i + 1 }}</span>\n </div>\n </div>\n }\n @if (switchCases().length === 0) {\n <p class=\"fp-ae-copy\">Add at least one stable case key. Backend validation points routes at missing case keys if a case is deleted.</p>\n }\n </div>\n </section>\n }\n }\n @case (\"Stop\") {\n @if (sectionInMain(\"stop\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">End execution</div>\n @if (supportsConfigKey('status') || supportsConfigKey('message') || supportsConfigKey('output')) {\n <div class=\"grid gap-3 md:grid-cols-2\">\n @if (supportsConfigKey('status')) {\n <mt-text-field\n [ngModel]=\"config()['status'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('status', $event)\"\n label=\"Result status\"\n hint=\"Terminal status emitted by the backend stop node.\"\n />\n }\n @if (supportsConfigKey('message')) {\n <mt-text-field\n [ngModel]=\"config()['message'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:message')\"\n (ngModelChange)=\"onConfigFieldChange('message', $event)\"\n label=\"Message\"\n hint=\"Optional message or reason saved with the terminal result.\"\n />\n }\n @if (supportsConfigKey('output')) {\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['output'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:output')\"\n (ngModelChange)=\"onConfigFieldChange('output', $event)\"\n label=\"Output payload\"\n hint=\"Terminal output payload when exposed by the backend schema.\"\n rows=\"5\"\n />\n }\n </div>\n } @else {\n <p class=\"fp-ae-copy\">\n This terminal node has no backend-exposed settings. It ends execution when reached.\n </p>\n }\n </section>\n }\n }\n }\n\n @if (sectionInMain(\"backendSchema\") && step() && editorType() !== \"HumanApproval\") {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Output data</div>\n @if (outputFields().length > 0) {\n <div class=\"grid gap-2 md:grid-cols-2\">\n @for (field of outputFields(); track field.key) {\n <div class=\"fp-ae-kv\">\n <span>{{ field.label }}</span>\n <strong>{{ field.type }}</strong>\n </div>\n }\n </div>\n }\n @if (routeOutputKeys().length > 0) {\n <div class=\"mt-3 fp-ae-label mb-2\">Route outputs</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n </div>\n }\n </section>\n }\n\n @if (sectionInAdvanced(\"mapping\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Request and result mapping</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <div>\n <div class=\"fp-ae-label mb-2\">Request data</div>\n @for (row of inputMappingRows(); track row.key) {\n <label class=\"mb-2 block space-y-1\">\n <span class=\"text-[11px] text-(--p-text-muted-color)\">{{ row.key }}</span>\n <mt-text-field\n [ngModel]=\"valueText(row.value)\"\n (focusin)=\"setExpressionTarget('config:inputMapping:' + row.key)\"\n (ngModelChange)=\"updateJsonField('inputMappingJson', row.key, $event)\"\n hint=\"Expression or literal value mapped into this node input.\"\n />\n </label>\n }\n </div>\n <div>\n <div class=\"fp-ae-label mb-2\">Output data</div>\n @for (row of outputMappingRows(); track row.key) {\n <label class=\"mb-2 block space-y-1\">\n <span class=\"text-[11px] text-(--p-text-muted-color)\">{{ row.key }}</span>\n <mt-text-field\n [ngModel]=\"valueText(row.value)\"\n (focusin)=\"setExpressionTarget('config:outputMapping:' + row.key)\"\n (ngModelChange)=\"updateJsonField('outputMappingJson', row.key, $event)\"\n hint=\"Expression or literal value mapped from this node output.\"\n />\n </label>\n }\n </div>\n </div>\n @if (\n sectionHasAdvancedEmptyState(\"mapping\") &&\n inputMappingRows().length === 0 &&\n outputMappingRows().length === 0\n ) {\n <p class=\"fp-ae-copy mt-3\">\n This node is using backend default request and output data. Add mappings only when this step needs a custom payload.\n </p>\n }\n </section>\n }\n\n @if (sectionInAdvanced(\"credentials\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Auth and credentials</div>\n <ng-container *ngTemplateOutlet=\"credentialSelector\" />\n </section>\n }\n\n @if (sectionInAdvanced(\"policy\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Retry, timeout, error policy</div>\n <div class=\"grid gap-3 md:grid-cols-3\">\n <mt-number-field\n [ngModel]=\"numberValue(policyObject('timeoutPolicyJson')['timeoutSeconds'])\"\n (ngModelChange)=\"updateJsonField('timeoutPolicyJson', 'timeoutSeconds', $event)\"\n label=\"Timeout seconds\"\n hint=\"Maximum runtime before the backend times out this node.\"\n [min]=\"0\"\n />\n <mt-number-field\n [ngModel]=\"numberValue(policyObject('retryPolicyJson')['maxAttempts'])\"\n (ngModelChange)=\"updateJsonField('retryPolicyJson', 'maxAttempts', $event)\"\n label=\"Max attempts\"\n hint=\"Maximum retry attempts after retryable failures.\"\n [min]=\"0\"\n />\n <mt-select-field\n [ngModel]=\"policyObject('errorPolicyJson')['onFailure'] ?? ''\"\n (ngModelChange)=\"updateJsonField('errorPolicyJson', 'onFailure', $event)\"\n label=\"On failure\"\n hint=\"Backend error policy to apply when this node fails.\"\n [options]=\"errorPolicyOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n @if (sectionHasAdvancedEmptyState(\"policy\")) {\n <p class=\"fp-ae-copy mt-3\">\n Backend defaults apply until you override a timeout, retry, or error policy here.\n </p>\n }\n </section>\n }\n\n @if (sectionInAdvanced(\"triggerContext\") && trigger()) {\n <details class=\"fp-ae-panel\" open>\n <summary class=\"fp-ae-section-title cursor-pointer select-none\">\n Trigger payload and context\n </summary>\n <p class=\"fp-ae-copy mt-3\">\n Trigger payload/context is documentation for expressions only. Triggers\n do not edit node input or output mappings here.\n </p>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Config schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(configSchema()) }}</pre>\n </details>\n @if (hasTriggerPayloadSchema() && triggerPayloadSchema(); as schema) {\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Payload schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(schema) }}</pre>\n </details>\n }\n @if (authPolicySchema()) {\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Auth policy schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(authPolicySchema()) }}</pre>\n </details>\n }\n @if (triggerPayloadSample(); as sample) {\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Payload or request sample</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(sample) }}</pre>\n </details>\n }\n </div>\n </details>\n }\n\n @if (sectionInAdvanced(\"backendSchema\") && step()) {\n <details class=\"fp-ae-panel\" open>\n <summary class=\"fp-ae-section-title cursor-pointer select-none\">\n Backend schemas and outputs\n </summary>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Config schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(configSchema()) }}</pre>\n </details>\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Input schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(inputSchema()) }}</pre>\n </details>\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Output schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(outputSchema()) }}</pre>\n </details>\n <div class=\"rounded-lg border border-(--p-content-border-color) p-3\">\n <div class=\"fp-ae-label mb-2\">Route output keys</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n @if (routeOutputKeys().length === 0) {\n <span class=\"text-[12px] text-(--p-text-muted-color)\">No outgoing route keys</span>\n }\n </div>\n </div>\n </div>\n </details>\n }\n\n @if (sectionInAdvanced(\"schemaFields\") && configRows().length > 0) {\n <details class=\"fp-ae-panel\" open>\n <summary class=\"fp-ae-section-title cursor-pointer select-none\">\n Backend schema fields\n </summary>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n @for (field of configRows(); track field.key) {\n <div\n class=\"space-y-1.5\"\n [class.md:col-span-2]=\"field.type === 'object' || field.type === 'array'\"\n >\n @if (field.enumValues.length) {\n <mt-select-field\n [ngModel]=\"fieldValue(field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n [options]=\"enumOptions(field.enumValues)\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n } @else if (field.type === \"boolean\") {\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"!!fieldValue(field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event === true)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n />\n } @else if (field.type === \"number\") {\n <mt-number-field\n [ngModel]=\"numberValue(fieldValue(field.key))\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n />\n } @else if (field.type === \"date\") {\n <mt-date-field\n [ngModel]=\"fieldValue(field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n [showTime]=\"field.format !== 'date'\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n } @else if (field.type === \"object\" || field.type === \"array\") {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(fieldValue(field.key))\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n rows=\"5\"\n />\n } @else {\n <mt-text-field\n [ngModel]=\"fieldText(field.key)\"\n (focusin)=\"field.expressionEnabled && setExpressionTarget('config:' + field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n />\n }\n </div>\n }\n </div>\n </details>\n }\n </div>\n</div>\n\n<ng-template #credentialSelector>\n @if (credentials().length) {\n <div class=\"space-y-2\">\n @for (credential of credentials(); track credential.credentialRef) {\n <label class=\"flex items-center justify-between gap-3 rounded-lg border border-(--p-content-border-color) bg-(--p-surface-50) px-3 py-2\">\n <span class=\"min-w-0\">\n <span class=\"block truncate text-[12px] font-semibold\">{{ credential.displayName ?? credential.credentialRef }}</span>\n <span class=\"block truncate font-mono text-[11px] text-(--p-text-muted-color)\">\n {{ credential.credentialRef }} / {{ credential.status ?? (credential.resolved ? \"Resolved\" : \"Unresolved\") }}\n </span>\n </span>\n <span class=\"flex items-center gap-2\">\n <mt-button\n size=\"small\"\n variant=\"text\"\n label=\"Test\"\n (onClick)=\"testCredential(credential.credentialRef)\"\n />\n <mt-toggle-field\n size=\"small\"\n [ngModel]=\"credentialRefs().includes(credential.credentialRef)\"\n (ngModelChange)=\"toggleCredential(credential.credentialRef, $event === true)\"\n hint=\"Attach or detach this backend credential reference. Masked secrets are preserved.\"\n />\n </span>\n </label>\n }\n </div>\n } @else if (sectionHasAdvancedEmptyState(\"credentials\")) {\n <p class=\"fp-ae-copy\">\n The backend helper did not return credential choices for this node. Existing masked references remain preserved in the saved JSON.\n </p>\n }\n @if (credentialTest(); as test) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n {{ test.status ?? (test.succeeded ? \"Succeeded\" : \"Placeholder\") }}\n @if (test.message) { <span>- {{ test.message }}</span> }\n </div>\n }\n</ng-template>\n\n<ng-template\n #mapEditor\n let-objectKey=\"objectKey\"\n let-rows=\"rows\"\n let-keyLabel=\"keyLabel\"\n let-valueLabel=\"valueLabel\"\n let-addLabel=\"addLabel\"\n>\n <div class=\"space-y-2\">\n @for (row of rows; track row.key) {\n <div class=\"grid gap-2 md:grid-cols-[180px_1fr_auto]\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"row.key\"\n (ngModelChange)=\"updateObjectRow(objectKey, row.key, $event, row.value)\"\n [label]=\"keyLabel\"\n hint=\"Configuration key saved to the backend JSON object.\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"valueText(row.value)\"\n (focusin)=\"setExpressionTarget('config:' + objectKey + ':' + row.key)\"\n (ngModelChange)=\"updateObjectRow(objectKey, row.key, row.key, $event)\"\n [label]=\"valueLabel\"\n hint=\"Configuration value. Use expressions when this field should resolve at runtime.\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n label=\"Remove\"\n (onClick)=\"removeObjectRow(objectKey, row.key)\"\n />\n </div>\n }\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n [label]=\"addLabel || ('Add ' + keyLabel)\"\n (onClick)=\"addObjectRow(objectKey)\"\n />\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DateField, selector: "mt-date-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "showIcon", "showClear", "showTime", "pInputs", "required"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "description", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "hint", "description", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "directive", type: DropDataDirective, selector: "[fpDropData]", inputs: ["fpDropAutoInsert"], outputs: ["dataDrop"] }] });
|
|
13810
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AutomationSmartEditorComponent, isStandalone: true, selector: "fp-automation-smart-editor", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "trigger", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block h-full min-h-0" }, ngImport: i0, template: "<div\n class=\"fp-scroll flex h-full min-h-0 flex-col overflow-y-auto\"\n fpDropData\n [fpDropAutoInsert]=\"false\"\n (dataDrop)=\"insertExpression($event)\"\n>\n <div class=\"space-y-4 px-5 py-5\">\n @if (helperError()) {\n <div\n class=\"rounded-lg border border-[rgb(var(--fp-warning))]/30 bg-[rgb(var(--fp-warning))]/10 px-3 py-2 text-[12px] leading-5 text-(--p-text-color)\"\n >\n {{ helperError() }}\n </div>\n }\n\n @if (sectionInMain(\"startConnection\") && trigger()) {\n <section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n >\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Start connection\n </h3>\n <div class=\"space-y-3 p-4\">\n @if (startConnection().key) {\n @if (startConnection().step) {\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div class=\"min-w-0 space-y-1\">\n <div class=\"text-[12px] font-semibold text-(--p-text-muted-color)\">\n First node connected\n </div>\n <div class=\"truncate text-[14px] font-semibold text-(--p-text-color)\">\n {{ startConnection().label }}\n </div>\n <div class=\"flex flex-wrap items-center gap-2 text-[12px] text-(--p-text-muted-color)\">\n <span>Managed on canvas</span>\n <span\n class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\"\n >\n {{ startConnection().key }}\n </span>\n </div>\n </div>\n <div class=\"flex shrink-0 flex-wrap gap-2\">\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n severity=\"secondary\"\n label=\"Focus connected node\"\n (onClick)=\"focusStartConnection()\"\n />\n </div>\n </div>\n } @else {\n <div class=\"space-y-2\">\n <div class=\"text-[14px] font-semibold text-(--p-text-color)\">\n No first node connected\n </div>\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n The saved start connection points to a node key that is not on\n the canvas. Connect this trigger to the first node on the\n canvas.\n </p>\n <div class=\"flex flex-wrap items-center gap-2 text-[12px] text-(--p-text-muted-color)\">\n <span>Technical key</span>\n <span\n class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\"\n >\n {{ startConnection().key }}\n </span>\n <span>Managed on canvas</span>\n </div>\n </div>\n }\n } @else {\n <div class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-surface-50) p-3\">\n <div class=\"text-[14px] font-semibold text-(--p-text-color)\">\n No first node connected\n </div>\n <p class=\"m-0 mt-1 text-[12px] leading-5 text-(--p-text-muted-color)\">\n Connect this trigger to the first node on the canvas.\n </p>\n </div>\n }\n </div>\n </section>\n }\n\n @switch (editorType()) {\n @case (\"ManualTrigger\") {\n @if (sectionInMain(\"manualTrigger\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Manual run input</div>\n @if (hasTriggerPayloadSchema() && triggerPayloadSchema(); as schema) {\n <div class=\"rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Payload schema</div>\n <pre class=\"fp-ae-code\">{{ schemaText(schema) }}</pre>\n </div>\n }\n @if (triggerPayloadSample(); as sample) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Sample payload</div>\n <pre class=\"fp-ae-code\">{{ schemaText(sample) }}</pre>\n </div>\n }\n @if (!hasTriggerPayloadSchema() && !triggerPayloadSample()) {\n <p class=\"fp-ae-copy\">\n This manual trigger has no backend-provided input schema. It can still be connected to the first step on the canvas.\n </p>\n }\n </section>\n }\n }\n @case (\"WebhookTrigger\") {\n @if (sectionInMain(\"webhookSetup\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Webhook setup</div>\n @if (webhookSetup(); as setup) {\n <div class=\"flex gap-2\">\n <mt-text-field\n class=\"flex-1 font-mono\"\n [ngModel]=\"setup.webhookUrl ?? ''\"\n [readonly]=\"true\"\n label=\"Webhook URL\"\n hint=\"Backend-generated endpoint clients should call to start this trigger.\"\n />\n <mt-button class=\"self-end\" size=\"small\" variant=\"outlined\" label=\"Copy\" (onClick)=\"copyWebhookUrl()\" />\n </div>\n <div class=\"grid gap-2 md:grid-cols-2\">\n <div class=\"fp-ae-kv\">\n <span>Auth mode</span>\n <strong>{{ setup.authMode ?? \"Backend default\" }}</strong>\n </div>\n <div class=\"fp-ae-kv\">\n <span>Required headers</span>\n <strong>{{ (setup.requiredHeaders ?? []).join(\", \") || \"-\" }}</strong>\n </div>\n </div>\n @if (setup.hmacSigning) {\n <p class=\"fp-ae-copy\">{{ setup.hmacSigning }}</p>\n }\n @if (setup.sampleRequest) {\n <details class=\"mt-3 rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">\n Sample request\n </summary>\n <pre class=\"fp-ae-code\">{{ schemaText(setup.sampleRequest) }}</pre>\n </details>\n }\n } @else {\n <p class=\"fp-ae-copy\">\n Webhook setup is not available for this draft yet.\n </p>\n }\n </section>\n }\n @if (sectionInMain(\"authPolicy\") && hasAuthPolicy()) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Authentication policy</div>\n <div class=\"grid gap-3 md:grid-cols-3\">\n <mt-select-field\n [ngModel]=\"authPolicy()['mode'] ?? ''\"\n (ngModelChange)=\"onAuthFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Authentication mode required by the backend webhook policy.\"\n [options]=\"authModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n [ngModel]=\"authPolicy()['signatureHeaderName'] ?? ''\"\n (ngModelChange)=\"onAuthFieldChange('signatureHeaderName', $event)\"\n label=\"Signature header\"\n hint=\"Header name that carries the webhook signature when the policy requires signed requests.\"\n />\n <mt-text-field\n [ngModel]=\"authPolicy()['timestampHeaderName'] ?? ''\"\n (ngModelChange)=\"onAuthFieldChange('timestampHeaderName', $event)\"\n label=\"Timestamp header\"\n hint=\"Header name that carries the request timestamp for replay protection.\"\n />\n </div>\n </section>\n }\n }\n @case (\"FormSubmitTrigger\") {\n @if (sectionInMain(\"formBinding\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Form binding</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"selectedFormId() || formBinding()?.formId || ''\"\n (ngModelChange)=\"onFormChange($event)\"\n label=\"Form\"\n hint=\"Choose a backend form that will submit data into this trigger.\"\n [options]=\"formOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"selectedFormVersionId() || formBinding()?.formVersionId || ''\"\n (ngModelChange)=\"onFormVersionChange($event)\"\n label=\"Form version\"\n hint=\"Persist the exact backend formVersionId. Do not type or generate IDs manually.\"\n [options]=\"formVersionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <div class=\"mt-3 flex flex-wrap items-center gap-2\">\n <mt-button size=\"small\" severity=\"primary\" label=\"Save binding\" (onClick)=\"saveFormBinding()\" />\n @if (formBinding()) {\n <span class=\"rounded-lg bg-(--p-surface-100) px-2 py-1 font-mono text-[11px] text-(--p-text-muted-color)\">\n {{ formBinding()!.formVersionId }}\n </span>\n }\n </div>\n @if (formSchema(); as schema) {\n <details class=\"mt-3 rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">\n Form schema preview\n </summary>\n <pre class=\"fp-ae-code\">{{ schemaText(schema.schema) }}</pre>\n </details>\n }\n </section>\n }\n }\n @case (\"ScheduleTrigger\") {\n @if (sectionInMain(\"schedule\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Schedule</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['mode'] ?? 'cron'\"\n (ngModelChange)=\"onConfigFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Pick one schedule mode: cron, interval, or once. Backend validation requires exactly one mode.\"\n [options]=\"scheduleModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['timezone'] ?? 'UTC'\"\n (ngModelChange)=\"onConfigFieldChange('timezone', $event)\"\n label=\"Timezone\"\n hint=\"Timezone used to calculate the next fire time.\"\n [options]=\"timeZoneOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['cron'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('cron', $event)\"\n label=\"Cron\"\n hint=\"Cron expression used only when mode is cron, for example 0 9 * * *.\"\n />\n <mt-number-field\n [ngModel]=\"numberValue(config()['intervalSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('intervalSeconds', $event)\"\n label=\"Interval seconds\"\n hint=\"Repeat interval in seconds used only when mode is interval.\"\n [min]=\"0\"\n />\n <mt-date-field\n [ngModel]=\"config()['startDate'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('startDate', $event)\"\n label=\"Start date UTC\"\n hint=\"UTC date/time used for once schedules or as the first allowed run time.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n <mt-select-field\n [ngModel]=\"config()['misfirePolicy'] ?? 'SkipMissed'\"\n (ngModelChange)=\"onConfigFieldChange('misfirePolicy', $event)\"\n label=\"Misfire policy\"\n hint=\"Backend behavior when a scheduled run is missed while the automation is unavailable.\"\n [options]=\"misfirePolicyOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <div class=\"mt-3 flex gap-2\">\n <mt-button size=\"small\" variant=\"outlined\" label=\"Validate\" (onClick)=\"validateSchedule()\" />\n <mt-button size=\"small\" severity=\"primary\" label=\"Preview next fire\" (onClick)=\"previewSchedule()\" />\n </div>\n @if (schedulePreview(); as preview) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n <strong class=\"text-emerald-600\">Valid</strong>\n <span class=\"ms-2\">Next fire: {{ preview.nextFireAtUtc ?? \"-\" }}</span>\n </div>\n }\n </section>\n }\n }\n @case (\"SetFields\") {\n @if (sectionInMain(\"setFields\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Set fields</div>\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'fields', rows: fieldsRows(), keyLabel: 'Field', valueLabel: 'Value', addLabel: 'Add field' }\" />\n </section>\n }\n }\n @case (\"If\") {\n @if (sectionInMain(\"condition\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Condition</div>\n <div class=\"grid gap-3 md:grid-cols-[1fr_180px_1fr]\">\n <mt-text-field\n [ngModel]=\"fieldText('left')\"\n (focusin)=\"setExpressionTarget('config:left')\"\n (ngModelChange)=\"onConfigFieldChange('left', $event)\"\n label=\"Left\"\n hint=\"Left-side value or expression to compare.\"\n />\n <mt-select-field\n [ngModel]=\"config()['operator'] ?? 'equals'\"\n (ngModelChange)=\"onConfigFieldChange('operator', $event)\"\n label=\"Operator\"\n hint=\"Comparison operator used by the backend condition evaluator.\"\n [options]=\"ifOperatorOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n [ngModel]=\"fieldText('right')\"\n (focusin)=\"setExpressionTarget('config:right')\"\n (ngModelChange)=\"onConfigFieldChange('right', $event)\"\n label=\"Right\"\n hint=\"Right-side value or expression to compare against.\"\n />\n </div>\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Route outputs</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n </div>\n </div>\n </section>\n }\n }\n @case (\"HTTP\") {\n @if (sectionInMain(\"httpRequest\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">HTTP request</div>\n <div class=\"grid gap-3 md:grid-cols-[150px_1fr]\">\n <mt-select-field\n [ngModel]=\"config()['method'] ?? 'GET'\"\n (ngModelChange)=\"onConfigFieldChange('method', $event)\"\n label=\"Method\"\n hint=\"HTTP method used for the outbound request.\"\n [options]=\"httpMethodOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['url'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:url')\"\n (ngModelChange)=\"onConfigFieldChange('url', $event)\"\n label=\"URL\"\n hint=\"Target URL. Expressions are supported for dynamic hosts, paths, and query values.\"\n />\n </div>\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'headers', rows: headerRows(), keyLabel: 'Header', valueLabel: 'Value', addLabel: 'Add header' }\" />\n </div>\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'query', rows: queryRows(), keyLabel: 'Query param', valueLabel: 'Value', addLabel: 'Add query param' }\" />\n </div>\n @if (supportsConfigKey('bodyMode')) {\n <mt-select-field\n class=\"mt-3\"\n [ngModel]=\"config()['bodyMode'] ?? 'json'\"\n (ngModelChange)=\"onConfigFieldChange('bodyMode', $event)\"\n label=\"Body mode\"\n hint=\"Backend-supported request body serialization mode.\"\n [options]=\"httpBodyModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n }\n <mt-textarea-field\n class=\"mt-3 w-full font-mono\"\n [ngModel]=\"valueText(config()['body'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:body')\"\n (ngModelChange)=\"onConfigFieldChange('body', $event)\"\n label=\"Body\"\n hint=\"Request body sent by the HTTP node. Use JSON or expressions when the backend schema allows it.\"\n rows=\"6\"\n />\n @if (supportsConfigKey('timeoutSeconds') || supportsConfigKey('responseHandling')) {\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n @if (supportsConfigKey('timeoutSeconds')) {\n <mt-number-field\n [ngModel]=\"numberValue(config()['timeoutSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('timeoutSeconds', $event)\"\n label=\"Timeout seconds\"\n hint=\"Request timeout when exposed by the backend schema.\"\n [min]=\"0\"\n />\n }\n @if (supportsConfigKey('responseHandling')) {\n <mt-text-field\n [ngModel]=\"config()['responseHandling'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('responseHandling', $event)\"\n label=\"Response handling\"\n hint=\"Backend-supported response handling mode or expression.\"\n />\n }\n </div>\n }\n @if (sectionInMain(\"credentials\")) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Credential</div>\n <ng-container *ngTemplateOutlet=\"credentialSelector\" />\n </div>\n }\n </section>\n }\n }\n @case (\"Wait\") {\n @if (sectionInMain(\"wait\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Wait</div>\n <div class=\"grid gap-3 md:grid-cols-3\">\n <mt-select-field\n [ngModel]=\"config()['mode'] ?? 'duration'\"\n (ngModelChange)=\"onConfigFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Choose whether this wait uses a duration or a specific date/time.\"\n [options]=\"waitModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-number-field\n [ngModel]=\"numberValue(config()['durationSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('durationSeconds', $event)\"\n label=\"Duration seconds\"\n hint=\"How long execution should wait when mode is duration.\"\n [min]=\"0\"\n />\n <mt-date-field\n [ngModel]=\"config()['waitUntil'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('waitUntil', $event)\"\n label=\"Wait until\"\n hint=\"Date/time that resolves to when execution should resume.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n </div>\n @if (supportsConfigKey('resumePayloadSchema') || supportsConfigKey('resumePayload')) {\n <mt-textarea-field\n class=\"mt-3 w-full font-mono\"\n [ngModel]=\"valueText(config()['resumePayloadSchema'] ?? config()['resumePayload'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:resumePayload')\"\n (ngModelChange)=\"onConfigFieldChange('resumePayload', $event)\"\n label=\"Resume payload\"\n hint=\"Expected payload when the backend supports manual or external resume data.\"\n rows=\"5\"\n />\n }\n </section>\n }\n }\n @case (\"HumanApproval\") {\n @if (sectionInMain(\"approvalTask\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Approval task</div>\n @if (assignmentOptions()?.providerStatus && assignmentOptions()?.providerStatus !== \"Available\") {\n <p class=\"fp-ae-copy\">\n Assignment provider status: {{ assignmentOptions()?.providerStatus }}.\n The backend provider is the source of truth for available assignees.\n </p>\n }\n <div class=\"grid gap-3 xl:grid-cols-3\">\n <mt-text-field\n [ngModel]=\"config()['title'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:title')\"\n (ngModelChange)=\"onConfigFieldChange('title', $event)\"\n label=\"Approval title\"\n hint=\"Approval title shown to the assigned human approver.\"\n />\n <mt-select-field\n [ngModel]=\"selectedAssignmentKey()\"\n (ngModelChange)=\"onAssignmentOptionChange($event)\"\n label=\"Assignment\"\n hint=\"Backend-provided assignee, role, or group that can decide this approval.\"\n [options]=\"assignmentSelectOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (humanApprovalSupportsConfig('priority')) {\n <mt-text-field\n [ngModel]=\"config()['priority'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('priority', $event)\"\n label=\"Priority\"\n hint=\"Approval priority when supported by the backend schema.\"\n />\n }\n </div>\n <mt-textarea-field\n class=\"mt-3 w-full\"\n [ngModel]=\"config()['message'] ?? config()['instructions'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:message')\"\n (ngModelChange)=\"onConfigFieldChange('message', $event)\"\n label=\"Approval message\"\n hint=\"Decision instructions shown to the approver.\"\n rows=\"4\"\n />\n @if (humanApprovalSupportsConfig('dueDate') || humanApprovalSupportsConfig('dueInSeconds') || humanApprovalSupportsConfig('timeoutSeconds') || humanApprovalSupportsConfig('expiresAt') || humanApprovalSupportsConfig('commentsRequired') || humanApprovalSupportsConfig('allowReturn')) {\n <div class=\"mt-3 grid gap-3 xl:grid-cols-4\">\n @if (humanApprovalSupportsConfig('dueDate')) {\n <mt-date-field\n [ngModel]=\"config()['dueDate'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('dueDate', $event)\"\n label=\"Due date\"\n hint=\"Backend-supported approval due date.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n }\n @if (humanApprovalSupportsConfig('dueInSeconds')) {\n <mt-number-field\n [ngModel]=\"numberValue(config()['dueInSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('dueInSeconds', $event)\"\n label=\"Due in seconds\"\n hint=\"Relative approval due duration when supported by the backend schema.\"\n [min]=\"0\"\n />\n }\n @if (humanApprovalSupportsConfig('timeoutSeconds')) {\n <mt-number-field\n [ngModel]=\"numberValue(config()['timeoutSeconds'])\"\n (ngModelChange)=\"onConfigFieldChange('timeoutSeconds', $event)\"\n label=\"Timeout seconds\"\n hint=\"Approval timeout emitted according to backend approval runtime support.\"\n [min]=\"0\"\n />\n }\n @if (humanApprovalSupportsConfig('expiresAt')) {\n <mt-date-field\n [ngModel]=\"config()['expiresAt'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('expiresAt', $event)\"\n label=\"Expires at\"\n hint=\"Backend-supported approval expiry timestamp.\"\n [showTime]=\"true\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n }\n @if (humanApprovalSupportsConfig('commentsRequired')) {\n <div class=\"flex min-h-[4.25rem] items-end rounded-md border border-surface-200 bg-surface-0 px-3 py-2\">\n <mt-toggle-field\n size=\"small\"\n label=\"Comments required\"\n labelPosition=\"end\"\n [ngModel]=\"config()['commentsRequired'] === true\"\n (ngModelChange)=\"onConfigFieldChange('commentsRequired', $event === true)\"\n hint=\"Require approver comments when the backend supports this flag.\"\n />\n </div>\n }\n @if (humanApprovalSupportsConfig('allowReturn')) {\n <div class=\"flex min-h-[4.25rem] items-end rounded-md border border-surface-200 bg-surface-0 px-3 py-2\">\n <mt-toggle-field\n size=\"small\"\n label=\"Allow return for changes\"\n labelPosition=\"end\"\n [ngModel]=\"config()['allowReturn'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('allowReturn', $event === true)\"\n hint=\"Allow ReturnForChanges when the backend schema exposes this option.\"\n />\n </div>\n }\n </div>\n }\n <div class=\"mt-3 space-y-3\">\n <div class=\"fp-ae-section-title\">Decision options</div>\n <div class=\"overflow-hidden rounded-md border border-surface-200 bg-surface-0\">\n @if (selectedApprovalDecisionRows().length > 0) {\n <div class=\"hidden border-b border-surface-200 bg-surface-50 px-3 py-2 text-[12px] font-semibold text-(--p-text-muted-color) xl:grid xl:grid-cols-[1.1fr_1fr_1.2fr_80px_44px] xl:gap-3\">\n <span>Decision label</span>\n <span>Canonical value</span>\n <span>Route output key</span>\n <span>Routes</span>\n <span>Action</span>\n </div>\n @for (decision of selectedApprovalDecisionRows(); track decision.value) {\n <div class=\"grid gap-2 border-b border-surface-100 px-3 py-3 last:border-b-0 xl:grid-cols-[1.1fr_1fr_1.2fr_80px_44px] xl:items-center xl:gap-3\">\n <div class=\"min-w-0\">\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Decision label</div>\n <div class=\"truncate text-[13px] font-semibold text-(--p-text-color)\">{{ decision.label }}</div>\n </div>\n <div class=\"min-w-0\">\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Canonical value</div>\n <div class=\"truncate font-mono text-[12px] text-(--p-text-color)\">{{ decision.value }}</div>\n </div>\n <div class=\"min-w-0\">\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Route output key</div>\n <div class=\"truncate font-mono text-[12px] text-(--p-text-muted-color)\">{{ decision.routeOutputKey }}</div>\n </div>\n <div>\n <div class=\"text-[11px] font-semibold text-(--p-text-muted-color) xl:hidden\">Routes</div>\n <div class=\"text-[13px] text-(--p-text-color)\">{{ decision.routeCount }}</div>\n </div>\n <mt-button\n class=\"justify-self-start xl:justify-self-end\"\n size=\"small\"\n variant=\"outlined\"\n severity=\"danger\"\n icon=\"general.trash-01\"\n tooltip=\"Remove decision option\"\n (onClick)=\"removeApprovalDecision(decision.value)\"\n />\n </div>\n }\n } @else {\n <div class=\"px-3 py-4 text-[12px] text-(--p-text-muted-color)\">\n Add at least one backend-supported approval decision.\n </div>\n }\n </div>\n @if (approvalDecisionIssues().length > 0) {\n <div class=\"rounded-lg border border-[rgb(var(--fp-error))]/30 bg-[rgb(var(--fp-error))]/10 px-3 py-2 text-[12px] leading-5 text-[rgb(var(--fp-error))]\">\n @for (issue of approvalDecisionIssues(); track issue) {\n <div>{{ issue }}</div>\n }\n </div>\n }\n <div class=\"grid gap-2 md:grid-cols-[minmax(0,1fr)_auto]\">\n <mt-select-field\n [ngModel]=\"approvalDecisionToAdd()\"\n (ngModelChange)=\"approvalDecisionToAdd.set($event)\"\n label=\"Decision to add\"\n hint=\"Only backend-supported decisions with matching route outputs are available.\"\n [options]=\"addableApprovalDecisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n variant=\"outlined\"\n icon=\"general.plus\"\n label=\"Add decision option\"\n [disabled]=\"!approvalDecisionToAdd()\"\n (onClick)=\"addApprovalDecision()\"\n />\n </div>\n </div>\n @if (humanApprovalSupportsConfig('payload') || humanApprovalSupportsConfig('context') || humanApprovalSupportsConfig('metadata')) {\n <div class=\"mt-3 grid gap-3\">\n @if (humanApprovalSupportsConfig('payload')) {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(config()['payload'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:payload')\"\n (ngModelChange)=\"onConfigFieldChange('payload', $event)\"\n label=\"Payload\"\n hint=\"Approval task payload or context fields supported by backend schema.\"\n rows=\"5\"\n />\n }\n @if (humanApprovalSupportsConfig('context')) {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(config()['context'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:context')\"\n (ngModelChange)=\"onConfigFieldChange('context', $event)\"\n label=\"Context\"\n hint=\"Additional approval context supported by backend schema.\"\n rows=\"5\"\n />\n }\n @if (humanApprovalSupportsConfig('metadata')) {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(config()['metadata'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:metadata')\"\n (ngModelChange)=\"onConfigFieldChange('metadata', $event)\"\n label=\"Metadata\"\n hint=\"Additional approval metadata supported by the backend schema.\"\n rows=\"4\"\n />\n }\n </div>\n }\n <div class=\"mt-3 flex gap-2\">\n <mt-button size=\"small\" variant=\"outlined\" label=\"Validate assignment\" (onClick)=\"validateAssignment()\" />\n </div>\n @if (assignmentValidation(); as result) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n {{ result.isValid === false ? \"Assignment invalid\" : \"Assignment accepted by helper\" }}\n @for (issue of resultIssues(result); track issue) {\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">{{ issue }}</div>\n }\n </div>\n }\n @if (sectionInMain(\"approvalOutput\")) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Output data</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (field of approvalOutputFieldLabels; track field) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ field }}</span>\n }\n </div>\n <div class=\"mt-3 fp-ae-label mb-2\">Approval routes</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n </div>\n </div>\n }\n </section>\n }\n }\n @case (\"FlowPlusCommit\") {\n @if (sectionInMain(\"flowplusCommit\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">FlowPlus commit</div>\n <p class=\"fp-ae-copy\">\n This node is the explicit module-data write boundary. Approval does\n not commit data unless this node is reached.\n </p>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['targetModule'] ?? ''\"\n (ngModelChange)=\"onModuleChange($event)\"\n label=\"Module\"\n hint=\"Module whose records will be written by this explicit FlowPlus commit.\"\n [options]=\"moduleOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['operation'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('operation', $event)\"\n label=\"Operation\"\n hint=\"Write operation supported by the selected backend module schema.\"\n [options]=\"operationOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <mt-text-field\n class=\"mt-3 font-mono\"\n [ngModel]=\"config()['idempotencyKey'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:idempotencyKey')\"\n (ngModelChange)=\"onConfigFieldChange('idempotencyKey', $event)\"\n label=\"Idempotency key\"\n hint=\"Optional stable key used by the backend to prevent duplicate writes.\"\n />\n @if (selectedModuleFields().length) {\n <div class=\"mt-3 space-y-2\">\n <div class=\"fp-ae-section-title\">Module schema</div>\n <div class=\"grid gap-2 md:grid-cols-2\">\n @for (field of selectedModuleFields(); track field.key) {\n <div class=\"fp-ae-kv\">\n <span>{{ field.displayName ?? field.key }}</span>\n <strong>{{ field.viewType ?? \"Value\" }} @if (field.required) { * }</strong>\n </div>\n }\n </div>\n </div>\n }\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'mapping', rows: mappingRows(), keyLabel: 'Module field', valueLabel: 'Expression / value', addLabel: 'Add module field' }\" />\n </div>\n <div class=\"mt-3 flex gap-2\">\n <mt-button size=\"small\" severity=\"primary\" label=\"Validate mapping\" (onClick)=\"validateCommitMapping()\" />\n </div>\n @if (commitValidation(); as result) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n {{ result.isValid === false ? \"Mapping invalid\" : \"Mapping accepted by helper\" }}\n @for (issue of resultIssues(result); track issue) {\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">{{ issue }}</div>\n }\n </div>\n }\n @if (sectionInMain(\"credentials\")) {\n <div class=\"mt-3 rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"fp-ae-label mb-2\">Credential</div>\n <ng-container *ngTemplateOutlet=\"credentialSelector\" />\n </div>\n }\n </section>\n }\n }\n @case (\"WebhookResponse\") {\n @if (sectionInMain(\"webhookResponse\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Webhook response</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-number-field\n [ngModel]=\"numberValue(config()['statusCode']) ?? 200\"\n (ngModelChange)=\"onConfigFieldChange('statusCode', $event)\"\n label=\"Status code\"\n hint=\"HTTP status code sent back by the webhook response node.\"\n [min]=\"100\"\n [max]=\"599\"\n />\n <mt-select-field\n [ngModel]=\"config()['responseMode'] ?? 'json'\"\n (ngModelChange)=\"onConfigFieldChange('responseMode', $event)\"\n label=\"Response mode\"\n hint=\"How the webhook response body should be serialized.\"\n [options]=\"responseModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n <div class=\"mt-3\">\n <ng-container *ngTemplateOutlet=\"mapEditor; context: { objectKey: 'headers', rows: headerRows(), keyLabel: 'Header', valueLabel: 'Value', addLabel: 'Add header' }\" />\n </div>\n <mt-textarea-field\n class=\"mt-3 w-full font-mono\"\n [ngModel]=\"valueText(config()['body'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:body')\"\n (ngModelChange)=\"onConfigFieldChange('body', $event)\"\n label=\"Body\"\n hint=\"Body returned to the webhook caller. Expressions can use current execution data.\"\n rows=\"6\"\n />\n </section>\n }\n }\n @case (\"CallAutomation\") {\n @if (sectionInMain(\"callAutomation\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Subworkflow</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['targetAutomationId'] ?? ''\"\n (ngModelChange)=\"onSubworkflowTargetChange($event)\"\n label=\"Target automation\"\n hint=\"Backend-provided automation candidate. Tenant and recursion validation stay on the backend.\"\n [options]=\"subworkflowAutomationOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['revisionMode'] ?? 'Active'\"\n (ngModelChange)=\"onConfigFieldChange('revisionMode', $event)\"\n label=\"Revision mode\"\n hint=\"Choose which published or active child revision to call.\"\n [options]=\"revisionModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (config()['revisionMode'] === \"SpecificRevision\") {\n <mt-text-field\n [ngModel]=\"config()['specificRevisionId'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('specificRevisionId', $event)\"\n label=\"Specific revision id\"\n hint=\"Required only when revision mode is Specific revision.\"\n />\n }\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['waitForCompletion'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('waitForCompletion', $event === true)\"\n label=\"Wait for completion\"\n hint=\"When disabled, the parent continues after dispatch and backend records the child linkage.\"\n />\n </div>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['inputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:inputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('inputMappingJson', $event)\"\n label=\"Input mapping JSON\"\n hint=\"JSON object or expression map sent into the child automation input.\"\n rows=\"7\"\n />\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['outputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:outputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('outputMappingJson', $event)\"\n label=\"Output mapping JSON\"\n hint=\"JSON object or expression map for child output/status returned to the parent.\"\n rows=\"7\"\n />\n </div>\n </section>\n }\n }\n @case (\"Subworkflow\") {\n @if (sectionInMain(\"callAutomation\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Subworkflow</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['targetAutomationId'] ?? ''\"\n (ngModelChange)=\"onSubworkflowTargetChange($event)\"\n label=\"Target automation\"\n hint=\"Backend-provided automation candidate. Tenant and recursion validation stay on the backend.\"\n [options]=\"subworkflowAutomationOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"config()['revisionMode'] ?? 'Active'\"\n (ngModelChange)=\"onConfigFieldChange('revisionMode', $event)\"\n label=\"Revision mode\"\n hint=\"Choose which published or active child revision to call.\"\n [options]=\"revisionModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (config()['revisionMode'] === \"SpecificRevision\") {\n <mt-text-field\n [ngModel]=\"config()['specificRevisionId'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('specificRevisionId', $event)\"\n label=\"Specific revision id\"\n hint=\"Required only when revision mode is Specific revision.\"\n />\n }\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['waitForCompletion'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('waitForCompletion', $event === true)\"\n label=\"Wait for completion\"\n hint=\"When disabled, the parent continues after dispatch and backend records the child linkage.\"\n />\n </div>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['inputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:inputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('inputMappingJson', $event)\"\n label=\"Input mapping JSON\"\n hint=\"JSON object or expression map sent into the child automation input.\"\n rows=\"7\"\n />\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['outputMappingJson'] ?? '{}')\"\n (focusin)=\"setExpressionTarget('config:outputMappingJson')\"\n (ngModelChange)=\"onConfigFieldChange('outputMappingJson', $event)\"\n label=\"Output mapping JSON\"\n hint=\"JSON object or expression map for child output/status returned to the parent.\"\n rows=\"7\"\n />\n </div>\n </section>\n }\n }\n @case (\"ParallelStart\") {\n @if (sectionInMain(\"parallelStart\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"flex items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\">\n <div class=\"fp-ae-section-title !m-0\">Parallel start</div>\n <mt-button size=\"small\" variant=\"outlined\" icon=\"general.plus\" label=\"Add branch\" (onClick)=\"addParallelBranch()\" />\n </div>\n <div class=\"space-y-3 p-4\">\n @for (branch of parallelBranches(); track branch.key; let i = $index) {\n <div class=\"rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"grid gap-3 md:grid-cols-[160px_1fr_1fr_auto]\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"branch.key\"\n (ngModelChange)=\"updateParallelBranch(i, 'key', $event)\"\n label=\"Branch key\"\n hint=\"Persisted route key. Do not use array index names.\"\n />\n <mt-text-field\n [ngModel]=\"branch.label\"\n (ngModelChange)=\"updateParallelBranch(i, 'label', $event)\"\n label=\"Label\"\n hint=\"Visual branch label. Changing it does not change existing routes.\"\n />\n <mt-text-field\n [ngModel]=\"branch.description\"\n (ngModelChange)=\"updateParallelBranch(i, 'description', $event)\"\n label=\"Description\"\n hint=\"Optional branch note for reviewers.\"\n />\n <div class=\"flex items-end gap-1\">\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-up\" tooltip=\"Move up\" (onClick)=\"moveParallelBranch(i, -1)\" />\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-down\" tooltip=\"Move down\" (onClick)=\"moveParallelBranch(i, 1)\" />\n <mt-button size=\"small\" variant=\"outlined\" severity=\"danger\" icon=\"general.trash-01\" tooltip=\"Remove branch\" (onClick)=\"removeParallelBranch(i)\" />\n </div>\n </div>\n <div class=\"mt-3 flex flex-wrap items-center gap-1.5 text-[11px]\">\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-50 px-2 font-mono font-semibold text-(--p-text-color)\">{{ branch.key }}</span>\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-0 px-2 font-medium text-(--p-text-muted-color)\">{{ branch.routeCount }} connected route{{ branch.routeCount === 1 ? \"\" : \"s\" }}</span>\n </div>\n </div>\n }\n @if (parallelBranches().length === 0) {\n <p class=\"fp-ae-copy\">Add at least two stable branch keys. Backend validation blocks publish until branches are valid.</p>\n }\n </div>\n </section>\n }\n }\n @case (\"ParallelJoin\") {\n @if (sectionInMain(\"parallelJoin\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Parallel join</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <mt-select-field\n [ngModel]=\"config()['joinPolicy'] ?? 'All'\"\n (ngModelChange)=\"onConfigFieldChange('joinPolicy', $event)\"\n label=\"Join policy\"\n hint=\"Backend policy used to decide when branch wait is complete.\"\n [options]=\"joinPolicyOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n @if (config()['joinPolicy'] === \"Threshold\") {\n <mt-number-field\n [ngModel]=\"numberValue(config()['threshold'])\"\n (ngModelChange)=\"onConfigFieldChange('threshold', $event)\"\n label=\"Threshold\"\n hint=\"Minimum completed branch count required for Threshold policy.\"\n [min]=\"1\"\n />\n }\n <mt-select-field\n [ngModel]=\"config()['aggregationStrategy'] ?? 'MergeObjects'\"\n (ngModelChange)=\"onConfigFieldChange('aggregationStrategy', $event)\"\n label=\"Aggregation strategy\"\n hint=\"How backend joins branch outputs into the join node output.\"\n [options]=\"aggregationStrategyOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['outputTargetPath'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:outputTargetPath')\"\n (ngModelChange)=\"onConfigFieldChange('outputTargetPath', $event)\"\n label=\"Output target path\"\n hint=\"Optional context path where joined output should be written.\"\n />\n </div>\n </section>\n }\n }\n @case (\"Switch\") {\n @if (sectionInMain(\"switch\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Switch</div>\n <div class=\"grid gap-4\">\n <div class=\"grid items-end gap-3 md:grid-cols-[minmax(0,1fr)_minmax(280px,0.7fr)]\">\n <mt-select-field\n [ngModel]=\"config()['mode'] ?? 'value'\"\n (ngModelChange)=\"onConfigFieldChange('mode', $event)\"\n label=\"Mode\"\n hint=\"Backend evaluation mode: expression, rules, or source-value matching.\"\n [options]=\"switchModeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-toggle-field\n class=\"self-end pb-1\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['firstMatch'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('firstMatch', $event === true)\"\n label=\"First match\"\n hint=\"Use the first matching case unless backend explicitly supports multi-match.\"\n />\n </div>\n @if (showSwitchSourceValue() || showSwitchExpression()) {\n <div class=\"grid gap-3 md:grid-cols-2\">\n @if (showSwitchSourceValue()) {\n <mt-text-field\n [class]=\"showSwitchExpression() ? 'font-mono' : 'font-mono md:col-span-2'\"\n [ngModel]=\"config()['sourceValue'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:sourceValue')\"\n (ngModelChange)=\"onConfigFieldChange('sourceValue', $event)\"\n label=\"Source value\"\n hint=\"Value or expression used for value matching.\"\n />\n }\n @if (showSwitchExpression()) {\n <mt-text-field\n [class]=\"showSwitchSourceValue() ? 'font-mono' : 'font-mono md:col-span-2'\"\n [ngModel]=\"config()['expression'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:expression')\"\n (ngModelChange)=\"onConfigFieldChange('expression', $event)\"\n label=\"Expression\"\n hint=\"Expression evaluated when mode is expression or rules.\"\n />\n }\n </div>\n }\n <div class=\"grid items-end gap-3 md:grid-cols-[minmax(0,1fr)_minmax(280px,0.7fr)]\">\n @if (showSwitchDefaultOutputKey()) {\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"config()['defaultOutputKey'] ?? 'default'\"\n (ngModelChange)=\"onConfigFieldChange('defaultOutputKey', $event)\"\n label=\"Default output key\"\n hint=\"Stable default route key used when no case matches.\"\n />\n }\n <mt-toggle-field\n [class]=\"showSwitchDefaultOutputKey() ? 'self-end pb-1' : 'self-end pb-1 md:col-span-2'\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"config()['includeDefaultOutput'] !== false\"\n (ngModelChange)=\"onConfigFieldChange('includeDefaultOutput', $event === true)\"\n label=\"Include default output\"\n hint=\"Expose the default route output key in the canvas.\"\n />\n </div>\n </div>\n </section>\n <section class=\"fp-ae-panel\">\n <div class=\"flex items-center justify-between gap-3 border-b border-surface-200 bg-surface-50 px-4 py-3\">\n <div class=\"fp-ae-section-title !m-0\">Switch cases</div>\n <mt-button size=\"small\" variant=\"outlined\" icon=\"general.plus\" label=\"Add case\" (onClick)=\"addSwitchCase()\" />\n </div>\n <div class=\"space-y-3 p-4\">\n @for (item of switchCases(); track item.key; let i = $index) {\n <div class=\"rounded-md border border-surface-200 bg-surface-0 p-3\">\n <div class=\"grid gap-3 md:grid-cols-[160px_1fr_1fr_auto]\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"item.key\"\n (ngModelChange)=\"updateSwitchCase(i, 'key', $event)\"\n label=\"Case key\"\n hint=\"Persisted stable key. The route output becomes case_key.\"\n />\n <mt-text-field\n [ngModel]=\"item.label\"\n (ngModelChange)=\"updateSwitchCase(i, 'label', $event)\"\n label=\"Label\"\n hint=\"Visual case label. Routes stay attached to the case key.\"\n />\n <mt-text-field\n [ngModel]=\"item.value\"\n (focusin)=\"setExpressionTarget('config:cases:' + item.key + ':value')\"\n (ngModelChange)=\"updateSwitchCase(i, 'value', $event)\"\n label=\"Value\"\n hint=\"Expected value for value matching.\"\n />\n <div class=\"flex items-end gap-1\">\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-up\" tooltip=\"Move up\" (onClick)=\"moveSwitchCase(i, -1)\" />\n <mt-button size=\"small\" variant=\"outlined\" icon=\"arrow.arrow-down\" tooltip=\"Move down\" (onClick)=\"moveSwitchCase(i, 1)\" />\n <mt-button size=\"small\" variant=\"outlined\" severity=\"danger\" icon=\"general.trash-01\" tooltip=\"Remove case\" (onClick)=\"removeSwitchCase(i)\" />\n </div>\n </div>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"item.condition\"\n (focusin)=\"setExpressionTarget('config:cases:' + item.key + ':condition')\"\n (ngModelChange)=\"updateSwitchCase(i, 'condition', $event)\"\n label=\"Condition\"\n hint=\"Optional condition for rule matching.\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"item.expression\"\n (focusin)=\"setExpressionTarget('config:cases:' + item.key + ':expression')\"\n (ngModelChange)=\"updateSwitchCase(i, 'expression', $event)\"\n label=\"Case expression\"\n hint=\"Optional expression for this case.\"\n />\n </div>\n <div class=\"mt-3 flex flex-wrap items-center gap-1.5 text-[11px]\">\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-50 px-2 font-mono font-semibold text-(--p-text-color)\">{{ item.routeKey }}</span>\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-0 px-2 font-medium text-(--p-text-muted-color)\">{{ item.routeCount }} connected route{{ item.routeCount === 1 ? \"\" : \"s\" }}</span>\n <span class=\"inline-flex h-7 items-center rounded-md border border-surface-200 bg-surface-0 px-2 font-medium text-(--p-text-muted-color)\">Visual order {{ i + 1 }}</span>\n </div>\n </div>\n }\n @if (switchCases().length === 0) {\n <p class=\"fp-ae-copy\">Add at least one stable case key. Backend validation points routes at missing case keys if a case is deleted.</p>\n }\n </div>\n </section>\n }\n }\n @case (\"Stop\") {\n @if (sectionInMain(\"stop\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">End execution</div>\n @if (supportsConfigKey('status') || supportsConfigKey('message') || supportsConfigKey('output')) {\n <div class=\"grid gap-3 md:grid-cols-2\">\n @if (supportsConfigKey('status')) {\n <mt-text-field\n [ngModel]=\"config()['status'] ?? ''\"\n (ngModelChange)=\"onConfigFieldChange('status', $event)\"\n label=\"Result status\"\n hint=\"Terminal status emitted by the backend stop node.\"\n />\n }\n @if (supportsConfigKey('message')) {\n <mt-text-field\n [ngModel]=\"config()['message'] ?? ''\"\n (focusin)=\"setExpressionTarget('config:message')\"\n (ngModelChange)=\"onConfigFieldChange('message', $event)\"\n label=\"Message\"\n hint=\"Optional message or reason saved with the terminal result.\"\n />\n }\n @if (supportsConfigKey('output')) {\n <mt-textarea-field\n class=\"w-full font-mono md:col-span-2\"\n [ngModel]=\"valueText(config()['output'] ?? '')\"\n (focusin)=\"setExpressionTarget('config:output')\"\n (ngModelChange)=\"onConfigFieldChange('output', $event)\"\n label=\"Output payload\"\n hint=\"Terminal output payload when exposed by the backend schema.\"\n rows=\"5\"\n />\n }\n </div>\n } @else {\n <p class=\"fp-ae-copy\">\n This terminal node has no backend-exposed settings. It ends execution when reached.\n </p>\n }\n </section>\n }\n }\n }\n\n @if (sectionInMain(\"backendSchema\") && step() && editorType() !== \"HumanApproval\") {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Output data</div>\n @if (outputFields().length > 0) {\n <div class=\"grid gap-2 md:grid-cols-2\">\n @for (field of outputFields(); track field.key) {\n <div class=\"fp-ae-kv\">\n <span>{{ field.label }}</span>\n <strong>{{ field.type }}</strong>\n </div>\n }\n </div>\n }\n @if (routeOutputKeys().length > 0) {\n <div class=\"mt-3 fp-ae-label mb-2\">Route outputs</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n </div>\n }\n </section>\n }\n\n @if (sectionInAdvanced(\"mapping\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Request and result mapping</div>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <div>\n <div class=\"fp-ae-label mb-2\">Request data</div>\n @for (row of inputMappingRows(); track row.key) {\n <label class=\"mb-2 block space-y-1\">\n <span class=\"text-[11px] text-(--p-text-muted-color)\">{{ row.key }}</span>\n <mt-text-field\n [ngModel]=\"valueText(row.value)\"\n (focusin)=\"setExpressionTarget('config:inputMapping:' + row.key)\"\n (ngModelChange)=\"updateJsonField('inputMappingJson', row.key, $event)\"\n hint=\"Expression or literal value mapped into this node input.\"\n />\n </label>\n }\n </div>\n <div>\n <div class=\"fp-ae-label mb-2\">Output data</div>\n @for (row of outputMappingRows(); track row.key) {\n <label class=\"mb-2 block space-y-1\">\n <span class=\"text-[11px] text-(--p-text-muted-color)\">{{ row.key }}</span>\n <mt-text-field\n [ngModel]=\"valueText(row.value)\"\n (focusin)=\"setExpressionTarget('config:outputMapping:' + row.key)\"\n (ngModelChange)=\"updateJsonField('outputMappingJson', row.key, $event)\"\n hint=\"Expression or literal value mapped from this node output.\"\n />\n </label>\n }\n </div>\n </div>\n @if (\n sectionHasAdvancedEmptyState(\"mapping\") &&\n inputMappingRows().length === 0 &&\n outputMappingRows().length === 0\n ) {\n <p class=\"fp-ae-copy mt-3\">\n This node is using backend default request and output data. Add mappings only when this step needs a custom payload.\n </p>\n }\n </section>\n }\n\n @if (sectionInAdvanced(\"credentials\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Auth and credentials</div>\n <ng-container *ngTemplateOutlet=\"credentialSelector\" />\n </section>\n }\n\n @if (sectionInAdvanced(\"policy\")) {\n <section class=\"fp-ae-panel\">\n <div class=\"fp-ae-section-title\">Retry, timeout, error policy</div>\n <div class=\"grid gap-3 md:grid-cols-3\">\n <mt-number-field\n [ngModel]=\"numberValue(policyObject('timeoutPolicyJson')['timeoutSeconds'])\"\n (ngModelChange)=\"updateJsonField('timeoutPolicyJson', 'timeoutSeconds', $event)\"\n label=\"Timeout seconds\"\n hint=\"Maximum runtime before the backend times out this node.\"\n [min]=\"0\"\n />\n <mt-number-field\n [ngModel]=\"numberValue(policyObject('retryPolicyJson')['maxAttempts'])\"\n (ngModelChange)=\"updateJsonField('retryPolicyJson', 'maxAttempts', $event)\"\n label=\"Max attempts\"\n hint=\"Maximum retry attempts after retryable failures.\"\n [min]=\"0\"\n />\n <mt-select-field\n [ngModel]=\"policyObject('errorPolicyJson')['onFailure'] ?? ''\"\n (ngModelChange)=\"updateJsonField('errorPolicyJson', 'onFailure', $event)\"\n label=\"On failure\"\n hint=\"Backend error policy to apply when this node fails.\"\n [options]=\"errorPolicyOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n @if (sectionHasAdvancedEmptyState(\"policy\")) {\n <p class=\"fp-ae-copy mt-3\">\n Backend defaults apply until you override a timeout, retry, or error policy here.\n </p>\n }\n </section>\n }\n\n @if (sectionInAdvanced(\"triggerContext\") && trigger()) {\n <details class=\"fp-ae-panel\" open>\n <summary class=\"fp-ae-section-title cursor-pointer select-none\">\n Trigger payload and context\n </summary>\n <p class=\"fp-ae-copy mt-3\">\n Trigger payload/context is documentation for expressions only. Triggers\n do not edit node input or output mappings here.\n </p>\n <div class=\"grid gap-3 md:grid-cols-2\">\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Config schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(configSchema()) }}</pre>\n </details>\n @if (hasTriggerPayloadSchema() && triggerPayloadSchema(); as schema) {\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Payload schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(schema) }}</pre>\n </details>\n }\n @if (authPolicySchema()) {\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Auth policy schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(authPolicySchema()) }}</pre>\n </details>\n }\n @if (triggerPayloadSample(); as sample) {\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Payload or request sample</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(sample) }}</pre>\n </details>\n }\n </div>\n </details>\n }\n\n @if (sectionInAdvanced(\"backendSchema\") && step()) {\n <details class=\"fp-ae-panel\" open>\n <summary class=\"fp-ae-section-title cursor-pointer select-none\">\n Backend schemas and outputs\n </summary>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Config schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(configSchema()) }}</pre>\n </details>\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Input schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(inputSchema()) }}</pre>\n </details>\n <details class=\"rounded-lg border border-(--p-content-border-color)\">\n <summary class=\"cursor-pointer px-3 py-2 text-[12px] font-semibold\">Output schema</summary>\n <pre class=\"fp-ae-code\">{{ schemaText(outputSchema()) }}</pre>\n </details>\n <div class=\"rounded-lg border border-(--p-content-border-color) p-3\">\n <div class=\"fp-ae-label mb-2\">Route output keys</div>\n <div class=\"flex flex-wrap gap-1.5\">\n @for (key of routeOutputKeys(); track key) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 font-mono text-[11px]\">{{ key }}</span>\n }\n @if (routeOutputKeys().length === 0) {\n <span class=\"text-[12px] text-(--p-text-muted-color)\">No outgoing route keys</span>\n }\n </div>\n </div>\n </div>\n </details>\n }\n\n @if (sectionInAdvanced(\"schemaFields\") && configRows().length > 0) {\n <details class=\"fp-ae-panel\" open>\n <summary class=\"fp-ae-section-title cursor-pointer select-none\">\n Backend schema fields\n </summary>\n <div class=\"mt-3 grid gap-3 md:grid-cols-2\">\n @for (field of configRows(); track field.key) {\n <div\n class=\"space-y-1.5\"\n [class.md:col-span-2]=\"field.type === 'object' || field.type === 'array'\"\n >\n @if (field.enumValues.length) {\n <mt-select-field\n [ngModel]=\"fieldValue(field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n [options]=\"enumOptions(field.enumValues)\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n } @else if (field.type === \"boolean\") {\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"!!fieldValue(field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event === true)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n />\n } @else if (field.type === \"number\") {\n <mt-number-field\n [ngModel]=\"numberValue(fieldValue(field.key))\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n />\n } @else if (field.type === \"date\") {\n <mt-date-field\n [ngModel]=\"fieldValue(field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n [showTime]=\"field.format !== 'date'\"\n [showClear]=\"true\"\n [pInputs]=\"dateTimePickerInputs\"\n />\n } @else if (field.type === \"object\" || field.type === \"array\") {\n <mt-textarea-field\n class=\"w-full font-mono\"\n [ngModel]=\"valueText(fieldValue(field.key))\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n rows=\"5\"\n />\n } @else {\n <mt-text-field\n [ngModel]=\"fieldText(field.key)\"\n (focusin)=\"field.expressionEnabled && setExpressionTarget('config:' + field.key)\"\n (ngModelChange)=\"onConfigFieldChange(field.key, $event)\"\n [label]=\"field.label + (field.required ? ' *' : '')\"\n [hint]=\"field.description ?? ''\"\n />\n }\n </div>\n }\n </div>\n </details>\n }\n </div>\n</div>\n\n<ng-template #credentialSelector>\n @if (credentials().length) {\n <div class=\"space-y-2\">\n @for (credential of credentials(); track credential.credentialRef) {\n <label class=\"flex items-center justify-between gap-3 rounded-lg border border-(--p-content-border-color) bg-(--p-surface-50) px-3 py-2\">\n <span class=\"min-w-0\">\n <span class=\"block truncate text-[12px] font-semibold\">{{ credential.displayName ?? credential.credentialRef }}</span>\n <span class=\"block truncate font-mono text-[11px] text-(--p-text-muted-color)\">\n {{ credential.credentialRef }} / {{ credential.status ?? (credential.resolved ? \"Resolved\" : \"Unresolved\") }}\n </span>\n </span>\n <span class=\"flex items-center gap-2\">\n <mt-button\n size=\"small\"\n variant=\"text\"\n label=\"Test\"\n (onClick)=\"testCredential(credential.credentialRef)\"\n />\n <mt-toggle-field\n size=\"small\"\n [ngModel]=\"credentialRefs().includes(credential.credentialRef)\"\n (ngModelChange)=\"toggleCredential(credential.credentialRef, $event === true)\"\n hint=\"Attach or detach this backend credential reference. Masked secrets are preserved.\"\n />\n </span>\n </label>\n }\n </div>\n } @else if (sectionHasAdvancedEmptyState(\"credentials\")) {\n <p class=\"fp-ae-copy\">\n The backend helper did not return credential choices for this node. Existing masked references remain preserved in the saved JSON.\n </p>\n }\n @if (credentialTest(); as test) {\n <div class=\"mt-3 rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px]\">\n {{ test.status ?? (test.succeeded ? \"Succeeded\" : \"Placeholder\") }}\n @if (test.message) { <span>- {{ test.message }}</span> }\n </div>\n }\n</ng-template>\n\n<ng-template\n #mapEditor\n let-objectKey=\"objectKey\"\n let-rows=\"rows\"\n let-keyLabel=\"keyLabel\"\n let-valueLabel=\"valueLabel\"\n let-addLabel=\"addLabel\"\n>\n <div class=\"space-y-2\">\n @for (row of rows; track row.key) {\n <div class=\"grid gap-2 md:grid-cols-[180px_1fr_auto]\">\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"row.key\"\n (ngModelChange)=\"updateObjectRow(objectKey, row.key, $event, row.value)\"\n [label]=\"keyLabel\"\n hint=\"Configuration key saved to the backend JSON object.\"\n />\n <mt-text-field\n class=\"font-mono\"\n [ngModel]=\"valueText(row.value)\"\n (focusin)=\"setExpressionTarget('config:' + objectKey + ':' + row.key)\"\n (ngModelChange)=\"updateObjectRow(objectKey, row.key, row.key, $event)\"\n [label]=\"valueLabel\"\n hint=\"Configuration value. Use expressions when this field should resolve at runtime.\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n label=\"Remove\"\n (onClick)=\"removeObjectRow(objectKey, row.key)\"\n />\n </div>\n }\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n [label]=\"addLabel || ('Add ' + keyLabel)\"\n (onClick)=\"addObjectRow(objectKey)\"\n />\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: DateField, selector: "mt-date-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "showIcon", "showClear", "showTime", "pInputs", "required"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "directive", type: DropDataDirective, selector: "[fpDropData]", inputs: ["fpDropAutoInsert"], outputs: ["dataDrop"] }] });
|
|
13811
13811
|
}
|
|
13812
13812
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AutomationSmartEditorComponent, decorators: [{
|
|
13813
13813
|
type: Component,
|
|
@@ -14416,7 +14416,7 @@ class WorkflowSettingsInspectorComponent {
|
|
|
14416
14416
|
});
|
|
14417
14417
|
}
|
|
14418
14418
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowSettingsInspectorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
14419
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowSettingsInspectorComponent, isStandalone: true, selector: "fp-workflow-settings-inspector", outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Automation metadata\n </h3>\n <div class=\"grid gap-3 p-4 md:grid-cols-2\">\n <mt-text-field\n [ngModel]=\"nameEn()\"\n (ngModelChange)=\"setName('en', $event)\"\n [label]=\"'flowplus.create.nameEn' | transloco\"\n />\n <mt-text-field\n [ngModel]=\"nameAr()\"\n (ngModelChange)=\"setName('ar', $event)\"\n [label]=\"'flowplus.create.nameAr' | transloco\"\n [pInputs]=\"{ dir: 'rtl' }\"\n />\n <mt-textarea-field\n class=\"md:col-span-2\"\n [ngModel]=\"descEn()\"\n (ngModelChange)=\"setDesc('en', $event)\"\n [label]=\"'flowplus.create.descEn' | transloco\"\n rows=\"3\"\n />\n\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Automation id\n </div>\n <div class=\"mt-1 font-mono text-[12px] text-(--p-text-color)\">\n {{ automationId() ?? \"-\" }}\n </div>\n </div>\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Owner and project\n </div>\n <div class=\"mt-1 text-[12px] text-(--p-text-color)\">\n {{ ownerProjectSummary().join(\" / \") || \"Not returned by backend\" }}\n </div>\n </div>\n </div>\n</section>\n\n<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Publish and activation\n </h3>\n <div class=\"space-y-4 p-4\">\n <div class=\"grid gap-2 md:grid-cols-3\">\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Current status\n </div>\n <div class=\"mt-1 text-[12px] font-semibold text-(--p-text-color)\">\n {{ store.workflow()?.status ?? \"Draft\" }}\n </div>\n </div>\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Active revision\n </div>\n <div class=\"mt-1 font-mono text-[12px] text-(--p-text-color)\">\n {{ activeRevisionId() ?? \"-\" }}\n </div>\n </div>\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Latest draft revision\n </div>\n <div class=\"mt-1 font-mono text-[12px] text-(--p-text-color)\">\n {{ latestRevisionId() ?? \"-\" }}\n </div>\n </div>\n </div>\n\n @if (revisionsError()) {\n <div\n class=\"rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-3 py-2 text-[12px] leading-5 text-(--p-text-color)\"\n >\n {{ revisionsError() }}\n </div>\n }\n @if (operationMessage()) {\n <div\n class=\"rounded-md border border-emerald-500/25 bg-emerald-500/10 px-3 py-2 text-[12px] leading-5 text-emerald-700\"\n >\n {{ operationMessage() }}\n </div>\n }\n @if (operationError()) {\n <div\n class=\"rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-3 py-2 text-[12px] leading-5 text-(--p-text-color)\"\n >\n {{ operationError() }}\n </div>\n }\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Validate\"\n [disabled]=\"operationBusy()\"\n (onClick)=\"validateDraft()\"\n />\n <mt-button\n size=\"small\"\n severity=\"primary\"\n label=\"Publish\"\n [disabled]=\"operationBusy()\"\n (onClick)=\"publishDraft()\"\n />\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Deactivate\"\n [disabled]=\"operationBusy() || !activeRevisionId()\"\n (onClick)=\"deactivateAutomation()\"\n />\n <mt-toggle-field\n class=\"min-w-[15rem]\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"activateAfterPublish()\"\n (ngModelChange)=\"activateAfterPublish.set($event === true)\"\n label=\"Activate returned revision\"\n />\n </div>\n\n @if (validationReport(); as report) {\n <div class=\"rounded-md border border-surface-200 bg-surface-50 p-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-[12px] font-semibold text-(--p-text-color)\">\n Validation result\n </span>\n <span\n class=\"rounded-full px-2 py-0.5 text-[10.5px] font-semibold\"\n [class.bg-emerald-500]=\"report.canPublish\"\n [class.text-white]=\"report.canPublish\"\n [class.bg-[rgb(var(--fp-warning))]/20]=\"!report.canPublish\"\n [class.text-(--p-text-color)]=\"!report.canPublish\"\n >\n {{ report.canPublish ? \"Can publish\" : \"Blocked\" }}\n </span>\n </div>\n @if (validationIssues().length > 0) {\n <ul class=\"mt-2 list-none space-y-1 p-0\">\n @for (issue of validationIssues(); track issue.code + issue.message + issue.fieldPath) {\n <li\n class=\"rounded-md bg-(--p-content-background) px-2 py-1.5 text-[11.5px] leading-5 text-(--p-text-muted-color)\"\n >\n <span class=\"font-semibold text-(--p-text-color)\">\n {{ issue.severity }}\n </span>\n <span> / {{ issue.targetType }}</span>\n @if (issue.targetKey) {\n <code class=\"ms-1 font-mono\">{{ issue.targetKey }}</code>\n }\n <span> - {{ issue.message }}</span>\n </li>\n }\n </ul>\n } @else {\n <p class=\"m-0 mt-2 text-[11.5px] text-(--p-text-muted-color)\">\n No validation issues returned by the backend.\n </p>\n }\n </div>\n }\n </div>\n</section>\n\n<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Revisions\n </h3>\n <div class=\"space-y-4 p-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n Immutable backend revisions, active revision state, activation, and\n rollback.\n </p>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n label=\"Refresh\"\n [disabled]=\"revisionsLoading() || operationBusy()\"\n (onClick)=\"refreshRevisions()\"\n />\n </div>\n\n @if (revisions().length > 0) {\n <ul class=\"list-none space-y-1.5 p-0\">\n @for (revision of revisions(); track revisionId(revision)) {\n <li\n class=\"rounded-md border border-surface-200 bg-surface-50 px-3 py-2\"\n [style.borderColor]=\"\n isActiveRevision(revision) ? 'var(--p-primary-color)' : null\n \"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"font-mono text-[12px] font-semibold text-(--p-text-color)\">\n {{ revisionLabel(revision) }}\n </span>\n <span class=\"text-[10.5px] text-(--p-text-muted-color)\">\n {{ revisionDate(revision) }}\n </span>\n </div>\n <div class=\"mt-1 text-[11.5px] text-(--p-text-muted-color)\">\n {{ revision.changeSummary || \"No change summary\" }}\n @if (revision.publishedBy || revision.createdBy) {\n <span> / {{ revision.publishedBy ?? revision.createdBy }}</span>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div\n class=\"rounded-md border border-dashed border-surface-200 px-3 py-3 text-center text-[12px] text-(--p-text-muted-color)\"\n >\n No revisions returned yet. Publish the draft to create the first\n immutable revision.\n </div>\n }\n\n <div class=\"grid gap-2 md:grid-cols-[1fr_auto_auto]\">\n <mt-select-field\n [ngModel]=\"selectedRevisionId()\"\n (ngModelChange)=\"selectedRevisionId.set($event)\"\n label=\"Selected revision\"\n [options]=\"revisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"secondary\"\n label=\"Activate\"\n [disabled]=\"operationBusy() || !selectedRevisionId()\"\n (onClick)=\"activateSelectedRevision()\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"danger\"\n label=\"Rollback\"\n [disabled]=\"operationBusy() || !canRollback()\"\n (onClick)=\"rollbackSelectedRevision()\"\n />\n </div>\n </div>\n</section>\n\n<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Import, export, and diff\n </h3>\n <div class=\"space-y-4 p-4\">\n <div class=\"space-y-2 rounded-md border border-surface-200 bg-surface-50 p-3\">\n <div class=\"grid gap-2 md:grid-cols-[1fr_1fr_auto]\">\n <mt-select-field\n [ngModel]=\"leftRevisionId()\"\n (ngModelChange)=\"leftRevisionId.set($event)\"\n label=\"Left revision\"\n [options]=\"revisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"rightRevisionId()\"\n (ngModelChange)=\"rightRevisionId.set($event)\"\n label=\"Right revision\"\n [options]=\"revisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"secondary\"\n label=\"Compare\"\n [disabled]=\"operationBusy() || !leftRevisionId() || !rightRevisionId()\"\n (onClick)=\"diffSelectedRevisions()\"\n />\n </div>\n @if (diffResult()) {\n @if (diffEntries().length > 0) {\n <ul class=\"list-none space-y-1 p-0\">\n @for (entry of diffEntries(); track entry.group + entry.value) {\n <li class=\"flex items-center gap-2 rounded-md bg-(--p-content-background) px-2 py-1.5 text-[11.5px]\">\n <span class=\"shrink-0 rounded bg-surface-100 px-1.5 py-0.5 font-semibold text-(--p-text-muted-color)\">\n {{ entry.group }}\n </span>\n <code class=\"min-w-0 truncate font-mono text-(--p-text-color)\">\n {{ entry.value }}\n </code>\n </li>\n }\n </ul>\n } @else {\n <p class=\"m-0 text-[11.5px] text-(--p-text-muted-color)\">\n Backend diff returned no changes for the selected revisions.\n </p>\n }\n }\n </div>\n\n <div class=\"grid gap-3 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-[12px] font-semibold text-(--p-text-color)\">\n Export\n </span>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Load export\"\n [disabled]=\"operationBusy()\"\n (onClick)=\"exportAutomation()\"\n />\n </div>\n <mt-textarea-field\n [ngModel]=\"exportText()\"\n [readonly]=\"true\"\n rows=\"7\"\n class=\"font-mono\"\n placeholder=\"Backend export JSON appears here. Credential refs are placeholders only.\"\n />\n </div>\n\n <div class=\"space-y-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-[12px] font-semibold text-(--p-text-color)\">\n Import\n </span>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Import draft\"\n [disabled]=\"operationBusy() || !importText().trim()\"\n (onClick)=\"importAutomation()\"\n />\n </div>\n <mt-textarea-field\n [ngModel]=\"importText()\"\n (ngModelChange)=\"importText.set($event ?? '')\"\n rows=\"7\"\n class=\"font-mono\"\n placeholder=\"Paste AutomationExportDefinition JSON. Imported drafts must be validated before publish.\"\n />\n </div>\n </div>\n </div>\n</section>\n\n<details\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <summary\n class=\"m-0 cursor-pointer list-none border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Trigger configuration\n </summary>\n <div class=\"space-y-3 p-4\">\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n Add and edit triggers from the canvas. This settings drawer only summarizes\n trigger state and backend setup hints.\n </p>\n <ul class=\"m-0 flex list-none flex-col gap-1.5 p-0\">\n @for (t of store.triggers(); track t.id) {\n <li\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md bg-surface-50 p-2\"\n >\n <div class=\"min-w-0\">\n <div class=\"truncate text-[12px] font-semibold text-(--p-text-color)\">\n {{ t.name?.[\"en\"] || t.type }}\n </div>\n <div class=\"text-[11.5px] text-(--p-text-muted-color)\">\n {{ triggerTypeLabel(t.type) }}\n <span>-</span>\n {{\n t.enabled\n ? (\"flowplus.inspector.workflow.triggerEnabled\" | transloco)\n : (\"flowplus.common.disabled\" | transloco)\n }}\n </div>\n </div>\n <span\n class=\"shrink-0 rounded-full border border-surface-200 px-2 py-0.5 text-[10.5px] font-medium text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.trigger.node.addFromCanvas\" | transloco }}\n </span>\n @if (isWebhookTrigger(t.type)) {\n <div class=\"flex items-center gap-2 text-[11.5px] text-(--p-text-muted-color)\">\n <code class=\"font-mono\">{{ t.webhookKey ?? \"-\" }}</code>\n <span> / {{ t.expectedHttpMethod ?? \"POST\" }}</span>\n <span>\n /\n {{\n t.secretConfigured\n ? (\"flowplus.inspector.workflow.secretConfigured\" | transloco)\n : (\"flowplus.inspector.workflow.secretMissing\" | transloco)\n }}\n </span>\n </div>\n }\n </li>\n }\n </ul>\n @if ((store.triggers()?.length ?? 0) === 0) {\n <div\n class=\"rounded-md border border-dashed border-surface-200 px-3 py-3 text-center text-[12px] text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.trigger.node.addFromCanvas\" | transloco }}\n </div>\n }\n </div>\n</details>\n\n<details\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <summary\n class=\"m-0 cursor-pointer list-none border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Resources and variables\n </summary>\n <div class=\"grid gap-4 p-4 lg:grid-cols-2\">\n <div>\n <h4 class=\"m-0 mb-2 text-[12px] font-semibold text-(--p-text-color)\">\n Resources\n </h4>\n <ul class=\"m-0 flex list-none flex-col gap-1.5 p-0\">\n @for (r of store.resources(); track r.key) {\n <li class=\"flex items-center gap-2 rounded-md bg-surface-50 p-2\">\n <strong class=\"text-[12px]\">{{ r.key }}</strong>\n <span class=\"text-[11.5px] text-(--p-text-muted-color)\">\n {{ r.entityType }}\n </span>\n <mt-number-field\n class=\"w-[120px]\"\n [ngModel]=\"r.entityId\"\n (ngModelChange)=\"bindResource(r.key, +($event ?? 0))\"\n [min]=\"0\"\n />\n @if (r.required) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(var(--fp-warning))]/18 px-2 py-0.5 text-[10.5px] font-medium text-[rgb(146_91_0)]\"\n >\n Required\n </span>\n }\n </li>\n }\n </ul>\n @if ((store.resources()?.length ?? 0) === 0) {\n <p class=\"m-0 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.inspector.workflow.noResources\" | transloco }}\n </p>\n }\n </div>\n\n <div>\n <h4 class=\"m-0 mb-2 text-[12px] font-semibold text-(--p-text-color)\">\n Variables\n </h4>\n <ul class=\"m-0 flex list-none flex-col gap-1 p-0\">\n @for (v of store.variables(); track v.key) {\n <li class=\"flex items-center gap-2 rounded-md bg-surface-50 px-2 py-1.5\">\n <code class=\"font-mono text-[12px]\">{{ v.key }}</code>\n <span class=\"text-[12px] text-(--p-text-muted-color)\">\n {{ v.type }}\n </span>\n </li>\n }\n </ul>\n </div>\n </div>\n</details>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "description", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "hint", "description", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
14419
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowSettingsInspectorComponent, isStandalone: true, selector: "fp-workflow-settings-inspector", outputs: { change: "change" }, host: { classAttribute: "block space-y-4" }, ngImport: i0, template: "<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Automation metadata\n </h3>\n <div class=\"grid gap-3 p-4 md:grid-cols-2\">\n <mt-text-field\n [ngModel]=\"nameEn()\"\n (ngModelChange)=\"setName('en', $event)\"\n [label]=\"'flowplus.create.nameEn' | transloco\"\n />\n <mt-text-field\n [ngModel]=\"nameAr()\"\n (ngModelChange)=\"setName('ar', $event)\"\n [label]=\"'flowplus.create.nameAr' | transloco\"\n [pInputs]=\"{ dir: 'rtl' }\"\n />\n <mt-textarea-field\n class=\"md:col-span-2\"\n [ngModel]=\"descEn()\"\n (ngModelChange)=\"setDesc('en', $event)\"\n [label]=\"'flowplus.create.descEn' | transloco\"\n rows=\"3\"\n />\n\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Automation id\n </div>\n <div class=\"mt-1 font-mono text-[12px] text-(--p-text-color)\">\n {{ automationId() ?? \"-\" }}\n </div>\n </div>\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Owner and project\n </div>\n <div class=\"mt-1 text-[12px] text-(--p-text-color)\">\n {{ ownerProjectSummary().join(\" / \") || \"Not returned by backend\" }}\n </div>\n </div>\n </div>\n</section>\n\n<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Publish and activation\n </h3>\n <div class=\"space-y-4 p-4\">\n <div class=\"grid gap-2 md:grid-cols-3\">\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Current status\n </div>\n <div class=\"mt-1 text-[12px] font-semibold text-(--p-text-color)\">\n {{ store.workflow()?.status ?? \"Draft\" }}\n </div>\n </div>\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Active revision\n </div>\n <div class=\"mt-1 font-mono text-[12px] text-(--p-text-color)\">\n {{ activeRevisionId() ?? \"-\" }}\n </div>\n </div>\n <div class=\"rounded-md bg-surface-50 px-3 py-2\">\n <div class=\"text-[11.5px] font-semibold text-(--p-text-muted-color)\">\n Latest draft revision\n </div>\n <div class=\"mt-1 font-mono text-[12px] text-(--p-text-color)\">\n {{ latestRevisionId() ?? \"-\" }}\n </div>\n </div>\n </div>\n\n @if (revisionsError()) {\n <div\n class=\"rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-3 py-2 text-[12px] leading-5 text-(--p-text-color)\"\n >\n {{ revisionsError() }}\n </div>\n }\n @if (operationMessage()) {\n <div\n class=\"rounded-md border border-emerald-500/25 bg-emerald-500/10 px-3 py-2 text-[12px] leading-5 text-emerald-700\"\n >\n {{ operationMessage() }}\n </div>\n }\n @if (operationError()) {\n <div\n class=\"rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-3 py-2 text-[12px] leading-5 text-(--p-text-color)\"\n >\n {{ operationError() }}\n </div>\n }\n\n <div class=\"flex flex-wrap items-center gap-2\">\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Validate\"\n [disabled]=\"operationBusy()\"\n (onClick)=\"validateDraft()\"\n />\n <mt-button\n size=\"small\"\n severity=\"primary\"\n label=\"Publish\"\n [disabled]=\"operationBusy()\"\n (onClick)=\"publishDraft()\"\n />\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Deactivate\"\n [disabled]=\"operationBusy() || !activeRevisionId()\"\n (onClick)=\"deactivateAutomation()\"\n />\n <mt-toggle-field\n class=\"min-w-[15rem]\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"activateAfterPublish()\"\n (ngModelChange)=\"activateAfterPublish.set($event === true)\"\n label=\"Activate returned revision\"\n />\n </div>\n\n @if (validationReport(); as report) {\n <div class=\"rounded-md border border-surface-200 bg-surface-50 p-3\">\n <div class=\"flex items-center justify-between gap-3\">\n <span class=\"text-[12px] font-semibold text-(--p-text-color)\">\n Validation result\n </span>\n <span\n class=\"rounded-full px-2 py-0.5 text-[10.5px] font-semibold\"\n [class.bg-emerald-500]=\"report.canPublish\"\n [class.text-white]=\"report.canPublish\"\n [class.bg-[rgb(var(--fp-warning))]/20]=\"!report.canPublish\"\n [class.text-(--p-text-color)]=\"!report.canPublish\"\n >\n {{ report.canPublish ? \"Can publish\" : \"Blocked\" }}\n </span>\n </div>\n @if (validationIssues().length > 0) {\n <ul class=\"mt-2 list-none space-y-1 p-0\">\n @for (issue of validationIssues(); track issue.code + issue.message + issue.fieldPath) {\n <li\n class=\"rounded-md bg-(--p-content-background) px-2 py-1.5 text-[11.5px] leading-5 text-(--p-text-muted-color)\"\n >\n <span class=\"font-semibold text-(--p-text-color)\">\n {{ issue.severity }}\n </span>\n <span> / {{ issue.targetType }}</span>\n @if (issue.targetKey) {\n <code class=\"ms-1 font-mono\">{{ issue.targetKey }}</code>\n }\n <span> - {{ issue.message }}</span>\n </li>\n }\n </ul>\n } @else {\n <p class=\"m-0 mt-2 text-[11.5px] text-(--p-text-muted-color)\">\n No validation issues returned by the backend.\n </p>\n }\n </div>\n }\n </div>\n</section>\n\n<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Revisions\n </h3>\n <div class=\"space-y-4 p-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n Immutable backend revisions, active revision state, activation, and\n rollback.\n </p>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n label=\"Refresh\"\n [disabled]=\"revisionsLoading() || operationBusy()\"\n (onClick)=\"refreshRevisions()\"\n />\n </div>\n\n @if (revisions().length > 0) {\n <ul class=\"list-none space-y-1.5 p-0\">\n @for (revision of revisions(); track revisionId(revision)) {\n <li\n class=\"rounded-md border border-surface-200 bg-surface-50 px-3 py-2\"\n [style.borderColor]=\"\n isActiveRevision(revision) ? 'var(--p-primary-color)' : null\n \"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"font-mono text-[12px] font-semibold text-(--p-text-color)\">\n {{ revisionLabel(revision) }}\n </span>\n <span class=\"text-[10.5px] text-(--p-text-muted-color)\">\n {{ revisionDate(revision) }}\n </span>\n </div>\n <div class=\"mt-1 text-[11.5px] text-(--p-text-muted-color)\">\n {{ revision.changeSummary || \"No change summary\" }}\n @if (revision.publishedBy || revision.createdBy) {\n <span> / {{ revision.publishedBy ?? revision.createdBy }}</span>\n }\n </div>\n </li>\n }\n </ul>\n } @else {\n <div\n class=\"rounded-md border border-dashed border-surface-200 px-3 py-3 text-center text-[12px] text-(--p-text-muted-color)\"\n >\n No revisions returned yet. Publish the draft to create the first\n immutable revision.\n </div>\n }\n\n <div class=\"grid gap-2 md:grid-cols-[1fr_auto_auto]\">\n <mt-select-field\n [ngModel]=\"selectedRevisionId()\"\n (ngModelChange)=\"selectedRevisionId.set($event)\"\n label=\"Selected revision\"\n [options]=\"revisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"secondary\"\n label=\"Activate\"\n [disabled]=\"operationBusy() || !selectedRevisionId()\"\n (onClick)=\"activateSelectedRevision()\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"danger\"\n label=\"Rollback\"\n [disabled]=\"operationBusy() || !canRollback()\"\n (onClick)=\"rollbackSelectedRevision()\"\n />\n </div>\n </div>\n</section>\n\n<section\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <h3\n class=\"m-0 border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Import, export, and diff\n </h3>\n <div class=\"space-y-4 p-4\">\n <div class=\"space-y-2 rounded-md border border-surface-200 bg-surface-50 p-3\">\n <div class=\"grid gap-2 md:grid-cols-[1fr_1fr_auto]\">\n <mt-select-field\n [ngModel]=\"leftRevisionId()\"\n (ngModelChange)=\"leftRevisionId.set($event)\"\n label=\"Left revision\"\n [options]=\"revisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"rightRevisionId()\"\n (ngModelChange)=\"rightRevisionId.set($event)\"\n label=\"Right revision\"\n [options]=\"revisionOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-button\n class=\"self-end\"\n size=\"small\"\n severity=\"secondary\"\n label=\"Compare\"\n [disabled]=\"operationBusy() || !leftRevisionId() || !rightRevisionId()\"\n (onClick)=\"diffSelectedRevisions()\"\n />\n </div>\n @if (diffResult()) {\n @if (diffEntries().length > 0) {\n <ul class=\"list-none space-y-1 p-0\">\n @for (entry of diffEntries(); track entry.group + entry.value) {\n <li class=\"flex items-center gap-2 rounded-md bg-(--p-content-background) px-2 py-1.5 text-[11.5px]\">\n <span class=\"shrink-0 rounded bg-surface-100 px-1.5 py-0.5 font-semibold text-(--p-text-muted-color)\">\n {{ entry.group }}\n </span>\n <code class=\"min-w-0 truncate font-mono text-(--p-text-color)\">\n {{ entry.value }}\n </code>\n </li>\n }\n </ul>\n } @else {\n <p class=\"m-0 text-[11.5px] text-(--p-text-muted-color)\">\n Backend diff returned no changes for the selected revisions.\n </p>\n }\n }\n </div>\n\n <div class=\"grid gap-3 lg:grid-cols-2\">\n <div class=\"space-y-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-[12px] font-semibold text-(--p-text-color)\">\n Export\n </span>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Load export\"\n [disabled]=\"operationBusy()\"\n (onClick)=\"exportAutomation()\"\n />\n </div>\n <mt-textarea-field\n [ngModel]=\"exportText()\"\n [readonly]=\"true\"\n rows=\"7\"\n class=\"font-mono\"\n placeholder=\"Backend export JSON appears here. Credential refs are placeholders only.\"\n />\n </div>\n\n <div class=\"space-y-2\">\n <div class=\"flex items-center justify-between gap-2\">\n <span class=\"text-[12px] font-semibold text-(--p-text-color)\">\n Import\n </span>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n label=\"Import draft\"\n [disabled]=\"operationBusy() || !importText().trim()\"\n (onClick)=\"importAutomation()\"\n />\n </div>\n <mt-textarea-field\n [ngModel]=\"importText()\"\n (ngModelChange)=\"importText.set($event ?? '')\"\n rows=\"7\"\n class=\"font-mono\"\n placeholder=\"Paste AutomationExportDefinition JSON. Imported drafts must be validated before publish.\"\n />\n </div>\n </div>\n </div>\n</section>\n\n<details\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <summary\n class=\"m-0 cursor-pointer list-none border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Trigger configuration\n </summary>\n <div class=\"space-y-3 p-4\">\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n Add and edit triggers from the canvas. This settings drawer only summarizes\n trigger state and backend setup hints.\n </p>\n <ul class=\"m-0 flex list-none flex-col gap-1.5 p-0\">\n @for (t of store.triggers(); track t.id) {\n <li\n class=\"flex flex-wrap items-center justify-between gap-3 rounded-md bg-surface-50 p-2\"\n >\n <div class=\"min-w-0\">\n <div class=\"truncate text-[12px] font-semibold text-(--p-text-color)\">\n {{ t.name?.[\"en\"] || t.type }}\n </div>\n <div class=\"text-[11.5px] text-(--p-text-muted-color)\">\n {{ triggerTypeLabel(t.type) }}\n <span>-</span>\n {{\n t.enabled\n ? (\"flowplus.inspector.workflow.triggerEnabled\" | transloco)\n : (\"flowplus.common.disabled\" | transloco)\n }}\n </div>\n </div>\n <span\n class=\"shrink-0 rounded-full border border-surface-200 px-2 py-0.5 text-[10.5px] font-medium text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.trigger.node.addFromCanvas\" | transloco }}\n </span>\n @if (isWebhookTrigger(t.type)) {\n <div class=\"flex items-center gap-2 text-[11.5px] text-(--p-text-muted-color)\">\n <code class=\"font-mono\">{{ t.webhookKey ?? \"-\" }}</code>\n <span> / {{ t.expectedHttpMethod ?? \"POST\" }}</span>\n <span>\n /\n {{\n t.secretConfigured\n ? (\"flowplus.inspector.workflow.secretConfigured\" | transloco)\n : (\"flowplus.inspector.workflow.secretMissing\" | transloco)\n }}\n </span>\n </div>\n }\n </li>\n }\n </ul>\n @if ((store.triggers()?.length ?? 0) === 0) {\n <div\n class=\"rounded-md border border-dashed border-surface-200 px-3 py-3 text-center text-[12px] text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.trigger.node.addFromCanvas\" | transloco }}\n </div>\n }\n </div>\n</details>\n\n<details\n class=\"flex flex-col gap-0 overflow-hidden rounded-md border border-surface-200 bg-surface-0\"\n>\n <summary\n class=\"m-0 cursor-pointer list-none border-b border-surface-200 bg-surface-50 px-4 py-3 text-lg font-semibold text-color\"\n >\n Resources and variables\n </summary>\n <div class=\"grid gap-4 p-4 lg:grid-cols-2\">\n <div>\n <h4 class=\"m-0 mb-2 text-[12px] font-semibold text-(--p-text-color)\">\n Resources\n </h4>\n <ul class=\"m-0 flex list-none flex-col gap-1.5 p-0\">\n @for (r of store.resources(); track r.key) {\n <li class=\"flex items-center gap-2 rounded-md bg-surface-50 p-2\">\n <strong class=\"text-[12px]\">{{ r.key }}</strong>\n <span class=\"text-[11.5px] text-(--p-text-muted-color)\">\n {{ r.entityType }}\n </span>\n <mt-number-field\n class=\"w-[120px]\"\n [ngModel]=\"r.entityId\"\n (ngModelChange)=\"bindResource(r.key, +($event ?? 0))\"\n [min]=\"0\"\n />\n @if (r.required) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(var(--fp-warning))]/18 px-2 py-0.5 text-[10.5px] font-medium text-[rgb(146_91_0)]\"\n >\n Required\n </span>\n }\n </li>\n }\n </ul>\n @if ((store.resources()?.length ?? 0) === 0) {\n <p class=\"m-0 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.inspector.workflow.noResources\" | transloco }}\n </p>\n }\n </div>\n\n <div>\n <h4 class=\"m-0 mb-2 text-[12px] font-semibold text-(--p-text-color)\">\n Variables\n </h4>\n <ul class=\"m-0 flex list-none flex-col gap-1 p-0\">\n @for (v of store.variables(); track v.key) {\n <li class=\"flex items-center gap-2 rounded-md bg-surface-50 px-2 py-1.5\">\n <code class=\"font-mono text-[12px]\">{{ v.key }}</code>\n <span class=\"text-[12px] text-(--p-text-muted-color)\">\n {{ v.type }}\n </span>\n </li>\n }\n </ul>\n </div>\n </div>\n</details>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: NumberField, selector: "mt-number-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "pInputs", "format", "useGrouping", "maxFractionDigits", "min", "max", "required"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
14420
14420
|
}
|
|
14421
14421
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowSettingsInspectorComponent, decorators: [{
|
|
14422
14422
|
type: Component,
|
|
@@ -16488,7 +16488,7 @@ class InspectorModalBodyComponent {
|
|
|
16488
16488
|
return (resolveTranslatable(trigger.name ?? null, this.transloco.getActiveLang()) || trigger.type);
|
|
16489
16489
|
}
|
|
16490
16490
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: InspectorModalBodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
16491
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: InspectorModalBodyComponent, isStandalone: true, selector: "fp-inspector-modal-body", host: { classAttribute: "block h-full" }, viewQueries: [{ propertyName: "resizeHost", first: true, predicate: ["resizeHost"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- A connected node rendered as a solid mt-avatar sitting on the modal's outer\r\n edge (n8n NDV style). Color-only avatar (no utility classes); the floating\r\n chrome (shadow, radius, hover) lives in CSS on `.fp-edge-tab .p-avatar`.\r\n `side` = 'start' for upstream nodes that feed in, 'end' for downstream\r\n nodes this step feeds. Clicking moves the selection \u2014 the modal re-derives\r\n in place with a brief loading beat, never closing. -->\r\n<ng-template #edgeTab let-n let-side=\"side\">\r\n <span\n class=\"fp-edge-tab group pointer-events-auto relative flex items-center justify-center transition duration-200 ease-out hover:-translate-y-0.5 active:scale-95\"\n [style.--fp-edge-tab-color]=\"n.color\"\n >\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n [icon]=\"n.icon\"\n [tooltip]=\"n.label\"\n styleClass=\"fp-edge-tab-button\"\n (onClick)=\"navigateToStep(n.stepId)\"\n />\n\r\n <!-- hover label \u2014 a solid, high-contrast pill that opens toward the center -->\r\n <span\r\n class=\"pointer-events-none absolute top-1/2 z-30 max-w-[15rem] -translate-y-1/2 scale-95 rounded-xl bg-(--p-text-color) px-3 py-2 text-start opacity-0 shadow-xl transition duration-200 ease-out group-hover:scale-100 group-hover:opacity-100\"\r\n [class.start-full]=\"side === 'start'\"\r\n [class.ms-3]=\"side === 'start'\"\r\n [class.end-full]=\"side === 'end'\"\r\n [class.me-3]=\"side === 'end'\"\r\n >\r\n <span\r\n class=\"block truncate text-[12.5px] font-semibold leading-tight text-(--p-content-background)\"\r\n >{{ n.label }}</span\r\n >\r\n <span\r\n class=\"mt-0.5 block truncate text-[10px] font-semibold text-(--p-content-background)/55\"\n >{{ n.typeLabel }}</span\r\n >\r\n </span>\r\n </span>\n</ng-template>\n\r\n<!-- Type glyph badge for a context value (string / number / date / \u2026). -->\r\n<ng-template #typeBadge let-kind>\r\n <span\r\n class=\"flex h-5 min-w-5 shrink-0 items-center justify-center rounded-[5px] border border-(--p-content-border-color) bg-(--p-content-background) px-1 font-mono text-[11px] font-bold leading-none text-(--p-text-color)\"\r\n >\r\n @switch (kind) {\r\n @case (\"string\") {\r\n T\r\n }\r\n @case (\"number\") {\r\n #\r\n }\r\n @case (\"object\") {\r\n {}\r\n }\r\n @case (\"array\") {\r\n [ ]\r\n }\r\n @case (\"boolean\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2.4]\"\r\n >\r\n <rect x=\"3\" y=\"7\" width=\"18\" height=\"10\" rx=\"5\" />\r\n <circle cx=\"9\" cy=\"12\" r=\"2\" fill=\"currentColor\" stroke=\"none\" />\r\n </svg>\r\n }\r\n @case (\"date\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <rect x=\"4\" y=\"5\" width=\"16\" height=\"16\" rx=\"2.5\" />\r\n <path d=\"M4 9h16M8 3v4M16 3v4\" stroke-linecap=\"round\" />\r\n </svg>\r\n }\r\n @case (\"file\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <path\r\n d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8Z\"\r\n />\r\n <path d=\"M14 3v5h5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n }\r\n @case (\"secret\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"9\" rx=\"2\" />\r\n <path d=\"M8 11V8a4 4 0 0 1 8 0v3\" stroke-linecap=\"round\" />\r\n </svg>\r\n }\r\n @case (\"reference\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <path\r\n d=\"M7 17 17 7M9 7h8v8\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n }\r\n @default {\r\n \u2022\r\n }\r\n }\r\n </span>\r\n</ng-template>\n\n<ng-template #inputPickerTree let-nodes=\"nodes\" let-level=\"level\">\n <ul class=\"border-t border-(--p-content-border-color)\">\n @for (node of nodes; track node.id) {\n <li class=\"border-b border-(--p-content-border-color) last:border-b-0\">\n <div\n [fpDragData]=\"node.rawExpression\"\n class=\"fp-data-row group flex cursor-grab items-start gap-2 px-3 py-2 transition duration-200 ease-out hover:bg-(--p-surface-50) active:cursor-grabbing\"\n [style.padding-inline-start.rem]=\"0.75 + level * 0.75\"\n >\n @if (node.children.length > 0) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"\n isInputPickerNodeExpanded(node)\n ? 'arrow.chevron-down'\n : 'arrow.chevron-right'\n \"\n [tooltip]=\"\n isInputPickerNodeExpanded(node) ? 'Collapse' : 'Expand'\n \"\n styleClass=\"fp-modal-tree-toggle\"\n (onClick)=\"toggleInputPickerNode(node)\"\n />\n } @else {\n <span class=\"h-7 w-7 shrink-0\"></span>\n }\n\n <ng-container\n *ngTemplateOutlet=\"\n typeBadge;\n context: { $implicit: fieldKind(node.type) }\n \"\n />\n\n <div class=\"min-w-0 flex-1 space-y-0.5\">\n <div class=\"flex min-w-0 items-center gap-1.5\">\n <span\n class=\"min-w-0 flex-1 truncate text-[12.5px] font-semibold text-(--p-text-color)\"\n >{{ node.label }}</span\n >\n <span\n class=\"shrink-0 rounded-md bg-(--p-surface-100) px-1.5 py-0.5 text-[10px] font-semibold text-(--p-text-muted-color)\"\n >{{ node.type }}</span\n >\n @if (node.availability !== \"available\" || node.valuePreview) {\n <span\n class=\"shrink-0 rounded-md border border-(--p-content-border-color) bg-(--p-content-background) px-1.5 py-0.5 text-[10px] font-medium text-(--p-text-muted-color)\"\n >{{ pickerNodeStatus(node) }}</span\n >\n }\n </div>\n <div\n class=\"truncate font-mono text-[10.5px] leading-4 text-(--p-text-muted-color)\"\n >\n {{ node.rawExpression }}\n </div>\n <div\n class=\"truncate font-mono text-[10.5px] leading-4 text-(--p-primary-color)\"\n >\n {{ node.expression }}\n </div>\n @if (node.description) {\n <p\n class=\"m-0 line-clamp-2 text-[11px] leading-4 text-(--p-text-muted-color)\"\n >\n {{ node.description }}\n </p>\n }\n </div>\n </div>\n\n @if (isInputPickerNodeExpanded(node)) {\n <ng-container\n *ngTemplateOutlet=\"\n inputPickerTree;\n context: { nodes: node.children, level: level + 1 }\n \"\n />\n }\n </li>\n }\n </ul>\n</ng-template>\n\n<!-- Outer wrapper: positioning context only (overflow visible) so the connected-\n node avatars can straddle the modal border. The inner card clips its own\n rounded corners + carries the shadow. -->\n<div class=\"relative h-full w-full text-(--p-text-color)\">\n <div\r\n class=\"flex h-full min-h-0 flex-col overflow-hidden rounded-2xl bg-(--p-content-background) shadow-2xl shadow-slate-950/25\"\r\n >\r\n <!-- ============================ HEADER ============================ -->\r\n <header\r\n class=\"flex shrink-0 items-center gap-3.5 border-b border-(--p-content-border-color) px-6 py-4\"\r\n >\r\n @if (currentChip(); as chip) {\r\n <span\r\n class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl shadow-sm\"\r\n [style.color]=\"chip.color\"\r\n [style.background]=\"chip.bg\"\r\n >\r\n <mt-icon [icon]=\"chip.icon\" class=\"text-[22px]\" />\r\n </span>\r\n } @else {\r\n <span\r\n class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl bg-(--p-surface-100) text-(--p-text-muted-color)\"\r\n >\r\n <mt-icon\r\n [icon]=\"\r\n selectionKind() === 'connection'\r\n ? 'dev.dataflow-04'\r\n : 'general.settings-01'\r\n \"\r\n class=\"text-[22px]\"\r\n />\r\n </span>\r\n }\r\n\r\n <div class=\"min-w-0 flex-1\">\n <h2\n class=\"truncate text-[17px] font-semibold tracking-tight text-(--p-text-color)\"\n >\n {{ activeDescriptorLabel() }}\n </h2>\n <p\n class=\"m-0 mt-1 line-clamp-2 text-[12px] leading-5 text-(--p-text-muted-color)\"\n >\n {{ activeDescriptorSubtitle() }}\n </p>\n </div>\n\r\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.x-close\"\n [tooltip]=\"'flowplus.inspector.modal.close' | transloco\"\n (onClick)=\"close()\"\n />\n </header>\r\n\r\n <!-- ===================== BODY: INPUT | CONFIG | OUTPUT ===================== -->\r\n <div\r\n #resizeHost\r\n class=\"relative flex min-h-0 flex-1\"\r\n [class.select-none]=\"resizing()\"\r\n [style.cursor]=\"resizing() ? 'col-resize' : null\"\r\n >\r\n <!-- Navigation loading beat \u2014 a slim primary progress sweep across the top\r\n while the modal re-derives for a newly selected (connected) step. -->\r\n @if (navigating()) {\r\n <div\r\n class=\"pointer-events-none absolute inset-x-0 top-0 z-40 h-[3px] overflow-hidden\"\r\n >\r\n <div\r\n class=\"h-full w-2/5 rounded-full bg-(--p-primary-color) animate-[fp-busy-bar-slide_0.9s_ease-in-out_infinite]\"\r\n ></div>\r\n </div>\r\n }\r\n\r\n @if (selectionKind() !== \"trigger\") {\n <!-- --------------------------- INPUT --------------------------- -->\n <section\n class=\"flex h-full min-h-0 shrink-0 flex-col bg-(--p-surface-50)\"\n [style.width.px]=\"leftWidth()\"\n >\n <div\r\n class=\"shrink-0 border-b border-(--p-content-border-color) px-5 py-4\"\r\n >\r\n <div class=\"flex items-center justify-between gap-2\">\r\n <p class=\"text-[13.5px] font-semibold text-(--p-text-color)\">\n {{\n selectionKind() === \"trigger\"\n ? \"Trigger payload\"\n : (\"flowplus.inspector.modal.inputsTitle\" | transloco)\n }}\n </p>\n @if (selectionKind() === \"step\") {\r\n <div class=\"flex items-center gap-1.5\">\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [tooltip]=\"'flowplus.context.pickerPlaceholder' | transloco\"\n [styleClass]=\"\n contextSearchOpen()\n ? 'fp-modal-icon-button is-active'\n : 'fp-modal-icon-button'\n \"\n (onClick)=\"toggleContextSearch()\"\n />\n <div\r\n class=\"inline-flex rounded-lg bg-(--p-surface-100) p-0.5 text-[11px] font-semibold\"\r\n >\r\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewSchema' | transloco\"\n [styleClass]=\"\n inputView() === 'schema'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"inputView.set('schema')\"\n />\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewJson' | transloco\"\n [styleClass]=\"\n inputView() === 'json'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"inputView.set('json')\"\n />\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (selectionKind() === \"step\" && contextSearchOpen()) {\r\n <div class=\"mt-3\">\r\n <mt-text-field\r\n [ngModel]=\"contextSearch()\"\r\n (ngModelChange)=\"contextSearch.set($event)\"\r\n [placeholder]=\"'flowplus.context.pickerPlaceholder' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fp-scroll min-h-0 flex-1 overflow-y-auto px-5 py-4\">\r\n @if (selectionKind() === \"step\") {\r\n @if (inputView() === \"schema\") {\n <div class=\"space-y-3\">\n <section class=\"space-y-2.5\">\n @if (filteredInputPickerGroups().length > 0) {\n <div class=\"flex items-center justify-between gap-3\">\n <h3\n class=\"flex items-center gap-2 text-[13px] font-semibold text-(--p-text-color)\"\n >\n {{\r\n \"flowplus.inspector.modal.availableData\" | transloco\r\n }}\r\n </h3>\n </div>\n }\n\n @if (filteredInputPickerGroups().length === 0) {\n <div\n class=\"flex min-h-[18rem] flex-col items-center justify-center gap-4 px-6 py-10 text-center\"\n >\n <span\r\n class=\"flex h-14 w-14 items-center justify-center rounded-2xl bg-(--p-surface-100) text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-7 w-7 fill-none stroke-current stroke-[1.6]\"\r\n >\r\n <path\r\n d=\"M3 7.5 12 3l9 4.5-9 4.5-9-4.5Z\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"m3 12 9 4.5 9-4.5M3 16.5 12 21l9-4.5\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n </span>\r\n <div class=\"space-y-1\">\r\n <p\r\n class=\"text-[14px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{\r\n \"flowplus.inspector.modal.noInputData\" | transloco\r\n }}\r\n </p>\r\n <p\r\n class=\"mx-auto max-w-[16rem] text-[12px] leading-5 text-(--p-text-muted-color)\"\r\n >\r\n {{\r\n \"flowplus.inspector.modal.noAvailableData\"\r\n | transloco\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n } @else {\r\n <p\r\n class=\"flex items-center gap-1.5 text-[11px] text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 16 16\"\r\n class=\"h-3.5 w-3.5 shrink-0 fill-current opacity-70\"\r\n aria-hidden=\"true\"\r\n >\r\n <circle cx=\"5\" cy=\"4\" r=\"1.1\" />\r\n <circle cx=\"9\" cy=\"4\" r=\"1.1\" />\r\n <circle cx=\"5\" cy=\"8\" r=\"1.1\" />\r\n <circle cx=\"9\" cy=\"8\" r=\"1.1\" />\r\n <circle cx=\"5\" cy=\"12\" r=\"1.1\" />\r\n <circle cx=\"9\" cy=\"12\" r=\"1.1\" />\r\n </svg>\r\n {{ \"flowplus.inspector.modal.dragHint\" | transloco }}\r\n </p>\r\n\n <div class=\"space-y-1.5\">\n @for (group of filteredInputPickerGroups(); track group.id) {\n <section\n class=\"overflow-hidden rounded-xl border border-(--p-content-border-color) bg-(--p-content-background)\"\n >\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-down\"\n [label]=\"\n group.label + ' (' + group.children.length + ')'\n \"\n styleClass=\"fp-modal-group-toggle\"\n (onClick)=\"toggleGroup(group.id)\"\n />\n\n @if (!isGroupCollapsed(group.id)) {\n <ng-container\n *ngTemplateOutlet=\"\n inputPickerTree;\n context: { nodes: group.children, level: 0 }\n \"\n />\n }\n </section>\n }\n </div>\r\n }\r\n </section>\r\n </div>\r\n } @else {\r\n <pre\r\n class=\"fp-scroll overflow-auto rounded-lg border border-(--p-content-border-color) bg-(--p-surface-100) p-3 font-mono text-[11px] leading-5 text-(--p-text-color)\"\r\n >{{ inputJson() }}</pre\r\n >\r\n }\r\n } @else if (\r\n selectionKind() === \"connection\" && connectionEndpoints();\r\n as endpoints\r\n ) {\r\n <section class=\"space-y-3\">\r\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.modal.connectionSummary\" | transloco }}\r\n </h3>\r\n <div\r\n class=\"space-y-2.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\r\n >\r\n <div>\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.source\" | transloco }}\r\n </div>\r\n <div class=\"mt-1 text-[13px] text-(--p-text-color)\">\r\n {{ endpoints.source }}\r\n </div>\r\n </div>\r\n <div>\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.target\" | transloco }}\r\n </div>\r\n <div class=\"mt-1 text-[13px] text-(--p-text-color)\">\r\n {{ endpoints.target }}\r\n </div>\r\n </div>\r\n @if (selectedConnection()!.sourcePortKey) {\r\n <div\r\n class=\"font-mono text-[11px] text-(--p-text-muted-color)\"\r\n >\r\n {{ selectedConnection()!.sourcePortKey }} ->\r\n {{ selectedConnection()!.targetPortKey || \"in\" }}\r\n </div>\r\n }\r\n </div>\n </section>\n } @else if (selectionKind() === \"trigger\") {\n <section class=\"space-y-3\">\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\n Trigger payload and setup\n </h3>\n <div\n class=\"space-y-2.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\n >\n @if (selectedTriggerPayloadSchemaJson()) {\n <div>\n <div\n class=\"text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Payload schema\n </div>\n <pre\n class=\"fp-scroll mt-1 max-h-36 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTriggerPayloadSchemaJson() }}</pre\n >\n </div>\n } @else {\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n The backend did not return a payload schema for this trigger.\n </p>\n }\n @if (selectedTriggerPayloadSampleJson()) {\n <div>\n <div\n class=\"text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Sample payload\n </div>\n <pre\n class=\"fp-scroll mt-1 max-h-36 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTriggerPayloadSampleJson() }}</pre\n >\n </div>\n }\n @if (selectedTrigger()?.type === \"WebhookTrigger\") {\n <div class=\"space-y-1 text-[12px] leading-5 text-(--p-text-muted-color)\">\n <div>Expected method: {{ selectedTrigger()?.expectedHttpMethod ?? \"POST\" }}</div>\n <div>\n Content types:\n {{ (selectedTrigger()?.allowedContentTypes ?? []).join(\", \") || \"Backend default\" }}\n </div>\n @if (selectedTrigger()?.exampleCurl) {\n <pre\n class=\"fp-scroll mt-1 max-h-28 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTrigger()?.exampleCurl }}</pre\n >\n }\n </div>\n }\n @if (selectedTrigger()?.type === \"FormSubmitTrigger\") {\n <div class=\"rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px] text-(--p-text-muted-color)\">\n Pinned formVersionId:\n <code>{{ selectedTriggerFormVersionId() ?? \"Not selected\" }}</code>\n </div>\n }\n @if (selectedTrigger()?.type === \"ScheduleTrigger\") {\n <div>\n <div\n class=\"text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Schedule config\n </div>\n <pre\n class=\"fp-scroll mt-1 max-h-36 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTriggerConfigJson() }}</pre\n >\n </div>\n }\n </div>\n\n @if (selectedTriggerIssues().length > 0) {\n <div class=\"space-y-1.5\">\n @for (issue of selectedTriggerIssues(); track issue.code + issue.message) {\n <div\n class=\"rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-[12px] leading-5 text-amber-800\"\n >\n {{ resolveTranslatable(issue.message) }}\n </div>\n }\n </div>\n }\n </section>\n } @else if (selectionKind() === \"workflow\") {\n <section class=\"space-y-3\">\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\n {{ \"flowplus.inspector.workflow.triggers\" | transloco }}\r\n </h3>\r\n @if (store.triggers().length > 0) {\r\n <ul class=\"space-y-1.5\">\r\n @for (trigger of store.triggers(); track trigger.id) {\r\n <li\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-2.5\"\r\n >\r\n <div class=\"flex items-center justify-between gap-3\">\r\n <div>\r\n <div\r\n class=\"text-[13px] font-medium text-(--p-text-color)\"\r\n >\r\n {{ triggerLabel(trigger) }}\r\n </div>\r\n <div\r\n class=\"mt-0.5 text-[11px] text-(--p-text-muted-color)\"\n >\r\n {{ trigger.type }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"rounded-md px-2 py-0.5 text-[10.5px] font-medium\"\r\n [class.bg-emerald-50]=\"trigger.enabled\"\r\n [class.text-emerald-700]=\"trigger.enabled\"\r\n [class.bg-slate-100]=\"!trigger.enabled\"\r\n [class.text-slate-500]=\"!trigger.enabled\"\r\n >\r\n {{\r\n (trigger.enabled\r\n ? \"flowplus.inspector.workflow.triggerEnabled\"\r\n : \"flowplus.inspector.modal.disabled\"\r\n ) | transloco\r\n }}\r\n </span>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <p\r\n class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-[12.5px] text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.workflow.noTriggers\" | transloco }}\r\n </p>\r\n }\r\n </section>\r\n } @else {\r\n <p\r\n class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-[12.5px] text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.noSelectionHint\" | transloco }}\r\n </p>\r\n }\r\n </div>\r\n </section>\n\n <!-- resize handle: INPUT | CONFIG -->\n <div\n class=\"fp-resizer\"\n [class.is-active]=\"resizing() === 'left'\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n (pointerdown)=\"startResize('left', $event)\"\n (pointermove)=\"onResizeMove($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <span class=\"fp-resizer-grip\" aria-hidden=\"true\"></span>\n </div>\n }\n\n <!-- --------------------------- CONFIGURATION --------------------------- -->\n <section\n class=\"flex h-full min-h-0 min-w-0 flex-1 flex-col bg-(--p-content-background)\"\n >\n @if (selectionKind() === \"trigger\" && selectedTrigger(); as trigger) {\n <fp-automation-smart-editor [trigger]=\"trigger\" mode=\"modal\" />\n } @else {\n <fp-inspector-shell\n mode=\"modal\"\n (stepChange)=\"onStepChange($event)\"\n (connectionChange)=\"onConnectionChange($event)\"\n (workflowChange)=\"onWorkflowChange($event)\"\n />\n }\n </section>\n\r\n <!-- resize handle: CONFIG | OUTPUT -->\r\n <div\r\n class=\"fp-resizer\"\r\n [class.is-active]=\"resizing() === 'right'\"\r\n role=\"separator\"\r\n aria-orientation=\"vertical\"\r\n (pointerdown)=\"startResize('right', $event)\"\r\n (pointermove)=\"onResizeMove($event)\"\r\n (pointerup)=\"endResize($event)\"\r\n (pointercancel)=\"endResize($event)\"\r\n >\r\n <span class=\"fp-resizer-grip\" aria-hidden=\"true\"></span>\r\n </div>\r\n\r\n <!-- --------------------------- OUTPUT --------------------------- -->\r\n <section\r\n class=\"flex h-full min-h-0 shrink-0 flex-col bg-(--p-surface-50)\"\r\n [style.width.px]=\"rightWidth()\"\r\n >\r\n <div\r\n class=\"shrink-0 border-b border-(--p-content-border-color) px-5 py-4\"\r\n >\r\n @if (selectionKind() === \"step\" && mockEditorOpen()) {\r\n <!-- EDIT OUTPUT replaces the OUTPUT header while pinning mock data -->\r\n <div class=\"flex items-center justify-between gap-2\">\r\n <p\r\n class=\"text-[13.5px] font-semibold text-(--p-text-color)\"\n >\r\n {{ \"flowplus.inspector.modal.editOutput\" | transloco }}\r\n </p>\r\n <div class=\"flex items-center gap-1.5\">\r\n <mt-button\r\n variant=\"outlined\"\r\n size=\"small\"\r\n [label]=\"'flowplus.inspector.modal.cancel' | transloco\"\r\n (onClick)=\"cancelMockEditor()\"\r\n />\r\n <mt-button\r\n severity=\"primary\"\r\n size=\"small\"\r\n [label]=\"'flowplus.inspector.modal.save' | transloco\"\r\n (onClick)=\"saveMock()\"\r\n />\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex items-center justify-between gap-2\">\r\n <p\n class=\"text-[13.5px] font-semibold text-(--p-text-color)\"\n >\n {{\n selectionKind() === \"trigger\"\n ? \"Trigger runtime\"\n : (\"flowplus.inspector.modal.outputsTitle\" | transloco)\n }}\n </p>\n @if (selectionKind() === \"step\" && hasOutputData()) {\r\n <div\r\n class=\"inline-flex rounded-lg bg-(--p-surface-100) p-0.5 text-[11px] font-semibold\"\r\n >\r\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewSchema' | transloco\"\n [styleClass]=\"\n outputView() === 'schema'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"outputView.set('schema')\"\n />\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewJson' | transloco\"\n [styleClass]=\"\n outputView() === 'json'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"outputView.set('json')\"\n />\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fp-scroll min-h-0 flex-1 overflow-y-auto\">\r\n @if (selectionKind() === \"step\") {\n @if (selectedStepRuntimeState(); as runtimeState) {\n <section\n class=\"mx-5 mt-5 rounded-md border border-surface-200 bg-surface-0\"\n >\n <header\n class=\"flex items-center justify-between gap-2 border-b border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <div class=\"min-w-0\">\n <h3 class=\"m-0 text-[12.5px] font-semibold text-(--p-text-color)\">\n Latest run\n </h3>\n <p class=\"m-0 mt-0.5 truncate text-[11px] text-(--p-text-muted-color)\">\n {{ runtimeState.status }}\n @if (runtimeState.durationMs != null) {\n / {{ runtimeState.durationMs }} ms\n }\n </p>\n </div>\n <div class=\"flex shrink-0 items-center gap-1\">\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.eye\"\n label=\"Inspect data\"\n [loading]=\"runtimeNodeDataState() === 'loading'\"\n [disabled]=\"!runtimeState.nodeRunId\"\n (onClick)=\"inspectSelectedNodeData(false)\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open full run'\"\n (onClick)=\"openSelectedRuntimeRun()\"\n />\n </div>\n </header>\n <div class=\"space-y-2 px-3 py-2.5\">\n @if (runtimeState.errorMessage) {\n <p class=\"m-0 text-[11.5px] leading-5 text-[rgb(var(--fp-error))]\">\n {{ runtimeState.errorMessage }}\n </p>\n }\n @if (runtimeState.waitReason) {\n <p class=\"m-0 text-[11.5px] leading-5 text-[rgb(var(--fp-warning))]\">\n {{ runtimeState.waitReason }}\n </p>\n }\n <div class=\"flex flex-wrap gap-1.5\">\n @if (runtimeState.attempts != null) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Attempts {{ runtimeState.attempts }}\n </span>\n }\n @if (runtimeState.routeOutputKey) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Output {{ runtimeState.routeOutputKey }}\n </span>\n }\n @if (runtimeState.selectedCaseKey) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Case\n {{\n runtimeState.selectedCaseLabel\n ? runtimeState.selectedCaseLabel + \" / \"\n : \"\"\n }}{{ runtimeState.selectedCaseKey }}\n </span>\n }\n @if (runtimeState.childExecutionId) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Child execution {{ runtimeState.childExecutionId }}\n </span>\n }\n </div>\n @if (runtimeNodeData(); as data) {\n @if (runtimeNodeNotice(data); as notice) {\n <p class=\"m-0 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1.5 text-[11.5px] leading-5 text-(--p-text-muted-color)\">\n {{ notice }}\n </p>\n }\n @if (runtimeNodeSummaryRows().length > 0) {\n <ul class=\"space-y-1\">\n @for (row of runtimeNodeSummaryRows(); track row.key) {\n <li class=\"flex items-center gap-2 rounded-md bg-(--p-surface-50) px-2 py-1.5 text-[11.5px]\">\n <span class=\"min-w-0 flex-1 truncate font-mono text-(--p-text-color)\">\n {{ row.key }}\n </span>\n <span class=\"shrink-0 text-(--p-text-muted-color)\">\n {{ row.preview }}\n </span>\n </li>\n }\n </ul>\n }\n @if (!data.includeFull) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.eye\"\n label=\"Load full data\"\n (onClick)=\"inspectSelectedNodeData(true)\"\n />\n }\n } @else if (runtimeNodeDataError()) {\n <p class=\"m-0 text-[11.5px] leading-5 text-[rgb(var(--fp-error))]\">\n {{ runtimeNodeDataError() }}\n </p>\n }\n </div>\n </section>\n }\n @if (mockEditorOpen()) {\n <!-- ----- mock data editor (header lives in the panel header) ----- -->\r\n <div class=\"flex h-full min-h-0 flex-col gap-2 p-4\">\r\n <div\n class=\"fp-code-frame flex min-h-0 flex-1 overflow-hidden rounded-xl border bg-(--p-surface-100) p-2 transition\"\n [style.borderColor]=\"\n mockError() ? '#f43f5e' : 'var(--p-content-border-color)'\n \"\n >\n <mt-textarea-field\n class=\"min-h-0 flex-1 [&_textarea]:fp-scroll [&_textarea]:min-h-[18rem] [&_textarea]:resize-none [&_textarea]:bg-transparent [&_textarea]:font-mono [&_textarea]:text-[12px] [&_textarea]:leading-5\"\n [field]=\"false\"\n [ngModel]=\"mockText()\"\n (ngModelChange)=\"mockText.set($event ?? '')\"\n [pInputs]=\"mockTextareaInputs\"\n [maxLength]=\"null\"\n rows=\"18\"\n />\n </div>\n @if (mockError()) {\r\n <p\r\n class=\"flex items-center gap-1.5 text-[11.5px] text-rose-600\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3.5 w-3.5 fill-none stroke-current stroke-[2]\"\r\n >\r\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\r\n <path d=\"M12 8v4M12 16h.01\" stroke-linecap=\"round\" />\r\n </svg>\r\n {{ mockError() }}\r\n </p>\r\n }\r\n </div>\r\n } @else if (hasOutputData()) {\r\n <!-- ----- output payload ----- -->\r\n <div class=\"space-y-3 px-5 py-5\">\r\n @if (outputIsMock()) {\r\n <div\r\n class=\"flex items-center justify-between gap-2 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-2.5 py-1.5\"\r\n >\r\n <span\r\n class=\"flex items-center gap-1.5 text-[11px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3.5 w-3.5 fill-none stroke-current stroke-[2] text-(--p-primary-color)\"\r\n >\r\n <path\r\n d=\"M9 4v6l-2 2v2h10v-2l-2-2V4M12 16v4\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n {{ \"flowplus.inspector.modal.mockPinned\" | transloco }}\r\n </span>\n <div class=\"flex items-center gap-1\">\n <mt-button\n variant=\"text\"\n severity=\"primary\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.edit' | transloco\"\n (onClick)=\"openMockEditor()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.clear' | transloco\"\n (onClick)=\"clearMock()\"\n />\n </div>\r\n </div>\r\n }\r\n\r\n @if (outputView() === \"json\") {\r\n <pre\r\n class=\"fp-scroll overflow-auto rounded-lg border border-(--p-content-border-color) bg-(--p-surface-100) p-3 font-mono text-[11px] leading-5 text-(--p-text-color)\"\r\n >{{ outputJson() }}</pre\r\n >\r\n } @else {\r\n <ul class=\"space-y-2\">\r\n @for (row of outputRows(); track row.key) {\r\n <li\r\n class=\"flex items-center gap-2.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3.5 py-2.5 shadow-sm\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n typeBadge;\r\n context: { $implicit: fieldKind(row.type) }\r\n \"\r\n />\r\n <span\r\n class=\"min-w-0 flex-1 truncate font-mono text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ row.key }}</span\r\n >\r\n <span\r\n class=\"shrink-0 truncate font-mono text-[12px] text-(--p-text-muted-color)\"\r\n >{{ row.preview }}</span\r\n >\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n } @else {\r\n <!-- ----- empty state ----- -->\r\n <div\r\n class=\"flex h-full flex-col items-center justify-center gap-4 px-6 py-10 text-center\"\r\n >\r\n <span\r\n class=\"flex h-14 w-14 items-center justify-center rounded-2xl bg-(--p-surface-100) text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-7 w-7 fill-none stroke-current stroke-[1.6]\"\r\n >\r\n <path\r\n d=\"M3 12h12m0 0-4-4m4 4-4 4\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path d=\"M21 4v16\" stroke-linecap=\"round\" />\r\n </svg>\r\n </span>\r\n <div class=\"space-y-1\">\r\n <p class=\"text-[14px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.modal.noOutputData\" | transloco }}\r\n </p>\r\n <p class=\"text-[12px] leading-5 text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.modal.noOutputHint\" | transloco }}\r\n </p>\r\n </div>\r\n <div class=\"flex flex-col items-center gap-2\">\r\n <mt-button\r\n severity=\"primary\"\r\n size=\"small\"\r\n icon=\"general.play\"\r\n [label]=\"'flowplus.inspector.modal.executeStep' | transloco\"\r\n [loading]=\"runningTest()\"\r\n [disabled]=\"runningTest() || !supportsTestRun()\"\r\n (onClick)=\"executeStep()\"\r\n />\r\n <mt-button\n variant=\"text\"\n severity=\"primary\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.setMockData' | transloco\"\n (onClick)=\"openMockEditor()\"\n />\n </div>\r\n @if (!supportsTestRun()) {\r\n <p\r\n class=\"max-w-[16rem] rounded-lg bg-(--p-surface-100) px-3 py-2 text-[11px] leading-5 text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.testRun.unavailableHint\" | transloco }}\r\n </p>\r\n }\r\n </div>\r\n }\n } @else if (selectionKind() === \"trigger\" && selectedTrigger(); as trigger) {\n <div class=\"space-y-3 px-5 py-5\">\n <section\n class=\"rounded-md border border-surface-200 bg-surface-0\"\n >\n <header\n class=\"flex items-center justify-between gap-2 border-b border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <h3 class=\"m-0 text-[13px] font-semibold text-(--p-text-color)\">\n Latest run\n </h3>\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open full run'\"\n [disabled]=\"!selectedTriggerRuntimeState()?.executionId\"\n (onClick)=\"openSelectedRuntimeRun()\"\n />\n </header>\n <div class=\"p-3\">\n @if (selectedTriggerRuntimeState(); as state) {\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n class=\"rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px] font-semibold text-(--p-text-color)\"\n >{{ state.status }}</span\n >\n @if (state.executionId) {\n <span class=\"font-mono text-[11px] text-(--p-text-muted-color)\">\n #{{ state.executionId }}\n </span>\n }\n @if (state.durationMs != null) {\n <span class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ state.durationMs }} ms\n </span>\n }\n </div>\n @if (state.errorMessage) {\n <p class=\"mt-2 text-[12px] leading-5 text-[rgb(var(--fp-error))]\">\n {{ state.errorMessage }}\n </p>\n }\n } @else {\n <p class=\"m-0 text-[12.5px] leading-5 text-(--p-text-muted-color)\">\n No runtime state is loaded for this trigger yet. Execute a\n real run or open the executions page for history.\n </p>\n }\n </div>\n </section>\n </div>\n } @else if (selectionKind() === \"connection\") {\n <div class=\"px-4 py-4\">\n <section class=\"space-y-3\">\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.tabs.routing\" | transloco }}\r\n </h3>\r\n <div\r\n class=\"space-y-2 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\r\n >\r\n @if (selectedConnectionActionsLabel()) {\r\n <div class=\"font-mono text-[11.5px] text-(--p-text-color)\">\r\n {{ selectedConnectionActionsLabel() }}\r\n </div>\r\n } @else {\r\n <div class=\"text-[12.5px] text-(--p-text-muted-color)\">\r\n {{\r\n \"flowplus.inspector.modal.noConfiguredOutputs\"\r\n | transloco\r\n }}\r\n </div>\r\n }\r\n @if (selectedConnection()!.expressionText) {\r\n <pre\r\n class=\"fp-scroll overflow-auto rounded-lg bg-(--p-surface-100) p-3 font-mono text-[11px] leading-5 text-(--p-text-color)\"\r\n >{{ selectedConnection()!.expressionText }}</pre\r\n >\r\n }\r\n </div>\r\n </section>\r\n </div>\r\n } @else if (selectionKind() === \"workflow\") {\r\n <div class=\"px-4 py-4\">\r\n <section class=\"space-y-3\">\r\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.modal.workflowHealth\" | transloco }}\r\n </h3>\r\n <div class=\"grid grid-cols-3 gap-2\">\r\n <div\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-center\"\r\n >\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.errorsLabel\" | transloco }}\r\n </div>\r\n <div\r\n class=\"mt-1.5 text-xl font-semibold text-(--p-text-color)\"\r\n >\r\n {{ workflowHealth().errors }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-center\"\r\n >\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.warningsLabel\" | transloco }}\r\n </div>\r\n <div\r\n class=\"mt-1.5 text-xl font-semibold text-(--p-text-color)\"\r\n >\r\n {{ workflowHealth().warnings }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-center\"\r\n >\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.publishLabel\" | transloco }}\r\n </div>\r\n <div\r\n class=\"mt-1.5 text-[13px] font-semibold\"\r\n [class.text-emerald-600]=\"workflowHealth().canPublish\"\r\n [class.text-amber-600]=\"!workflowHealth().canPublish\"\r\n >\r\n {{\r\n (workflowHealth().canPublish\r\n ? \"flowplus.inspector.modal.publishReady\"\r\n : \"flowplus.inspector.modal.publishBlocked\"\r\n ) | transloco\r\n }}\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n </div>\r\n } @else {\r\n <div class=\"px-4 py-4\">\r\n <p\r\n class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-[12.5px] text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.noSelectionHint\" | transloco }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n </div>\r\n\r\n <!-- Connected-node avatars \u2014 straddle the modal border (half in / half out),\r\n vertically centered. Rendered outside the clipping card so they protrude. -->\r\n @if (selectionKind() === \"step\") {\r\n @if (previousNeighbours().length > 0) {\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 start-0 z-50 flex -translate-x-1/2 flex-col items-center justify-center gap-3 rtl:translate-x-1/2\"\r\n >\r\n @for (n of previousNeighbours(); track n.stepId) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n edgeTab;\r\n context: { $implicit: n, side: 'start' }\r\n \"\r\n />\r\n }\r\n </div>\r\n }\r\n @if (nextNeighbours().length > 0) {\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 end-0 z-50 flex translate-x-1/2 flex-col items-center justify-center gap-3 rtl:-translate-x-1/2\"\r\n >\r\n @for (n of nextNeighbours(); track n.stepId) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n edgeTab;\r\n context: { $implicit: n, side: 'end' }\r\n \"\r\n />\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: InspectorShellComponent, selector: "fp-inspector-shell", inputs: ["mode"], outputs: ["stepChange", "connectionChange", "workflowChange", "closeRequested"] }, { kind: "component", type: AutomationSmartEditorComponent, selector: "fp-automation-smart-editor", inputs: ["step", "trigger", "mode", "view"] }, { kind: "directive", type: DragDataDirective, selector: "[fpDragData]", inputs: ["fpDragData"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
16491
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: InspectorModalBodyComponent, isStandalone: true, selector: "fp-inspector-modal-body", host: { classAttribute: "block h-full" }, viewQueries: [{ propertyName: "resizeHost", first: true, predicate: ["resizeHost"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- A connected node rendered as a solid mt-avatar sitting on the modal's outer\r\n edge (n8n NDV style). Color-only avatar (no utility classes); the floating\r\n chrome (shadow, radius, hover) lives in CSS on `.fp-edge-tab .p-avatar`.\r\n `side` = 'start' for upstream nodes that feed in, 'end' for downstream\r\n nodes this step feeds. Clicking moves the selection \u2014 the modal re-derives\r\n in place with a brief loading beat, never closing. -->\r\n<ng-template #edgeTab let-n let-side=\"side\">\r\n <span\n class=\"fp-edge-tab group pointer-events-auto relative flex items-center justify-center transition duration-200 ease-out hover:-translate-y-0.5 active:scale-95\"\n [style.--fp-edge-tab-color]=\"n.color\"\n >\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n [icon]=\"n.icon\"\n [tooltip]=\"n.label\"\n styleClass=\"fp-edge-tab-button\"\n (onClick)=\"navigateToStep(n.stepId)\"\n />\n\r\n <!-- hover label \u2014 a solid, high-contrast pill that opens toward the center -->\r\n <span\r\n class=\"pointer-events-none absolute top-1/2 z-30 max-w-[15rem] -translate-y-1/2 scale-95 rounded-xl bg-(--p-text-color) px-3 py-2 text-start opacity-0 shadow-xl transition duration-200 ease-out group-hover:scale-100 group-hover:opacity-100\"\r\n [class.start-full]=\"side === 'start'\"\r\n [class.ms-3]=\"side === 'start'\"\r\n [class.end-full]=\"side === 'end'\"\r\n [class.me-3]=\"side === 'end'\"\r\n >\r\n <span\r\n class=\"block truncate text-[12.5px] font-semibold leading-tight text-(--p-content-background)\"\r\n >{{ n.label }}</span\r\n >\r\n <span\r\n class=\"mt-0.5 block truncate text-[10px] font-semibold text-(--p-content-background)/55\"\n >{{ n.typeLabel }}</span\r\n >\r\n </span>\r\n </span>\n</ng-template>\n\r\n<!-- Type glyph badge for a context value (string / number / date / \u2026). -->\r\n<ng-template #typeBadge let-kind>\r\n <span\r\n class=\"flex h-5 min-w-5 shrink-0 items-center justify-center rounded-[5px] border border-(--p-content-border-color) bg-(--p-content-background) px-1 font-mono text-[11px] font-bold leading-none text-(--p-text-color)\"\r\n >\r\n @switch (kind) {\r\n @case (\"string\") {\r\n T\r\n }\r\n @case (\"number\") {\r\n #\r\n }\r\n @case (\"object\") {\r\n {}\r\n }\r\n @case (\"array\") {\r\n [ ]\r\n }\r\n @case (\"boolean\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2.4]\"\r\n >\r\n <rect x=\"3\" y=\"7\" width=\"18\" height=\"10\" rx=\"5\" />\r\n <circle cx=\"9\" cy=\"12\" r=\"2\" fill=\"currentColor\" stroke=\"none\" />\r\n </svg>\r\n }\r\n @case (\"date\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <rect x=\"4\" y=\"5\" width=\"16\" height=\"16\" rx=\"2.5\" />\r\n <path d=\"M4 9h16M8 3v4M16 3v4\" stroke-linecap=\"round\" />\r\n </svg>\r\n }\r\n @case (\"file\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <path\r\n d=\"M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8Z\"\r\n />\r\n <path d=\"M14 3v5h5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" />\r\n </svg>\r\n }\r\n @case (\"secret\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <rect x=\"5\" y=\"11\" width=\"14\" height=\"9\" rx=\"2\" />\r\n <path d=\"M8 11V8a4 4 0 0 1 8 0v3\" stroke-linecap=\"round\" />\r\n </svg>\r\n }\r\n @case (\"reference\") {\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3 w-3 fill-none stroke-current stroke-[2]\"\r\n >\r\n <path\r\n d=\"M7 17 17 7M9 7h8v8\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n }\r\n @default {\r\n \u2022\r\n }\r\n }\r\n </span>\r\n</ng-template>\n\n<ng-template #inputPickerTree let-nodes=\"nodes\" let-level=\"level\">\n <ul class=\"border-t border-(--p-content-border-color)\">\n @for (node of nodes; track node.id) {\n <li class=\"border-b border-(--p-content-border-color) last:border-b-0\">\n <div\n [fpDragData]=\"node.rawExpression\"\n class=\"fp-data-row group flex cursor-grab items-start gap-2 px-3 py-2 transition duration-200 ease-out hover:bg-(--p-surface-50) active:cursor-grabbing\"\n [style.padding-inline-start.rem]=\"0.75 + level * 0.75\"\n >\n @if (node.children.length > 0) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"\n isInputPickerNodeExpanded(node)\n ? 'arrow.chevron-down'\n : 'arrow.chevron-right'\n \"\n [tooltip]=\"\n isInputPickerNodeExpanded(node) ? 'Collapse' : 'Expand'\n \"\n styleClass=\"fp-modal-tree-toggle\"\n (onClick)=\"toggleInputPickerNode(node)\"\n />\n } @else {\n <span class=\"h-7 w-7 shrink-0\"></span>\n }\n\n <ng-container\n *ngTemplateOutlet=\"\n typeBadge;\n context: { $implicit: fieldKind(node.type) }\n \"\n />\n\n <div class=\"min-w-0 flex-1 space-y-0.5\">\n <div class=\"flex min-w-0 items-center gap-1.5\">\n <span\n class=\"min-w-0 flex-1 truncate text-[12.5px] font-semibold text-(--p-text-color)\"\n >{{ node.label }}</span\n >\n <span\n class=\"shrink-0 rounded-md bg-(--p-surface-100) px-1.5 py-0.5 text-[10px] font-semibold text-(--p-text-muted-color)\"\n >{{ node.type }}</span\n >\n @if (node.availability !== \"available\" || node.valuePreview) {\n <span\n class=\"shrink-0 rounded-md border border-(--p-content-border-color) bg-(--p-content-background) px-1.5 py-0.5 text-[10px] font-medium text-(--p-text-muted-color)\"\n >{{ pickerNodeStatus(node) }}</span\n >\n }\n </div>\n <div\n class=\"truncate font-mono text-[10.5px] leading-4 text-(--p-text-muted-color)\"\n >\n {{ node.rawExpression }}\n </div>\n <div\n class=\"truncate font-mono text-[10.5px] leading-4 text-(--p-primary-color)\"\n >\n {{ node.expression }}\n </div>\n @if (node.description) {\n <p\n class=\"m-0 line-clamp-2 text-[11px] leading-4 text-(--p-text-muted-color)\"\n >\n {{ node.description }}\n </p>\n }\n </div>\n </div>\n\n @if (isInputPickerNodeExpanded(node)) {\n <ng-container\n *ngTemplateOutlet=\"\n inputPickerTree;\n context: { nodes: node.children, level: level + 1 }\n \"\n />\n }\n </li>\n }\n </ul>\n</ng-template>\n\n<!-- Outer wrapper: positioning context only (overflow visible) so the connected-\n node avatars can straddle the modal border. The inner card clips its own\n rounded corners + carries the shadow. -->\n<div class=\"relative h-full w-full text-(--p-text-color)\">\n <div\r\n class=\"flex h-full min-h-0 flex-col overflow-hidden rounded-2xl bg-(--p-content-background) shadow-2xl shadow-slate-950/25\"\r\n >\r\n <!-- ============================ HEADER ============================ -->\r\n <header\r\n class=\"flex shrink-0 items-center gap-3.5 border-b border-(--p-content-border-color) px-6 py-4\"\r\n >\r\n @if (currentChip(); as chip) {\r\n <span\r\n class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl shadow-sm\"\r\n [style.color]=\"chip.color\"\r\n [style.background]=\"chip.bg\"\r\n >\r\n <mt-icon [icon]=\"chip.icon\" class=\"text-[22px]\" />\r\n </span>\r\n } @else {\r\n <span\r\n class=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-2xl bg-(--p-surface-100) text-(--p-text-muted-color)\"\r\n >\r\n <mt-icon\r\n [icon]=\"\r\n selectionKind() === 'connection'\r\n ? 'dev.dataflow-04'\r\n : 'general.settings-01'\r\n \"\r\n class=\"text-[22px]\"\r\n />\r\n </span>\r\n }\r\n\r\n <div class=\"min-w-0 flex-1\">\n <h2\n class=\"truncate text-[17px] font-semibold tracking-tight text-(--p-text-color)\"\n >\n {{ activeDescriptorLabel() }}\n </h2>\n <p\n class=\"m-0 mt-1 line-clamp-2 text-[12px] leading-5 text-(--p-text-muted-color)\"\n >\n {{ activeDescriptorSubtitle() }}\n </p>\n </div>\n\r\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.x-close\"\n [tooltip]=\"'flowplus.inspector.modal.close' | transloco\"\n (onClick)=\"close()\"\n />\n </header>\r\n\r\n <!-- ===================== BODY: INPUT | CONFIG | OUTPUT ===================== -->\r\n <div\r\n #resizeHost\r\n class=\"relative flex min-h-0 flex-1\"\r\n [class.select-none]=\"resizing()\"\r\n [style.cursor]=\"resizing() ? 'col-resize' : null\"\r\n >\r\n <!-- Navigation loading beat \u2014 a slim primary progress sweep across the top\r\n while the modal re-derives for a newly selected (connected) step. -->\r\n @if (navigating()) {\r\n <div\r\n class=\"pointer-events-none absolute inset-x-0 top-0 z-40 h-[3px] overflow-hidden\"\r\n >\r\n <div\r\n class=\"h-full w-2/5 rounded-full bg-(--p-primary-color) animate-[fp-busy-bar-slide_0.9s_ease-in-out_infinite]\"\r\n ></div>\r\n </div>\r\n }\r\n\r\n @if (selectionKind() !== \"trigger\") {\n <!-- --------------------------- INPUT --------------------------- -->\n <section\n class=\"flex h-full min-h-0 shrink-0 flex-col bg-(--p-surface-50)\"\n [style.width.px]=\"leftWidth()\"\n >\n <div\r\n class=\"shrink-0 border-b border-(--p-content-border-color) px-5 py-4\"\r\n >\r\n <div class=\"flex items-center justify-between gap-2\">\r\n <p class=\"text-[13.5px] font-semibold text-(--p-text-color)\">\n {{\n selectionKind() === \"trigger\"\n ? \"Trigger payload\"\n : (\"flowplus.inspector.modal.inputsTitle\" | transloco)\n }}\n </p>\n @if (selectionKind() === \"step\") {\r\n <div class=\"flex items-center gap-1.5\">\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [tooltip]=\"'flowplus.context.pickerPlaceholder' | transloco\"\n [styleClass]=\"\n contextSearchOpen()\n ? 'fp-modal-icon-button is-active'\n : 'fp-modal-icon-button'\n \"\n (onClick)=\"toggleContextSearch()\"\n />\n <div\r\n class=\"inline-flex rounded-lg bg-(--p-surface-100) p-0.5 text-[11px] font-semibold\"\r\n >\r\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewSchema' | transloco\"\n [styleClass]=\"\n inputView() === 'schema'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"inputView.set('schema')\"\n />\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewJson' | transloco\"\n [styleClass]=\"\n inputView() === 'json'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"inputView.set('json')\"\n />\n </div>\r\n </div>\r\n }\r\n </div>\r\n\r\n @if (selectionKind() === \"step\" && contextSearchOpen()) {\r\n <div class=\"mt-3\">\r\n <mt-text-field\r\n [ngModel]=\"contextSearch()\"\r\n (ngModelChange)=\"contextSearch.set($event)\"\r\n [placeholder]=\"'flowplus.context.pickerPlaceholder' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fp-scroll min-h-0 flex-1 overflow-y-auto px-5 py-4\">\r\n @if (selectionKind() === \"step\") {\r\n @if (inputView() === \"schema\") {\n <div class=\"space-y-3\">\n <section class=\"space-y-2.5\">\n @if (filteredInputPickerGroups().length > 0) {\n <div class=\"flex items-center justify-between gap-3\">\n <h3\n class=\"flex items-center gap-2 text-[13px] font-semibold text-(--p-text-color)\"\n >\n {{\r\n \"flowplus.inspector.modal.availableData\" | transloco\r\n }}\r\n </h3>\n </div>\n }\n\n @if (filteredInputPickerGroups().length === 0) {\n <div\n class=\"flex min-h-[18rem] flex-col items-center justify-center gap-4 px-6 py-10 text-center\"\n >\n <span\r\n class=\"flex h-14 w-14 items-center justify-center rounded-2xl bg-(--p-surface-100) text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-7 w-7 fill-none stroke-current stroke-[1.6]\"\r\n >\r\n <path\r\n d=\"M3 7.5 12 3l9 4.5-9 4.5-9-4.5Z\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path\r\n d=\"m3 12 9 4.5 9-4.5M3 16.5 12 21l9-4.5\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n </span>\r\n <div class=\"space-y-1\">\r\n <p\r\n class=\"text-[14px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{\r\n \"flowplus.inspector.modal.noInputData\" | transloco\r\n }}\r\n </p>\r\n <p\r\n class=\"mx-auto max-w-[16rem] text-[12px] leading-5 text-(--p-text-muted-color)\"\r\n >\r\n {{\r\n \"flowplus.inspector.modal.noAvailableData\"\r\n | transloco\r\n }}\r\n </p>\r\n </div>\r\n </div>\r\n } @else {\r\n <p\r\n class=\"flex items-center gap-1.5 text-[11px] text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 16 16\"\r\n class=\"h-3.5 w-3.5 shrink-0 fill-current opacity-70\"\r\n aria-hidden=\"true\"\r\n >\r\n <circle cx=\"5\" cy=\"4\" r=\"1.1\" />\r\n <circle cx=\"9\" cy=\"4\" r=\"1.1\" />\r\n <circle cx=\"5\" cy=\"8\" r=\"1.1\" />\r\n <circle cx=\"9\" cy=\"8\" r=\"1.1\" />\r\n <circle cx=\"5\" cy=\"12\" r=\"1.1\" />\r\n <circle cx=\"9\" cy=\"12\" r=\"1.1\" />\r\n </svg>\r\n {{ \"flowplus.inspector.modal.dragHint\" | transloco }}\r\n </p>\r\n\n <div class=\"space-y-1.5\">\n @for (group of filteredInputPickerGroups(); track group.id) {\n <section\n class=\"overflow-hidden rounded-xl border border-(--p-content-border-color) bg-(--p-content-background)\"\n >\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-down\"\n [label]=\"\n group.label + ' (' + group.children.length + ')'\n \"\n styleClass=\"fp-modal-group-toggle\"\n (onClick)=\"toggleGroup(group.id)\"\n />\n\n @if (!isGroupCollapsed(group.id)) {\n <ng-container\n *ngTemplateOutlet=\"\n inputPickerTree;\n context: { nodes: group.children, level: 0 }\n \"\n />\n }\n </section>\n }\n </div>\r\n }\r\n </section>\r\n </div>\r\n } @else {\r\n <pre\r\n class=\"fp-scroll overflow-auto rounded-lg border border-(--p-content-border-color) bg-(--p-surface-100) p-3 font-mono text-[11px] leading-5 text-(--p-text-color)\"\r\n >{{ inputJson() }}</pre\r\n >\r\n }\r\n } @else if (\r\n selectionKind() === \"connection\" && connectionEndpoints();\r\n as endpoints\r\n ) {\r\n <section class=\"space-y-3\">\r\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.modal.connectionSummary\" | transloco }}\r\n </h3>\r\n <div\r\n class=\"space-y-2.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\r\n >\r\n <div>\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.source\" | transloco }}\r\n </div>\r\n <div class=\"mt-1 text-[13px] text-(--p-text-color)\">\r\n {{ endpoints.source }}\r\n </div>\r\n </div>\r\n <div>\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.target\" | transloco }}\r\n </div>\r\n <div class=\"mt-1 text-[13px] text-(--p-text-color)\">\r\n {{ endpoints.target }}\r\n </div>\r\n </div>\r\n @if (selectedConnection()!.sourcePortKey) {\r\n <div\r\n class=\"font-mono text-[11px] text-(--p-text-muted-color)\"\r\n >\r\n {{ selectedConnection()!.sourcePortKey }} ->\r\n {{ selectedConnection()!.targetPortKey || \"in\" }}\r\n </div>\r\n }\r\n </div>\n </section>\n } @else if (selectionKind() === \"trigger\") {\n <section class=\"space-y-3\">\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\n Trigger payload and setup\n </h3>\n <div\n class=\"space-y-2.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\n >\n @if (selectedTriggerPayloadSchemaJson()) {\n <div>\n <div\n class=\"text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Payload schema\n </div>\n <pre\n class=\"fp-scroll mt-1 max-h-36 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTriggerPayloadSchemaJson() }}</pre\n >\n </div>\n } @else {\n <p class=\"m-0 text-[12px] leading-5 text-(--p-text-muted-color)\">\n The backend did not return a payload schema for this trigger.\n </p>\n }\n @if (selectedTriggerPayloadSampleJson()) {\n <div>\n <div\n class=\"text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Sample payload\n </div>\n <pre\n class=\"fp-scroll mt-1 max-h-36 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTriggerPayloadSampleJson() }}</pre\n >\n </div>\n }\n @if (selectedTrigger()?.type === \"WebhookTrigger\") {\n <div class=\"space-y-1 text-[12px] leading-5 text-(--p-text-muted-color)\">\n <div>Expected method: {{ selectedTrigger()?.expectedHttpMethod ?? \"POST\" }}</div>\n <div>\n Content types:\n {{ (selectedTrigger()?.allowedContentTypes ?? []).join(\", \") || \"Backend default\" }}\n </div>\n @if (selectedTrigger()?.exampleCurl) {\n <pre\n class=\"fp-scroll mt-1 max-h-28 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTrigger()?.exampleCurl }}</pre\n >\n }\n </div>\n }\n @if (selectedTrigger()?.type === \"FormSubmitTrigger\") {\n <div class=\"rounded-lg bg-(--p-surface-100) px-3 py-2 text-[12px] text-(--p-text-muted-color)\">\n Pinned formVersionId:\n <code>{{ selectedTriggerFormVersionId() ?? \"Not selected\" }}</code>\n </div>\n }\n @if (selectedTrigger()?.type === \"ScheduleTrigger\") {\n <div>\n <div\n class=\"text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Schedule config\n </div>\n <pre\n class=\"fp-scroll mt-1 max-h-36 overflow-auto rounded-lg bg-(--p-surface-100) p-2.5 font-mono text-[11px] leading-5 text-(--p-text-color)\"\n >{{ selectedTriggerConfigJson() }}</pre\n >\n </div>\n }\n </div>\n\n @if (selectedTriggerIssues().length > 0) {\n <div class=\"space-y-1.5\">\n @for (issue of selectedTriggerIssues(); track issue.code + issue.message) {\n <div\n class=\"rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-[12px] leading-5 text-amber-800\"\n >\n {{ resolveTranslatable(issue.message) }}\n </div>\n }\n </div>\n }\n </section>\n } @else if (selectionKind() === \"workflow\") {\n <section class=\"space-y-3\">\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\n {{ \"flowplus.inspector.workflow.triggers\" | transloco }}\r\n </h3>\r\n @if (store.triggers().length > 0) {\r\n <ul class=\"space-y-1.5\">\r\n @for (trigger of store.triggers(); track trigger.id) {\r\n <li\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-2.5\"\r\n >\r\n <div class=\"flex items-center justify-between gap-3\">\r\n <div>\r\n <div\r\n class=\"text-[13px] font-medium text-(--p-text-color)\"\r\n >\r\n {{ triggerLabel(trigger) }}\r\n </div>\r\n <div\r\n class=\"mt-0.5 text-[11px] text-(--p-text-muted-color)\"\n >\r\n {{ trigger.type }}\r\n </div>\r\n </div>\r\n <span\r\n class=\"rounded-md px-2 py-0.5 text-[10.5px] font-medium\"\r\n [class.bg-emerald-50]=\"trigger.enabled\"\r\n [class.text-emerald-700]=\"trigger.enabled\"\r\n [class.bg-slate-100]=\"!trigger.enabled\"\r\n [class.text-slate-500]=\"!trigger.enabled\"\r\n >\r\n {{\r\n (trigger.enabled\r\n ? \"flowplus.inspector.workflow.triggerEnabled\"\r\n : \"flowplus.inspector.modal.disabled\"\r\n ) | transloco\r\n }}\r\n </span>\r\n </div>\r\n </li>\r\n }\r\n </ul>\r\n } @else {\r\n <p\r\n class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-[12.5px] text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.workflow.noTriggers\" | transloco }}\r\n </p>\r\n }\r\n </section>\r\n } @else {\r\n <p\r\n class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-[12.5px] text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.noSelectionHint\" | transloco }}\r\n </p>\r\n }\r\n </div>\r\n </section>\n\n <!-- resize handle: INPUT | CONFIG -->\n <div\n class=\"fp-resizer\"\n [class.is-active]=\"resizing() === 'left'\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n (pointerdown)=\"startResize('left', $event)\"\n (pointermove)=\"onResizeMove($event)\"\n (pointerup)=\"endResize($event)\"\n (pointercancel)=\"endResize($event)\"\n >\n <span class=\"fp-resizer-grip\" aria-hidden=\"true\"></span>\n </div>\n }\n\n <!-- --------------------------- CONFIGURATION --------------------------- -->\n <section\n class=\"flex h-full min-h-0 min-w-0 flex-1 flex-col bg-(--p-content-background)\"\n >\n @if (selectionKind() === \"trigger\" && selectedTrigger(); as trigger) {\n <fp-automation-smart-editor [trigger]=\"trigger\" mode=\"modal\" />\n } @else {\n <fp-inspector-shell\n mode=\"modal\"\n (stepChange)=\"onStepChange($event)\"\n (connectionChange)=\"onConnectionChange($event)\"\n (workflowChange)=\"onWorkflowChange($event)\"\n />\n }\n </section>\n\r\n <!-- resize handle: CONFIG | OUTPUT -->\r\n <div\r\n class=\"fp-resizer\"\r\n [class.is-active]=\"resizing() === 'right'\"\r\n role=\"separator\"\r\n aria-orientation=\"vertical\"\r\n (pointerdown)=\"startResize('right', $event)\"\r\n (pointermove)=\"onResizeMove($event)\"\r\n (pointerup)=\"endResize($event)\"\r\n (pointercancel)=\"endResize($event)\"\r\n >\r\n <span class=\"fp-resizer-grip\" aria-hidden=\"true\"></span>\r\n </div>\r\n\r\n <!-- --------------------------- OUTPUT --------------------------- -->\r\n <section\r\n class=\"flex h-full min-h-0 shrink-0 flex-col bg-(--p-surface-50)\"\r\n [style.width.px]=\"rightWidth()\"\r\n >\r\n <div\r\n class=\"shrink-0 border-b border-(--p-content-border-color) px-5 py-4\"\r\n >\r\n @if (selectionKind() === \"step\" && mockEditorOpen()) {\r\n <!-- EDIT OUTPUT replaces the OUTPUT header while pinning mock data -->\r\n <div class=\"flex items-center justify-between gap-2\">\r\n <p\r\n class=\"text-[13.5px] font-semibold text-(--p-text-color)\"\n >\r\n {{ \"flowplus.inspector.modal.editOutput\" | transloco }}\r\n </p>\r\n <div class=\"flex items-center gap-1.5\">\r\n <mt-button\r\n variant=\"outlined\"\r\n size=\"small\"\r\n [label]=\"'flowplus.inspector.modal.cancel' | transloco\"\r\n (onClick)=\"cancelMockEditor()\"\r\n />\r\n <mt-button\r\n severity=\"primary\"\r\n size=\"small\"\r\n [label]=\"'flowplus.inspector.modal.save' | transloco\"\r\n (onClick)=\"saveMock()\"\r\n />\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"flex items-center justify-between gap-2\">\r\n <p\n class=\"text-[13.5px] font-semibold text-(--p-text-color)\"\n >\n {{\n selectionKind() === \"trigger\"\n ? \"Trigger runtime\"\n : (\"flowplus.inspector.modal.outputsTitle\" | transloco)\n }}\n </p>\n @if (selectionKind() === \"step\" && hasOutputData()) {\r\n <div\r\n class=\"inline-flex rounded-lg bg-(--p-surface-100) p-0.5 text-[11px] font-semibold\"\r\n >\r\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewSchema' | transloco\"\n [styleClass]=\"\n outputView() === 'schema'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"outputView.set('schema')\"\n />\n <mt-button\n variant=\"text\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.viewJson' | transloco\"\n [styleClass]=\"\n outputView() === 'json'\n ? 'fp-modal-segment-button is-active'\n : 'fp-modal-segment-button'\n \"\n (onClick)=\"outputView.set('json')\"\n />\n </div>\r\n }\r\n </div>\r\n }\r\n </div>\r\n\r\n <div class=\"fp-scroll min-h-0 flex-1 overflow-y-auto\">\r\n @if (selectionKind() === \"step\") {\n @if (selectedStepRuntimeState(); as runtimeState) {\n <section\n class=\"mx-5 mt-5 rounded-md border border-surface-200 bg-surface-0\"\n >\n <header\n class=\"flex items-center justify-between gap-2 border-b border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <div class=\"min-w-0\">\n <h3 class=\"m-0 text-[12.5px] font-semibold text-(--p-text-color)\">\n Latest run\n </h3>\n <p class=\"m-0 mt-0.5 truncate text-[11px] text-(--p-text-muted-color)\">\n {{ runtimeState.status }}\n @if (runtimeState.durationMs != null) {\n / {{ runtimeState.durationMs }} ms\n }\n </p>\n </div>\n <div class=\"flex shrink-0 items-center gap-1\">\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.eye\"\n label=\"Inspect data\"\n [loading]=\"runtimeNodeDataState() === 'loading'\"\n [disabled]=\"!runtimeState.nodeRunId\"\n (onClick)=\"inspectSelectedNodeData(false)\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open full run'\"\n (onClick)=\"openSelectedRuntimeRun()\"\n />\n </div>\n </header>\n <div class=\"space-y-2 px-3 py-2.5\">\n @if (runtimeState.errorMessage) {\n <p class=\"m-0 text-[11.5px] leading-5 text-[rgb(var(--fp-error))]\">\n {{ runtimeState.errorMessage }}\n </p>\n }\n @if (runtimeState.waitReason) {\n <p class=\"m-0 text-[11.5px] leading-5 text-[rgb(var(--fp-warning))]\">\n {{ runtimeState.waitReason }}\n </p>\n }\n <div class=\"flex flex-wrap gap-1.5\">\n @if (runtimeState.attempts != null) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Attempts {{ runtimeState.attempts }}\n </span>\n }\n @if (runtimeState.routeOutputKey) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Output {{ runtimeState.routeOutputKey }}\n </span>\n }\n @if (runtimeState.selectedCaseKey) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Case\n {{\n runtimeState.selectedCaseLabel\n ? runtimeState.selectedCaseLabel + \" / \"\n : \"\"\n }}{{ runtimeState.selectedCaseKey }}\n </span>\n }\n @if (runtimeState.childExecutionId) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n Child execution {{ runtimeState.childExecutionId }}\n </span>\n }\n </div>\n @if (runtimeNodeData(); as data) {\n @if (runtimeNodeNotice(data); as notice) {\n <p class=\"m-0 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1.5 text-[11.5px] leading-5 text-(--p-text-muted-color)\">\n {{ notice }}\n </p>\n }\n @if (runtimeNodeSummaryRows().length > 0) {\n <ul class=\"space-y-1\">\n @for (row of runtimeNodeSummaryRows(); track row.key) {\n <li class=\"flex items-center gap-2 rounded-md bg-(--p-surface-50) px-2 py-1.5 text-[11.5px]\">\n <span class=\"min-w-0 flex-1 truncate font-mono text-(--p-text-color)\">\n {{ row.key }}\n </span>\n <span class=\"shrink-0 text-(--p-text-muted-color)\">\n {{ row.preview }}\n </span>\n </li>\n }\n </ul>\n }\n @if (!data.includeFull) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.eye\"\n label=\"Load full data\"\n (onClick)=\"inspectSelectedNodeData(true)\"\n />\n }\n } @else if (runtimeNodeDataError()) {\n <p class=\"m-0 text-[11.5px] leading-5 text-[rgb(var(--fp-error))]\">\n {{ runtimeNodeDataError() }}\n </p>\n }\n </div>\n </section>\n }\n @if (mockEditorOpen()) {\n <!-- ----- mock data editor (header lives in the panel header) ----- -->\r\n <div class=\"flex h-full min-h-0 flex-col gap-2 p-4\">\r\n <div\n class=\"fp-code-frame flex min-h-0 flex-1 overflow-hidden rounded-xl border bg-(--p-surface-100) p-2 transition\"\n [style.borderColor]=\"\n mockError() ? '#f43f5e' : 'var(--p-content-border-color)'\n \"\n >\n <mt-textarea-field\n class=\"min-h-0 flex-1 [&_textarea]:fp-scroll [&_textarea]:min-h-[18rem] [&_textarea]:resize-none [&_textarea]:bg-transparent [&_textarea]:font-mono [&_textarea]:text-[12px] [&_textarea]:leading-5\"\n [field]=\"false\"\n [ngModel]=\"mockText()\"\n (ngModelChange)=\"mockText.set($event ?? '')\"\n [pInputs]=\"mockTextareaInputs\"\n [maxLength]=\"null\"\n rows=\"18\"\n />\n </div>\n @if (mockError()) {\r\n <p\r\n class=\"flex items-center gap-1.5 text-[11.5px] text-rose-600\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3.5 w-3.5 fill-none stroke-current stroke-[2]\"\r\n >\r\n <circle cx=\"12\" cy=\"12\" r=\"9\" />\r\n <path d=\"M12 8v4M12 16h.01\" stroke-linecap=\"round\" />\r\n </svg>\r\n {{ mockError() }}\r\n </p>\r\n }\r\n </div>\r\n } @else if (hasOutputData()) {\r\n <!-- ----- output payload ----- -->\r\n <div class=\"space-y-3 px-5 py-5\">\r\n @if (outputIsMock()) {\r\n <div\r\n class=\"flex items-center justify-between gap-2 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-2.5 py-1.5\"\r\n >\r\n <span\r\n class=\"flex items-center gap-1.5 text-[11px] font-medium text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-3.5 w-3.5 fill-none stroke-current stroke-[2] text-(--p-primary-color)\"\r\n >\r\n <path\r\n d=\"M9 4v6l-2 2v2h10v-2l-2-2V4M12 16v4\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n </svg>\r\n {{ \"flowplus.inspector.modal.mockPinned\" | transloco }}\r\n </span>\n <div class=\"flex items-center gap-1\">\n <mt-button\n variant=\"text\"\n severity=\"primary\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.edit' | transloco\"\n (onClick)=\"openMockEditor()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.clear' | transloco\"\n (onClick)=\"clearMock()\"\n />\n </div>\r\n </div>\r\n }\r\n\r\n @if (outputView() === \"json\") {\r\n <pre\r\n class=\"fp-scroll overflow-auto rounded-lg border border-(--p-content-border-color) bg-(--p-surface-100) p-3 font-mono text-[11px] leading-5 text-(--p-text-color)\"\r\n >{{ outputJson() }}</pre\r\n >\r\n } @else {\r\n <ul class=\"space-y-2\">\r\n @for (row of outputRows(); track row.key) {\r\n <li\r\n class=\"flex items-center gap-2.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3.5 py-2.5 shadow-sm\"\r\n >\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n typeBadge;\r\n context: { $implicit: fieldKind(row.type) }\r\n \"\r\n />\r\n <span\r\n class=\"min-w-0 flex-1 truncate font-mono text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ row.key }}</span\r\n >\r\n <span\r\n class=\"shrink-0 truncate font-mono text-[12px] text-(--p-text-muted-color)\"\r\n >{{ row.preview }}</span\r\n >\r\n </li>\r\n }\r\n </ul>\r\n }\r\n </div>\r\n } @else {\r\n <!-- ----- empty state ----- -->\r\n <div\r\n class=\"flex h-full flex-col items-center justify-center gap-4 px-6 py-10 text-center\"\r\n >\r\n <span\r\n class=\"flex h-14 w-14 items-center justify-center rounded-2xl bg-(--p-surface-100) text-(--p-text-muted-color)\"\r\n >\r\n <svg\r\n viewBox=\"0 0 24 24\"\r\n class=\"h-7 w-7 fill-none stroke-current stroke-[1.6]\"\r\n >\r\n <path\r\n d=\"M3 12h12m0 0-4-4m4 4-4 4\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"\r\n />\r\n <path d=\"M21 4v16\" stroke-linecap=\"round\" />\r\n </svg>\r\n </span>\r\n <div class=\"space-y-1\">\r\n <p class=\"text-[14px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.modal.noOutputData\" | transloco }}\r\n </p>\r\n <p class=\"text-[12px] leading-5 text-(--p-text-muted-color)\">\r\n {{ \"flowplus.inspector.modal.noOutputHint\" | transloco }}\r\n </p>\r\n </div>\r\n <div class=\"flex flex-col items-center gap-2\">\r\n <mt-button\r\n severity=\"primary\"\r\n size=\"small\"\r\n icon=\"general.play\"\r\n [label]=\"'flowplus.inspector.modal.executeStep' | transloco\"\r\n [loading]=\"runningTest()\"\r\n [disabled]=\"runningTest() || !supportsTestRun()\"\r\n (onClick)=\"executeStep()\"\r\n />\r\n <mt-button\n variant=\"text\"\n severity=\"primary\"\n size=\"small\"\n [label]=\"'flowplus.inspector.modal.setMockData' | transloco\"\n (onClick)=\"openMockEditor()\"\n />\n </div>\r\n @if (!supportsTestRun()) {\r\n <p\r\n class=\"max-w-[16rem] rounded-lg bg-(--p-surface-100) px-3 py-2 text-[11px] leading-5 text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.testRun.unavailableHint\" | transloco }}\r\n </p>\r\n }\r\n </div>\r\n }\n } @else if (selectionKind() === \"trigger\" && selectedTrigger(); as trigger) {\n <div class=\"space-y-3 px-5 py-5\">\n <section\n class=\"rounded-md border border-surface-200 bg-surface-0\"\n >\n <header\n class=\"flex items-center justify-between gap-2 border-b border-surface-200 bg-surface-50 px-3 py-2\"\n >\n <h3 class=\"m-0 text-[13px] font-semibold text-(--p-text-color)\">\n Latest run\n </h3>\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open full run'\"\n [disabled]=\"!selectedTriggerRuntimeState()?.executionId\"\n (onClick)=\"openSelectedRuntimeRun()\"\n />\n </header>\n <div class=\"p-3\">\n @if (selectedTriggerRuntimeState(); as state) {\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n class=\"rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px] font-semibold text-(--p-text-color)\"\n >{{ state.status }}</span\n >\n @if (state.executionId) {\n <span class=\"font-mono text-[11px] text-(--p-text-muted-color)\">\n #{{ state.executionId }}\n </span>\n }\n @if (state.durationMs != null) {\n <span class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ state.durationMs }} ms\n </span>\n }\n </div>\n @if (state.errorMessage) {\n <p class=\"mt-2 text-[12px] leading-5 text-[rgb(var(--fp-error))]\">\n {{ state.errorMessage }}\n </p>\n }\n } @else {\n <p class=\"m-0 text-[12.5px] leading-5 text-(--p-text-muted-color)\">\n No runtime state is loaded for this trigger yet. Execute a\n real run or open the executions page for history.\n </p>\n }\n </div>\n </section>\n </div>\n } @else if (selectionKind() === \"connection\") {\n <div class=\"px-4 py-4\">\n <section class=\"space-y-3\">\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.tabs.routing\" | transloco }}\r\n </h3>\r\n <div\r\n class=\"space-y-2 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\r\n >\r\n @if (selectedConnectionActionsLabel()) {\r\n <div class=\"font-mono text-[11.5px] text-(--p-text-color)\">\r\n {{ selectedConnectionActionsLabel() }}\r\n </div>\r\n } @else {\r\n <div class=\"text-[12.5px] text-(--p-text-muted-color)\">\r\n {{\r\n \"flowplus.inspector.modal.noConfiguredOutputs\"\r\n | transloco\r\n }}\r\n </div>\r\n }\r\n @if (selectedConnection()!.expressionText) {\r\n <pre\r\n class=\"fp-scroll overflow-auto rounded-lg bg-(--p-surface-100) p-3 font-mono text-[11px] leading-5 text-(--p-text-color)\"\r\n >{{ selectedConnection()!.expressionText }}</pre\r\n >\r\n }\r\n </div>\r\n </section>\r\n </div>\r\n } @else if (selectionKind() === \"workflow\") {\r\n <div class=\"px-4 py-4\">\r\n <section class=\"space-y-3\">\r\n <h3 class=\"text-[13px] font-semibold text-(--p-text-color)\">\r\n {{ \"flowplus.inspector.modal.workflowHealth\" | transloco }}\r\n </h3>\r\n <div class=\"grid grid-cols-3 gap-2\">\r\n <div\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-center\"\r\n >\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.errorsLabel\" | transloco }}\r\n </div>\r\n <div\r\n class=\"mt-1.5 text-xl font-semibold text-(--p-text-color)\"\r\n >\r\n {{ workflowHealth().errors }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-center\"\r\n >\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.warningsLabel\" | transloco }}\r\n </div>\r\n <div\r\n class=\"mt-1.5 text-xl font-semibold text-(--p-text-color)\"\r\n >\r\n {{ workflowHealth().warnings }}\r\n </div>\r\n </div>\r\n <div\r\n class=\"rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-center\"\r\n >\r\n <div\r\n class=\"text-[11.5px] font-medium text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.inspector.modal.publishLabel\" | transloco }}\r\n </div>\r\n <div\r\n class=\"mt-1.5 text-[13px] font-semibold\"\r\n [class.text-emerald-600]=\"workflowHealth().canPublish\"\r\n [class.text-amber-600]=\"!workflowHealth().canPublish\"\r\n >\r\n {{\r\n (workflowHealth().canPublish\r\n ? \"flowplus.inspector.modal.publishReady\"\r\n : \"flowplus.inspector.modal.publishBlocked\"\r\n ) | transloco\r\n }}\r\n </div>\r\n </div>\r\n </div>\r\n </section>\r\n </div>\r\n } @else {\r\n <div class=\"px-4 py-4\">\r\n <p\r\n class=\"rounded-lg border border-dashed border-(--p-content-border-color) bg-(--p-content-background) px-3 py-3 text-[12.5px] text-(--p-text-muted-color)\"\r\n >\r\n {{ \"flowplus.inspector.noSelectionHint\" | transloco }}\r\n </p>\r\n </div>\r\n }\r\n </div>\r\n </section>\r\n </div>\r\n\r\n <!-- Connected-node avatars \u2014 straddle the modal border (half in / half out),\r\n vertically centered. Rendered outside the clipping card so they protrude. -->\r\n @if (selectionKind() === \"step\") {\r\n @if (previousNeighbours().length > 0) {\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 start-0 z-50 flex -translate-x-1/2 flex-col items-center justify-center gap-3 rtl:translate-x-1/2\"\r\n >\r\n @for (n of previousNeighbours(); track n.stepId) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n edgeTab;\r\n context: { $implicit: n, side: 'start' }\r\n \"\r\n />\r\n }\r\n </div>\r\n }\r\n @if (nextNeighbours().length > 0) {\r\n <div\r\n class=\"pointer-events-none absolute inset-y-0 end-0 z-50 flex translate-x-1/2 flex-col items-center justify-center gap-3 rtl:-translate-x-1/2\"\r\n >\r\n @for (n of nextNeighbours(); track n.stepId) {\r\n <ng-container\r\n *ngTemplateOutlet=\"\r\n edgeTab;\r\n context: { $implicit: n, side: 'end' }\r\n \"\r\n />\r\n }\r\n </div>\r\n }\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: InspectorShellComponent, selector: "fp-inspector-shell", inputs: ["mode"], outputs: ["stepChange", "connectionChange", "workflowChange", "closeRequested"] }, { kind: "component", type: AutomationSmartEditorComponent, selector: "fp-automation-smart-editor", inputs: ["step", "trigger", "mode", "view"] }, { kind: "directive", type: DragDataDirective, selector: "[fpDragData]", inputs: ["fpDragData"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
16492
16492
|
}
|
|
16493
16493
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: InspectorModalBodyComponent, decorators: [{
|
|
16494
16494
|
type: Component,
|
|
@@ -16865,7 +16865,7 @@ class TriggerConfigDialogComponent {
|
|
|
16865
16865
|
/>
|
|
16866
16866
|
</div>
|
|
16867
16867
|
</div>
|
|
16868
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "
|
|
16868
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
16869
16869
|
}
|
|
16870
16870
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TriggerConfigDialogComponent, decorators: [{
|
|
16871
16871
|
type: Component,
|
|
@@ -17054,7 +17054,7 @@ class NodeFinderDrawerBodyComponent {
|
|
|
17054
17054
|
return t && t !== key ? t : category;
|
|
17055
17055
|
}
|
|
17056
17056
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NodeFinderDrawerBodyComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17057
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NodeFinderDrawerBodyComponent, isStandalone: true, selector: "fp-node-finder-drawer-body", ngImport: i0, template: "<div>\r\n <header>\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div role=\"list\">\r\n @for (group of groups(); track group.category) {\r\n <section\r\n class=\"flex flex-col gap-2\"\r\n role=\"group\"\r\n [attr.aria-label]=\"group.label\"\r\n >\r\n <header\r\n class=\"mx-1 mb-1 text-[12px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ group.label }}\r\n </header>\r\n @for (item of group.items; track paletteItemKey(item, $index)) {\n <div\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"!flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-xl border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-[11px] shadow-[0_1px_1px_rgba(15,23,42,0.04)] transition-[border-color,box-shadow,background] duration-150 hover:border-[var(--fp-avatar-color)] hover:shadow-[0_1px_2px_rgba(15,23,42,0.06),0_1px_3px_rgba(15,23,42,0.04)] active:cursor-grabbing focus-visible:border-(--p-primary-color) focus-visible:shadow-[0_0_0_2px_color-mix(in_srgb,var(--p-primary-color)_30%,transparent)] focus-visible:outline-none\"\n role=\"listitem\"\n [attr.title]=\"\n resolve(item.description) || resolve(item.displayName)\n \"\n tabindex=\"0\"\n >\n <ng-template fExternalItemPreview>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"iconColor(item)\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,var(--fp-avatar-color)_16%,transparent)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span\n class=\"block truncate text-[13.5px] font-bold leading-5\"\n >\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n </ng-template>\n <span\n class=\"[&_.p-avatar]:h-8 [&_.p-avatar]:w-8 [&_.p-avatar]:bg-[color-mix(in_srgb,var(--fp-avatar-color)_16%,transparent)] [&_.p-avatar]:text-[var(--fp-avatar-color)] [&_mt-icon]:size-4 [&_svg]:size-4 [&_svg]:text-[var(--fp-avatar-color)]\"\n [style.--fp-avatar-color]=\"iconColor(item)\"\n >\r\n <mt-avatar size=\"normal\" shape=\"circle\" [icon]=\"iconFor(item)\" />\r\n </span>\r\n <div class=\"flex min-w-0 flex-1 flex-col gap-[3px]\">\r\n <div\r\n class=\"text-[13px] font-semibold leading-[1.25] text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </section>\r\n }\r\n\r\n @if (groups().length === 0) {\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: FExternalItem, selector: "[fExternalItem]", inputs: ["fExternalItemId", "fData", "fDisabled", "fPreview", "fPreviewMatchSize", "fPlaceholder"], outputs: ["fPreviewChange", "fPlaceholderChange"] }, { kind: "directive", type: FExternalItemPreview, selector: "ng-template[fExternalItemPreview]" }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
17057
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: NodeFinderDrawerBodyComponent, isStandalone: true, selector: "fp-node-finder-drawer-body", ngImport: i0, template: "<div>\r\n <header>\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div role=\"list\">\r\n @for (group of groups(); track group.category) {\r\n <section\r\n class=\"flex flex-col gap-2\"\r\n role=\"group\"\r\n [attr.aria-label]=\"group.label\"\r\n >\r\n <header\r\n class=\"mx-1 mb-1 text-[12px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ group.label }}\r\n </header>\r\n @for (item of group.items; track paletteItemKey(item, $index)) {\n <div\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"!flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-xl border border-(--p-content-border-color) bg-(--p-content-background) px-3 py-[11px] shadow-[0_1px_1px_rgba(15,23,42,0.04)] transition-[border-color,box-shadow,background] duration-150 hover:border-[var(--fp-avatar-color)] hover:shadow-[0_1px_2px_rgba(15,23,42,0.06),0_1px_3px_rgba(15,23,42,0.04)] active:cursor-grabbing focus-visible:border-(--p-primary-color) focus-visible:shadow-[0_0_0_2px_color-mix(in_srgb,var(--p-primary-color)_30%,transparent)] focus-visible:outline-none\"\n role=\"listitem\"\n [attr.title]=\"\n resolve(item.description) || resolve(item.displayName)\n \"\n tabindex=\"0\"\n >\n <ng-template fExternalItemPreview>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"iconColor(item)\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,var(--fp-avatar-color)_16%,transparent)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span\n class=\"block truncate text-[13.5px] font-bold leading-5\"\n >\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n </ng-template>\n <span\n class=\"[&_.p-avatar]:h-8 [&_.p-avatar]:w-8 [&_.p-avatar]:bg-[color-mix(in_srgb,var(--fp-avatar-color)_16%,transparent)] [&_.p-avatar]:text-[var(--fp-avatar-color)] [&_mt-icon]:size-4 [&_svg]:size-4 [&_svg]:text-[var(--fp-avatar-color)]\"\n [style.--fp-avatar-color]=\"iconColor(item)\"\n >\r\n <mt-avatar size=\"normal\" shape=\"circle\" [icon]=\"iconFor(item)\" />\r\n </span>\r\n <div class=\"flex min-w-0 flex-1 flex-col gap-[3px]\">\r\n <div\r\n class=\"text-[13px] font-semibold leading-[1.25] text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n </section>\r\n }\r\n\r\n @if (groups().length === 0) {\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\r\n }\r\n </div>\r\n</div>\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: FExternalItem, selector: "[fExternalItem]", inputs: ["fExternalItemId", "fData", "fDisabled", "fPreview", "fPreviewMatchSize", "fPlaceholder"], outputs: ["fPreviewChange", "fPlaceholderChange"] }, { kind: "directive", type: FExternalItemPreview, selector: "ng-template[fExternalItemPreview]" }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
17058
17058
|
}
|
|
17059
17059
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: NodeFinderDrawerBodyComponent, decorators: [{
|
|
17060
17060
|
type: Component,
|
|
@@ -17589,7 +17589,7 @@ class WorkflowStudioPageComponent {
|
|
|
17589
17589
|
return this.transloco.translate('flowplus.studio.errorGeneric');
|
|
17590
17590
|
}
|
|
17591
17591
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowStudioPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17592
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowStudioPageComponent, isStandalone: true, selector: "fp-workflow-studio-page", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'flowplus'\">\r\n <div class=\"p-5\">\r\n <div class=\"mb-4 flex flex-wrap items-center justify-between gap-3\">\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n ></mt-breadcrumb>\r\n\r\n <div class=\"flex items-center gap-2\">\r\n <div class=\"min-w-[260px]\">\n <mt-text-field\n [ngModel]=\"search\"\n (ngModelChange)=\"onSearchChange($event)\"\n [placeholder]=\"t('studio.search')\"\n icon=\"general.search-md\"\n />\n </div>\n <mt-button\n icon=\"general.plus\"\r\n severity=\"primary\"\r\n [label]=\"t('studio.createProcess')\"\r\n (onClick)=\"openCreate()\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-4\">\n <mt-tabs\n mode=\"underline\"\n [options]=\"filterOptions()\"\n [active]=\"filter()\"\n (onChange)=\"setFilter($event)\"\n />\n </div>\n\n @if (error()) {\n <div\n class=\"mb-4 flex items-center justify-between gap-3 rounded-xl border border-[rgb(var(--fp-error))]/24 bg-[rgb(var(--fp-error))]/8 px-4 py-3\"\n role=\"status\"\n >\n <p class=\"m-0 text-[12px] text-[rgb(var(--fp-error))]\">\n {{ error() }}\n </p>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"t('studio.retry')\"\n (onClick)=\"retry()\"\n />\n </div>\n }\n\n @if (loading()) {\n <div\n class=\"grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6\"\n >\n @for (i of skeletonRange; track $index) {\n <p-skeleton width=\"100%\" height=\"210px\" />\n }\n </div>\n } @else if (visibleItems().length === 0 && hasActiveFilters()) {\n <div\n class=\"flex min-h-[240px] flex-col items-center justify-center rounded-2xl border border-dashed border-(--p-content-border-color) bg-(--p-surface-50) px-6 text-center\"\n >\n <h3 class=\"mb-2 text-[18px] font-semibold\">\n {{ t(\"studio.emptyFilteredTitle\") }}\n </h3>\n <p class=\"m-0 max-w-[520px] text-[13px] text-(--p-text-muted-color)\">\n {{ t(\"studio.emptyFilteredHint\") }}\n </p>\n <div class=\"mt-4\">\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.x-close\"\n [label]=\"t('studio.clearFilters')\"\n (onClick)=\"clearFilters()\"\n />\n </div>\n </div>\n } @else if (visibleItems().length === 0) {\n <div\n class=\"grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6\"\n >\n <mt-card class=\"block h-full\">\n <div class=\"flex h-full min-h-[210px] flex-col items-center p-1\">\r\n <div class=\"flex flex-col items-center gap-3\">\r\n <mt-avatar size=\"large\" shape=\"square\" icon=\"general.plus\" />\r\n <h4\r\n class=\"flex min-h-14 items-center justify-center px-2 text-center font-bold\"\r\n >\r\n {{ t(\"studio.createProcess\") }}\r\n </h4>\r\n </div>\r\n <div class=\"mt-auto flex w-full items-center justify-center pt-4\">\r\n <mt-button\r\n [label]=\"t('studio.createProcess')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"openCreate()\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n </div>\r\n } @else {\n <div class=\"flex flex-col gap-5\">\n <div\n class=\"grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6\"\n >\n @for (w of visibleItems(); track w.id) {\n <mt-card\n class=\"block h-full cursor-pointer\"\n (click)=\"openBuilder(w)\"\n >\n <div class=\"flex h-full min-h-[210px] flex-col items-center p-1\">\n <div class=\"flex flex-col items-center gap-3\">\n <mt-avatar\n size=\"large\"\n shape=\"square\"\n [icon]=\"workflowIcon(w)\"\n />\n <h4\n class=\"flex min-h-14 items-center justify-center px-2 text-center font-semibold\"\n >\n {{ resolve(w.name) }}\n </h4>\n </div>\n\n <div\n class=\"mt-auto flex w-full flex-wrap items-center justify-center gap-1.5 pt-3\"\n >\n @if (w.isPublished) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10.5px] font-medium bg-[rgb(var(--fp-commit))]/16 text-[rgb(var(--fp-commit))]\"\n >{{ t(\"topbar.published\") }}</span\n >\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-surface-100) px-2 py-0.5 text-[10.5px] font-medium text-(--p-text-muted-color)\"\n >{{ t(\"topbar.draft\") }}</span\n >\n }\n @if (!w.isValid) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10.5px] font-medium bg-[rgb(var(--fp-error))]/14 text-[rgb(var(--fp-error))]\"\n >{{ t(\"validation.invalid\") }}</span\n >\n }\n @if (w.projectId) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-surface-100) px-2 py-0.5 text-[10.5px] font-medium text-(--p-text-muted-color)\"\n >{{ w.projectId }}</span\n >\n }\n </div>\n\n <div class=\"mt-2 flex w-full items-center justify-between gap-3\">\n <span\n class=\"truncate text-[11.5px] text-(--p-text-muted-color)\"\n >{{ w.updatedAt | date: \"mediumDate\" }}</span\n >\n @if (w.lastExecutionStatus) {\n <span\n class=\"truncate text-[11px] font-medium text-(--p-text-muted-color)\"\n >{{ w.lastExecutionStatus }}</span\n >\n }\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n icon=\"custom.config\"\n (onClick)=\"openBuilder(w); $event.stopPropagation()\"\n />\n </div>\n </div>\n </mt-card>\n }\n\n <mt-card class=\"block h-full\">\n <div class=\"flex h-full min-h-[210px] flex-col items-center p-1\">\n <div class=\"flex flex-col items-center gap-3\">\n <mt-avatar size=\"large\" shape=\"square\" icon=\"general.plus\" />\n <h4\n class=\"flex min-h-14 items-center justify-center px-2 text-center font-bold\"\n >\n {{ t(\"studio.createProcess\") }}\n </h4>\n </div>\n <div class=\"mt-auto flex w-full items-center justify-center pt-4\">\n <mt-button\n [label]=\"t('studio.createProcess')\"\n icon=\"general.plus\"\n (onClick)=\"openCreate()\"\n />\n </div>\n </div>\n </mt-card>\n\n </div>\n\n @if (hasMore()) {\n <div class=\"flex justify-center\">\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.chevron-down\"\n [label]=\"t('studio.loadMore')\"\n [loading]=\"loadingMore()\"\n (onClick)=\"loadMore()\"\n />\n </div>\n }\n </div>\n }\n </div>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i2.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i3.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
|
|
17592
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowStudioPageComponent, isStandalone: true, selector: "fp-workflow-studio-page", ngImport: i0, template: "<ng-container *transloco=\"let t; prefix: 'flowplus'\">\r\n <div class=\"p-5\">\r\n <div class=\"mb-4 flex flex-wrap items-center justify-between gap-3\">\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start mx-1'\"\r\n ></mt-breadcrumb>\r\n\r\n <div class=\"flex items-center gap-2\">\r\n <div class=\"min-w-[260px]\">\n <mt-text-field\n [ngModel]=\"search\"\n (ngModelChange)=\"onSearchChange($event)\"\n [placeholder]=\"t('studio.search')\"\n icon=\"general.search-md\"\n />\n </div>\n <mt-button\n icon=\"general.plus\"\r\n severity=\"primary\"\r\n [label]=\"t('studio.createProcess')\"\r\n (onClick)=\"openCreate()\"\r\n />\r\n </div>\r\n </div>\r\n\r\n <div class=\"mb-4\">\n <mt-tabs\n mode=\"underline\"\n [options]=\"filterOptions()\"\n [active]=\"filter()\"\n (onChange)=\"setFilter($event)\"\n />\n </div>\n\n @if (error()) {\n <div\n class=\"mb-4 flex items-center justify-between gap-3 rounded-xl border border-[rgb(var(--fp-error))]/24 bg-[rgb(var(--fp-error))]/8 px-4 py-3\"\n role=\"status\"\n >\n <p class=\"m-0 text-[12px] text-[rgb(var(--fp-error))]\">\n {{ error() }}\n </p>\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"t('studio.retry')\"\n (onClick)=\"retry()\"\n />\n </div>\n }\n\n @if (loading()) {\n <div\n class=\"grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6\"\n >\n @for (i of skeletonRange; track $index) {\n <p-skeleton width=\"100%\" height=\"210px\" />\n }\n </div>\n } @else if (visibleItems().length === 0 && hasActiveFilters()) {\n <div\n class=\"flex min-h-[240px] flex-col items-center justify-center rounded-2xl border border-dashed border-(--p-content-border-color) bg-(--p-surface-50) px-6 text-center\"\n >\n <h3 class=\"mb-2 text-[18px] font-semibold\">\n {{ t(\"studio.emptyFilteredTitle\") }}\n </h3>\n <p class=\"m-0 max-w-[520px] text-[13px] text-(--p-text-muted-color)\">\n {{ t(\"studio.emptyFilteredHint\") }}\n </p>\n <div class=\"mt-4\">\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.x-close\"\n [label]=\"t('studio.clearFilters')\"\n (onClick)=\"clearFilters()\"\n />\n </div>\n </div>\n } @else if (visibleItems().length === 0) {\n <div\n class=\"grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6\"\n >\n <mt-card class=\"block h-full\">\n <div class=\"flex h-full min-h-[210px] flex-col items-center p-1\">\r\n <div class=\"flex flex-col items-center gap-3\">\r\n <mt-avatar size=\"large\" shape=\"square\" icon=\"general.plus\" />\r\n <h4\r\n class=\"flex min-h-14 items-center justify-center px-2 text-center font-bold\"\r\n >\r\n {{ t(\"studio.createProcess\") }}\r\n </h4>\r\n </div>\r\n <div class=\"mt-auto flex w-full items-center justify-center pt-4\">\r\n <mt-button\r\n [label]=\"t('studio.createProcess')\"\r\n icon=\"general.plus\"\r\n (onClick)=\"openCreate()\"\r\n />\r\n </div>\r\n </div>\r\n </mt-card>\r\n </div>\r\n } @else {\n <div class=\"flex flex-col gap-5\">\n <div\n class=\"grid grid-cols-2 gap-5 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6\"\n >\n @for (w of visibleItems(); track w.id) {\n <mt-card\n class=\"block h-full cursor-pointer\"\n (click)=\"openBuilder(w)\"\n >\n <div class=\"flex h-full min-h-[210px] flex-col items-center p-1\">\n <div class=\"flex flex-col items-center gap-3\">\n <mt-avatar\n size=\"large\"\n shape=\"square\"\n [icon]=\"workflowIcon(w)\"\n />\n <h4\n class=\"flex min-h-14 items-center justify-center px-2 text-center font-semibold\"\n >\n {{ resolve(w.name) }}\n </h4>\n </div>\n\n <div\n class=\"mt-auto flex w-full flex-wrap items-center justify-center gap-1.5 pt-3\"\n >\n @if (w.isPublished) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10.5px] font-medium bg-[rgb(var(--fp-commit))]/16 text-[rgb(var(--fp-commit))]\"\n >{{ t(\"topbar.published\") }}</span\n >\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-surface-100) px-2 py-0.5 text-[10.5px] font-medium text-(--p-text-muted-color)\"\n >{{ t(\"topbar.draft\") }}</span\n >\n }\n @if (!w.isValid) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10.5px] font-medium bg-[rgb(var(--fp-error))]/14 text-[rgb(var(--fp-error))]\"\n >{{ t(\"validation.invalid\") }}</span\n >\n }\n @if (w.projectId) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-surface-100) px-2 py-0.5 text-[10.5px] font-medium text-(--p-text-muted-color)\"\n >{{ w.projectId }}</span\n >\n }\n </div>\n\n <div class=\"mt-2 flex w-full items-center justify-between gap-3\">\n <span\n class=\"truncate text-[11.5px] text-(--p-text-muted-color)\"\n >{{ w.updatedAt | date: \"mediumDate\" }}</span\n >\n @if (w.lastExecutionStatus) {\n <span\n class=\"truncate text-[11px] font-medium text-(--p-text-muted-color)\"\n >{{ w.lastExecutionStatus }}</span\n >\n }\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n icon=\"custom.config\"\n (onClick)=\"openBuilder(w); $event.stopPropagation()\"\n />\n </div>\n </div>\n </mt-card>\n }\n\n <mt-card class=\"block h-full\">\n <div class=\"flex h-full min-h-[210px] flex-col items-center p-1\">\n <div class=\"flex flex-col items-center gap-3\">\n <mt-avatar size=\"large\" shape=\"square\" icon=\"general.plus\" />\n <h4\n class=\"flex min-h-14 items-center justify-center px-2 text-center font-bold\"\n >\n {{ t(\"studio.createProcess\") }}\n </h4>\n </div>\n <div class=\"mt-auto flex w-full items-center justify-center pt-4\">\n <mt-button\n [label]=\"t('studio.createProcess')\"\n icon=\"general.plus\"\n (onClick)=\"openCreate()\"\n />\n </div>\n </div>\n </mt-card>\n\n </div>\n\n @if (hasMore()) {\n <div class=\"flex justify-center\">\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.chevron-down\"\n [label]=\"t('studio.loadMore')\"\n [loading]=\"loadingMore()\"\n (onClick)=\"loadMore()\"\n />\n </div>\n }\n </div>\n }\n </div>\n</ng-container>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "directive", type: i2.TranslocoDirective, selector: "[transloco]", inputs: ["transloco", "translocoParams", "translocoScope", "translocoRead", "translocoPrefix", "translocoLang", "translocoLoadingTpl"] }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Card, selector: "mt-card", inputs: ["class", "title", "paddingless"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "ngmodule", type: SkeletonModule }, { kind: "component", type: i3.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }] });
|
|
17593
17593
|
}
|
|
17594
17594
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowStudioPageComponent, decorators: [{
|
|
17595
17595
|
type: Component,
|
|
@@ -17641,9 +17641,9 @@ class ProblemsPanelComponent {
|
|
|
17641
17641
|
icon: 'alert.alert-triangle',
|
|
17642
17642
|
issues: all,
|
|
17643
17643
|
tone: all.some((issue) => issue.severity === 'Error')
|
|
17644
|
-
? '
|
|
17644
|
+
? 'danger'
|
|
17645
17645
|
: all.length
|
|
17646
|
-
? '
|
|
17646
|
+
? 'warn'
|
|
17647
17647
|
: 'neutral',
|
|
17648
17648
|
},
|
|
17649
17649
|
{
|
|
@@ -17652,10 +17652,10 @@ class ProblemsPanelComponent {
|
|
|
17652
17652
|
description: 'Issues that prevent publishing this draft.',
|
|
17653
17653
|
icon: 'alert.alert-octagon',
|
|
17654
17654
|
issues: publishBlockers,
|
|
17655
|
-
tone: publishBlockers.length ? '
|
|
17655
|
+
tone: publishBlockers.length ? 'danger' : 'neutral',
|
|
17656
17656
|
},
|
|
17657
17657
|
this.group('errors', 'Errors', 'Backend error severity.', 'alert.alert-circle', groups?.errors ?? v?.errors ?? []),
|
|
17658
|
-
this.group('warnings', 'Warnings', 'Backend warning severity.', 'alert.announcement-02', groups?.warnings ?? v?.warnings ?? [], '
|
|
17658
|
+
this.group('warnings', 'Warnings', 'Backend warning severity.', 'alert.announcement-02', groups?.warnings ?? v?.warnings ?? [], 'warn'),
|
|
17659
17659
|
this.group('graphErrors', 'Graph', 'Start-node and reachability checks.', 'dev.git-branch-01', groups?.graphErrors ?? []),
|
|
17660
17660
|
this.group('triggerErrors', 'Triggers', 'Trigger configuration and bindings.', 'general.zap-fast', groups?.triggerErrors ?? []),
|
|
17661
17661
|
this.group('nodeErrors', 'Nodes', 'Node configuration issues.', 'general.target-05', groups?.nodeErrors ?? []),
|
|
@@ -17672,6 +17672,13 @@ class ProblemsPanelComponent {
|
|
|
17672
17672
|
this.group('issues', 'All issues', '', 'alert.alert-triangle', []));
|
|
17673
17673
|
}, ...(ngDevMode ? [{ debugName: "selectedGroup" }] : /* istanbul ignore next */ []));
|
|
17674
17674
|
selectedIssues = computed(() => this.selectedGroup().issues, ...(ngDevMode ? [{ debugName: "selectedIssues" }] : /* istanbul ignore next */ []));
|
|
17675
|
+
issueGroupOptions = computed(() => this.issueGroups().map((group) => ({
|
|
17676
|
+
label: group.label,
|
|
17677
|
+
value: group.key,
|
|
17678
|
+
icon: group.icon,
|
|
17679
|
+
badge: this.groupIssueBadge(group),
|
|
17680
|
+
severity: this.groupSeverity(group),
|
|
17681
|
+
})), ...(ngDevMode ? [{ debugName: "issueGroupOptions" }] : /* istanbul ignore next */ []));
|
|
17675
17682
|
issueRows = computed(() => this.selectedIssues().map((issue) => ({
|
|
17676
17683
|
key: this.issueKey(issue),
|
|
17677
17684
|
issue,
|
|
@@ -17686,23 +17693,25 @@ class ProblemsPanelComponent {
|
|
|
17686
17693
|
label: 'Issue',
|
|
17687
17694
|
type: 'custom',
|
|
17688
17695
|
customCellTpl: this.issueCellTpl(),
|
|
17689
|
-
width: '
|
|
17696
|
+
width: '56%',
|
|
17690
17697
|
},
|
|
17691
17698
|
{
|
|
17692
17699
|
key: 'target',
|
|
17693
17700
|
label: 'Target',
|
|
17701
|
+
type: 'text',
|
|
17694
17702
|
width: '20%',
|
|
17695
17703
|
},
|
|
17696
17704
|
{
|
|
17697
17705
|
key: 'code',
|
|
17698
17706
|
label: 'Code',
|
|
17699
|
-
|
|
17707
|
+
type: 'text',
|
|
17708
|
+
width: '16%',
|
|
17700
17709
|
},
|
|
17701
17710
|
{
|
|
17702
17711
|
key: 'status',
|
|
17703
17712
|
label: 'Status',
|
|
17704
17713
|
type: 'status',
|
|
17705
|
-
width: '
|
|
17714
|
+
width: '7rem',
|
|
17706
17715
|
},
|
|
17707
17716
|
], ...(ngDevMode ? [{ debugName: "issueColumns" }] : /* istanbul ignore next */ []));
|
|
17708
17717
|
issueKey(i) {
|
|
@@ -17720,6 +17729,10 @@ class ProblemsPanelComponent {
|
|
|
17720
17729
|
this.selectedGroupKey.set(group.key);
|
|
17721
17730
|
this.selectedIssueKey.set(null);
|
|
17722
17731
|
}
|
|
17732
|
+
selectGroupKey(value) {
|
|
17733
|
+
this.selectedGroupKey.set(value);
|
|
17734
|
+
this.selectedIssueKey.set(null);
|
|
17735
|
+
}
|
|
17723
17736
|
selectIssue(issue) {
|
|
17724
17737
|
this.selectedIssueKey.set(this.issueKey(issue));
|
|
17725
17738
|
this.focus.emit(issue);
|
|
@@ -17734,84 +17747,19 @@ class ProblemsPanelComponent {
|
|
|
17734
17747
|
return 'alert.alert-circle';
|
|
17735
17748
|
return 'general.info-circle';
|
|
17736
17749
|
}
|
|
17737
|
-
severityClass(i) {
|
|
17738
|
-
if (i.severity === 'Error')
|
|
17739
|
-
return 'error';
|
|
17740
|
-
if (i.severity === 'Warning')
|
|
17741
|
-
return 'warning';
|
|
17742
|
-
return 'info';
|
|
17743
|
-
}
|
|
17744
|
-
severityIconClass(i) {
|
|
17745
|
-
const base = 'mt-0.5 shrink-0 [&_svg]:size-4';
|
|
17746
|
-
if (i.severity === 'Error')
|
|
17747
|
-
return `${base} text-[rgb(var(--fp-error))]`;
|
|
17748
|
-
if (i.severity === 'Warning')
|
|
17749
|
-
return `${base} text-[rgb(202_138_4)]`;
|
|
17750
|
-
return `${base} text-[rgb(var(--fp-automated))]`;
|
|
17751
|
-
}
|
|
17752
|
-
severityPillClass(i) {
|
|
17753
|
-
const base = 'inline-flex items-center rounded-full border px-2 py-0.5 text-[10.5px] font-semibold';
|
|
17754
|
-
if (i.severity === 'Error') {
|
|
17755
|
-
return `${base} border-[rgb(var(--fp-error)/0.28)] bg-[rgb(var(--fp-error)/0.08)] text-[rgb(var(--fp-error))]`;
|
|
17756
|
-
}
|
|
17757
|
-
if (i.severity === 'Warning') {
|
|
17758
|
-
return `${base} border-[rgb(234_179_8/0.35)] bg-[rgb(234_179_8/0.12)] text-[rgb(146_91_0)]`;
|
|
17759
|
-
}
|
|
17760
|
-
return `${base} border-(--p-content-border-color) bg-(--p-surface-100) text-(--p-text-muted-color)`;
|
|
17761
|
-
}
|
|
17762
|
-
issueCellClass(row) {
|
|
17763
|
-
const active = this.selectedIssueKey() === row.key;
|
|
17764
|
-
const base = 'flex min-w-0 items-start gap-2 rounded-lg px-1.5 py-1 transition-colors duration-150';
|
|
17765
|
-
if (active && row.issue.severity === 'Error') {
|
|
17766
|
-
return `${base} bg-[rgb(var(--fp-error)/0.06)]`;
|
|
17767
|
-
}
|
|
17768
|
-
if (active && row.issue.severity === 'Warning') {
|
|
17769
|
-
return `${base} bg-[rgb(234_179_8/0.09)]`;
|
|
17770
|
-
}
|
|
17771
|
-
if (active)
|
|
17772
|
-
return `${base} bg-(--p-surface-100)`;
|
|
17773
|
-
return base;
|
|
17774
|
-
}
|
|
17775
|
-
groupButtonClass(group) {
|
|
17776
|
-
const active = this.selectedGroupKey() === group.key;
|
|
17777
|
-
const base = 'flex w-full items-center gap-2 rounded-lg border px-2.5 py-2 text-start transition-colors duration-150';
|
|
17778
|
-
if (active && group.tone === 'error') {
|
|
17779
|
-
return `${base} border-[rgb(var(--fp-error)/0.34)] bg-[rgb(var(--fp-error)/0.08)]`;
|
|
17780
|
-
}
|
|
17781
|
-
if (active && group.tone === 'warning') {
|
|
17782
|
-
return `${base} border-[rgb(234_179_8/0.36)] bg-[rgb(234_179_8/0.12)]`;
|
|
17783
|
-
}
|
|
17784
|
-
if (active) {
|
|
17785
|
-
return `${base} border-[rgb(var(--fp-automated)/0.34)] bg-[rgb(var(--fp-automated)/0.08)]`;
|
|
17786
|
-
}
|
|
17787
|
-
return `${base} border-transparent hover:border-(--p-content-border-color) hover:bg-(--p-surface-100)`;
|
|
17788
|
-
}
|
|
17789
|
-
groupIconClass(group) {
|
|
17790
|
-
const base = 'shrink-0 [&_svg]:size-4';
|
|
17791
|
-
if (group.tone === 'error')
|
|
17792
|
-
return `${base} text-[rgb(var(--fp-error))]`;
|
|
17793
|
-
if (group.tone === 'warning')
|
|
17794
|
-
return `${base} text-[rgb(202_138_4)]`;
|
|
17795
|
-
if (group.tone === 'info')
|
|
17796
|
-
return `${base} text-[rgb(var(--fp-automated))]`;
|
|
17797
|
-
return `${base} text-(--p-text-muted-color)`;
|
|
17798
|
-
}
|
|
17799
17750
|
groupIssueBadge(group) {
|
|
17800
|
-
return String(group.issues.length);
|
|
17801
|
-
}
|
|
17802
|
-
|
|
17803
|
-
|
|
17804
|
-
|
|
17805
|
-
if (
|
|
17806
|
-
return
|
|
17807
|
-
|
|
17808
|
-
|
|
17809
|
-
|
|
17810
|
-
|
|
17811
|
-
|
|
17812
|
-
return `${base} bg-(--p-surface-100)`;
|
|
17813
|
-
}
|
|
17814
|
-
return `${base} hover:bg-(--p-surface-50)`;
|
|
17751
|
+
return group.issues.length > 0 ? String(group.issues.length) : null;
|
|
17752
|
+
}
|
|
17753
|
+
groupSeverity(group) {
|
|
17754
|
+
if (group.issues.length === 0)
|
|
17755
|
+
return 'secondary';
|
|
17756
|
+
if (group.tone === 'danger')
|
|
17757
|
+
return 'danger';
|
|
17758
|
+
if (group.tone === 'warn')
|
|
17759
|
+
return 'warn';
|
|
17760
|
+
if (group.tone === 'info')
|
|
17761
|
+
return 'info';
|
|
17762
|
+
return 'secondary';
|
|
17815
17763
|
}
|
|
17816
17764
|
message(i) {
|
|
17817
17765
|
return resolveTranslatable(i.message, this.transloco.getActiveLang());
|
|
@@ -17823,9 +17771,6 @@ class ProblemsPanelComponent {
|
|
|
17823
17771
|
const targetText = target ? `${targetType} ${target}` : targetType;
|
|
17824
17772
|
return [targetText, field].filter(Boolean).join(' | ');
|
|
17825
17773
|
}
|
|
17826
|
-
blocksPublishLabel(i) {
|
|
17827
|
-
return i.blocksPublish ? 'Blocks publish' : 'Non-blocking';
|
|
17828
|
-
}
|
|
17829
17774
|
detailValue(value) {
|
|
17830
17775
|
if (value == null || value === '')
|
|
17831
17776
|
return '-';
|
|
@@ -17834,9 +17779,9 @@ class ProblemsPanelComponent {
|
|
|
17834
17779
|
group(key, label, description, icon, issues, tone) {
|
|
17835
17780
|
const resolvedTone = tone ??
|
|
17836
17781
|
(issues.some((issue) => issue.severity === 'Error')
|
|
17837
|
-
? '
|
|
17782
|
+
? 'danger'
|
|
17838
17783
|
: issues.some((issue) => issue.severity === 'Warning')
|
|
17839
|
-
? '
|
|
17784
|
+
? 'warn'
|
|
17840
17785
|
: issues.length
|
|
17841
17786
|
? 'info'
|
|
17842
17787
|
: 'neutral');
|
|
@@ -17852,11 +17797,11 @@ class ProblemsPanelComponent {
|
|
|
17852
17797
|
return { key: 'info', display: issue.severity ?? 'Info', color: '#0891b2' };
|
|
17853
17798
|
}
|
|
17854
17799
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ProblemsPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
17855
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ProblemsPanelComponent, isStandalone: true, selector: "fp-problems-panel", outputs: { focus: "focus" }, viewQueries: [{ propertyName: "issueCellTpl", first: true, predicate: ["issueCellTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "
|
|
17800
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: ProblemsPanelComponent, isStandalone: true, selector: "fp-problems-panel", outputs: { focus: "focus" }, viewQueries: [{ propertyName: "issueCellTpl", first: true, predicate: ["issueCellTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-md border border-surface-300 bg-(--p-content-background) shadow-sm\"\n>\n <div\n class=\"flex flex-none items-center border-b border-surface-300 bg-(--p-surface-50) px-3\"\n aria-label=\"Validation issue groups\"\n >\n <mt-tabs\n mode=\"underline\"\n [fluid]=\"true\"\n [options]=\"issueGroupOptions()\"\n [active]=\"selectedGroupKey()\"\n (onChange)=\"selectGroupKey($event)\"\n />\n </div>\n\n @if (selectedIssues().length === 0) {\n <div\n class=\"flex min-h-0 flex-1 flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\n >\n <mt-icon\n icon=\"general.check-circle\"\n class=\"mb-3 text-emerald-600 [&_svg]:size-7\"\n />\n <p class=\"m-0 text-[12.5px]\">\n {{ \"flowplus.validation.noIssues\" | transloco }}\n </p>\n </div>\n } @else {\n <div class=\"fp-scroll min-h-0 flex-1 overflow-auto\">\n <mt-table\n [data]=\"issueRows()\"\n [columns]=\"issueColumns()\"\n dataKey=\"key\"\n size=\"small\"\n [clickableRows]=\"true\"\n [noCard]=\"true\"\n [tableLayout]=\"'fixed'\"\n [pageSize]=\"15\"\n [rowsPerPageOptions]=\"[15, 25, 50]\"\n [alwaysShowPaginator]=\"false\"\n (rowClick)=\"selectIssueRow($event)\"\n />\n </div>\n }\n</div>\n\n<ng-template #issueCellTpl let-row>\n <div class=\"flex min-w-0 items-start gap-2\">\n <mt-icon\n [icon]=\"severityIcon(row.issue)\"\n [class]=\"\n 'mt-0.5 shrink-0 [&_svg]:size-4 ' +\n (row.issue.severity === 'Error'\n ? 'text-red-600'\n : row.issue.severity === 'Warning'\n ? 'text-amber-600'\n : 'text-sky-600')\n \"\n />\n <span class=\"min-w-0 flex-1\">\n <span\n class=\"block truncate text-sm font-semibold leading-5 text-(--p-text-color)\"\n >\n {{ row.message }}\n </span>\n @if (row.issue.suggestedFix) {\n <span\n class=\"block truncate text-xs leading-4 text-(--p-text-muted-color)\"\n >\n {{ row.issue.suggestedFix }}\n </span>\n }\n </span>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
17856
17801
|
}
|
|
17857
17802
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: ProblemsPanelComponent, decorators: [{
|
|
17858
17803
|
type: Component,
|
|
17859
|
-
args: [{ selector: 'fp-problems-panel', imports: [CommonModule, TranslocoModule,
|
|
17804
|
+
args: [{ selector: 'fp-problems-panel', imports: [CommonModule, TranslocoModule, Icon, Table, Tabs], template: "<div\n class=\"flex h-full min-h-0 min-w-0 flex-col overflow-hidden rounded-md border border-surface-300 bg-(--p-content-background) shadow-sm\"\n>\n <div\n class=\"flex flex-none items-center border-b border-surface-300 bg-(--p-surface-50) px-3\"\n aria-label=\"Validation issue groups\"\n >\n <mt-tabs\n mode=\"underline\"\n [fluid]=\"true\"\n [options]=\"issueGroupOptions()\"\n [active]=\"selectedGroupKey()\"\n (onChange)=\"selectGroupKey($event)\"\n />\n </div>\n\n @if (selectedIssues().length === 0) {\n <div\n class=\"flex min-h-0 flex-1 flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\n >\n <mt-icon\n icon=\"general.check-circle\"\n class=\"mb-3 text-emerald-600 [&_svg]:size-7\"\n />\n <p class=\"m-0 text-[12.5px]\">\n {{ \"flowplus.validation.noIssues\" | transloco }}\n </p>\n </div>\n } @else {\n <div class=\"fp-scroll min-h-0 flex-1 overflow-auto\">\n <mt-table\n [data]=\"issueRows()\"\n [columns]=\"issueColumns()\"\n dataKey=\"key\"\n size=\"small\"\n [clickableRows]=\"true\"\n [noCard]=\"true\"\n [tableLayout]=\"'fixed'\"\n [pageSize]=\"15\"\n [rowsPerPageOptions]=\"[15, 25, 50]\"\n [alwaysShowPaginator]=\"false\"\n (rowClick)=\"selectIssueRow($event)\"\n />\n </div>\n }\n</div>\n\n<ng-template #issueCellTpl let-row>\n <div class=\"flex min-w-0 items-start gap-2\">\n <mt-icon\n [icon]=\"severityIcon(row.issue)\"\n [class]=\"\n 'mt-0.5 shrink-0 [&_svg]:size-4 ' +\n (row.issue.severity === 'Error'\n ? 'text-red-600'\n : row.issue.severity === 'Warning'\n ? 'text-amber-600'\n : 'text-sky-600')\n \"\n />\n <span class=\"min-w-0 flex-1\">\n <span\n class=\"block truncate text-sm font-semibold leading-5 text-(--p-text-color)\"\n >\n {{ row.message }}\n </span>\n @if (row.issue.suggestedFix) {\n <span\n class=\"block truncate text-xs leading-4 text-(--p-text-muted-color)\"\n >\n {{ row.issue.suggestedFix }}\n </span>\n }\n </span>\n </div>\n</ng-template>\n" }]
|
|
17860
17805
|
}], propDecorators: { issueCellTpl: [{ type: i0.ViewChild, args: ['issueCellTpl', { isSignal: true }] }], focus: [{ type: i0.Output, args: ["focus"] }] } });
|
|
17861
17806
|
|
|
17862
17807
|
class TestRunPanelComponent {
|
|
@@ -18054,7 +17999,7 @@ class TestRunPanelComponent {
|
|
|
18054
17999
|
return !translated || translated === key ? fallback : translated;
|
|
18055
18000
|
}
|
|
18056
18001
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TestRunPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18057
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: TestRunPanelComponent, isStandalone: true, selector: "fp-test-run-panel", ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n @if (triggerOptions().length === 0) {\n <div\n class=\"flex flex-col items-center justify-center rounded-xl border border-dashed border-(--p-content-border-color) bg-(--p-surface-50) px-6 py-8 text-center text-(--p-text-muted-color)\"\n >\n <h3 class=\"mb-1 text-base font-semibold text-(--p-text-color)\">\n {{ \"flowplus.testRun.title\" | transloco }}\n </h3>\n <p class=\"m-0 text-[12px]\">{{ helperText() }}</p>\n </div>\n } @else {\n <section\n class=\"rounded-xl border border-(--p-content-border-color) bg-(--p-content-background) p-3 shadow-[0_1px_2px_rgba(15,23,42,0.05)]\"\n >\n <div class=\"flex flex-col gap-3 xl:flex-row xl:items-end\">\n <div class=\"min-w-0 flex-1\">\n <mt-select-field\n [ngModel]=\"selectedTriggerId()\"\n (ngModelChange)=\"onTriggerChange($event)\"\n [label]=\"'flowplus.inspector.workflow.triggerType' | transloco\"\n [options]=\"triggerOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n\n <div class=\"flex items-center gap-2\">\n @if (canExecute()) {\n <mt-button\n severity=\"primary\"\n size=\"large\"\n icon=\"media.play\"\n [label]=\"\n runtimeRunning()\n ? ('flowplus.runtime.executing' | transloco)\n : 'Execute workflow'\n \"\n [loading]=\"runtimeRunning()\"\n [disabled]=\"runtimeRunning()\"\n [tooltip]=\"helperText()\"\n (onClick)=\"execute()\"\n />\n } @else {\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"large\"\n icon=\"general.settings-01\"\n label=\"Open trigger setup\"\n [tooltip]=\"helperText()\"\n (onClick)=\"openTriggerSetup()\"\n />\n }\n\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.x-close\"\n [tooltip]=\"'Clear runtime state'\"\n (onClick)=\"clearRuntime()\"\n />\n </div>\n </div>\n\n <p class=\"mt-2 text-[11.5px] leading-5 text-(--p-text-muted-color)\">\n {{ helperText() }}\n </p>\n\n @if (manualTrigger()) {\n <div class=\"mt-3 rounded-xl bg-(--p-surface-50) p-2.5\">\n <header\n class=\"mb-1.5 text-[12px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.testRun.triggerInput\" | transloco }}\n </header>\n <mt-textarea-field\n [(ngModel)]=\"payloadText\"\n [rows]=\"5\"\n placeholder='{\"foo\":\"bar\"}'\n />\n @if (payloadError()) {\n <p class=\"mt-2 text-[11px] font-medium text-[rgb(var(--fp-error))]\">\n {{ payloadError() }}\n </p>\n }\n </div>\n }\n </section>\n\n @if (runtimeError()) {\n <section\n class=\"rounded-xl border border-[rgb(var(--fp-error))]/20 bg-[rgb(var(--fp-error))]/10 px-3 py-2.5 text-[12px] text-[rgb(var(--fp-error))]\"\n >\n {{ runtimeError() }}\n </section>\n }\n\n <section\n class=\"rounded-xl border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div class=\"min-w-0 space-y-2\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n class=\"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-[11px] font-semibold\"\n [ngClass]=\"triggerStatusTone()\"\n >\n {{ runtimeStatus() || selectedTrigger()?.executionStatus || \"Idle\" }}\n </span>\n\n @if (activeExecutionId()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2.5 py-1 text-[11px] font-medium text-(--p-text-color)\"\n >\n {{ \"flowplus.execution.execution\" | transloco }}\n #{{ activeExecutionId() }}\n </span>\n }\n\n @if (runtimeAccepted()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2.5 py-1 text-[11px] font-medium text-(--p-text-color)\"\n >\n Accepted\n </span>\n }\n\n @if (selectedTrigger(); as trigger) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2.5 py-1 text-[11px] font-medium text-(--p-text-color)\"\n >\n {{ triggerTypeLabel(trigger.type) }}\n </span>\n }\n </div>\n\n @if (latestRun()) {\n <div class=\"flex flex-wrap gap-x-4 gap-y-1 text-[11.5px] text-(--p-text-muted-color)\">\n @if (runStartedLabel()) {\n <span>Started: {{ runStartedLabel() | date: \"short\" }}</span>\n }\n @if (runDurationLabel()) {\n <span>Duration: {{ runDurationLabel() }}</span>\n }\n @if (problemNodeSummary().failed.length > 0) {\n <span class=\"text-[rgb(var(--fp-error))]\">\n Failed: {{ problemNodeSummary().failed.join(\", \") }}\n @if (problemNodeSummary().failedExtra) {\n +{{ problemNodeSummary().failedExtra }}\n }\n </span>\n }\n @if (problemNodeSummary().waiting.length > 0) {\n <span class=\"text-[rgb(var(--fp-warning))]\">\n Waiting: {{ problemNodeSummary().waiting.join(\", \") }}\n @if (problemNodeSummary().waitingExtra) {\n +{{ problemNodeSummary().waitingExtra }}\n }\n </span>\n }\n </div>\n } @else if (!runtimeRunning()) {\n <p class=\"m-0 text-[11.5px] leading-5 text-(--p-text-muted-color)\">\n No run has been loaded for this automation yet.\n </p>\n }\n </div>\n\n <div class=\"flex shrink-0 items-center gap-1.5\">\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.refresh-cw-05\"\n [tooltip]=\"'Refresh runtime overlay'\"\n [disabled]=\"runtimeRunning()\"\n (onClick)=\"refreshRuntime()\"\n />\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n label=\"Open runs\"\n (onClick)=\"openRuns()\"\n />\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n label=\"Open full run\"\n [disabled]=\"!activeExecutionId()\"\n (onClick)=\"openFullRun()\"\n />\n </div>\n </div>\n\n @if (lastError()) {\n <p class=\"mt-2 text-[12px] text-[rgb(var(--fp-error))]\">\n {{ lastError() }}\n </p>\n }\n </section>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "
|
|
18002
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: TestRunPanelComponent, isStandalone: true, selector: "fp-test-run-panel", ngImport: i0, template: "<div class=\"flex flex-col gap-3\">\n @if (triggerOptions().length === 0) {\n <div\n class=\"flex flex-col items-center justify-center rounded-xl border border-dashed border-(--p-content-border-color) bg-(--p-surface-50) px-6 py-8 text-center text-(--p-text-muted-color)\"\n >\n <h3 class=\"mb-1 text-base font-semibold text-(--p-text-color)\">\n {{ \"flowplus.testRun.title\" | transloco }}\n </h3>\n <p class=\"m-0 text-[12px]\">{{ helperText() }}</p>\n </div>\n } @else {\n <section\n class=\"rounded-xl border border-(--p-content-border-color) bg-(--p-content-background) p-3 shadow-[0_1px_2px_rgba(15,23,42,0.05)]\"\n >\n <div class=\"flex flex-col gap-3 xl:flex-row xl:items-end\">\n <div class=\"min-w-0 flex-1\">\n <mt-select-field\n [ngModel]=\"selectedTriggerId()\"\n (ngModelChange)=\"onTriggerChange($event)\"\n [label]=\"'flowplus.inspector.workflow.triggerType' | transloco\"\n [options]=\"triggerOptions()\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n </div>\n\n <div class=\"flex items-center gap-2\">\n @if (canExecute()) {\n <mt-button\n severity=\"primary\"\n size=\"large\"\n icon=\"media.play\"\n [label]=\"\n runtimeRunning()\n ? ('flowplus.runtime.executing' | transloco)\n : 'Execute workflow'\n \"\n [loading]=\"runtimeRunning()\"\n [disabled]=\"runtimeRunning()\"\n [tooltip]=\"helperText()\"\n (onClick)=\"execute()\"\n />\n } @else {\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"large\"\n icon=\"general.settings-01\"\n label=\"Open trigger setup\"\n [tooltip]=\"helperText()\"\n (onClick)=\"openTriggerSetup()\"\n />\n }\n\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.x-close\"\n [tooltip]=\"'Clear runtime state'\"\n (onClick)=\"clearRuntime()\"\n />\n </div>\n </div>\n\n <p class=\"mt-2 text-[11.5px] leading-5 text-(--p-text-muted-color)\">\n {{ helperText() }}\n </p>\n\n @if (manualTrigger()) {\n <div class=\"mt-3 rounded-xl bg-(--p-surface-50) p-2.5\">\n <header\n class=\"mb-1.5 text-[12px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.testRun.triggerInput\" | transloco }}\n </header>\n <mt-textarea-field\n [(ngModel)]=\"payloadText\"\n [rows]=\"5\"\n placeholder='{\"foo\":\"bar\"}'\n />\n @if (payloadError()) {\n <p class=\"mt-2 text-[11px] font-medium text-[rgb(var(--fp-error))]\">\n {{ payloadError() }}\n </p>\n }\n </div>\n }\n </section>\n\n @if (runtimeError()) {\n <section\n class=\"rounded-xl border border-[rgb(var(--fp-error))]/20 bg-[rgb(var(--fp-error))]/10 px-3 py-2.5 text-[12px] text-[rgb(var(--fp-error))]\"\n >\n {{ runtimeError() }}\n </section>\n }\n\n <section\n class=\"rounded-xl border border-(--p-content-border-color) bg-(--p-content-background) p-3\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-3\">\n <div class=\"min-w-0 space-y-2\">\n <div class=\"flex flex-wrap items-center gap-2\">\n <span\n class=\"inline-flex items-center gap-1 rounded-full border px-2.5 py-1 text-[11px] font-semibold\"\n [ngClass]=\"triggerStatusTone()\"\n >\n {{ runtimeStatus() || selectedTrigger()?.executionStatus || \"Idle\" }}\n </span>\n\n @if (activeExecutionId()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2.5 py-1 text-[11px] font-medium text-(--p-text-color)\"\n >\n {{ \"flowplus.execution.execution\" | transloco }}\n #{{ activeExecutionId() }}\n </span>\n }\n\n @if (runtimeAccepted()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2.5 py-1 text-[11px] font-medium text-(--p-text-color)\"\n >\n Accepted\n </span>\n }\n\n @if (selectedTrigger(); as trigger) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full border border-(--p-content-border-color) bg-(--p-surface-50) px-2.5 py-1 text-[11px] font-medium text-(--p-text-color)\"\n >\n {{ triggerTypeLabel(trigger.type) }}\n </span>\n }\n </div>\n\n @if (latestRun()) {\n <div class=\"flex flex-wrap gap-x-4 gap-y-1 text-[11.5px] text-(--p-text-muted-color)\">\n @if (runStartedLabel()) {\n <span>Started: {{ runStartedLabel() | date: \"short\" }}</span>\n }\n @if (runDurationLabel()) {\n <span>Duration: {{ runDurationLabel() }}</span>\n }\n @if (problemNodeSummary().failed.length > 0) {\n <span class=\"text-[rgb(var(--fp-error))]\">\n Failed: {{ problemNodeSummary().failed.join(\", \") }}\n @if (problemNodeSummary().failedExtra) {\n +{{ problemNodeSummary().failedExtra }}\n }\n </span>\n }\n @if (problemNodeSummary().waiting.length > 0) {\n <span class=\"text-[rgb(var(--fp-warning))]\">\n Waiting: {{ problemNodeSummary().waiting.join(\", \") }}\n @if (problemNodeSummary().waitingExtra) {\n +{{ problemNodeSummary().waitingExtra }}\n }\n </span>\n }\n </div>\n } @else if (!runtimeRunning()) {\n <p class=\"m-0 text-[11.5px] leading-5 text-(--p-text-muted-color)\">\n No run has been loaded for this automation yet.\n </p>\n }\n </div>\n\n <div class=\"flex shrink-0 items-center gap-1.5\">\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.refresh-cw-05\"\n [tooltip]=\"'Refresh runtime overlay'\"\n [disabled]=\"runtimeRunning()\"\n (onClick)=\"refreshRuntime()\"\n />\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n label=\"Open runs\"\n (onClick)=\"openRuns()\"\n />\n <mt-button\n variant=\"outlined\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n label=\"Open full run\"\n [disabled]=\"!activeExecutionId()\"\n (onClick)=\"openFullRun()\"\n />\n </div>\n </div>\n\n @if (lastError()) {\n <p class=\"mt-2 text-[12px] text-[rgb(var(--fp-error))]\">\n {{ lastError() }}\n </p>\n }\n </section>\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "pipe", type: i1$1.DatePipe, name: "date" }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
18058
18003
|
}
|
|
18059
18004
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: TestRunPanelComponent, decorators: [{
|
|
18060
18005
|
type: Component,
|
|
@@ -18108,22 +18053,40 @@ class BottomPanelComponent {
|
|
|
18108
18053
|
this.store.setBottomPanelTab(tab);
|
|
18109
18054
|
}
|
|
18110
18055
|
tabOptions = computed(() => {
|
|
18111
|
-
const
|
|
18056
|
+
const issueCount = this.issueCount();
|
|
18057
|
+
const contextCount = this.contextSources().length;
|
|
18058
|
+
const testRunCount = this.store.runtime().activeExecutionId ||
|
|
18059
|
+
this.store.runtime().lastRunSummary
|
|
18060
|
+
? 1
|
|
18061
|
+
: null;
|
|
18112
18062
|
return [
|
|
18113
18063
|
{
|
|
18114
18064
|
label: this.transloco.translate('flowplus.bottom.problems'),
|
|
18115
18065
|
value: 'problems',
|
|
18116
|
-
|
|
18066
|
+
icon: this.issueIcon(),
|
|
18067
|
+
badge: issueCount > 0 ? issueCount : null,
|
|
18068
|
+
severity: this.issueSeverity(),
|
|
18117
18069
|
},
|
|
18118
18070
|
{
|
|
18119
18071
|
label: this.transloco.translate('flowplus.bottom.testRun'),
|
|
18120
18072
|
value: 'testRun',
|
|
18073
|
+
icon: 'general.activity',
|
|
18074
|
+
badge: testRunCount,
|
|
18075
|
+
severity: this.runtimeSeverity(),
|
|
18121
18076
|
},
|
|
18122
18077
|
{
|
|
18123
18078
|
label: this.transloco.translate('flowplus.bottom.context'),
|
|
18124
18079
|
value: 'context',
|
|
18080
|
+
icon: 'layout.list',
|
|
18081
|
+
badge: contextCount > 0 ? contextCount : null,
|
|
18082
|
+
severity: contextCount > 0 ? 'info' : 'primary',
|
|
18083
|
+
},
|
|
18084
|
+
{
|
|
18085
|
+
label: this.transloco.translate('flowplus.bottom.raw'),
|
|
18086
|
+
value: 'raw',
|
|
18087
|
+
icon: 'dev.code-02',
|
|
18088
|
+
severity: 'primary',
|
|
18125
18089
|
},
|
|
18126
|
-
{ label: this.transloco.translate('flowplus.bottom.raw'), value: 'raw' },
|
|
18127
18090
|
];
|
|
18128
18091
|
}, ...(ngDevMode ? [{ debugName: "tabOptions" }] : /* istanbul ignore next */ []));
|
|
18129
18092
|
contextRows = computed(() => this.contextSources().map((source) => ({
|
|
@@ -18162,6 +18125,51 @@ class BottomPanelComponent {
|
|
|
18162
18125
|
return 0;
|
|
18163
18126
|
return v.issues?.length ?? v.errors.length + v.warnings.length;
|
|
18164
18127
|
}
|
|
18128
|
+
issueIcon() {
|
|
18129
|
+
const issues = this.validationIssues();
|
|
18130
|
+
if (issues.length === 0)
|
|
18131
|
+
return 'general.check-circle';
|
|
18132
|
+
if (issues.some((issue) => issue.severity === 'Error')) {
|
|
18133
|
+
return 'alert.alert-triangle';
|
|
18134
|
+
}
|
|
18135
|
+
if (issues.some((issue) => issue.severity === 'Warning')) {
|
|
18136
|
+
return 'alert.alert-circle';
|
|
18137
|
+
}
|
|
18138
|
+
return 'general.info-circle';
|
|
18139
|
+
}
|
|
18140
|
+
issueSeverity() {
|
|
18141
|
+
const issues = this.validationIssues();
|
|
18142
|
+
if (issues.length === 0)
|
|
18143
|
+
return 'primary';
|
|
18144
|
+
return 'danger';
|
|
18145
|
+
}
|
|
18146
|
+
runtimeSeverity() {
|
|
18147
|
+
const status = String(this.store.runtimeStatus() ?? '').toLowerCase();
|
|
18148
|
+
if (!status)
|
|
18149
|
+
return 'primary';
|
|
18150
|
+
if (status.includes('fail') || status.includes('error'))
|
|
18151
|
+
return 'danger';
|
|
18152
|
+
if (status.includes('warn') || status.includes('cancel'))
|
|
18153
|
+
return 'warn';
|
|
18154
|
+
if (status.includes('success') ||
|
|
18155
|
+
status.includes('complete') ||
|
|
18156
|
+
status.includes('succeed')) {
|
|
18157
|
+
return 'primary';
|
|
18158
|
+
}
|
|
18159
|
+
if (status.includes('run') ||
|
|
18160
|
+
status.includes('progress') ||
|
|
18161
|
+
status.includes('accepted') ||
|
|
18162
|
+
status.includes('pending')) {
|
|
18163
|
+
return 'info';
|
|
18164
|
+
}
|
|
18165
|
+
return 'primary';
|
|
18166
|
+
}
|
|
18167
|
+
validationIssues() {
|
|
18168
|
+
const v = this.store.validation();
|
|
18169
|
+
if (!v)
|
|
18170
|
+
return [];
|
|
18171
|
+
return v.issues ?? [...v.errors, ...v.warnings, ...(v.info ?? [])];
|
|
18172
|
+
}
|
|
18165
18173
|
refreshRuntimeOverlay() {
|
|
18166
18174
|
this.store.refreshRuntimeOverlay();
|
|
18167
18175
|
}
|
|
@@ -18226,7 +18234,7 @@ class BottomPanelComponent {
|
|
|
18226
18234
|
return JSON.stringify(safe, null, 2);
|
|
18227
18235
|
}
|
|
18228
18236
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BottomPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
18229
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BottomPanelComponent, isStandalone: true, selector: "fp-bottom-panel", outputs: { focus: "focus" }, host: { classAttribute: "absolute start-11 end-0 bottom-0 z-[5] flex flex-col min-h-10 max-h-[
|
|
18237
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: BottomPanelComponent, isStandalone: true, selector: "fp-bottom-panel", outputs: { focus: "focus" }, host: { classAttribute: "absolute start-11 end-0 bottom-0 z-[5] flex flex-col min-h-10 max-h-[58%] bg-(--p-content-background) border-t border-(--p-content-border-color) transition-[height] duration-200" }, viewQueries: [{ propertyName: "contextPathTpl", first: true, predicate: ["contextPathTpl"], descendants: true, isSignal: true }, { propertyName: "contextTypeTpl", first: true, predicate: ["contextTypeTpl"], descendants: true, isSignal: true }, { propertyName: "contextSourceTpl", first: true, predicate: ["contextSourceTpl"], descendants: true, isSignal: true }, { propertyName: "contextAvailabilityTpl", first: true, predicate: ["contextAvailabilityTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"flex h-10 flex-none items-center gap-2 border-b border-(--p-content-border-color) bg-(--p-surface-50) px-3 [&_.p-button]:size-8 [&_.p-button_svg]:size-[18px]\"\n>\n <mt-tabs\n mode=\"underline\"\n [fluid]=\"true\"\n [options]=\"tabOptions()\"\n [active]=\"tab()\"\n (onChange)=\"onTabChange($event)\"\n />\n\n <div class=\"flex flex-none items-center gap-1\">\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.refresh-cw-05\"\n [tooltip]=\"'Refresh runtime overlay'\"\n [disabled]=\"store.runtime().loading\"\n (onClick)=\"refreshRuntimeOverlay()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.x-close\"\n [tooltip]=\"'Clear replay overlay'\"\n [disabled]=\"\n !store.runtime().activeExecutionId && !store.runtime().lastRunSummary\n \"\n (onClick)=\"clearReplay()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open runs'\"\n (onClick)=\"openRuns()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open run'\"\n [disabled]=\"!store.runtime().activeExecutionId\"\n (onClick)=\"openCurrentRun()\"\n />\n </div>\n\n <span\n class=\"h-5 w-px flex-none bg-(--p-content-border-color)\"\n aria-hidden=\"true\"\n ></span>\n\n <!-- Bottom-panel chrome: a pure secondary mt-button with a real\n @masterteam/icons key. When open we show a close (\u00D7) button so\n the user can always collapse the drawer. When collapsed a single\n chevron-up expands it. -->\n @if (store.ui().bottomPanelOpen) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n icon=\"general.x-close\"\n [tooltip]=\"'flowplus.bottom.close' | transloco\"\n (onClick)=\"store.toggleBottomPanel(false)\"\n />\n } @else {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n icon=\"arrow.chevron-up\"\n [tooltip]=\"'flowplus.bottom.expand' | transloco\"\n (onClick)=\"store.toggleBottomPanel(true)\"\n />\n }\n</div>\n\n@if (store.ui().bottomPanelOpen) {\n <div class=\"min-h-0 flex-1 overflow-hidden px-4 py-3\">\n @switch (tab()) {\n @case (\"problems\") {\n <fp-problems-panel (focus)=\"focus.emit($event)\" />\n }\n @case (\"testRun\") {\n <fp-test-run-panel />\n }\n @case (\"context\") {\n @if (contextSources().length === 0) {\n <div\n class=\"flex h-full flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\n >\n <p class=\"text-[12px]\">\n {{ \"flowplus.bottom.contextEmpty\" | transloco }}\n </p>\n </div>\n } @else {\n <div\n class=\"fp-scroll h-full overflow-auto rounded-md border border-surface-300 bg-(--p-content-background)\"\n >\n <mt-table\n [data]=\"contextRows()\"\n [columns]=\"contextColumns()\"\n dataKey=\"key\"\n [noCard]=\"true\"\n [tableLayout]=\"'auto'\"\n [pageSize]=\"50\"\n [rowsPerPageOptions]=\"[25, 50, 100]\"\n [alwaysShowPaginator]=\"false\"\n />\n </div>\n }\n }\n @case (\"raw\") {\n <pre\n class=\"fp-scroll h-full overflow-auto rounded-md border border-surface-300 bg-(--p-surface-100) p-3.5 font-mono text-[11px] leading-[1.65] text-(--p-text-color)\"\n >{{ rawJson() }}</pre\n >\n }\n }\n </div>\n}\n\n<ng-template #contextPathTpl let-row>\n <code class=\"font-mono text-[12px]\">{{ row.source.path }}</code>\n</ng-template>\n\n<ng-template #contextTypeTpl let-row>\n <span>{{ row.source.type }}</span>\n @if (row.source.isSecret) {\n <span\n class=\"ms-1 inline-flex items-center gap-1 rounded-full bg-[rgb(239_68_68/0.14)] px-2 py-0.5 text-[10.5px] font-medium text-[rgb(var(--fp-error))]\"\n >secret</span\n >\n }\n</ng-template>\n\n<ng-template #contextSourceTpl let-row>\n {{ row.source.sourceType }}\n <span class=\"text-(--p-text-muted-color)\">{{\n row.source.stepKey ?? \"\"\n }}</span>\n</ng-template>\n\n<ng-template #contextAvailabilityTpl let-row>\n @if (row.source.isProducedBeforeCurrentStep === false) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(234_179_8/0.18)] px-2 py-0.5 text-[10.5px] font-medium text-[rgb(146_91_0)]\"\n >\n {{ \"flowplus.context.availabilityWarning\" | transloco }}\n </span>\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(16_185_129/0.16)] px-2 py-0.5 text-[10.5px] font-medium text-[rgb(var(--fp-commit))]\"\n >{{ \"flowplus.bottom.contextOk\" | transloco }}</span\n >\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "component", type: Tabs, selector: "mt-tabs", inputs: ["options", "optionLabel", "optionValue", "active", "mode", "moreLabel", "defaultIcon", "size", "fluid", "disabled", "searchThreshold"], outputs: ["activeChange", "onChange"] }, { kind: "component", type: ProblemsPanelComponent, selector: "fp-problems-panel", outputs: ["focus"] }, { kind: "component", type: TestRunPanelComponent, selector: "fp-test-run-panel" }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
18230
18238
|
}
|
|
18231
18239
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BottomPanelComponent, decorators: [{
|
|
18232
18240
|
type: Component,
|
|
@@ -18239,8 +18247,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
18239
18247
|
ProblemsPanelComponent,
|
|
18240
18248
|
TestRunPanelComponent,
|
|
18241
18249
|
], host: {
|
|
18242
|
-
class: 'absolute start-11 end-0 bottom-0 z-[5] flex flex-col min-h-10 max-h-[
|
|
18243
|
-
}, template: "<div\
|
|
18250
|
+
class: 'absolute start-11 end-0 bottom-0 z-[5] flex flex-col min-h-10 max-h-[58%] bg-(--p-content-background) border-t border-(--p-content-border-color) transition-[height] duration-200',
|
|
18251
|
+
}, template: "<div\n class=\"flex h-10 flex-none items-center gap-2 border-b border-(--p-content-border-color) bg-(--p-surface-50) px-3 [&_.p-button]:size-8 [&_.p-button_svg]:size-[18px]\"\n>\n <mt-tabs\n mode=\"underline\"\n [fluid]=\"true\"\n [options]=\"tabOptions()\"\n [active]=\"tab()\"\n (onChange)=\"onTabChange($event)\"\n />\n\n <div class=\"flex flex-none items-center gap-1\">\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.refresh-cw-05\"\n [tooltip]=\"'Refresh runtime overlay'\"\n [disabled]=\"store.runtime().loading\"\n (onClick)=\"refreshRuntimeOverlay()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.x-close\"\n [tooltip]=\"'Clear replay overlay'\"\n [disabled]=\"\n !store.runtime().activeExecutionId && !store.runtime().lastRunSummary\n \"\n (onClick)=\"clearReplay()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open runs'\"\n (onClick)=\"openRuns()\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.link-external-02\"\n [tooltip]=\"'Open run'\"\n [disabled]=\"!store.runtime().activeExecutionId\"\n (onClick)=\"openCurrentRun()\"\n />\n </div>\n\n <span\n class=\"h-5 w-px flex-none bg-(--p-content-border-color)\"\n aria-hidden=\"true\"\n ></span>\n\n <!-- Bottom-panel chrome: a pure secondary mt-button with a real\n @masterteam/icons key. When open we show a close (\u00D7) button so\n the user can always collapse the drawer. When collapsed a single\n chevron-up expands it. -->\n @if (store.ui().bottomPanelOpen) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n icon=\"general.x-close\"\n [tooltip]=\"'flowplus.bottom.close' | transloco\"\n (onClick)=\"store.toggleBottomPanel(false)\"\n />\n } @else {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n icon=\"arrow.chevron-up\"\n [tooltip]=\"'flowplus.bottom.expand' | transloco\"\n (onClick)=\"store.toggleBottomPanel(true)\"\n />\n }\n</div>\n\n@if (store.ui().bottomPanelOpen) {\n <div class=\"min-h-0 flex-1 overflow-hidden px-4 py-3\">\n @switch (tab()) {\n @case (\"problems\") {\n <fp-problems-panel (focus)=\"focus.emit($event)\" />\n }\n @case (\"testRun\") {\n <fp-test-run-panel />\n }\n @case (\"context\") {\n @if (contextSources().length === 0) {\n <div\n class=\"flex h-full flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\n >\n <p class=\"text-[12px]\">\n {{ \"flowplus.bottom.contextEmpty\" | transloco }}\n </p>\n </div>\n } @else {\n <div\n class=\"fp-scroll h-full overflow-auto rounded-md border border-surface-300 bg-(--p-content-background)\"\n >\n <mt-table\n [data]=\"contextRows()\"\n [columns]=\"contextColumns()\"\n dataKey=\"key\"\n [noCard]=\"true\"\n [tableLayout]=\"'auto'\"\n [pageSize]=\"50\"\n [rowsPerPageOptions]=\"[25, 50, 100]\"\n [alwaysShowPaginator]=\"false\"\n />\n </div>\n }\n }\n @case (\"raw\") {\n <pre\n class=\"fp-scroll h-full overflow-auto rounded-md border border-surface-300 bg-(--p-surface-100) p-3.5 font-mono text-[11px] leading-[1.65] text-(--p-text-color)\"\n >{{ rawJson() }}</pre\n >\n }\n }\n </div>\n}\n\n<ng-template #contextPathTpl let-row>\n <code class=\"font-mono text-[12px]\">{{ row.source.path }}</code>\n</ng-template>\n\n<ng-template #contextTypeTpl let-row>\n <span>{{ row.source.type }}</span>\n @if (row.source.isSecret) {\n <span\n class=\"ms-1 inline-flex items-center gap-1 rounded-full bg-[rgb(239_68_68/0.14)] px-2 py-0.5 text-[10.5px] font-medium text-[rgb(var(--fp-error))]\"\n >secret</span\n >\n }\n</ng-template>\n\n<ng-template #contextSourceTpl let-row>\n {{ row.source.sourceType }}\n <span class=\"text-(--p-text-muted-color)\">{{\n row.source.stepKey ?? \"\"\n }}</span>\n</ng-template>\n\n<ng-template #contextAvailabilityTpl let-row>\n @if (row.source.isProducedBeforeCurrentStep === false) {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(234_179_8/0.18)] px-2 py-0.5 text-[10.5px] font-medium text-[rgb(146_91_0)]\"\n >\n {{ \"flowplus.context.availabilityWarning\" | transloco }}\n </span>\n } @else {\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-[rgb(16_185_129/0.16)] px-2 py-0.5 text-[10.5px] font-medium text-[rgb(var(--fp-commit))]\"\n >{{ \"flowplus.bottom.contextOk\" | transloco }}</span\n >\n }\n</ng-template>\n" }]
|
|
18244
18252
|
}], propDecorators: { contextPathTpl: [{ type: i0.ViewChild, args: ['contextPathTpl', { isSignal: true }] }], contextTypeTpl: [{ type: i0.ViewChild, args: ['contextTypeTpl', { isSignal: true }] }], contextSourceTpl: [{ type: i0.ViewChild, args: ['contextSourceTpl', { isSignal: true }] }], contextAvailabilityTpl: [{ type: i0.ViewChild, args: ['contextAvailabilityTpl', { isSignal: true }] }], focus: [{
|
|
18245
18253
|
type: Output
|
|
18246
18254
|
}] } });
|
|
@@ -20721,7 +20729,7 @@ class PaletteComponent {
|
|
|
20721
20729
|
return v && v !== key ? v : fallback;
|
|
20722
20730
|
}
|
|
20723
20731
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
20724
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PaletteComponent, isStandalone: true, selector: "fp-palette", outputs: { closeRequested: "closeRequested", openRequested: "openRequested", add: "add", addTrigger: "addTrigger" }, host: { listeners: { "document:pointerdown": "onDocumentPointerDown($event)" }, classAttribute: "block absolute top-0 bottom-0 start-0 w-11 z-[5] overflow-visible bg-(--p-surface-200) dark:bg-(--p-surface-800) border-e border-(--p-content-border-color)" }, ngImport: i0, template: "<div\r\n class=\"flex h-full w-11 flex-col items-center gap-2 bg-transparent py-3\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"'flowplus.palette.rail' | transloco\"\r\n>\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"isExpanded() ? 'general.x-close' : 'general.plus'\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.toggle' | transloco\"\n [mtTooltip]=\"'flowplus.palette.toggle' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"isExpanded() ? closeDrawer() : openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.search' | transloco\"\n [mtTooltip]=\"'flowplus.palette.search' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"openDrawer()\"\n />\n</div>\r\n\r\n@if (isExpanded()) {\r\n <aside\r\n class=\"absolute start-11 top-0 bottom-10 z-[6] flex w-80 min-h-0 flex-col overflow-hidden border-e border-(--p-content-border-color) bg-(--p-surface-50) shadow-lg animate-[fp-palette-slide-in_280ms_cubic-bezier(0,0,0.2,1)_both] rtl:animate-[fp-palette-slide-in-rtl_280ms_cubic-bezier(0,0,0.2,1)_both]\"\r\n role=\"complementary\"\r\n [attr.aria-label]=\"'flowplus.palette.title' | transloco\"\r\n >\r\n <header class=\"flex flex-col gap-2.5 px-3 pt-3 pb-2.5\">\r\n <div class=\"flex items-center gap-2\">\r\n @if (!searching() && view() !== \"root\") {\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-left\"\n styleClass=\"fp-palette-back-button\"\n (onClick)=\"back()\"\n [attr.aria-label]=\"'flowplus.palette.back' | transloco\"\n [mtTooltip]=\"'flowplus.palette.back' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n />\n }\r\n <h3\r\n class=\"min-w-0 flex-1 truncate text-[13.5px] font-bold text-(--p-text-color)\"\r\n >\r\n {{ headerTitle() }}\r\n </h3>\r\n </div>\r\n\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"fp-scroll fp-pal-list flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 pt-1 pb-6\"\r\n role=\"list\"\r\n >\r\n @if (searching() && view() !== \"triggers\") {\n @for (item of searchResults(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n @if (searchResults().length === 0) {\r\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\r\n }\r\n } @else if (view() === \"triggers\") {\n @for (option of visibleTriggerOptions(); track option.key) {\n <div\n fExternalItem\n [fData]=\"triggerPaletteItem(option)\"\n [fExternalItemId]=\"triggerPaletteItemKey(option, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\n tabindex=\"0\"\n [attr.title]=\"\n resolve(option.description) || resolve(option.displayName)\n \"\n (click)=\"pickTrigger(option)\"\n (keydown.enter)=\"pickTrigger(option)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n triggerDragPreview;\n context: { $implicit: option }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n [style.--fp-avatar-color]=\"triggerOptionVars(option).color\"\n [style.--fp-avatar-bg]=\"triggerOptionVars(option).bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ resolve(option.displayName) }}</span\r\n >\r\n @if (option.description) {\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ resolve(option.description) }}</span\r\n >\n }\n </span>\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\n icon=\"general.dots-grid\"\n />\n </div>\n }\n @if (visibleTriggerOptions().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n }\n } @else if (view() === \"category\") {\n @for (item of activeItems(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n } @else {\r\n @for (group of groups(); track group.category) {\r\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openCategory(group.category)\"\n (keydown.enter)=\"openCategory(group.category)\"\n (keydown.space)=\"$event.preventDefault(); openCategory(group.category)\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n [icon]=\"group.icon\"\r\n [style.--fp-avatar-color]=\"group.color\"\r\n [style.--fp-avatar-bg]=\"group.bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ group.label }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ group.description }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n\n @if (groups().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n } @else {\n <div class=\"mx-2.5 my-1.5 h-px bg-(--p-content-border-color)\"></div>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openTriggers()\"\n (keydown.enter)=\"openTriggers()\"\n (keydown.space)=\"$event.preventDefault(); openTriggers()\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n icon=\"general.zap-fast\"\r\n [style.--fp-avatar-color]=\"triggerVars().color\"\r\n [style.--fp-avatar-bg]=\"triggerVars().bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ \"flowplus.palette.addAnotherTrigger\" | transloco }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{\r\n \"flowplus.palette.addAnotherTriggerDesc\" | transloco\r\n }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n }\r\n </div>\r\n </aside>\r\n}\r\n\r\n<ng-template #emptyRow>\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\n</ng-template>\n\n<ng-template #stepDragPreview let-item>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[var(--fp-avatar-bg)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar size=\"normal\" shape=\"square\" [icon]=\"iconFor(item)\" />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n\n<ng-template #triggerDragPreview let-option>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,rgb(var(--fp-app-action))_30%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(245,158,11,0.20),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_16%,transparent)] text-[rgb(var(--fp-app-action))] shadow-inner ring-1 ring-[color-mix(in_srgb,rgb(var(--fp-app-action))_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(option.displayName) }}\n </span>\n @if (option.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(option.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_8%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Trigger\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[rgb(var(--fp-app-action))] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "directive", type:
|
|
20732
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PaletteComponent, isStandalone: true, selector: "fp-palette", outputs: { closeRequested: "closeRequested", openRequested: "openRequested", add: "add", addTrigger: "addTrigger" }, host: { listeners: { "document:pointerdown": "onDocumentPointerDown($event)" }, classAttribute: "block absolute top-0 bottom-0 start-0 w-11 z-[5] overflow-visible bg-(--p-surface-200) dark:bg-(--p-surface-800) border-e border-(--p-content-border-color)" }, ngImport: i0, template: "<div\r\n class=\"flex h-full w-11 flex-col items-center gap-2 bg-transparent py-3\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"'flowplus.palette.rail' | transloco\"\r\n>\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"isExpanded() ? 'general.x-close' : 'general.plus'\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.toggle' | transloco\"\n [mtTooltip]=\"'flowplus.palette.toggle' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"isExpanded() ? closeDrawer() : openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.search' | transloco\"\n [mtTooltip]=\"'flowplus.palette.search' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"openDrawer()\"\n />\n</div>\r\n\r\n@if (isExpanded()) {\r\n <aside\r\n class=\"absolute start-11 top-0 bottom-10 z-[6] flex w-80 min-h-0 flex-col overflow-hidden border-e border-(--p-content-border-color) bg-(--p-surface-50) shadow-lg animate-[fp-palette-slide-in_280ms_cubic-bezier(0,0,0.2,1)_both] rtl:animate-[fp-palette-slide-in-rtl_280ms_cubic-bezier(0,0,0.2,1)_both]\"\r\n role=\"complementary\"\r\n [attr.aria-label]=\"'flowplus.palette.title' | transloco\"\r\n >\r\n <header class=\"flex flex-col gap-2.5 px-3 pt-3 pb-2.5\">\r\n <div class=\"flex items-center gap-2\">\r\n @if (!searching() && view() !== \"root\") {\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-left\"\n styleClass=\"fp-palette-back-button\"\n (onClick)=\"back()\"\n [attr.aria-label]=\"'flowplus.palette.back' | transloco\"\n [mtTooltip]=\"'flowplus.palette.back' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n />\n }\r\n <h3\r\n class=\"min-w-0 flex-1 truncate text-[13.5px] font-bold text-(--p-text-color)\"\r\n >\r\n {{ headerTitle() }}\r\n </h3>\r\n </div>\r\n\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"fp-scroll fp-pal-list flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 pt-1 pb-6\"\r\n role=\"list\"\r\n >\r\n @if (searching() && view() !== \"triggers\") {\n @for (item of searchResults(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n @if (searchResults().length === 0) {\r\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\r\n }\r\n } @else if (view() === \"triggers\") {\n @for (option of visibleTriggerOptions(); track option.key) {\n <div\n fExternalItem\n [fData]=\"triggerPaletteItem(option)\"\n [fExternalItemId]=\"triggerPaletteItemKey(option, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\n tabindex=\"0\"\n [attr.title]=\"\n resolve(option.description) || resolve(option.displayName)\n \"\n (click)=\"pickTrigger(option)\"\n (keydown.enter)=\"pickTrigger(option)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n triggerDragPreview;\n context: { $implicit: option }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n [style.--fp-avatar-color]=\"triggerOptionVars(option).color\"\n [style.--fp-avatar-bg]=\"triggerOptionVars(option).bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ resolve(option.displayName) }}</span\r\n >\r\n @if (option.description) {\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ resolve(option.description) }}</span\r\n >\n }\n </span>\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\n icon=\"general.dots-grid\"\n />\n </div>\n }\n @if (visibleTriggerOptions().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n }\n } @else if (view() === \"category\") {\n @for (item of activeItems(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n } @else {\r\n @for (group of groups(); track group.category) {\r\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openCategory(group.category)\"\n (keydown.enter)=\"openCategory(group.category)\"\n (keydown.space)=\"$event.preventDefault(); openCategory(group.category)\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n [icon]=\"group.icon\"\r\n [style.--fp-avatar-color]=\"group.color\"\r\n [style.--fp-avatar-bg]=\"group.bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ group.label }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ group.description }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n\n @if (groups().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n } @else {\n <div class=\"mx-2.5 my-1.5 h-px bg-(--p-content-border-color)\"></div>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openTriggers()\"\n (keydown.enter)=\"openTriggers()\"\n (keydown.space)=\"$event.preventDefault(); openTriggers()\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n icon=\"general.zap-fast\"\r\n [style.--fp-avatar-color]=\"triggerVars().color\"\r\n [style.--fp-avatar-bg]=\"triggerVars().bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ \"flowplus.palette.addAnotherTrigger\" | transloco }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{\r\n \"flowplus.palette.addAnotherTriggerDesc\" | transloco\r\n }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n }\r\n </div>\r\n </aside>\r\n}\r\n\r\n<ng-template #emptyRow>\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\n</ng-template>\n\n<ng-template #stepDragPreview let-item>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[var(--fp-avatar-bg)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar size=\"normal\" shape=\"square\" [icon]=\"iconFor(item)\" />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n\n<ng-template #triggerDragPreview let-option>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,rgb(var(--fp-app-action))_30%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(245,158,11,0.20),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_16%,transparent)] text-[rgb(var(--fp-app-action))] shadow-inner ring-1 ring-[color-mix(in_srgb,rgb(var(--fp-app-action))_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(option.displayName) }}\n </span>\n @if (option.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(option.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_8%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Trigger\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[rgb(var(--fp-app-action))] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "directive", type:
|
|
20725
20733
|
// Native Foblex external-item drag (clone preview, like the official
|
|
20726
20734
|
// add-node-from-palette example).
|
|
20727
20735
|
FExternalItem, selector: "[fExternalItem]", inputs: ["fExternalItemId", "fData", "fDisabled", "fPreview", "fPreviewMatchSize", "fPlaceholder"], outputs: ["fPreviewChange", "fPlaceholderChange"] }, { kind: "directive", type: FExternalItemPreview, selector: "ng-template[fExternalItemPreview]" }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
@@ -20813,7 +20821,7 @@ class BuilderTopbarComponent {
|
|
|
20813
20821
|
this.unpublish.emit();
|
|
20814
20822
|
}
|
|
20815
20823
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BuilderTopbarComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
20816
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: BuilderTopbarComponent, isStandalone: true, selector: "fp-builder-topbar", outputs: { validate: "validate", testRun: "testRun", publish: "publish", unpublish: "unpublish", openSettings: "openSettings", toggleNodeFinder: "toggleNodeFinder" }, host: { classAttribute: "block flex-none min-w-0 z-[6]" }, ngImport: i0, template: "<header\n class=\"flex min-h-[74px] items-center gap-[10px] border-b border-(--p-content-border-color) bg-(--p-content-background) px-3.5 py-3\"\n role=\"toolbar\"\n>\n <!-- Back button \u2014 same icon + size + variant as mt-page-header so\r\n the builder topbar feels native against the host shell. -->\r\n <mt-button\r\n [text]=\"true\"\r\n size=\"large\"\r\n [icon]=\"backIcon()\"\r\n [tooltip]=\"'Back'\"\r\n (onClick)=\"back()\"\r\n />\r\n\r\n <!-- Breadcrumb: home \u203A Workflow Studio \u203A <workflow name>. The current\r\n workflow name is the last (non-link) breadcrumb item. -->\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start text-sm fp-builder-breadcrumb'\"\r\n />\r\n\r\n <span class=\"flex-1\"></span>\r\n\r\n <mt-button\r\n variant=\"outlined\"\r\n [label]=\"'flowplus.topbar.validate' | transloco\"\r\n [disabled]=\"busy()\"\r\n (onClick)=\"validate.emit()\"\r\n />\r\n <mt-button\r\n variant=\"outlined\"\r\n [label]=\"'flowplus.topbar.test' | transloco\"\r\n [disabled]=\"busy()\"\r\n (onClick)=\"testRun.emit()\"\r\n />\r\n\r\n <!-- Publish label + toggle grouped in one bordered box. On \u2192 publish,\r\n off \u2192 unpublish; the parent confirms before dispatching, and the\r\n label flips so the user always sees the *current* state. -->\r\n <div\r\n class=\"ms-1 inline-flex h-10 items-center rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) ps-3 pe-2.5\"\r\n >\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onTogglePublish($event)\"\r\n [disabled]=\"publishToggleDisabled()\"\r\n [label]=\"\r\n (isPublished()\r\n ? 'flowplus.topbar.published'\r\n : 'flowplus.topbar.publish'\r\n ) | transloco\r\n \"\r\n labelPosition=\"start\"\r\n size=\"small\"\r\n />\r\n </div>\r\n\r\n <!-- Separator before the settings action. -->\r\n <span\r\n class=\"mx-1 h-7 w-px bg-(--p-content-border-color)\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n\r\n <mt-button\n variant=\"outlined\"\n severity=\"primary\"\n icon=\"general.settings-02\"\n [tooltip]=\"'flowplus.topbar.settings' | transloco\"\n (onClick)=\"openSettings.emit()\"\n />\n</header>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "
|
|
20824
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.8", type: BuilderTopbarComponent, isStandalone: true, selector: "fp-builder-topbar", outputs: { validate: "validate", testRun: "testRun", publish: "publish", unpublish: "unpublish", openSettings: "openSettings", toggleNodeFinder: "toggleNodeFinder" }, host: { classAttribute: "block flex-none min-w-0 z-[6]" }, ngImport: i0, template: "<header\n class=\"flex min-h-[74px] items-center gap-[10px] border-b border-(--p-content-border-color) bg-(--p-content-background) px-3.5 py-3\"\n role=\"toolbar\"\n>\n <!-- Back button \u2014 same icon + size + variant as mt-page-header so\r\n the builder topbar feels native against the host shell. -->\r\n <mt-button\r\n [text]=\"true\"\r\n size=\"large\"\r\n [icon]=\"backIcon()\"\r\n [tooltip]=\"'Back'\"\r\n (onClick)=\"back()\"\r\n />\r\n\r\n <!-- Breadcrumb: home \u203A Workflow Studio \u203A <workflow name>. The current\r\n workflow name is the last (non-link) breadcrumb item. -->\r\n <mt-breadcrumb\r\n [items]=\"breadcrumbItems()\"\r\n [styleClass]=\"'flex justify-start text-sm fp-builder-breadcrumb'\"\r\n />\r\n\r\n <span class=\"flex-1\"></span>\r\n\r\n <mt-button\r\n variant=\"outlined\"\r\n [label]=\"'flowplus.topbar.validate' | transloco\"\r\n [disabled]=\"busy()\"\r\n (onClick)=\"validate.emit()\"\r\n />\r\n <mt-button\r\n variant=\"outlined\"\r\n [label]=\"'flowplus.topbar.test' | transloco\"\r\n [disabled]=\"busy()\"\r\n (onClick)=\"testRun.emit()\"\r\n />\r\n\r\n <!-- Publish label + toggle grouped in one bordered box. On \u2192 publish,\r\n off \u2192 unpublish; the parent confirms before dispatching, and the\r\n label flips so the user always sees the *current* state. -->\r\n <div\r\n class=\"ms-1 inline-flex h-10 items-center rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) ps-3 pe-2.5\"\r\n >\r\n <mt-toggle-field\r\n [ngModel]=\"isPublished()\"\r\n (ngModelChange)=\"onTogglePublish($event)\"\r\n [disabled]=\"publishToggleDisabled()\"\r\n [label]=\"\r\n (isPublished()\r\n ? 'flowplus.topbar.published'\r\n : 'flowplus.topbar.publish'\r\n ) | transloco\r\n \"\r\n labelPosition=\"start\"\r\n size=\"small\"\r\n />\r\n </div>\r\n\r\n <!-- Separator before the settings action. -->\r\n <span\r\n class=\"mx-1 h-7 w-px bg-(--p-content-border-color)\"\r\n aria-hidden=\"true\"\r\n ></span>\r\n\r\n <mt-button\n variant=\"outlined\"\n severity=\"primary\"\n icon=\"general.settings-02\"\n [tooltip]=\"'flowplus.topbar.settings' | transloco\"\n (onClick)=\"openSettings.emit()\"\n />\n</header>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Breadcrumb, selector: "mt-breadcrumb", inputs: ["items", "styleClass"], outputs: ["onItemClick"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
20817
20825
|
}
|
|
20818
20826
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: BuilderTopbarComponent, decorators: [{
|
|
20819
20827
|
type: Component,
|
|
@@ -20947,7 +20955,7 @@ class CommandPaletteComponent {
|
|
|
20947
20955
|
trackAction = (_i, a) => a.id;
|
|
20948
20956
|
trackStep = (_i, s) => `${s.type}:${s.key ?? ''}:${_i}`;
|
|
20949
20957
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: CommandPaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
20950
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: CommandPaletteComponent, isStandalone: true, selector: "fp-command-palette", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick", dismiss: "dismiss" }, host: { classAttribute: "z-[60]" }, ngImport: i0, template: "@if (open()) {\r\n <div\r\n class=\"fixed inset-0 z-[50] flex items-start justify-center bg-[rgba(15,23,42,0.48)] pt-[12vh]\"\r\n role=\"dialog\"\r\n [attr.aria-label]=\"'flowplus.commandPalette.title' | transloco\"\r\n (click)=\"dismiss.emit()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n >\r\n <div\r\n class=\"flex max-h-[70vh] w-[min(560px,90vw)] flex-col overflow-hidden rounded-2xl border border-(--p-content-border-color) bg-(--p-content-background) shadow-lg\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <header class=\"border-b border-(--p-content-border-color) p-3\">\r\n <mt-text-field\r\n [ngModel]=\"query()\"\r\n (ngModelChange)=\"onQueryChange($event)\"\r\n [placeholder]=\"'flowplus.commandPalette.placeholder' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"min-h-0 flex-1 overflow-y-auto px-2 pt-1.5 pb-3\"\r\n role=\"listbox\"\r\n >\r\n @if (filteredActions().length > 0) {\r\n <div class=\"mt-2 first-of-type:mt-0\">\r\n <h6\r\n class=\"mx-2 my-1 text-[11px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.commandPalette.actions\" | transloco }}\r\n </h6>\r\n @for (\r\n a of filteredActions();\r\n track trackAction($index, a);\r\n let i = $index\r\n ) {\r\n <div\n role=\"option\"\n tabindex=\"-1\"\n class=\"flex w-full cursor-pointer items-center gap-[10px] rounded-lg border-0 bg-transparent px-2.5 py-2 text-start font-[inherit] text-(--p-text-color) outline-none hover:bg-(--p-surface-100)\"\n [class.bg-(--p-surface-100)]=\"selectedIndex() === i\"\n [attr.aria-selected]=\"selectedIndex() === i\"\n (click)=\"pickAction(a)\"\n (mouseenter)=\"selectedIndex.set(i)\"\n >\n <span class=\"min-w-0 flex-1\">{{ a.label }}</span>\r\n @if (a.shortcut) {\r\n <kbd\r\n class=\"rounded-[4px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1.5 py-px font-mono text-[10.5px] text-(--p-text-muted-color)\"\r\n >{{ a.shortcut }}</kbd\n >\n }\n </div>\n }\n </div>\n }\n\r\n @if (filteredSteps().length > 0) {\r\n <div class=\"mt-2 first-of-type:mt-0\">\r\n <h6\r\n class=\"mx-2 my-1 text-[11px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.commandPalette.addStep\" | transloco }}\r\n </h6>\r\n @for (\r\n s of filteredSteps();\r\n track trackStep($index, s);\r\n let i = $index\r\n ) {\r\n <div\n role=\"option\"\n tabindex=\"-1\"\n class=\"flex w-full cursor-pointer items-center gap-[10px] rounded-lg border-0 bg-transparent px-2.5 py-2 text-start font-[inherit] text-(--p-text-color) outline-none hover:bg-(--p-surface-100)\"\n [class.bg-(--p-surface-100)]=\"\n selectedIndex() === i + filteredActions().length\n \"\n [attr.aria-selected]=\"\n selectedIndex() === i + filteredActions().length\n \"\n (click)=\"pickStep(s)\"\n (mouseenter)=\"selectedIndex.set(i + filteredActions().length)\"\n >\n <span\r\n class=\"inline-flex h-[26px] w-[26px] flex-none items-center justify-center rounded-md text-[12px] font-bold text-white\"\r\n [style.background]=\"iconColor(s)\"\r\n >\r\n {{ resolve(s.displayName).charAt(0) }}\r\n </span>\r\n <span class=\"flex min-w-0 flex-col\">\r\n <span class=\"min-w-0 flex-1\">{{\r\n resolve(s.displayName)\r\n }}</span>\r\n @if (s.description) {\r\n <small\r\n class=\"mt-px text-[11px] text-(--p-text-muted-color)\"\r\n >{{ resolve(s.description) }}</small\n >\n }\n </span>\n </div>\n }\n </div>\n }\n\r\n @if (flatList().length === 0) {\r\n <div class=\"p-6 text-center text-[12px] text-(--p-text-muted-color)\">\r\n {{ \"flowplus.commandPalette.noMatches\" | transloco }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <footer\r\n class=\"flex items-center justify-between gap-3 border-t border-(--p-content-border-color) px-3 py-2 text-[11px] text-(--p-text-muted-color)\"\r\n >\r\n <span\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >\u2191</kbd\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >\u2193</kbd\r\n >\r\n {{ \"flowplus.commandPalette.navigate\" | transloco }}</span\r\n >\r\n <span\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >Enter</kbd\r\n >\r\n {{ \"flowplus.commandPalette.select\" | transloco }}</span\r\n >\r\n <span\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >Esc</kbd\r\n >\r\n {{ \"flowplus.commandPalette.close\" | transloco }}</span\r\n >\r\n </footer>\r\n </div>\r\n </div>\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "
|
|
20958
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: CommandPaletteComponent, isStandalone: true, selector: "fp-command-palette", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pick: "pick", dismiss: "dismiss" }, host: { classAttribute: "z-[60]" }, ngImport: i0, template: "@if (open()) {\r\n <div\r\n class=\"fixed inset-0 z-[50] flex items-start justify-center bg-[rgba(15,23,42,0.48)] pt-[12vh]\"\r\n role=\"dialog\"\r\n [attr.aria-label]=\"'flowplus.commandPalette.title' | transloco\"\r\n (click)=\"dismiss.emit()\"\r\n (keydown)=\"onKeyDown($event)\"\r\n >\r\n <div\r\n class=\"flex max-h-[70vh] w-[min(560px,90vw)] flex-col overflow-hidden rounded-2xl border border-(--p-content-border-color) bg-(--p-content-background) shadow-lg\"\r\n (click)=\"$event.stopPropagation()\"\r\n >\r\n <header class=\"border-b border-(--p-content-border-color) p-3\">\r\n <mt-text-field\r\n [ngModel]=\"query()\"\r\n (ngModelChange)=\"onQueryChange($event)\"\r\n [placeholder]=\"'flowplus.commandPalette.placeholder' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"min-h-0 flex-1 overflow-y-auto px-2 pt-1.5 pb-3\"\r\n role=\"listbox\"\r\n >\r\n @if (filteredActions().length > 0) {\r\n <div class=\"mt-2 first-of-type:mt-0\">\r\n <h6\r\n class=\"mx-2 my-1 text-[11px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.commandPalette.actions\" | transloco }}\r\n </h6>\r\n @for (\r\n a of filteredActions();\r\n track trackAction($index, a);\r\n let i = $index\r\n ) {\r\n <div\n role=\"option\"\n tabindex=\"-1\"\n class=\"flex w-full cursor-pointer items-center gap-[10px] rounded-lg border-0 bg-transparent px-2.5 py-2 text-start font-[inherit] text-(--p-text-color) outline-none hover:bg-(--p-surface-100)\"\n [class.bg-(--p-surface-100)]=\"selectedIndex() === i\"\n [attr.aria-selected]=\"selectedIndex() === i\"\n (click)=\"pickAction(a)\"\n (mouseenter)=\"selectedIndex.set(i)\"\n >\n <span class=\"min-w-0 flex-1\">{{ a.label }}</span>\r\n @if (a.shortcut) {\r\n <kbd\r\n class=\"rounded-[4px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1.5 py-px font-mono text-[10.5px] text-(--p-text-muted-color)\"\r\n >{{ a.shortcut }}</kbd\n >\n }\n </div>\n }\n </div>\n }\n\r\n @if (filteredSteps().length > 0) {\r\n <div class=\"mt-2 first-of-type:mt-0\">\r\n <h6\r\n class=\"mx-2 my-1 text-[11px] font-semibold text-(--p-text-muted-color)\"\n >\r\n {{ \"flowplus.commandPalette.addStep\" | transloco }}\r\n </h6>\r\n @for (\r\n s of filteredSteps();\r\n track trackStep($index, s);\r\n let i = $index\r\n ) {\r\n <div\n role=\"option\"\n tabindex=\"-1\"\n class=\"flex w-full cursor-pointer items-center gap-[10px] rounded-lg border-0 bg-transparent px-2.5 py-2 text-start font-[inherit] text-(--p-text-color) outline-none hover:bg-(--p-surface-100)\"\n [class.bg-(--p-surface-100)]=\"\n selectedIndex() === i + filteredActions().length\n \"\n [attr.aria-selected]=\"\n selectedIndex() === i + filteredActions().length\n \"\n (click)=\"pickStep(s)\"\n (mouseenter)=\"selectedIndex.set(i + filteredActions().length)\"\n >\n <span\r\n class=\"inline-flex h-[26px] w-[26px] flex-none items-center justify-center rounded-md text-[12px] font-bold text-white\"\r\n [style.background]=\"iconColor(s)\"\r\n >\r\n {{ resolve(s.displayName).charAt(0) }}\r\n </span>\r\n <span class=\"flex min-w-0 flex-col\">\r\n <span class=\"min-w-0 flex-1\">{{\r\n resolve(s.displayName)\r\n }}</span>\r\n @if (s.description) {\r\n <small\r\n class=\"mt-px text-[11px] text-(--p-text-muted-color)\"\r\n >{{ resolve(s.description) }}</small\n >\n }\n </span>\n </div>\n }\n </div>\n }\n\r\n @if (flatList().length === 0) {\r\n <div class=\"p-6 text-center text-[12px] text-(--p-text-muted-color)\">\r\n {{ \"flowplus.commandPalette.noMatches\" | transloco }}\r\n </div>\r\n }\r\n </div>\r\n\r\n <footer\r\n class=\"flex items-center justify-between gap-3 border-t border-(--p-content-border-color) px-3 py-2 text-[11px] text-(--p-text-muted-color)\"\r\n >\r\n <span\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >\u2191</kbd\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >\u2193</kbd\r\n >\r\n {{ \"flowplus.commandPalette.navigate\" | transloco }}</span\r\n >\r\n <span\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >Enter</kbd\r\n >\r\n {{ \"flowplus.commandPalette.select\" | transloco }}</span\r\n >\r\n <span\r\n ><kbd\r\n class=\"me-1 rounded-[3px] border border-(--p-content-border-color) bg-(--p-surface-100) px-1 font-mono text-[10px]\"\r\n >Esc</kbd\r\n >\r\n {{ \"flowplus.commandPalette.close\" | transloco }}</span\r\n >\r\n </footer>\r\n </div>\r\n </div>\r\n}\r\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
20951
20959
|
}
|
|
20952
20960
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: CommandPaletteComponent, decorators: [{
|
|
20953
20961
|
type: Component,
|
|
@@ -22208,7 +22216,7 @@ class WorkflowBuilderPageComponent {
|
|
|
22208
22216
|
this.store.setInspectorOpen(true);
|
|
22209
22217
|
}
|
|
22210
22218
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilderPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22211
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilderPageComponent, isStandalone: true, selector: "fp-workflow-builder-page", viewQueries: [{ propertyName: "canvas", first: true, predicate: FlowCanvasComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-
|
|
22219
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilderPageComponent, isStandalone: true, selector: "fp-workflow-builder-page", viewQueries: [{ propertyName: "canvas", first: true, predicate: FlowCanvasComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-96]=\"store.ui().bottomPanelOpen\"\n [class.h-10]=\"!store.ui().bottomPanelOpen\"\n (focus)=\"onFocusIssue($event)\"\n />\n </div>\n\n <fp-command-palette\n [open]=\"commandPaletteOpen()\"\n [actions]=\"commandPaletteActions()\"\n (pick)=\"onCommandPalettePick($event)\"\n (dismiss)=\"closeCommandPalette()\"\n />\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: BuilderTopbarComponent, selector: "fp-builder-topbar", outputs: ["validate", "testRun", "publish", "unpublish", "openSettings", "toggleNodeFinder"] }, { kind: "component", type: PaletteComponent, selector: "fp-palette", outputs: ["closeRequested", "openRequested", "add", "addTrigger"] }, { kind: "component", type: FlowCanvasComponent, selector: "fp-flow-canvas", outputs: ["paletteDrop", "connectionCreate", "connectionReassign", "triggerStartConnect", "triggerStartReassign", "triggerStartDisconnect", "connectionQuickAdd", "quickAddPick", "autoLayoutRequested", "loaded", "nodeQuickAdd", "nodePortPlusClick", "nodeDuplicate", "nodeRemove", "nodeOpenDetails", "edgeInsertStep", "edgeRemove", "edgeEditFormula", "edgeOpenDetails", "assignNodeToConnection", "openChildWorkflow", "starterAddTrigger", "triggerOpenDetails", "triggerExecute", "triggerToggleEnabled", "triggerDelete", "canvasBackgroundClick", "requestAddStep"] }, { kind: "component", type: BottomPanelComponent, selector: "fp-bottom-panel", outputs: ["focus"] }, { kind: "component", type: CommandPaletteComponent, selector: "fp-command-palette", inputs: ["open", "actions"], outputs: ["pick", "dismiss"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
22212
22220
|
}
|
|
22213
22221
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilderPageComponent, decorators: [{
|
|
22214
22222
|
type: Component,
|
|
@@ -22220,7 +22228,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
|
|
|
22220
22228
|
FlowCanvasComponent,
|
|
22221
22229
|
BottomPanelComponent,
|
|
22222
22230
|
CommandPaletteComponent,
|
|
22223
|
-
], template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-
|
|
22231
|
+
], template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-96]=\"store.ui().bottomPanelOpen\"\n [class.h-10]=\"!store.ui().bottomPanelOpen\"\n (focus)=\"onFocusIssue($event)\"\n />\n </div>\n\n <fp-command-palette\n [open]=\"commandPaletteOpen()\"\n [actions]=\"commandPaletteActions()\"\n (pick)=\"onCommandPalettePick($event)\"\n (dismiss)=\"closeCommandPalette()\"\n />\n</div>\n" }]
|
|
22224
22232
|
}], ctorParameters: () => [], propDecorators: { canvas: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FlowCanvasComponent), { isSignal: true }] }] } });
|
|
22225
22233
|
function readObject(value) {
|
|
22226
22234
|
return value && typeof value === 'object' && !Array.isArray(value)
|
|
@@ -22847,7 +22855,7 @@ class AutomationExecutionsPageComponent {
|
|
|
22847
22855
|
this.operationState.set('error');
|
|
22848
22856
|
}
|
|
22849
22857
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AutomationExecutionsPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
22850
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AutomationExecutionsPageComponent, isStandalone: true, selector: "fp-automation-executions-page", viewQueries: [{ propertyName: "executionCellTpl", first: true, predicate: ["executionCellTpl"], descendants: true, isSignal: true }, { propertyName: "executionStatusTpl", first: true, predicate: ["executionStatusTpl"], descendants: true, isSignal: true }, { propertyName: "executionRevisionTpl", first: true, predicate: ["executionRevisionTpl"], descendants: true, isSignal: true }, { propertyName: "executionStartedTpl", first: true, predicate: ["executionStartedTpl"], descendants: true, isSignal: true }, { propertyName: "nodeRunCellTpl", first: true, predicate: ["nodeRunCellTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<section\n class=\"flex h-full min-h-[720px] flex-col bg-(--p-surface-0) text-(--p-text-color)\"\n>\n <header\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-(--p-content-border-color) px-4 py-3\"\n >\n <div>\n <h1 class=\"m-0 text-[18px] font-semibold\">\n {{ \"flowplus.execution.title\" | transloco }}\n </h1>\n <p class=\"m-0 mt-0.5 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.subtitle\" | transloco }}\n </p>\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (health(); as h) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.queued\" | transloco }} {{ h.queuedCount }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.running\" | transloco }} {{ h.runningCount }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.waiting\" | transloco }} {{ h.waitingCount }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.failed\" | transloco }} {{ h.failedCount }}\n </span>\n @if (h.scheduleDueCount != null) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n Schedule due {{ h.scheduleDueCount }}\n </span>\n }\n }\n @if (retentionPreview(); as retention) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n Retention candidates {{ retention.candidateCount }}\n </span>\n }\n <mt-button\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"'flowplus.execution.refresh' | transloco\"\n [loading]=\"listState() === 'loading'\"\n (onClick)=\"refresh(); loadHealth(); loadRetentionPreview()\"\n />\n </div>\n </header>\n\n @if (healthError()) {\n <aside\n class=\"border-b border-[rgb(var(--fp-warning))]/25 bg-[rgb(var(--fp-warning))]/10 px-4 py-2 text-[12px]\"\n role=\"status\"\n >\n Health: {{ healthError() }}\n </aside>\n }\n\n @if (retentionError()) {\n <aside\n class=\"border-b border-[rgb(var(--fp-warning))]/25 bg-[rgb(var(--fp-warning))]/10 px-4 py-2 text-[12px]\"\n role=\"status\"\n >\n Retention preview: {{ retentionError() }}\n </aside>\n }\n\n <div\n class=\"grid flex-1 min-h-0 grid-cols-[minmax(380px,1fr)_minmax(0,1.6fr)]\"\n >\n <aside\n class=\"flex min-h-0 flex-col border-e border-(--p-content-border-color)\"\n >\n <form\n class=\"grid grid-cols-2 gap-2 border-b border-(--p-content-border-color) p-3\"\n (submit)=\"$event.preventDefault(); refresh()\"\n >\n <mt-select-field\n [ngModel]=\"statusFilter()\"\n (ngModelChange)=\"statusFilter.set($event)\"\n name=\"status\"\n [label]=\"'flowplus.execution.status' | transloco\"\n [options]=\"statusOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"triggerTypeFilter()\"\n (ngModelChange)=\"triggerTypeFilter.set($event)\"\n name=\"triggerType\"\n label=\"Trigger type\"\n [options]=\"triggerTypeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n [ngModel]=\"revisionFilter()\"\n (ngModelChange)=\"revisionFilter.set($event)\"\n name=\"revision\"\n label=\"Revision id\"\n />\n <mt-text-field\n [ngModel]=\"correlationFilter()\"\n (ngModelChange)=\"correlationFilter.set($event)\"\n name=\"correlation\"\n [label]=\"'flowplus.execution.correlation' | transloco\"\n />\n <mt-text-field\n [ngModel]=\"startedFrom()\"\n (ngModelChange)=\"startedFrom.set($event)\"\n name=\"startedFrom\"\n label=\"Started from\"\n placeholder=\"YYYY-MM-DD\"\n />\n <mt-text-field\n [ngModel]=\"startedTo()\"\n (ngModelChange)=\"startedTo.set($event)\"\n name=\"startedTo\"\n label=\"Started to\"\n placeholder=\"YYYY-MM-DD\"\n />\n <mt-toggle-field\n class=\"self-center\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"waitingOnly()\"\n (ngModelChange)=\"waitingOnly.set($event)\"\n name=\"waitingOnly\"\n [label]=\"'flowplus.execution.waitingOnly' | transloco\"\n />\n <mt-toggle-field\n class=\"self-center\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"failuresOnly()\"\n (ngModelChange)=\"failuresOnly.set($event)\"\n name=\"failuresOnly\"\n [label]=\"'flowplus.execution.failuresOnly' | transloco\"\n />\n </form>\n\n @if (listState() === \"loading\") {\n <div class=\"flex flex-col gap-2 p-3\">\n @for (i of [1, 2, 3, 4, 5]; track i) {\n <div\n class=\"h-14 animate-pulse rounded-md bg-(--p-surface-100)\"\n ></div>\n }\n </div>\n } @else if (listState() === \"error\") {\n <div\n class=\"flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center text-[13px]\"\n >\n <div class=\"text-(--p-text-color)\">{{ error() ?? \"Operation failed\" }}</div>\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"'flowplus.execution.refresh' | transloco\"\n (onClick)=\"refresh()\"\n />\n </div>\n } @else if (executions().length === 0) {\n <div\n class=\"flex flex-1 items-center justify-center px-6 text-center text-[13px] text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.execution.empty\" | transloco }}\n </div>\n } @else {\n <div class=\"min-h-0 flex-1 overflow-auto\">\n <mt-table\n [data]=\"executions()\"\n [columns]=\"executionColumns()\"\n dataKey=\"executionId\"\n [clickableRows]=\"true\"\n [noCard]=\"true\"\n [tableLayout]=\"'auto'\"\n [pageSize]=\"25\"\n [rowsPerPageOptions]=\"[25, 50, 100]\"\n [alwaysShowPaginator]=\"false\"\n (rowClick)=\"openExecution($event.executionId)\"\n />\n\n @if (hasMore()) {\n <div class=\"border-t border-(--p-content-border-color) p-3\">\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.chevron-down\"\n label=\"Load more executions\"\n (onClick)=\"loadMoreExecutions()\"\n />\n </div>\n }\n </div>\n }\n </aside>\n\n <main class=\"flex min-h-0 flex-col\">\n @if (error()) {\n <aside\n class=\"border-b border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-4 py-2 text-[12px]\"\n role=\"status\"\n >\n {{ error() }}\n </aside>\n }\n\n @if (detailState() === \"loading\") {\n <div class=\"flex flex-col gap-3 p-4\">\n <div class=\"h-20 animate-pulse rounded-md bg-(--p-surface-100)\"></div>\n <div class=\"h-40 animate-pulse rounded-md bg-(--p-surface-100)\"></div>\n </div>\n } @else if (detail(); as d) {\n <section\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-(--p-content-border-color) px-4 py-3\"\n >\n <div>\n <div class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.selected\" | transloco }}\n </div>\n <h2 class=\"m-0 text-[16px] font-semibold\">\n <code class=\"font-mono\">{{ d.executionId }}</code>\n </h2>\n <p class=\"m-0 mt-0.5 text-[12px] text-(--p-text-muted-color)\">\n {{ d.status }} / {{ d.correlationId ?? \"-\" }}\n </p>\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n <mt-button\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n label=\"Refresh detail\"\n (onClick)=\"openExecution(d.executionId)\"\n />\n <mt-button\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"'flowplus.execution.retry' | transloco\"\n [disabled]=\"!canRetry() || operationState() === 'loading'\"\n (onClick)=\"retry()\"\n />\n <mt-button\n severity=\"danger\"\n icon=\"general.x-close\"\n [label]=\"'flowplus.execution.cancel' | transloco\"\n [disabled]=\"!canCancel() || operationState() === 'loading'\"\n (onClick)=\"cancel()\"\n />\n </div>\n </section>\n\n <div\n class=\"flex flex-wrap gap-2 border-b border-(--p-content-border-color) px-4 py-3 text-[11.5px]\"\n >\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1\"\n >\n Trigger {{ d.triggerType ?? \"-\" }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1\"\n >\n Revision {{ d.revisionNumber ?? \"-\" }}\n </span>\n @if (failedNode()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-2 py-1\"\n >\n Failed node {{ dataPreview(failedNode()) }}\n </span>\n }\n @if (waitingNode()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1\"\n >\n Waiting node {{ dataPreview(waitingNode()) }}\n </span>\n }\n </div>\n\n <div class=\"grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_380px]\">\n <div class=\"min-h-0 overflow-auto p-4\">\n @if (d.triggerSummary || d.triggerSummaryJson) {\n <section class=\"mb-5\">\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n Trigger summary\n </h3>\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) p-3\"\n >\n <pre\n class=\"m-0 max-h-52 overflow-auto whitespace-pre-wrap font-mono text-[11px]\"\n >{{ dataPreview(d.triggerSummary ?? d.triggerSummaryJson) }}</pre\n >\n </div>\n </section>\n }\n\n <section>\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n {{ \"flowplus.execution.nodeRuns\" | transloco }}\n </h3>\n <div\n class=\"overflow-hidden rounded-md border border-(--p-content-border-color)\"\n >\n <mt-table\n [data]=\"nodeRunRows()\"\n [columns]=\"nodeRunColumns()\"\n dataKey=\"nodeRunId\"\n [rowActions]=\"nodeRunActions\"\n [noCard]=\"true\"\n [tableLayout]=\"'auto'\"\n [pageSize]=\"25\"\n [rowsPerPageOptions]=\"[25, 50, 100]\"\n [alwaysShowPaginator]=\"false\"\n />\n </div>\n </section>\n\n <section class=\"mt-5\">\n <div class=\"mb-2 flex items-center justify-between gap-3\">\n <h3 class=\"m-0 text-[13px] font-semibold\">\n {{ \"flowplus.execution.timeline\" | transloco }}\n </h3>\n @if (d.timelineHasMore) {\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.chevron-down\"\n label=\"Load more timeline\"\n [loading]=\"timelineState() === 'loading'\"\n (onClick)=\"loadMoreTimeline()\"\n />\n }\n </div>\n <ol class=\"m-0 flex list-none flex-col gap-2 p-0\">\n @for (event of d.events; track event.eventId) {\n <li\n class=\"rounded-md border border-(--p-content-border-color) px-3 py-2\"\n >\n <div class=\"flex items-center justify-between gap-3\">\n <strong class=\"text-[12.5px]\">{{\n event.eventType\n }}</strong>\n <span class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ event.occurredAtUtc }}\n </span>\n </div>\n <div class=\"mt-1 text-[11.5px] text-(--p-text-muted-color)\">\n {{ event.severity }} / {{ event.nodeKey ?? \"-\" }}\n </div>\n @if (advancedTimelineSummary(event); as summary) {\n <div class=\"mt-2 rounded-md bg-(--p-surface-50) px-2 py-1 text-[11.5px] text-(--p-text-color)\">\n {{ summary }}\n </div>\n }\n @if (event.data || event.dataJson) {\n <pre\n class=\"mt-2 max-h-28 overflow-auto whitespace-pre-wrap rounded-md bg-(--p-surface-50) p-2 font-mono text-[10.5px]\"\n >{{ dataPreview(event.data ?? event.dataJson) }}</pre\n >\n }\n </li>\n }\n </ol>\n </section>\n\n @if (hasOperationalDetails()) {\n <section class=\"mt-5\">\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n Operator details\n </h3>\n <div class=\"grid gap-3\">\n @if (d.externalCallAudits?.length) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) p-3\"\n >\n <div class=\"mb-2 text-[12px] font-semibold\">\n External call audits\n </div>\n <pre\n class=\"m-0 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[10.5px]\"\n >{{ dataPreview(d.externalCallAudits) }}</pre\n >\n </div>\n }\n @if (d.flowPlusCommitAudits?.length) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) p-3\"\n >\n <div class=\"mb-2 text-[12px] font-semibold\">\n FlowPlus commit audits\n </div>\n <pre\n class=\"m-0 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[10.5px]\"\n >{{ dataPreview(d.flowPlusCommitAudits) }}</pre\n >\n </div>\n }\n @if (d.approvalDecisions?.length) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) p-3\"\n >\n <div class=\"mb-2 text-[12px] font-semibold\">\n Approval decisions\n </div>\n <pre\n class=\"m-0 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[10.5px]\"\n >{{ dataPreview(d.approvalDecisions) }}</pre\n >\n </div>\n }\n </div>\n </section>\n }\n </div>\n\n <aside\n class=\"min-h-0 overflow-auto border-s border-(--p-content-border-color) bg-(--p-surface-50) p-3\"\n >\n <section>\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n {{ \"flowplus.execution.waits\" | transloco }}\n </h3>\n @if (waitingItems().length === 0) {\n <p class=\"m-0 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.noWaits\" | transloco }}\n </p>\n } @else {\n <div class=\"flex flex-col gap-2\">\n @for (wait of waitingItems(); track wait.runtimeWaitId) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <strong class=\"text-[12px]\">{{ wait.waitType }}</strong>\n <span class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ wait.status }}\n </span>\n </div>\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">\n {{ wait.nodeKey ?? \"-\" }}\n </div>\n <mt-textarea-field\n class=\"mt-2\"\n [ngModel]=\"resumePayloadText()\"\n (ngModelChange)=\"resumePayloadText.set($event)\"\n [label]=\"'flowplus.execution.resumePayload' | transloco\"\n rows=\"4\"\n />\n <mt-select-field\n class=\"mt-2\"\n [ngModel]=\"approvalDecision()\"\n (ngModelChange)=\"approvalDecision.set($event)\"\n label=\"Approval decision\"\n [options]=\"[\n { label: 'Approved', value: 'Approved' },\n { label: 'Rejected', value: 'Rejected' },\n { label: 'Returned', value: 'Returned' }\n ]\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-textarea-field\n class=\"mt-2\"\n [ngModel]=\"approvalComments()\"\n (ngModelChange)=\"approvalComments.set($event)\"\n label=\"Decision comments\"\n rows=\"3\"\n />\n <div class=\"mt-2 flex gap-2\">\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n icon=\"media.play-circle\"\n [label]=\"'flowplus.execution.resume' | transloco\"\n (onClick)=\"resume(wait)\"\n />\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n icon=\"general.check-circle\"\n [label]=\"'flowplus.execution.decide' | transloco\"\n (onClick)=\"decide(wait)\"\n />\n </div>\n </div>\n }\n </div>\n }\n </section>\n\n <section class=\"mt-4\">\n <div class=\"mb-2 flex items-center justify-between gap-2\">\n <h3 class=\"m-0 text-[13px] font-semibold\">\n {{ \"flowplus.execution.nodeData\" | transloco }}\n </h3>\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"includeFullNodeData()\"\n (ngModelChange)=\"onIncludeFullNodeDataChange($event)\"\n label=\"Full\"\n />\n </div>\n @if (nodeData(); as data) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2\"\n >\n <div\n class=\"mb-2 flex items-center justify-between gap-2 text-[11px]\"\n >\n <code class=\"font-mono\">{{ data.nodeRunId }}</code>\n <span>{{ nodeDataStateLabel(data) }}</span>\n </div>\n @if (nodeDataNotice(data); as notice) {\n <div\n class=\"mb-2 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1.5 text-[11.5px] leading-5 text-(--p-text-muted-color)\"\n >\n {{ notice }}\n </div>\n }\n @if (advancedNodeDataSummary(data).length > 0) {\n <div class=\"mb-2 flex flex-wrap gap-1.5\">\n @for (item of advancedNodeDataSummary(data); track item) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n {{ item }}\n </span>\n }\n </div>\n }\n <pre\n class=\"m-0 max-h-80 overflow-auto whitespace-pre-wrap font-mono text-[11px]\"\n >{{\n dataPreview(\n data.fullData ?? {\n summary: data.summary,\n input: data.input,\n output: data.output,\n errorJson: data.errorJson,\n attempts: data.attempts,\n logs: data.logs,\n replayAvailable: data.replayAvailable,\n replayAvailabilityReason: data.replayAvailabilityReason\n }\n )\n }}</pre\n >\n </div>\n } @else {\n <p class=\"m-0 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.noNodeData\" | transloco }}\n </p>\n }\n </section>\n\n <section class=\"mt-4\">\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n Health and operations\n </h3>\n <div class=\"grid gap-2\">\n @if (health(); as h) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2 text-[11.5px]\"\n >\n <div>Queued: {{ h.queuedCount }}</div>\n <div>Running: {{ h.runningCount }}</div>\n <div>Waiting: {{ h.waitingCount }}</div>\n <div>Failed: {{ h.failedCount }}</div>\n @if (h.scheduleDueCount != null) {\n <div>Schedule due: {{ h.scheduleDueCount }}</div>\n }\n </div>\n }\n @if (healthFlags().length > 0) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2\"\n >\n <div class=\"mb-1 text-[12px] font-semibold\">Flags</div>\n <div class=\"flex flex-wrap gap-1\">\n @for (flag of healthFlags(); track flag[0]) {\n <span\n class=\"inline-flex rounded-md bg-(--p-surface-100) px-2 py-0.5 text-[11px]\"\n >{{ flag[0] }}</span\n >\n }\n </div>\n </div>\n }\n @if (retentionPreview(); as retention) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2 text-[11.5px]\"\n >\n <div class=\"text-[12px] font-semibold\">Retention preview</div>\n <div>Candidate count: {{ retention.candidateCount }}</div>\n <div>\n Oldest candidate: {{ retention.oldestCandidateUtc ?? \"-\" }}\n </div>\n <div>Dry run: {{ retention.dryRun ? \"Yes\" : \"No\" }}</div>\n </div>\n }\n </div>\n </section>\n </aside>\n </div>\n } @else {\n <div\n class=\"flex flex-1 items-center justify-center px-6 text-center text-[13px] text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.execution.selectExecution\" | transloco }}\n </div>\n }\n </main>\n </div>\n</section>\n\n<ng-template #executionCellTpl let-row>\n <code class=\"font-mono\">{{ row.executionId }}</code>\n <div class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ row.triggerType ?? \"-\" }}\n </div>\n</ng-template>\n\n<ng-template #executionStatusTpl let-row>\n <span\n class=\"inline-flex rounded-md bg-(--p-surface-100) px-2 py-0.5 text-[11px] font-medium\"\n >\n {{ row.status }}\n </span>\n</ng-template>\n\n<ng-template #executionRevisionTpl let-row>\n <span class=\"text-(--p-text-muted-color)\">\n {{ row.revisionNumber ?? \"-\" }}\n </span>\n</ng-template>\n\n<ng-template #executionStartedTpl let-row>\n <span class=\"text-(--p-text-muted-color)\">\n {{ row.startedAtUtc ?? row.createdAtUtc ?? \"-\" }}\n </span>\n</ng-template>\n\n<ng-template #nodeRunCellTpl let-row>\n <strong>{{ row.nodeKey }}</strong>\n <div class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ row.nodeType }}\n </div>\n @if (advancedNodeRunSummary(row); as summary) {\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">\n {{ summary }}\n </div>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "description", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "description", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "hint", "description", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
22858
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: AutomationExecutionsPageComponent, isStandalone: true, selector: "fp-automation-executions-page", viewQueries: [{ propertyName: "executionCellTpl", first: true, predicate: ["executionCellTpl"], descendants: true, isSignal: true }, { propertyName: "executionStatusTpl", first: true, predicate: ["executionStatusTpl"], descendants: true, isSignal: true }, { propertyName: "executionRevisionTpl", first: true, predicate: ["executionRevisionTpl"], descendants: true, isSignal: true }, { propertyName: "executionStartedTpl", first: true, predicate: ["executionStartedTpl"], descendants: true, isSignal: true }, { propertyName: "nodeRunCellTpl", first: true, predicate: ["nodeRunCellTpl"], descendants: true, isSignal: true }], ngImport: i0, template: "<section\n class=\"flex h-full min-h-[720px] flex-col bg-(--p-surface-0) text-(--p-text-color)\"\n>\n <header\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-(--p-content-border-color) px-4 py-3\"\n >\n <div>\n <h1 class=\"m-0 text-[18px] font-semibold\">\n {{ \"flowplus.execution.title\" | transloco }}\n </h1>\n <p class=\"m-0 mt-0.5 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.subtitle\" | transloco }}\n </p>\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n @if (health(); as h) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.queued\" | transloco }} {{ h.queuedCount }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.running\" | transloco }} {{ h.runningCount }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.waiting\" | transloco }} {{ h.waitingCount }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-2 py-1 text-[11.5px]\"\n >\n {{ \"flowplus.execution.failed\" | transloco }} {{ h.failedCount }}\n </span>\n @if (h.scheduleDueCount != null) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n Schedule due {{ h.scheduleDueCount }}\n </span>\n }\n }\n @if (retentionPreview(); as retention) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1 text-[11.5px]\"\n >\n Retention candidates {{ retention.candidateCount }}\n </span>\n }\n <mt-button\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"'flowplus.execution.refresh' | transloco\"\n [loading]=\"listState() === 'loading'\"\n (onClick)=\"refresh(); loadHealth(); loadRetentionPreview()\"\n />\n </div>\n </header>\n\n @if (healthError()) {\n <aside\n class=\"border-b border-[rgb(var(--fp-warning))]/25 bg-[rgb(var(--fp-warning))]/10 px-4 py-2 text-[12px]\"\n role=\"status\"\n >\n Health: {{ healthError() }}\n </aside>\n }\n\n @if (retentionError()) {\n <aside\n class=\"border-b border-[rgb(var(--fp-warning))]/25 bg-[rgb(var(--fp-warning))]/10 px-4 py-2 text-[12px]\"\n role=\"status\"\n >\n Retention preview: {{ retentionError() }}\n </aside>\n }\n\n <div\n class=\"grid flex-1 min-h-0 grid-cols-[minmax(380px,1fr)_minmax(0,1.6fr)]\"\n >\n <aside\n class=\"flex min-h-0 flex-col border-e border-(--p-content-border-color)\"\n >\n <form\n class=\"grid grid-cols-2 gap-2 border-b border-(--p-content-border-color) p-3\"\n (submit)=\"$event.preventDefault(); refresh()\"\n >\n <mt-select-field\n [ngModel]=\"statusFilter()\"\n (ngModelChange)=\"statusFilter.set($event)\"\n name=\"status\"\n [label]=\"'flowplus.execution.status' | transloco\"\n [options]=\"statusOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-select-field\n [ngModel]=\"triggerTypeFilter()\"\n (ngModelChange)=\"triggerTypeFilter.set($event)\"\n name=\"triggerType\"\n label=\"Trigger type\"\n [options]=\"triggerTypeOptions\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-text-field\n [ngModel]=\"revisionFilter()\"\n (ngModelChange)=\"revisionFilter.set($event)\"\n name=\"revision\"\n label=\"Revision id\"\n />\n <mt-text-field\n [ngModel]=\"correlationFilter()\"\n (ngModelChange)=\"correlationFilter.set($event)\"\n name=\"correlation\"\n [label]=\"'flowplus.execution.correlation' | transloco\"\n />\n <mt-text-field\n [ngModel]=\"startedFrom()\"\n (ngModelChange)=\"startedFrom.set($event)\"\n name=\"startedFrom\"\n label=\"Started from\"\n placeholder=\"YYYY-MM-DD\"\n />\n <mt-text-field\n [ngModel]=\"startedTo()\"\n (ngModelChange)=\"startedTo.set($event)\"\n name=\"startedTo\"\n label=\"Started to\"\n placeholder=\"YYYY-MM-DD\"\n />\n <mt-toggle-field\n class=\"self-center\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"waitingOnly()\"\n (ngModelChange)=\"waitingOnly.set($event)\"\n name=\"waitingOnly\"\n [label]=\"'flowplus.execution.waitingOnly' | transloco\"\n />\n <mt-toggle-field\n class=\"self-center\"\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"failuresOnly()\"\n (ngModelChange)=\"failuresOnly.set($event)\"\n name=\"failuresOnly\"\n [label]=\"'flowplus.execution.failuresOnly' | transloco\"\n />\n </form>\n\n @if (listState() === \"loading\") {\n <div class=\"flex flex-col gap-2 p-3\">\n @for (i of [1, 2, 3, 4, 5]; track i) {\n <div\n class=\"h-14 animate-pulse rounded-md bg-(--p-surface-100)\"\n ></div>\n }\n </div>\n } @else if (listState() === \"error\") {\n <div\n class=\"flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center text-[13px]\"\n >\n <div class=\"text-(--p-text-color)\">{{ error() ?? \"Operation failed\" }}</div>\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"'flowplus.execution.refresh' | transloco\"\n (onClick)=\"refresh()\"\n />\n </div>\n } @else if (executions().length === 0) {\n <div\n class=\"flex flex-1 items-center justify-center px-6 text-center text-[13px] text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.execution.empty\" | transloco }}\n </div>\n } @else {\n <div class=\"min-h-0 flex-1 overflow-auto\">\n <mt-table\n [data]=\"executions()\"\n [columns]=\"executionColumns()\"\n dataKey=\"executionId\"\n [clickableRows]=\"true\"\n [noCard]=\"true\"\n [tableLayout]=\"'auto'\"\n [pageSize]=\"25\"\n [rowsPerPageOptions]=\"[25, 50, 100]\"\n [alwaysShowPaginator]=\"false\"\n (rowClick)=\"openExecution($event.executionId)\"\n />\n\n @if (hasMore()) {\n <div class=\"border-t border-(--p-content-border-color) p-3\">\n <mt-button\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.chevron-down\"\n label=\"Load more executions\"\n (onClick)=\"loadMoreExecutions()\"\n />\n </div>\n }\n </div>\n }\n </aside>\n\n <main class=\"flex min-h-0 flex-col\">\n @if (error()) {\n <aside\n class=\"border-b border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-4 py-2 text-[12px]\"\n role=\"status\"\n >\n {{ error() }}\n </aside>\n }\n\n @if (detailState() === \"loading\") {\n <div class=\"flex flex-col gap-3 p-4\">\n <div class=\"h-20 animate-pulse rounded-md bg-(--p-surface-100)\"></div>\n <div class=\"h-40 animate-pulse rounded-md bg-(--p-surface-100)\"></div>\n </div>\n } @else if (detail(); as d) {\n <section\n class=\"flex flex-wrap items-center justify-between gap-3 border-b border-(--p-content-border-color) px-4 py-3\"\n >\n <div>\n <div class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.selected\" | transloco }}\n </div>\n <h2 class=\"m-0 text-[16px] font-semibold\">\n <code class=\"font-mono\">{{ d.executionId }}</code>\n </h2>\n <p class=\"m-0 mt-0.5 text-[12px] text-(--p-text-muted-color)\">\n {{ d.status }} / {{ d.correlationId ?? \"-\" }}\n </p>\n </div>\n <div class=\"flex flex-wrap items-center gap-2\">\n <mt-button\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n label=\"Refresh detail\"\n (onClick)=\"openExecution(d.executionId)\"\n />\n <mt-button\n severity=\"secondary\"\n icon=\"arrow.refresh-cw-01\"\n [label]=\"'flowplus.execution.retry' | transloco\"\n [disabled]=\"!canRetry() || operationState() === 'loading'\"\n (onClick)=\"retry()\"\n />\n <mt-button\n severity=\"danger\"\n icon=\"general.x-close\"\n [label]=\"'flowplus.execution.cancel' | transloco\"\n [disabled]=\"!canCancel() || operationState() === 'loading'\"\n (onClick)=\"cancel()\"\n />\n </div>\n </section>\n\n <div\n class=\"flex flex-wrap gap-2 border-b border-(--p-content-border-color) px-4 py-3 text-[11.5px]\"\n >\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1\"\n >\n Trigger {{ d.triggerType ?? \"-\" }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1\"\n >\n Revision {{ d.revisionNumber ?? \"-\" }}\n </span>\n @if (failedNode()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-[rgb(var(--fp-danger))]/30 bg-[rgb(var(--fp-danger))]/10 px-2 py-1\"\n >\n Failed node {{ dataPreview(failedNode()) }}\n </span>\n }\n @if (waitingNode()) {\n <span\n class=\"inline-flex items-center gap-1 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1\"\n >\n Waiting node {{ dataPreview(waitingNode()) }}\n </span>\n }\n </div>\n\n <div class=\"grid min-h-0 flex-1 grid-cols-[minmax(0,1fr)_380px]\">\n <div class=\"min-h-0 overflow-auto p-4\">\n @if (d.triggerSummary || d.triggerSummaryJson) {\n <section class=\"mb-5\">\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n Trigger summary\n </h3>\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) p-3\"\n >\n <pre\n class=\"m-0 max-h-52 overflow-auto whitespace-pre-wrap font-mono text-[11px]\"\n >{{ dataPreview(d.triggerSummary ?? d.triggerSummaryJson) }}</pre\n >\n </div>\n </section>\n }\n\n <section>\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n {{ \"flowplus.execution.nodeRuns\" | transloco }}\n </h3>\n <div\n class=\"overflow-hidden rounded-md border border-(--p-content-border-color)\"\n >\n <mt-table\n [data]=\"nodeRunRows()\"\n [columns]=\"nodeRunColumns()\"\n dataKey=\"nodeRunId\"\n [rowActions]=\"nodeRunActions\"\n [noCard]=\"true\"\n [tableLayout]=\"'auto'\"\n [pageSize]=\"25\"\n [rowsPerPageOptions]=\"[25, 50, 100]\"\n [alwaysShowPaginator]=\"false\"\n />\n </div>\n </section>\n\n <section class=\"mt-5\">\n <div class=\"mb-2 flex items-center justify-between gap-3\">\n <h3 class=\"m-0 text-[13px] font-semibold\">\n {{ \"flowplus.execution.timeline\" | transloco }}\n </h3>\n @if (d.timelineHasMore) {\n <mt-button\n size=\"small\"\n severity=\"secondary\"\n variant=\"outlined\"\n icon=\"general.chevron-down\"\n label=\"Load more timeline\"\n [loading]=\"timelineState() === 'loading'\"\n (onClick)=\"loadMoreTimeline()\"\n />\n }\n </div>\n <ol class=\"m-0 flex list-none flex-col gap-2 p-0\">\n @for (event of d.events; track event.eventId) {\n <li\n class=\"rounded-md border border-(--p-content-border-color) px-3 py-2\"\n >\n <div class=\"flex items-center justify-between gap-3\">\n <strong class=\"text-[12.5px]\">{{\n event.eventType\n }}</strong>\n <span class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ event.occurredAtUtc }}\n </span>\n </div>\n <div class=\"mt-1 text-[11.5px] text-(--p-text-muted-color)\">\n {{ event.severity }} / {{ event.nodeKey ?? \"-\" }}\n </div>\n @if (advancedTimelineSummary(event); as summary) {\n <div class=\"mt-2 rounded-md bg-(--p-surface-50) px-2 py-1 text-[11.5px] text-(--p-text-color)\">\n {{ summary }}\n </div>\n }\n @if (event.data || event.dataJson) {\n <pre\n class=\"mt-2 max-h-28 overflow-auto whitespace-pre-wrap rounded-md bg-(--p-surface-50) p-2 font-mono text-[10.5px]\"\n >{{ dataPreview(event.data ?? event.dataJson) }}</pre\n >\n }\n </li>\n }\n </ol>\n </section>\n\n @if (hasOperationalDetails()) {\n <section class=\"mt-5\">\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n Operator details\n </h3>\n <div class=\"grid gap-3\">\n @if (d.externalCallAudits?.length) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) p-3\"\n >\n <div class=\"mb-2 text-[12px] font-semibold\">\n External call audits\n </div>\n <pre\n class=\"m-0 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[10.5px]\"\n >{{ dataPreview(d.externalCallAudits) }}</pre\n >\n </div>\n }\n @if (d.flowPlusCommitAudits?.length) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) p-3\"\n >\n <div class=\"mb-2 text-[12px] font-semibold\">\n FlowPlus commit audits\n </div>\n <pre\n class=\"m-0 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[10.5px]\"\n >{{ dataPreview(d.flowPlusCommitAudits) }}</pre\n >\n </div>\n }\n @if (d.approvalDecisions?.length) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) p-3\"\n >\n <div class=\"mb-2 text-[12px] font-semibold\">\n Approval decisions\n </div>\n <pre\n class=\"m-0 max-h-48 overflow-auto whitespace-pre-wrap font-mono text-[10.5px]\"\n >{{ dataPreview(d.approvalDecisions) }}</pre\n >\n </div>\n }\n </div>\n </section>\n }\n </div>\n\n <aside\n class=\"min-h-0 overflow-auto border-s border-(--p-content-border-color) bg-(--p-surface-50) p-3\"\n >\n <section>\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n {{ \"flowplus.execution.waits\" | transloco }}\n </h3>\n @if (waitingItems().length === 0) {\n <p class=\"m-0 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.noWaits\" | transloco }}\n </p>\n } @else {\n <div class=\"flex flex-col gap-2\">\n @for (wait of waitingItems(); track wait.runtimeWaitId) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2\"\n >\n <div class=\"flex items-center justify-between gap-2\">\n <strong class=\"text-[12px]\">{{ wait.waitType }}</strong>\n <span class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ wait.status }}\n </span>\n </div>\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">\n {{ wait.nodeKey ?? \"-\" }}\n </div>\n <mt-textarea-field\n class=\"mt-2\"\n [ngModel]=\"resumePayloadText()\"\n (ngModelChange)=\"resumePayloadText.set($event)\"\n [label]=\"'flowplus.execution.resumePayload' | transloco\"\n rows=\"4\"\n />\n <mt-select-field\n class=\"mt-2\"\n [ngModel]=\"approvalDecision()\"\n (ngModelChange)=\"approvalDecision.set($event)\"\n label=\"Approval decision\"\n [options]=\"[\n { label: 'Approved', value: 'Approved' },\n { label: 'Rejected', value: 'Rejected' },\n { label: 'Returned', value: 'Returned' }\n ]\"\n optionValue=\"value\"\n optionLabel=\"label\"\n />\n <mt-textarea-field\n class=\"mt-2\"\n [ngModel]=\"approvalComments()\"\n (ngModelChange)=\"approvalComments.set($event)\"\n label=\"Decision comments\"\n rows=\"3\"\n />\n <div class=\"mt-2 flex gap-2\">\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n icon=\"media.play-circle\"\n [label]=\"'flowplus.execution.resume' | transloco\"\n (onClick)=\"resume(wait)\"\n />\n <mt-button\n size=\"small\"\n variant=\"outlined\"\n icon=\"general.check-circle\"\n [label]=\"'flowplus.execution.decide' | transloco\"\n (onClick)=\"decide(wait)\"\n />\n </div>\n </div>\n }\n </div>\n }\n </section>\n\n <section class=\"mt-4\">\n <div class=\"mb-2 flex items-center justify-between gap-2\">\n <h3 class=\"m-0 text-[13px] font-semibold\">\n {{ \"flowplus.execution.nodeData\" | transloco }}\n </h3>\n <mt-toggle-field\n size=\"small\"\n labelPosition=\"end\"\n [ngModel]=\"includeFullNodeData()\"\n (ngModelChange)=\"onIncludeFullNodeDataChange($event)\"\n label=\"Full\"\n />\n </div>\n @if (nodeData(); as data) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2\"\n >\n <div\n class=\"mb-2 flex items-center justify-between gap-2 text-[11px]\"\n >\n <code class=\"font-mono\">{{ data.nodeRunId }}</code>\n <span>{{ nodeDataStateLabel(data) }}</span>\n </div>\n @if (nodeDataNotice(data); as notice) {\n <div\n class=\"mb-2 rounded-md border border-(--p-content-border-color) bg-(--p-surface-50) px-2 py-1.5 text-[11.5px] leading-5 text-(--p-text-muted-color)\"\n >\n {{ notice }}\n </div>\n }\n @if (advancedNodeDataSummary(data).length > 0) {\n <div class=\"mb-2 flex flex-wrap gap-1.5\">\n @for (item of advancedNodeDataSummary(data); track item) {\n <span class=\"rounded-md bg-(--p-surface-100) px-2 py-1 text-[11px]\">\n {{ item }}\n </span>\n }\n </div>\n }\n <pre\n class=\"m-0 max-h-80 overflow-auto whitespace-pre-wrap font-mono text-[11px]\"\n >{{\n dataPreview(\n data.fullData ?? {\n summary: data.summary,\n input: data.input,\n output: data.output,\n errorJson: data.errorJson,\n attempts: data.attempts,\n logs: data.logs,\n replayAvailable: data.replayAvailable,\n replayAvailabilityReason: data.replayAvailabilityReason\n }\n )\n }}</pre\n >\n </div>\n } @else {\n <p class=\"m-0 text-[12px] text-(--p-text-muted-color)\">\n {{ \"flowplus.execution.noNodeData\" | transloco }}\n </p>\n }\n </section>\n\n <section class=\"mt-4\">\n <h3 class=\"m-0 mb-2 text-[13px] font-semibold\">\n Health and operations\n </h3>\n <div class=\"grid gap-2\">\n @if (health(); as h) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2 text-[11.5px]\"\n >\n <div>Queued: {{ h.queuedCount }}</div>\n <div>Running: {{ h.runningCount }}</div>\n <div>Waiting: {{ h.waitingCount }}</div>\n <div>Failed: {{ h.failedCount }}</div>\n @if (h.scheduleDueCount != null) {\n <div>Schedule due: {{ h.scheduleDueCount }}</div>\n }\n </div>\n }\n @if (healthFlags().length > 0) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2\"\n >\n <div class=\"mb-1 text-[12px] font-semibold\">Flags</div>\n <div class=\"flex flex-wrap gap-1\">\n @for (flag of healthFlags(); track flag[0]) {\n <span\n class=\"inline-flex rounded-md bg-(--p-surface-100) px-2 py-0.5 text-[11px]\"\n >{{ flag[0] }}</span\n >\n }\n </div>\n </div>\n }\n @if (retentionPreview(); as retention) {\n <div\n class=\"rounded-md border border-(--p-content-border-color) bg-(--p-surface-0) p-2 text-[11.5px]\"\n >\n <div class=\"text-[12px] font-semibold\">Retention preview</div>\n <div>Candidate count: {{ retention.candidateCount }}</div>\n <div>\n Oldest candidate: {{ retention.oldestCandidateUtc ?? \"-\" }}\n </div>\n <div>Dry run: {{ retention.dryRun ? \"Yes\" : \"No\" }}</div>\n </div>\n }\n </div>\n </section>\n </aside>\n </div>\n } @else {\n <div\n class=\"flex flex-1 items-center justify-center px-6 text-center text-[13px] text-(--p-text-muted-color)\"\n >\n {{ \"flowplus.execution.selectExecution\" | transloco }}\n </div>\n }\n </main>\n </div>\n</section>\n\n<ng-template #executionCellTpl let-row>\n <code class=\"font-mono\">{{ row.executionId }}</code>\n <div class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ row.triggerType ?? \"-\" }}\n </div>\n</ng-template>\n\n<ng-template #executionStatusTpl let-row>\n <span\n class=\"inline-flex rounded-md bg-(--p-surface-100) px-2 py-0.5 text-[11px] font-medium\"\n >\n {{ row.status }}\n </span>\n</ng-template>\n\n<ng-template #executionRevisionTpl let-row>\n <span class=\"text-(--p-text-muted-color)\">\n {{ row.revisionNumber ?? \"-\" }}\n </span>\n</ng-template>\n\n<ng-template #executionStartedTpl let-row>\n <span class=\"text-(--p-text-muted-color)\">\n {{ row.startedAtUtc ?? row.createdAtUtc ?? \"-\" }}\n </span>\n</ng-template>\n\n<ng-template #nodeRunCellTpl let-row>\n <strong>{{ row.nodeKey }}</strong>\n <div class=\"text-[11px] text-(--p-text-muted-color)\">\n {{ row.nodeType }}\n </div>\n @if (advancedNodeRunSummary(row); as summary) {\n <div class=\"mt-1 text-[11px] text-(--p-text-muted-color)\">\n {{ summary }}\n </div>\n }\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: SelectField, selector: "mt-select-field", inputs: ["field", "hint", "label", "placeholder", "hasPlaceholderPrefix", "class", "readonly", "pInputs", "options", "optionValue", "optionLabel", "filter", "filterBy", "dataKey", "showClear", "clearAfterSelect", "required", "group", "size", "optionGroupLabel", "optionGroupChildren", "loading", "optionIcon", "optionIconColor", "optionIconShape", "optionAvatarShape", "optionGroupIcon", "optionGroupIconColor", "optionGroupIconShape", "optionGroupAvatarShape", "markCurrentUser"], outputs: ["onChange"] }, { kind: "component", type: Table, selector: "mt-table", inputs: ["filters", "data", "columns", "rowActions", "size", "showGridlines", "stripedRows", "selectableRows", "clickableRows", "generalSearch", "lazyLocalSearch", "showFilters", "filterMode", "loading", "updating", "lazy", "lazyLocalSort", "lazyTotalRecords", "reorderableColumns", "reorderableRows", "dataKey", "storageKey", "storageMode", "exportable", "printable", "groupable", "cellClickFilter", "freezeActions", "printTitle", "exportFilename", "actionShape", "rowActionsLoadingFn", "tableLayout", "noCard", "tabs", "tabsOptionLabel", "tabsOptionValue", "activeTab", "actions", "paginatorPosition", "alwaysShowPaginator", "rowsPerPageOptions", "pageSize", "currentPage", "first", "filterTerm", "groupBy"], outputs: ["selectionChange", "cellChange", "lazyLoad", "columnReorder", "rowReorder", "rowClick", "rowActionsRequested", "filtersChange", "activeTabChange", "onTabChange", "pageSizeChange", "currentPageChange", "firstChange", "filterTermChange", "groupByChange"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "component", type: ToggleField, selector: "mt-toggle-field", inputs: ["label", "inputId", "labelPosition", "placeholder", "readonly", "pInputs", "required", "toggleShape", "size", "icon", "descriptionCard"], outputs: ["onChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
|
|
22851
22859
|
}
|
|
22852
22860
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: AutomationExecutionsPageComponent, decorators: [{
|
|
22853
22861
|
type: Component,
|