@memberjunction/ng-agent-requests 0.0.1 → 5.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ import * as i0 from "@angular/core";
2
+ import * as i1 from "./panels/agent-request-panel/agent-request-panel.component";
3
+ import * as i2 from "./dialogs/agent-request-dialog.component";
4
+ import * as i3 from "@angular/common";
5
+ import * as i4 from "@angular/forms";
6
+ import * as i5 from "@progress/kendo-angular-dialog";
7
+ import * as i6 from "@progress/kendo-angular-buttons";
8
+ import * as i7 from "@progress/kendo-angular-dropdowns";
9
+ import * as i8 from "@progress/kendo-angular-inputs";
10
+ import * as i9 from "@progress/kendo-angular-dateinputs";
11
+ import * as i10 from "@memberjunction/ng-shared-generic";
12
+ import * as i11 from "@memberjunction/ng-forms";
13
+ export declare class AgentRequestsModule {
14
+ static ɵfac: i0.ɵɵFactoryDeclaration<AgentRequestsModule, never>;
15
+ static ɵmod: i0.ɵɵNgModuleDeclaration<AgentRequestsModule, [typeof i1.AgentRequestPanelComponent, typeof i2.AgentRequestDialogComponent], [typeof i3.CommonModule, typeof i4.FormsModule, typeof i5.DialogModule, typeof i6.ButtonsModule, typeof i7.DropDownsModule, typeof i8.InputsModule, typeof i9.DateInputsModule, typeof i10.SharedGenericModule, typeof i11.DynamicFormsModule], [typeof i1.AgentRequestPanelComponent, typeof i2.AgentRequestDialogComponent]>;
16
+ static ɵinj: i0.ɵɵInjectorDeclaration<AgentRequestsModule>;
17
+ }
18
+ //# sourceMappingURL=agent-requests.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-requests.module.d.ts","sourceRoot":"","sources":["../../src/lib/agent-requests.module.ts"],"names":[],"mappings":";;;;;;;;;;;;AA6BA,qBAqBa,mBAAmB;yCAAnB,mBAAmB;0CAAnB,mBAAmB;0CAAnB,mBAAmB;CAAI"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @fileoverview Agent Requests Module
3
+ *
4
+ * Angular module providing reusable components for viewing and responding
5
+ * to AI agent feedback requests. Supports both embedded panel and modal
6
+ * dialog usage patterns.
7
+ */
8
+ import { NgModule } from '@angular/core';
9
+ import { CommonModule } from '@angular/common';
10
+ import { FormsModule } from '@angular/forms';
11
+ // Kendo UI Angular imports
12
+ import { DialogModule } from '@progress/kendo-angular-dialog';
13
+ import { ButtonsModule } from '@progress/kendo-angular-buttons';
14
+ import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
15
+ import { InputsModule } from '@progress/kendo-angular-inputs';
16
+ import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
17
+ // MemberJunction modules
18
+ import { SharedGenericModule } from '@memberjunction/ng-shared-generic';
19
+ import { DynamicFormsModule } from '@memberjunction/ng-forms';
20
+ // Panel components
21
+ import { AgentRequestPanelComponent } from './panels/agent-request-panel/agent-request-panel.component';
22
+ // Dialog components
23
+ import { AgentRequestDialogComponent } from './dialogs/agent-request-dialog.component';
24
+ import * as i0 from "@angular/core";
25
+ export class AgentRequestsModule {
26
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestsModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
27
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestsModule, declarations: [AgentRequestPanelComponent,
28
+ AgentRequestDialogComponent], imports: [CommonModule,
29
+ FormsModule,
30
+ DialogModule,
31
+ ButtonsModule,
32
+ DropDownsModule,
33
+ InputsModule,
34
+ DateInputsModule,
35
+ SharedGenericModule,
36
+ DynamicFormsModule], exports: [AgentRequestPanelComponent,
37
+ AgentRequestDialogComponent] });
38
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestsModule, imports: [CommonModule,
39
+ FormsModule,
40
+ DialogModule,
41
+ ButtonsModule,
42
+ DropDownsModule,
43
+ InputsModule,
44
+ DateInputsModule,
45
+ SharedGenericModule,
46
+ DynamicFormsModule] });
47
+ }
48
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestsModule, decorators: [{
49
+ type: NgModule,
50
+ args: [{
51
+ declarations: [
52
+ AgentRequestPanelComponent,
53
+ AgentRequestDialogComponent
54
+ ],
55
+ imports: [
56
+ CommonModule,
57
+ FormsModule,
58
+ DialogModule,
59
+ ButtonsModule,
60
+ DropDownsModule,
61
+ InputsModule,
62
+ DateInputsModule,
63
+ SharedGenericModule,
64
+ DynamicFormsModule
65
+ ],
66
+ exports: [
67
+ AgentRequestPanelComponent,
68
+ AgentRequestDialogComponent
69
+ ]
70
+ }]
71
+ }] });
72
+ //# sourceMappingURL=agent-requests.module.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-requests.module.js","sourceRoot":"","sources":["../../src/lib/agent-requests.module.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C,2BAA2B;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,yBAAyB;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAE9D,mBAAmB;AACnB,OAAO,EAAE,0BAA0B,EAAE,MAAM,4DAA4D,CAAC;AAExG,oBAAoB;AACpB,OAAO,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAC;;AAuBvF,MAAM,OAAO,mBAAmB;uGAAnB,mBAAmB;wGAAnB,mBAAmB,iBAnBxB,0BAA0B;YAC1B,2BAA2B,aAG3B,YAAY;YACZ,WAAW;YACX,YAAY;YACZ,aAAa;YACb,eAAe;YACf,YAAY;YACZ,gBAAgB;YAChB,mBAAmB;YACnB,kBAAkB,aAGlB,0BAA0B;YAC1B,2BAA2B;wGAGtB,mBAAmB,YAfxB,YAAY;YACZ,WAAW;YACX,YAAY;YACZ,aAAa;YACb,eAAe;YACf,YAAY;YACZ,gBAAgB;YAChB,mBAAmB;YACnB,kBAAkB;;2FAOb,mBAAmB;kBArB/B,QAAQ;mBAAC;oBACN,YAAY,EAAE;wBACV,0BAA0B;wBAC1B,2BAA2B;qBAC9B;oBACD,OAAO,EAAE;wBACL,YAAY;wBACZ,WAAW;wBACX,YAAY;wBACZ,aAAa;wBACb,eAAe;wBACf,YAAY;wBACZ,gBAAgB;wBAChB,mBAAmB;wBACnB,kBAAkB;qBACrB;oBACD,OAAO,EAAE;wBACL,0BAA0B;wBAC1B,2BAA2B;qBAC9B;iBACJ","sourcesContent":["/**\n * @fileoverview Agent Requests Module\n *\n * Angular module providing reusable components for viewing and responding\n * to AI agent feedback requests. Supports both embedded panel and modal\n * dialog usage patterns.\n */\n\nimport { NgModule } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\n\n// Kendo UI Angular imports\nimport { DialogModule } from '@progress/kendo-angular-dialog';\nimport { ButtonsModule } from '@progress/kendo-angular-buttons';\nimport { DropDownsModule } from '@progress/kendo-angular-dropdowns';\nimport { InputsModule } from '@progress/kendo-angular-inputs';\nimport { DateInputsModule } from '@progress/kendo-angular-dateinputs';\n\n// MemberJunction modules\nimport { SharedGenericModule } from '@memberjunction/ng-shared-generic';\nimport { DynamicFormsModule } from '@memberjunction/ng-forms';\n\n// Panel components\nimport { AgentRequestPanelComponent } from './panels/agent-request-panel/agent-request-panel.component';\n\n// Dialog components\nimport { AgentRequestDialogComponent } from './dialogs/agent-request-dialog.component';\n\n@NgModule({\n declarations: [\n AgentRequestPanelComponent,\n AgentRequestDialogComponent\n ],\n imports: [\n CommonModule,\n FormsModule,\n DialogModule,\n ButtonsModule,\n DropDownsModule,\n InputsModule,\n DateInputsModule,\n SharedGenericModule,\n DynamicFormsModule\n ],\n exports: [\n AgentRequestPanelComponent,\n AgentRequestDialogComponent\n ]\n})\nexport class AgentRequestsModule { }\n"]}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @fileoverview Agent Request Dialog Component
3
+ *
4
+ * A dialog wrapper for the AgentRequestPanelComponent that provides
5
+ * a convenient way to view and respond to agent requests in a modal context.
6
+ */
7
+ import { EventEmitter, ChangeDetectorRef, OnInit, OnChanges, SimpleChanges } from '@angular/core';
8
+ import { MJAIAgentRequestEntity, MJAIAgentRequestTypeEntity } from '@memberjunction/core-entities';
9
+ import { AgentRequestPanelComponent, AgentRequestPanelResult } from '../panels/agent-request-panel/agent-request-panel.component';
10
+ import * as i0 from "@angular/core";
11
+ export declare class AgentRequestDialogComponent implements OnInit, OnChanges {
12
+ private cdr;
13
+ requestPanel: AgentRequestPanelComponent;
14
+ Visible: boolean;
15
+ Request: MJAIAgentRequestEntity | null;
16
+ Close: EventEmitter<AgentRequestPanelResult>;
17
+ IsLoading: boolean;
18
+ requestTypes: MJAIAgentRequestTypeEntity[];
19
+ constructor(cdr: ChangeDetectorRef);
20
+ ngOnInit(): void;
21
+ ngOnChanges(changes: SimpleChanges): void;
22
+ private loadRequestTypes;
23
+ OnPanelClose(result: AgentRequestPanelResult): void;
24
+ static ɵfac: i0.ɵɵFactoryDeclaration<AgentRequestDialogComponent, never>;
25
+ static ɵcmp: i0.ɵɵComponentDeclaration<AgentRequestDialogComponent, "mj-agent-request-dialog", never, { "Visible": { "alias": "Visible"; "required": false; }; "Request": { "alias": "Request"; "required": false; }; }, { "Close": "Close"; }, never, never, false, never>;
26
+ }
27
+ //# sourceMappingURL=agent-request-dialog.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-request-dialog.component.d.ts","sourceRoot":"","sources":["../../../src/lib/dialogs/agent-request-dialog.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACuB,YAAY,EAAE,iBAAiB,EACrB,MAAM,EAAE,SAAS,EAAE,aAAa,EACvE,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAEnG,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,6DAA6D,CAAC;;AAElI,qBAoCa,2BAA4B,YAAW,MAAM,EAAE,SAAS;IAWrD,OAAO,CAAC,GAAG;IAVI,YAAY,EAAG,0BAA0B,CAAC;IAE5D,OAAO,UAAS;IAChB,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAE7C,KAAK,wCAA+C;IAEvD,SAAS,UAAS;IAClB,YAAY,EAAE,0BAA0B,EAAE,CAAM;gBAEnC,GAAG,EAAE,iBAAiB;IAE1C,QAAQ,IAAI,IAAI;IAMhB,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;YAa3B,gBAAgB;IAyBvB,YAAY,CAAC,MAAM,EAAE,uBAAuB,GAAG,IAAI;yCAzDjD,2BAA2B;2CAA3B,2BAA2B;CA8DvC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @fileoverview Agent Request Dialog Component
3
+ *
4
+ * A dialog wrapper for the AgentRequestPanelComponent that provides
5
+ * a convenient way to view and respond to agent requests in a modal context.
6
+ */
7
+ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ViewChild } from '@angular/core';
8
+ import { RunView } from '@memberjunction/core';
9
+ import * as i0 from "@angular/core";
10
+ import * as i1 from "@memberjunction/ng-shared-generic";
11
+ import * as i2 from "../panels/agent-request-panel/agent-request-panel.component";
12
+ export class AgentRequestDialogComponent {
13
+ cdr;
14
+ requestPanel;
15
+ Visible = false;
16
+ Request = null;
17
+ Close = new EventEmitter();
18
+ IsLoading = false;
19
+ requestTypes = [];
20
+ constructor(cdr) {
21
+ this.cdr = cdr;
22
+ }
23
+ ngOnInit() {
24
+ if (this.Visible) {
25
+ this.loadRequestTypes();
26
+ }
27
+ }
28
+ ngOnChanges(changes) {
29
+ if (changes['Visible'] && changes['Visible'].currentValue === true) {
30
+ this.loadRequestTypes().then(() => {
31
+ this.cdr.detectChanges();
32
+ setTimeout(() => {
33
+ if (this.requestPanel && this.Request) {
34
+ this.requestPanel.Open(this.Request);
35
+ }
36
+ }, 0);
37
+ });
38
+ }
39
+ }
40
+ async loadRequestTypes() {
41
+ if (this.requestTypes.length > 0)
42
+ return;
43
+ this.IsLoading = true;
44
+ this.cdr.markForCheck();
45
+ try {
46
+ const rv = new RunView();
47
+ const result = await rv.RunView({
48
+ EntityName: 'MJ: AI Agent Request Types',
49
+ OrderBy: 'Name',
50
+ ResultType: 'entity_object'
51
+ });
52
+ if (result.Success) {
53
+ this.requestTypes = result.Results;
54
+ }
55
+ }
56
+ catch (error) {
57
+ console.error('Error loading request types:', error);
58
+ }
59
+ finally {
60
+ this.IsLoading = false;
61
+ this.cdr.markForCheck();
62
+ }
63
+ }
64
+ OnPanelClose(result) {
65
+ this.Visible = false;
66
+ this.Close.emit(result);
67
+ this.cdr.markForCheck();
68
+ }
69
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestDialogComponent, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
70
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: AgentRequestDialogComponent, isStandalone: false, selector: "mj-agent-request-dialog", inputs: { Visible: "Visible", Request: "Request" }, outputs: { Close: "Close" }, viewQueries: [{ propertyName: "requestPanel", first: true, predicate: ["requestPanel"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
71
+ @if (Visible) {
72
+ @if (IsLoading) {
73
+ <div class="loading-backdrop">
74
+ <mj-loading text="Loading request types..."></mj-loading>
75
+ </div>
76
+ } @else {
77
+ <mj-agent-request-panel
78
+ #requestPanel
79
+ [Request]="Request"
80
+ [RequestTypes]="requestTypes"
81
+ [IsOpen]="Visible"
82
+ (Close)="OnPanelClose($event)">
83
+ </mj-agent-request-panel>
84
+ }
85
+ }
86
+ `, isInline: true, styles: ["\n .loading-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n }\n "], dependencies: [{ kind: "component", type: i1.LoadingComponent, selector: "mj-loading", inputs: ["text", "showText", "animationDuration", "size", "textColor", "logoColor", "logoGradient", "animation"] }, { kind: "component", type: i2.AgentRequestPanelComponent, selector: "mj-agent-request-panel", inputs: ["Request", "RequestTypes", "IsOpen"], outputs: ["Close"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
87
+ }
88
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestDialogComponent, decorators: [{
89
+ type: Component,
90
+ args: [{ standalone: false, selector: 'mj-agent-request-dialog', template: `
91
+ @if (Visible) {
92
+ @if (IsLoading) {
93
+ <div class="loading-backdrop">
94
+ <mj-loading text="Loading request types..."></mj-loading>
95
+ </div>
96
+ } @else {
97
+ <mj-agent-request-panel
98
+ #requestPanel
99
+ [Request]="Request"
100
+ [RequestTypes]="requestTypes"
101
+ [IsOpen]="Visible"
102
+ (Close)="OnPanelClose($event)">
103
+ </mj-agent-request-panel>
104
+ }
105
+ }
106
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["\n .loading-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n }\n "] }]
107
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { requestPanel: [{
108
+ type: ViewChild,
109
+ args: ['requestPanel']
110
+ }], Visible: [{
111
+ type: Input
112
+ }], Request: [{
113
+ type: Input
114
+ }], Close: [{
115
+ type: Output
116
+ }] } });
117
+ //# sourceMappingURL=agent-request-dialog.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-request-dialog.component.js","sourceRoot":"","sources":["../../../src/lib/dialogs/agent-request-dialog.component.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACH,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EACtC,uBAAuB,EAAE,SAAS,EACrC,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;;;;AAuC/C,MAAM,OAAO,2BAA2B;IAWhB;IAVO,YAAY,CAA8B;IAE5D,OAAO,GAAG,KAAK,CAAC;IAChB,OAAO,GAAkC,IAAI,CAAC;IAE7C,KAAK,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEvD,SAAS,GAAG,KAAK,CAAC;IAClB,YAAY,GAAiC,EAAE,CAAC;IAEvD,YAAoB,GAAsB;QAAtB,QAAG,GAAH,GAAG,CAAmB;IAAG,CAAC;IAE9C,QAAQ;QACJ,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAED,WAAW,CAAC,OAAsB;QAC9B,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;YACjE,IAAI,CAAC,gBAAgB,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;gBAC9B,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC;gBACzB,UAAU,CAAC,GAAG,EAAE;oBACZ,IAAI,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;wBACpC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACzC,CAAC;gBACL,CAAC,EAAE,CAAC,CAAC,CAAC;YACV,CAAC,CAAC,CAAC;QACP,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC1B,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO;QAEzC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAExB,IAAI,CAAC;YACD,MAAM,EAAE,GAAG,IAAI,OAAO,EAAE,CAAC;YACzB,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,OAAO,CAA6B;gBACxD,UAAU,EAAE,4BAA4B;gBACxC,OAAO,EAAE,MAAM;gBACf,UAAU,EAAE,eAAe;aAC9B,CAAC,CAAC;YAEH,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC;YACvC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;QACzD,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAEM,YAAY,CAAC,MAA+B;QAC/C,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;uGA7DQ,2BAA2B;2FAA3B,2BAA2B,wSAjC1B;;;;;;;;;;;;;;;;KAgBT;;2FAiBQ,2BAA2B;kBApCvC,SAAS;iCACM,KAAK,YACP,yBAAyB,YACzB;;;;;;;;;;;;;;;;KAgBT,mBAegB,uBAAuB,CAAC,MAAM;;sBAG9C,SAAS;uBAAC,cAAc;;sBAExB,KAAK;;sBACL,KAAK;;sBAEL,MAAM","sourcesContent":["/**\n * @fileoverview Agent Request Dialog Component\n *\n * A dialog wrapper for the AgentRequestPanelComponent that provides\n * a convenient way to view and respond to agent requests in a modal context.\n */\n\nimport {\n Component, Input, Output, EventEmitter, ChangeDetectorRef,\n ChangeDetectionStrategy, ViewChild, OnInit, OnChanges, SimpleChanges\n} from '@angular/core';\nimport { MJAIAgentRequestEntity, MJAIAgentRequestTypeEntity } from '@memberjunction/core-entities';\nimport { RunView } from '@memberjunction/core';\nimport { AgentRequestPanelComponent, AgentRequestPanelResult } from '../panels/agent-request-panel/agent-request-panel.component';\n\n@Component({\n standalone: false,\n selector: 'mj-agent-request-dialog',\n template: `\n @if (Visible) {\n @if (IsLoading) {\n <div class=\"loading-backdrop\">\n <mj-loading text=\"Loading request types...\"></mj-loading>\n </div>\n } @else {\n <mj-agent-request-panel\n #requestPanel\n [Request]=\"Request\"\n [RequestTypes]=\"requestTypes\"\n [IsOpen]=\"Visible\"\n (Close)=\"OnPanelClose($event)\">\n </mj-agent-request-panel>\n }\n }\n `,\n styles: [`\n .loading-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n }\n `],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AgentRequestDialogComponent implements OnInit, OnChanges {\n @ViewChild('requestPanel') requestPanel!: AgentRequestPanelComponent;\n\n @Input() Visible = false;\n @Input() Request: MJAIAgentRequestEntity | null = null;\n\n @Output() Close = new EventEmitter<AgentRequestPanelResult>();\n\n public IsLoading = false;\n public requestTypes: MJAIAgentRequestTypeEntity[] = [];\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n ngOnInit(): void {\n if (this.Visible) {\n this.loadRequestTypes();\n }\n }\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['Visible'] && changes['Visible'].currentValue === true) {\n this.loadRequestTypes().then(() => {\n this.cdr.detectChanges();\n setTimeout(() => {\n if (this.requestPanel && this.Request) {\n this.requestPanel.Open(this.Request);\n }\n }, 0);\n });\n }\n }\n\n private async loadRequestTypes(): Promise<void> {\n if (this.requestTypes.length > 0) return;\n\n this.IsLoading = true;\n this.cdr.markForCheck();\n\n try {\n const rv = new RunView();\n const result = await rv.RunView<MJAIAgentRequestTypeEntity>({\n EntityName: 'MJ: AI Agent Request Types',\n OrderBy: 'Name',\n ResultType: 'entity_object'\n });\n\n if (result.Success) {\n this.requestTypes = result.Results;\n }\n } catch (error) {\n console.error('Error loading request types:', error);\n } finally {\n this.IsLoading = false;\n this.cdr.markForCheck();\n }\n }\n\n public OnPanelClose(result: AgentRequestPanelResult): void {\n this.Visible = false;\n this.Close.emit(result);\n this.cdr.markForCheck();\n }\n}\n"]}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * @fileoverview Agent Request Panel Component
3
+ *
4
+ * Slide-in panel for viewing and responding to AI agent feedback requests.
5
+ * Displays request details, renders ResponseSchema as a dynamic form,
6
+ * and submits ResponseData back to the request entity.
7
+ */
8
+ import { EventEmitter, ChangeDetectorRef, OnInit, OnDestroy, ElementRef } from '@angular/core';
9
+ import { MJAIAgentRequestEntity, MJAIAgentRequestTypeEntity } from '@memberjunction/core-entities';
10
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
11
+ import { AgentResponseForm } from '@memberjunction/ai-core-plus';
12
+ import { DynamicFormComponent } from '@memberjunction/ng-forms';
13
+ import * as i0 from "@angular/core";
14
+ /**
15
+ * Result returned when the panel closes after an action
16
+ */
17
+ export interface AgentRequestPanelResult {
18
+ /** Whether an action was taken (responded, approved, rejected) */
19
+ Success: boolean;
20
+ /** The updated request entity */
21
+ Request?: MJAIAgentRequestEntity;
22
+ /** The action that was taken */
23
+ Action: 'responded' | 'approved' | 'rejected' | 'cancelled';
24
+ }
25
+ export declare class AgentRequestPanelComponent implements OnInit, OnDestroy {
26
+ private cdr;
27
+ private notificationService;
28
+ Request: MJAIAgentRequestEntity | null;
29
+ RequestTypes: MJAIAgentRequestTypeEntity[];
30
+ IsOpen: boolean;
31
+ Close: EventEmitter<AgentRequestPanelResult>;
32
+ dynamicForm: DynamicFormComponent | undefined;
33
+ panelElement: ElementRef<HTMLDivElement> | undefined;
34
+ IsLoading: boolean;
35
+ IsSaving: boolean;
36
+ ResponseText: string;
37
+ ResponseFormDefinition: AgentResponseForm | null;
38
+ PanelWidth: number;
39
+ /** Tracks whether a response was submitted so the Close event carries the right result */
40
+ private completedAction;
41
+ IsResizing: boolean;
42
+ private resizeStartX;
43
+ private resizeStartWidth;
44
+ private boundOnResizeMove;
45
+ private boundOnResizeEnd;
46
+ ShowReassignDialog: boolean;
47
+ ReassignSearchTerm: string;
48
+ ReassignNote: string;
49
+ ReassignUsers: Array<{
50
+ ID: string;
51
+ Name: string;
52
+ Email: string;
53
+ }>;
54
+ SelectedReassignUser: {
55
+ ID: string;
56
+ Name: string;
57
+ Email: string;
58
+ } | null;
59
+ IsSearchingUsers: boolean;
60
+ IsReassigning: boolean;
61
+ private reassignSearchTimeout;
62
+ constructor(cdr: ChangeDetectorRef, notificationService: MJNotificationService);
63
+ ngOnInit(): void;
64
+ ngOnDestroy(): void;
65
+ OnEscapeKey(): void;
66
+ /** The request type entity for the current request */
67
+ get RequestType(): MJAIAgentRequestTypeEntity | null;
68
+ /** Whether this request is still actionable (status = Requested) */
69
+ get IsActionable(): boolean;
70
+ /** Whether this is an approval-type request (show approve/reject buttons) */
71
+ get IsApprovalType(): boolean;
72
+ /** Whether the request has a structured response form */
73
+ get HasResponseForm(): boolean;
74
+ /** Priority display label */
75
+ get PriorityLabel(): string;
76
+ /** Priority CSS class */
77
+ get PriorityClass(): string;
78
+ /** Whether the request is expired */
79
+ get IsExpired(): boolean;
80
+ /**
81
+ * Opens the panel with a specific request
82
+ */
83
+ Open(request: MJAIAgentRequestEntity): void;
84
+ /**
85
+ * Approve the request
86
+ */
87
+ OnApprove(): Promise<void>;
88
+ /**
89
+ * Reject the request
90
+ */
91
+ OnReject(): Promise<void>;
92
+ /**
93
+ * Submit a response (for non-approval types)
94
+ */
95
+ OnRespond(): Promise<void>;
96
+ /**
97
+ * Cancel / close the panel without action
98
+ */
99
+ OnCancel(): void;
100
+ OnBackdropClick(): void;
101
+ /** Open the reassign dialog */
102
+ OnReassignClick(): void;
103
+ /** Close the reassign dialog */
104
+ OnReassignCancel(): void;
105
+ /** Search users by name/email for reassignment */
106
+ OnReassignSearchChange(term: string): void;
107
+ /** Select a user from the search results */
108
+ OnSelectReassignUser(user: {
109
+ ID: string;
110
+ Name: string;
111
+ Email: string;
112
+ }): void;
113
+ /** Submit the reassignment */
114
+ OnReassignSubmit(): Promise<void>;
115
+ private searchUsers;
116
+ /** Begin resizing when the user mousedowns on the resize handle */
117
+ OnResizeStart(event: MouseEvent): void;
118
+ private onResizeMove;
119
+ private onResizeEnd;
120
+ private cleanupResizeListeners;
121
+ private loadPanelWidth;
122
+ private savePanelWidth;
123
+ /**
124
+ * Parse the ResponseSchema JSON into an AgentResponseForm for the dynamic form component.
125
+ */
126
+ private initializeForm;
127
+ /**
128
+ * Collect current form values from the DynamicFormComponent's reactive FormGroup.
129
+ * This reads values directly so the user doesn't need to click the form's
130
+ * internal submit button before clicking the panel's action buttons.
131
+ */
132
+ private collectFormData;
133
+ /**
134
+ * Shared logic for submitting any response type
135
+ */
136
+ private submitResponse;
137
+ static ɵfac: i0.ɵɵFactoryDeclaration<AgentRequestPanelComponent, never>;
138
+ static ɵcmp: i0.ɵɵComponentDeclaration<AgentRequestPanelComponent, "mj-agent-request-panel", never, { "Request": { "alias": "Request"; "required": false; }; "RequestTypes": { "alias": "RequestTypes"; "required": false; }; "IsOpen": { "alias": "IsOpen"; "required": false; }; }, { "Close": "Close"; }, never, never, false, never>;
139
+ }
140
+ //# sourceMappingURL=agent-request-panel.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-request-panel.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/panels/agent-request-panel/agent-request-panel.component.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,414 @@
1
+ /**
2
+ * @fileoverview Agent Request Panel Component
3
+ *
4
+ * Slide-in panel for viewing and responding to AI agent feedback requests.
5
+ * Displays request details, renders ResponseSchema as a dynamic form,
6
+ * and submits ResponseData back to the request entity.
7
+ */
8
+ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, HostListener, ViewChild } from '@angular/core';
9
+ import { RunView } from '@memberjunction/core';
10
+ import { UserInfoEngine } from '@memberjunction/core-entities';
11
+ import { UUIDsEqual } from '@memberjunction/global';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "@memberjunction/ng-notifications";
14
+ import * as i2 from "@angular/forms";
15
+ import * as i3 from "@memberjunction/ng-forms";
16
+ import * as i4 from "@angular/common";
17
+ const PANEL_WIDTH_SETTING_KEY = 'Explorer.AgentRequestPanelWidth';
18
+ const DEFAULT_PANEL_WIDTH = 520;
19
+ const MIN_PANEL_WIDTH = 360;
20
+ const MAX_PANEL_WIDTH = 900;
21
+ export class AgentRequestPanelComponent {
22
+ cdr;
23
+ notificationService;
24
+ Request = null;
25
+ RequestTypes = [];
26
+ IsOpen = false;
27
+ Close = new EventEmitter();
28
+ dynamicForm;
29
+ panelElement;
30
+ IsLoading = false;
31
+ IsSaving = false;
32
+ ResponseText = '';
33
+ ResponseFormDefinition = null;
34
+ PanelWidth = DEFAULT_PANEL_WIDTH;
35
+ /** Tracks whether a response was submitted so the Close event carries the right result */
36
+ completedAction = null;
37
+ // Resize state
38
+ IsResizing = false;
39
+ resizeStartX = 0;
40
+ resizeStartWidth = 0;
41
+ boundOnResizeMove = null;
42
+ boundOnResizeEnd = null;
43
+ // Reassign state
44
+ ShowReassignDialog = false;
45
+ ReassignSearchTerm = '';
46
+ ReassignNote = '';
47
+ ReassignUsers = [];
48
+ SelectedReassignUser = null;
49
+ IsSearchingUsers = false;
50
+ IsReassigning = false;
51
+ reassignSearchTimeout = null;
52
+ constructor(cdr, notificationService) {
53
+ this.cdr = cdr;
54
+ this.notificationService = notificationService;
55
+ }
56
+ ngOnInit() {
57
+ this.loadPanelWidth();
58
+ if (this.Request) {
59
+ this.initializeForm();
60
+ }
61
+ }
62
+ ngOnDestroy() {
63
+ this.cleanupResizeListeners();
64
+ }
65
+ OnEscapeKey() {
66
+ if (this.IsOpen) {
67
+ this.OnCancel();
68
+ }
69
+ }
70
+ /** The request type entity for the current request */
71
+ get RequestType() {
72
+ if (!this.Request?.RequestTypeID)
73
+ return null;
74
+ return this.RequestTypes.find(t => UUIDsEqual(t.ID, this.Request.RequestTypeID)) ?? null;
75
+ }
76
+ /** Whether this request is still actionable (status = Requested) */
77
+ get IsActionable() {
78
+ return this.Request?.Status === 'Requested';
79
+ }
80
+ /** Whether this is an approval-type request (show approve/reject buttons) */
81
+ get IsApprovalType() {
82
+ const typeName = this.RequestType?.Name;
83
+ return typeName === 'Approval';
84
+ }
85
+ /** Whether the request has a structured response form */
86
+ get HasResponseForm() {
87
+ return this.ResponseFormDefinition != null && (this.ResponseFormDefinition.questions?.length ?? 0) > 0;
88
+ }
89
+ /** Priority display label */
90
+ get PriorityLabel() {
91
+ if (!this.Request)
92
+ return '';
93
+ const p = this.Request.Priority;
94
+ if (p <= 25)
95
+ return 'Low';
96
+ if (p <= 50)
97
+ return 'Normal';
98
+ if (p <= 75)
99
+ return 'High';
100
+ return 'Critical';
101
+ }
102
+ /** Priority CSS class */
103
+ get PriorityClass() {
104
+ if (!this.Request)
105
+ return '';
106
+ const p = this.Request.Priority;
107
+ if (p <= 25)
108
+ return 'priority-low';
109
+ if (p <= 50)
110
+ return 'priority-normal';
111
+ if (p <= 75)
112
+ return 'priority-high';
113
+ return 'priority-critical';
114
+ }
115
+ /** Whether the request is expired */
116
+ get IsExpired() {
117
+ if (!this.Request?.ExpiresAt)
118
+ return false;
119
+ return new Date(this.Request.ExpiresAt) < new Date();
120
+ }
121
+ /**
122
+ * Opens the panel with a specific request
123
+ */
124
+ Open(request) {
125
+ this.Request = request;
126
+ this.IsOpen = true;
127
+ this.ResponseText = '';
128
+ this.completedAction = null;
129
+ this.initializeForm();
130
+ this.cdr.markForCheck();
131
+ }
132
+ /**
133
+ * Approve the request
134
+ */
135
+ async OnApprove() {
136
+ await this.submitResponse('Approved', 'approved');
137
+ }
138
+ /**
139
+ * Reject the request
140
+ */
141
+ async OnReject() {
142
+ await this.submitResponse('Rejected', 'rejected');
143
+ }
144
+ /**
145
+ * Submit a response (for non-approval types)
146
+ */
147
+ async OnRespond() {
148
+ await this.submitResponse('Responded', 'responded');
149
+ }
150
+ /**
151
+ * Cancel / close the panel without action
152
+ */
153
+ OnCancel() {
154
+ this.IsOpen = false;
155
+ if (this.completedAction) {
156
+ // A response was submitted — tell the parent about the action taken
157
+ this.Close.emit({ Success: true, Request: this.Request, Action: this.completedAction });
158
+ this.completedAction = null;
159
+ }
160
+ else {
161
+ this.Close.emit({ Success: false, Action: 'cancelled' });
162
+ }
163
+ this.cdr.markForCheck();
164
+ }
165
+ OnBackdropClick() {
166
+ this.OnCancel();
167
+ }
168
+ /** Open the reassign dialog */
169
+ OnReassignClick() {
170
+ this.ShowReassignDialog = true;
171
+ this.ReassignSearchTerm = '';
172
+ this.ReassignNote = '';
173
+ this.ReassignUsers = [];
174
+ this.SelectedReassignUser = null;
175
+ this.cdr.markForCheck();
176
+ }
177
+ /** Close the reassign dialog */
178
+ OnReassignCancel() {
179
+ this.ShowReassignDialog = false;
180
+ this.cdr.markForCheck();
181
+ }
182
+ /** Search users by name/email for reassignment */
183
+ OnReassignSearchChange(term) {
184
+ this.ReassignSearchTerm = term;
185
+ if (this.reassignSearchTimeout) {
186
+ clearTimeout(this.reassignSearchTimeout);
187
+ }
188
+ if (term.length < 2) {
189
+ this.ReassignUsers = [];
190
+ this.cdr.markForCheck();
191
+ return;
192
+ }
193
+ this.reassignSearchTimeout = setTimeout(() => this.searchUsers(term), 300);
194
+ }
195
+ /** Select a user from the search results */
196
+ OnSelectReassignUser(user) {
197
+ this.SelectedReassignUser = user;
198
+ this.cdr.markForCheck();
199
+ }
200
+ /** Submit the reassignment */
201
+ async OnReassignSubmit() {
202
+ if (!this.Request || !this.SelectedReassignUser || this.IsReassigning)
203
+ return;
204
+ this.IsReassigning = true;
205
+ this.cdr.markForCheck();
206
+ try {
207
+ this.Request.RequestForUserID = this.SelectedReassignUser.ID;
208
+ // Append reassignment note to Comments
209
+ const timestamp = new Date().toISOString();
210
+ const reassignEntry = `[${timestamp}] Reassigned to ${this.SelectedReassignUser.Name || this.SelectedReassignUser.Email}${this.ReassignNote ? ` — "${this.ReassignNote}"` : ''}`;
211
+ this.Request.Comments = this.Request.Comments
212
+ ? `${this.Request.Comments}\n${reassignEntry}`
213
+ : reassignEntry;
214
+ const saved = await this.Request.Save();
215
+ if (saved) {
216
+ this.notificationService.CreateSimpleNotification(`Request reassigned to ${this.SelectedReassignUser.Name || this.SelectedReassignUser.Email}`, 'success', 3000);
217
+ this.ShowReassignDialog = false;
218
+ this.IsOpen = false;
219
+ this.Close.emit({ Success: true, Request: this.Request, Action: 'cancelled' });
220
+ }
221
+ else {
222
+ this.notificationService.CreateSimpleNotification('Failed to reassign request. Please try again.', 'error', 5000);
223
+ }
224
+ }
225
+ catch (error) {
226
+ console.error('Error reassigning request:', error);
227
+ this.notificationService.CreateSimpleNotification('An unexpected error occurred.', 'error', 5000);
228
+ }
229
+ finally {
230
+ this.IsReassigning = false;
231
+ this.cdr.markForCheck();
232
+ }
233
+ }
234
+ async searchUsers(term) {
235
+ this.IsSearchingUsers = true;
236
+ this.cdr.markForCheck();
237
+ try {
238
+ const rv = new RunView();
239
+ const result = await rv.RunView({
240
+ EntityName: 'Users',
241
+ ExtraFilter: `(Name LIKE '%${term.replace(/'/g, "''")}%' OR Email LIKE '%${term.replace(/'/g, "''")}%')`,
242
+ OrderBy: 'Name',
243
+ MaxRows: 10,
244
+ Fields: ['ID', 'Name', 'Email'],
245
+ ResultType: 'simple'
246
+ });
247
+ this.ReassignUsers = result.Success ? result.Results : [];
248
+ }
249
+ catch {
250
+ this.ReassignUsers = [];
251
+ }
252
+ finally {
253
+ this.IsSearchingUsers = false;
254
+ this.cdr.markForCheck();
255
+ }
256
+ }
257
+ // ========================================================================
258
+ // Resize
259
+ // ========================================================================
260
+ /** Begin resizing when the user mousedowns on the resize handle */
261
+ OnResizeStart(event) {
262
+ event.preventDefault();
263
+ this.IsResizing = true;
264
+ this.resizeStartX = event.clientX;
265
+ this.resizeStartWidth = this.PanelWidth;
266
+ this.boundOnResizeMove = (e) => this.onResizeMove(e);
267
+ this.boundOnResizeEnd = () => this.onResizeEnd();
268
+ document.addEventListener('mousemove', this.boundOnResizeMove);
269
+ document.addEventListener('mouseup', this.boundOnResizeEnd);
270
+ document.body.style.cursor = 'col-resize';
271
+ document.body.style.userSelect = 'none';
272
+ }
273
+ onResizeMove(event) {
274
+ // Panel is on the right, so dragging left (negative deltaX) increases width
275
+ const deltaX = this.resizeStartX - event.clientX;
276
+ const newWidth = Math.max(MIN_PANEL_WIDTH, Math.min(MAX_PANEL_WIDTH, this.resizeStartWidth + deltaX));
277
+ this.PanelWidth = newWidth;
278
+ this.cdr.markForCheck();
279
+ }
280
+ onResizeEnd() {
281
+ this.IsResizing = false;
282
+ this.cleanupResizeListeners();
283
+ document.body.style.cursor = '';
284
+ document.body.style.userSelect = '';
285
+ this.savePanelWidth();
286
+ this.cdr.markForCheck();
287
+ }
288
+ cleanupResizeListeners() {
289
+ if (this.boundOnResizeMove) {
290
+ document.removeEventListener('mousemove', this.boundOnResizeMove);
291
+ this.boundOnResizeMove = null;
292
+ }
293
+ if (this.boundOnResizeEnd) {
294
+ document.removeEventListener('mouseup', this.boundOnResizeEnd);
295
+ this.boundOnResizeEnd = null;
296
+ }
297
+ }
298
+ loadPanelWidth() {
299
+ try {
300
+ const saved = UserInfoEngine.Instance.GetSetting(PANEL_WIDTH_SETTING_KEY);
301
+ if (saved) {
302
+ const parsed = parseInt(saved, 10);
303
+ if (!isNaN(parsed) && parsed >= MIN_PANEL_WIDTH && parsed <= MAX_PANEL_WIDTH) {
304
+ this.PanelWidth = parsed;
305
+ }
306
+ }
307
+ }
308
+ catch {
309
+ // UserInfoEngine may not be initialized yet — use default
310
+ }
311
+ }
312
+ savePanelWidth() {
313
+ try {
314
+ UserInfoEngine.Instance.SetSettingDebounced(PANEL_WIDTH_SETTING_KEY, String(this.PanelWidth));
315
+ }
316
+ catch {
317
+ // Silently ignore save failures
318
+ }
319
+ }
320
+ // ========================================================================
321
+ // Form handling
322
+ // ========================================================================
323
+ /**
324
+ * Parse the ResponseSchema JSON into an AgentResponseForm for the dynamic form component.
325
+ */
326
+ initializeForm() {
327
+ this.ResponseFormDefinition = null;
328
+ if (!this.Request?.ResponseSchema)
329
+ return;
330
+ try {
331
+ const schema = JSON.parse(this.Request.ResponseSchema);
332
+ if (schema?.questions && Array.isArray(schema.questions)) {
333
+ this.ResponseFormDefinition = schema;
334
+ }
335
+ }
336
+ catch {
337
+ // If schema is invalid JSON, ignore — user can still provide free-text response
338
+ }
339
+ }
340
+ /**
341
+ * Collect current form values from the DynamicFormComponent's reactive FormGroup.
342
+ * This reads values directly so the user doesn't need to click the form's
343
+ * internal submit button before clicking the panel's action buttons.
344
+ */
345
+ collectFormData() {
346
+ if (!this.dynamicForm?.FormGroup)
347
+ return null;
348
+ const values = this.dynamicForm.FormGroup.value;
349
+ if (values && Object.keys(values).length > 0) {
350
+ return values;
351
+ }
352
+ return null;
353
+ }
354
+ /**
355
+ * Shared logic for submitting any response type
356
+ */
357
+ async submitResponse(status, action) {
358
+ if (!this.Request || this.IsSaving)
359
+ return;
360
+ this.IsSaving = true;
361
+ this.cdr.markForCheck();
362
+ try {
363
+ this.Request.Status = status;
364
+ this.Request.Response = this.ResponseText || null;
365
+ this.Request.RespondedAt = new Date();
366
+ const formData = this.collectFormData();
367
+ if (formData) {
368
+ this.Request.ResponseData = JSON.stringify(formData);
369
+ }
370
+ const saved = await this.Request.Save();
371
+ if (saved) {
372
+ this.notificationService.CreateSimpleNotification(`Agent request has been ${action} successfully.`, 'success', 3000);
373
+ // Keep panel open so the user can see their submitted response.
374
+ // Template auto-switches to read-only mode since IsActionable becomes false.
375
+ this.completedAction = action;
376
+ }
377
+ else {
378
+ this.notificationService.CreateSimpleNotification('Failed to save response. Please try again.', 'error', 5000);
379
+ }
380
+ }
381
+ catch (error) {
382
+ console.error('Error submitting response:', error);
383
+ this.notificationService.CreateSimpleNotification('An unexpected error occurred while saving the response.', 'error', 5000);
384
+ }
385
+ finally {
386
+ this.IsSaving = false;
387
+ this.cdr.markForCheck();
388
+ }
389
+ }
390
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.MJNotificationService }], target: i0.ɵɵFactoryTarget.Component });
391
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: AgentRequestPanelComponent, isStandalone: false, selector: "mj-agent-request-panel", inputs: { Request: "Request", RequestTypes: "RequestTypes", IsOpen: "IsOpen" }, outputs: { Close: "Close" }, host: { listeners: { "document:keydown.escape": "OnEscapeKey()" } }, viewQueries: [{ propertyName: "dynamicForm", first: true, predicate: ["dynamicForm"], descendants: true }, { propertyName: "panelElement", first: true, predicate: ["panelElement"], descendants: true }], ngImport: i0, template: "@if (IsOpen && Request) {\n <div class=\"panel-backdrop\" (click)=\"OnBackdropClick()\">\n <div class=\"request-panel\" #panelElement [class.open]=\"IsOpen\" [class.resizing]=\"IsResizing\" [style.width.px]=\"PanelWidth\" (click)=\"$event.stopPropagation()\">\n <!-- Resize Handle (left edge) -->\n <div class=\"resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"resize-grip\"></div>\n </div>\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"header-content\">\n <div class=\"header-title-row\">\n @if (RequestType?.Icon) {\n <i class=\"{{ RequestType!.Icon }} header-icon\"></i>\n }\n <h3 class=\"panel-title\">{{ RequestType?.Name || 'Agent Request' }}</h3>\n <span class=\"priority-badge\" [class]=\"PriorityClass\">{{ PriorityLabel }}</span>\n <span class=\"status-badge status-{{ Request.Status.toLowerCase() }}\">{{ Request.Status }}</span>\n </div>\n @if (Request.Agent) {\n <span class=\"agent-name\">\n <i class=\"fa-solid fa-robot\"></i> {{ Request.Agent }}\n </span>\n }\n </div>\n <button class=\"close-btn\" (click)=\"OnCancel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"panel-body\">\n <!-- Request Details -->\n <div class=\"section\">\n <h4 class=\"section-title\">Request</h4>\n <p class=\"request-text\">{{ Request.Request }}</p>\n </div>\n\n <!-- Metadata -->\n <div class=\"section metadata-section\">\n <div class=\"metadata-grid\">\n @if (Request.ExpiresAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Expires</span>\n <span class=\"metadata-value\" [class.expired]=\"IsExpired\">\n {{ Request.ExpiresAt | date:'medium' }}\n @if (IsExpired) {\n <span class=\"expired-tag\">Expired</span>\n }\n </span>\n </div>\n }\n @if (Request.__mj_CreatedAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Created</span>\n <span class=\"metadata-value\">{{ Request.__mj_CreatedAt | date:'medium' }}</span>\n </div>\n }\n @if (Request.RespondedAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Responded</span>\n <span class=\"metadata-value\">{{ Request.RespondedAt | date:'medium' }}</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Dynamic Response Form (from ResponseSchema) -->\n @if (HasResponseForm && IsActionable && ResponseFormDefinition) {\n <div class=\"section\">\n <mj-dynamic-form #dynamicForm\n [FormDefinition]=\"ResponseFormDefinition\"\n [Disabled]=\"IsSaving\">\n </mj-dynamic-form>\n </div>\n }\n\n <!-- Free-text response (always available for actionable requests) -->\n @if (IsActionable) {\n <div class=\"section\">\n <h4 class=\"section-title\">\n @if (HasResponseForm) {\n Additional Comments\n } @else {\n Response\n }\n </h4>\n <textarea\n class=\"response-textarea\"\n [(ngModel)]=\"ResponseText\"\n placeholder=\"Type your response here...\"\n rows=\"4\">\n </textarea>\n </div>\n }\n\n <!-- Show existing response for already-handled requests -->\n @if (!IsActionable && (Request.Response || Request.ResponseData)) {\n @if (Request.ResponseData) {\n <div class=\"section\">\n <mj-dynamic-form-response\n [FormDefinition]=\"ResponseFormDefinition\"\n [ResponseData]=\"Request.ResponseData\"\n DisplayMode=\"card\">\n </mj-dynamic-form-response>\n </div>\n }\n @if (Request.Response) {\n <div class=\"section\">\n <h4 class=\"section-title\">\n @if (Request.ResponseData) {\n Additional Comments\n } @else {\n Response\n }\n </h4>\n <p class=\"response-text\">{{ Request.Response }}</p>\n </div>\n }\n }\n </div>\n\n <!-- Footer (action buttons) -->\n @if (IsActionable && !IsExpired) {\n <div class=\"panel-footer\">\n @if (IsApprovalType) {\n <button\n class=\"btn btn-approve\"\n (click)=\"OnApprove()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-check\"></i> Approve\n </button>\n <button\n class=\"btn btn-reject\"\n (click)=\"OnReject()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-times\"></i> Reject\n </button>\n } @else {\n <button\n class=\"btn btn-respond\"\n (click)=\"OnRespond()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-paper-plane\"></i> Submit Response\n </button>\n }\n <button\n class=\"btn btn-reassign\"\n (click)=\"OnReassignClick()\"\n [disabled]=\"IsSaving\"\n title=\"Reassign to another user\">\n <i class=\"fa-solid fa-user-plus\"></i> Reassign\n </button>\n <button\n class=\"btn btn-cancel\"\n (click)=\"OnCancel()\"\n [disabled]=\"IsSaving\">\n Cancel\n </button>\n </div>\n } @else if (IsExpired) {\n <div class=\"panel-footer\">\n <div class=\"expired-notice\">\n <i class=\"fa-solid fa-clock\"></i> This request has expired and can no longer be responded to.\n </div>\n <button class=\"btn btn-cancel\" (click)=\"OnCancel()\">Close</button>\n </div>\n } @else {\n <div class=\"panel-footer\">\n <button class=\"btn btn-cancel\" (click)=\"OnCancel()\">Close</button>\n </div>\n }\n </div>\n </div>\n\n <!-- Reassign Dialog Overlay -->\n @if (ShowReassignDialog) {\n <div class=\"reassign-overlay\" (click)=\"OnReassignCancel()\">\n <div class=\"reassign-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"reassign-header\">\n <h4 class=\"reassign-title\">\n <i class=\"fa-solid fa-user-plus\"></i> Reassign Request\n </h4>\n <button class=\"close-btn\" (click)=\"OnReassignCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"reassign-body\">\n <!-- User Search -->\n <div class=\"reassign-field\">\n <label class=\"field-label\">Search User</label>\n <div class=\"search-input-wrapper\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"field-input search-input\"\n placeholder=\"Type a name or email...\"\n [value]=\"ReassignSearchTerm\"\n (input)=\"OnReassignSearchChange($any($event.target).value)\">\n </div>\n </div>\n\n <!-- Search Results -->\n @if (IsSearchingUsers) {\n <div class=\"search-status\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Searching...\n </div>\n }\n @if (ReassignUsers.length > 0) {\n <div class=\"user-results\">\n @for (user of ReassignUsers; track user.ID) {\n <button\n class=\"user-result-item\"\n [class.selected]=\"SelectedReassignUser?.ID === user.ID\"\n (click)=\"OnSelectReassignUser(user)\">\n <i class=\"fa-solid fa-user user-avatar\"></i>\n <div class=\"user-info\">\n <span class=\"user-name\">{{ user.Name }}</span>\n <span class=\"user-email\">{{ user.Email }}</span>\n </div>\n @if (SelectedReassignUser?.ID === user.ID) {\n <i class=\"fa-solid fa-check selected-check\"></i>\n }\n </button>\n }\n </div>\n } @else if (ReassignSearchTerm.length >= 2 && !IsSearchingUsers) {\n <div class=\"search-status\">No users found</div>\n }\n\n <!-- Note -->\n <div class=\"reassign-field\">\n <label class=\"field-label\">Note (optional)</label>\n <textarea\n class=\"field-textarea\"\n [(ngModel)]=\"ReassignNote\"\n placeholder=\"Add a reason for reassignment...\"\n rows=\"2\">\n </textarea>\n </div>\n </div>\n\n <div class=\"reassign-footer\">\n <button\n class=\"btn btn-respond\"\n (click)=\"OnReassignSubmit()\"\n [disabled]=\"!SelectedReassignUser || IsReassigning\">\n @if (IsReassigning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Reassigning...\n } @else {\n <i class=\"fa-solid fa-user-plus\"></i> Reassign\n }\n </button>\n <button\n class=\"btn btn-cancel\"\n (click)=\"OnReassignCancel()\"\n [disabled]=\"IsReassigning\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n}\n", styles: ["/* Panel Backdrop */\n.panel-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n display: flex;\n justify-content: flex-end;\n z-index: 10000;\n}\n\n/* Slide-in Panel */\n.request-panel {\n max-width: 90vw;\n min-width: 360px;\n height: 100%;\n background: var(--mj-bg-surface);\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n transform: translateX(100%);\n transition: transform 0.25s ease-out;\n position: relative;\n}\n\n.request-panel.open {\n transform: translateX(0);\n}\n\n.request-panel.resizing {\n transition: none;\n}\n\n/* Resize Handle */\n.resize-handle {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 6px;\n cursor: col-resize;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.resize-handle:hover,\n.resize-handle:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.resize-grip {\n width: 2px;\n height: 32px;\n border-radius: 1px;\n background: var(--mj-border-strong);\n opacity: 0;\n transition: opacity 0.15s;\n}\n\n.resize-handle:hover .resize-grip,\n.resize-handle:active .resize-grip {\n opacity: 1;\n}\n\n/* Header */\n.panel-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.header-content {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n}\n\n.header-title-row {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.header-icon {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.agent-name {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.close-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px 8px;\n color: var(--mj-text-muted);\n font-size: 16px;\n border-radius: 4px;\n}\n\n.close-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-requested {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border: 1px solid var(--mj-status-info-border);\n}\n\n.status-approved {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.status-rejected,\n.status-canceled {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.status-responded {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.status-expired {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n/* Priority Badge */\n.priority-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.priority-low {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.priority-normal {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.priority-high {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n\n.priority-critical {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n/* Body */\n.panel-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n.section {\n margin-bottom: 20px;\n}\n\n.section-title {\n margin: 0 0 8px 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.request-text {\n margin: 0;\n font-size: 14px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n}\n\n.response-text {\n margin: 0;\n font-size: 14px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 6px;\n}\n\n/* Metadata */\n.metadata-section {\n background: var(--mj-bg-surface-card);\n padding: 12px;\n border-radius: 6px;\n}\n\n.metadata-grid {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.metadata-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.metadata-label {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.metadata-value {\n font-size: 12px;\n color: var(--mj-text-primary);\n}\n\n.metadata-value.expired {\n color: var(--mj-status-error-text);\n}\n\n.expired-tag {\n display: inline-block;\n margin-left: 6px;\n padding: 1px 6px;\n border-radius: 8px;\n font-size: 10px;\n font-weight: 600;\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n/* Shared field styles (used by reassign dialog and free-text response) */\n.field-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.field-input,\n.field-textarea {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n transition: border-color 0.15s;\n}\n\n.field-input:focus,\n.field-textarea:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.field-textarea {\n resize: vertical;\n min-height: 60px;\n}\n\n.response-textarea {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n font-family: inherit;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n resize: vertical;\n box-sizing: border-box;\n}\n\n.response-textarea:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n/* Footer */\n.panel-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.btn {\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: background-color 0.15s;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-approve {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n}\n\n.btn-approve:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-success) 85%, black);\n}\n\n.btn-reject {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.btn-reject:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 85%, black);\n}\n\n.btn-respond {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.btn-respond:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.btn-cancel {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n margin-left: auto;\n}\n\n.btn-cancel:hover:not(:disabled) {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n.btn-reassign {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n.btn-reassign:hover:not(:disabled) {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.expired-notice {\n font-size: 13px;\n color: var(--mj-status-warning-text);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n/* Reassign Dialog */\n.reassign-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n}\n\n.reassign-dialog {\n width: 420px;\n max-width: 90vw;\n max-height: 80vh;\n background: var(--mj-bg-surface);\n border-radius: 10px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.reassign-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.reassign-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.reassign-body {\n padding: 16px 20px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.reassign-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.search-input-wrapper {\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-muted);\n font-size: 13px;\n}\n\n.search-input {\n padding-left: 32px;\n}\n\n.search-status {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 8px 0;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.user-results {\n display: flex;\n flex-direction: column;\n gap: 2px;\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n}\n\n.user-result-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n background: none;\n border: none;\n cursor: pointer;\n text-align: left;\n width: 100%;\n transition: background-color 0.1s;\n}\n\n.user-result-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.user-result-item.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.user-avatar {\n color: var(--mj-text-muted);\n font-size: 14px;\n width: 20px;\n text-align: center;\n}\n\n.user-info {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.user-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.user-email {\n font-size: 12px;\n color: var(--mj-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.selected-check {\n color: var(--mj-brand-primary);\n font-size: 13px;\n}\n\n.reassign-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n"], dependencies: [{ kind: "directive", type: i2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.DynamicFormComponent, selector: "mj-dynamic-form", inputs: ["FormDefinition", "Disabled", "Visible"], outputs: ["FormSubmitted"] }, { kind: "component", type: i3.DynamicFormResponseComponent, selector: "mj-dynamic-form-response", inputs: ["FormDefinition", "ResponseData", "Title", "DisplayMode"] }, { kind: "pipe", type: i4.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
392
+ }
393
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AgentRequestPanelComponent, decorators: [{
394
+ type: Component,
395
+ args: [{ standalone: false, selector: 'mj-agent-request-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (IsOpen && Request) {\n <div class=\"panel-backdrop\" (click)=\"OnBackdropClick()\">\n <div class=\"request-panel\" #panelElement [class.open]=\"IsOpen\" [class.resizing]=\"IsResizing\" [style.width.px]=\"PanelWidth\" (click)=\"$event.stopPropagation()\">\n <!-- Resize Handle (left edge) -->\n <div class=\"resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"resize-grip\"></div>\n </div>\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"header-content\">\n <div class=\"header-title-row\">\n @if (RequestType?.Icon) {\n <i class=\"{{ RequestType!.Icon }} header-icon\"></i>\n }\n <h3 class=\"panel-title\">{{ RequestType?.Name || 'Agent Request' }}</h3>\n <span class=\"priority-badge\" [class]=\"PriorityClass\">{{ PriorityLabel }}</span>\n <span class=\"status-badge status-{{ Request.Status.toLowerCase() }}\">{{ Request.Status }}</span>\n </div>\n @if (Request.Agent) {\n <span class=\"agent-name\">\n <i class=\"fa-solid fa-robot\"></i> {{ Request.Agent }}\n </span>\n }\n </div>\n <button class=\"close-btn\" (click)=\"OnCancel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"panel-body\">\n <!-- Request Details -->\n <div class=\"section\">\n <h4 class=\"section-title\">Request</h4>\n <p class=\"request-text\">{{ Request.Request }}</p>\n </div>\n\n <!-- Metadata -->\n <div class=\"section metadata-section\">\n <div class=\"metadata-grid\">\n @if (Request.ExpiresAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Expires</span>\n <span class=\"metadata-value\" [class.expired]=\"IsExpired\">\n {{ Request.ExpiresAt | date:'medium' }}\n @if (IsExpired) {\n <span class=\"expired-tag\">Expired</span>\n }\n </span>\n </div>\n }\n @if (Request.__mj_CreatedAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Created</span>\n <span class=\"metadata-value\">{{ Request.__mj_CreatedAt | date:'medium' }}</span>\n </div>\n }\n @if (Request.RespondedAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Responded</span>\n <span class=\"metadata-value\">{{ Request.RespondedAt | date:'medium' }}</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Dynamic Response Form (from ResponseSchema) -->\n @if (HasResponseForm && IsActionable && ResponseFormDefinition) {\n <div class=\"section\">\n <mj-dynamic-form #dynamicForm\n [FormDefinition]=\"ResponseFormDefinition\"\n [Disabled]=\"IsSaving\">\n </mj-dynamic-form>\n </div>\n }\n\n <!-- Free-text response (always available for actionable requests) -->\n @if (IsActionable) {\n <div class=\"section\">\n <h4 class=\"section-title\">\n @if (HasResponseForm) {\n Additional Comments\n } @else {\n Response\n }\n </h4>\n <textarea\n class=\"response-textarea\"\n [(ngModel)]=\"ResponseText\"\n placeholder=\"Type your response here...\"\n rows=\"4\">\n </textarea>\n </div>\n }\n\n <!-- Show existing response for already-handled requests -->\n @if (!IsActionable && (Request.Response || Request.ResponseData)) {\n @if (Request.ResponseData) {\n <div class=\"section\">\n <mj-dynamic-form-response\n [FormDefinition]=\"ResponseFormDefinition\"\n [ResponseData]=\"Request.ResponseData\"\n DisplayMode=\"card\">\n </mj-dynamic-form-response>\n </div>\n }\n @if (Request.Response) {\n <div class=\"section\">\n <h4 class=\"section-title\">\n @if (Request.ResponseData) {\n Additional Comments\n } @else {\n Response\n }\n </h4>\n <p class=\"response-text\">{{ Request.Response }}</p>\n </div>\n }\n }\n </div>\n\n <!-- Footer (action buttons) -->\n @if (IsActionable && !IsExpired) {\n <div class=\"panel-footer\">\n @if (IsApprovalType) {\n <button\n class=\"btn btn-approve\"\n (click)=\"OnApprove()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-check\"></i> Approve\n </button>\n <button\n class=\"btn btn-reject\"\n (click)=\"OnReject()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-times\"></i> Reject\n </button>\n } @else {\n <button\n class=\"btn btn-respond\"\n (click)=\"OnRespond()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-paper-plane\"></i> Submit Response\n </button>\n }\n <button\n class=\"btn btn-reassign\"\n (click)=\"OnReassignClick()\"\n [disabled]=\"IsSaving\"\n title=\"Reassign to another user\">\n <i class=\"fa-solid fa-user-plus\"></i> Reassign\n </button>\n <button\n class=\"btn btn-cancel\"\n (click)=\"OnCancel()\"\n [disabled]=\"IsSaving\">\n Cancel\n </button>\n </div>\n } @else if (IsExpired) {\n <div class=\"panel-footer\">\n <div class=\"expired-notice\">\n <i class=\"fa-solid fa-clock\"></i> This request has expired and can no longer be responded to.\n </div>\n <button class=\"btn btn-cancel\" (click)=\"OnCancel()\">Close</button>\n </div>\n } @else {\n <div class=\"panel-footer\">\n <button class=\"btn btn-cancel\" (click)=\"OnCancel()\">Close</button>\n </div>\n }\n </div>\n </div>\n\n <!-- Reassign Dialog Overlay -->\n @if (ShowReassignDialog) {\n <div class=\"reassign-overlay\" (click)=\"OnReassignCancel()\">\n <div class=\"reassign-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"reassign-header\">\n <h4 class=\"reassign-title\">\n <i class=\"fa-solid fa-user-plus\"></i> Reassign Request\n </h4>\n <button class=\"close-btn\" (click)=\"OnReassignCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"reassign-body\">\n <!-- User Search -->\n <div class=\"reassign-field\">\n <label class=\"field-label\">Search User</label>\n <div class=\"search-input-wrapper\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"field-input search-input\"\n placeholder=\"Type a name or email...\"\n [value]=\"ReassignSearchTerm\"\n (input)=\"OnReassignSearchChange($any($event.target).value)\">\n </div>\n </div>\n\n <!-- Search Results -->\n @if (IsSearchingUsers) {\n <div class=\"search-status\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Searching...\n </div>\n }\n @if (ReassignUsers.length > 0) {\n <div class=\"user-results\">\n @for (user of ReassignUsers; track user.ID) {\n <button\n class=\"user-result-item\"\n [class.selected]=\"SelectedReassignUser?.ID === user.ID\"\n (click)=\"OnSelectReassignUser(user)\">\n <i class=\"fa-solid fa-user user-avatar\"></i>\n <div class=\"user-info\">\n <span class=\"user-name\">{{ user.Name }}</span>\n <span class=\"user-email\">{{ user.Email }}</span>\n </div>\n @if (SelectedReassignUser?.ID === user.ID) {\n <i class=\"fa-solid fa-check selected-check\"></i>\n }\n </button>\n }\n </div>\n } @else if (ReassignSearchTerm.length >= 2 && !IsSearchingUsers) {\n <div class=\"search-status\">No users found</div>\n }\n\n <!-- Note -->\n <div class=\"reassign-field\">\n <label class=\"field-label\">Note (optional)</label>\n <textarea\n class=\"field-textarea\"\n [(ngModel)]=\"ReassignNote\"\n placeholder=\"Add a reason for reassignment...\"\n rows=\"2\">\n </textarea>\n </div>\n </div>\n\n <div class=\"reassign-footer\">\n <button\n class=\"btn btn-respond\"\n (click)=\"OnReassignSubmit()\"\n [disabled]=\"!SelectedReassignUser || IsReassigning\">\n @if (IsReassigning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Reassigning...\n } @else {\n <i class=\"fa-solid fa-user-plus\"></i> Reassign\n }\n </button>\n <button\n class=\"btn btn-cancel\"\n (click)=\"OnReassignCancel()\"\n [disabled]=\"IsReassigning\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n}\n", styles: ["/* Panel Backdrop */\n.panel-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n display: flex;\n justify-content: flex-end;\n z-index: 10000;\n}\n\n/* Slide-in Panel */\n.request-panel {\n max-width: 90vw;\n min-width: 360px;\n height: 100%;\n background: var(--mj-bg-surface);\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.15);\n transform: translateX(100%);\n transition: transform 0.25s ease-out;\n position: relative;\n}\n\n.request-panel.open {\n transform: translateX(0);\n}\n\n.request-panel.resizing {\n transition: none;\n}\n\n/* Resize Handle */\n.resize-handle {\n position: absolute;\n left: 0;\n top: 0;\n bottom: 0;\n width: 6px;\n cursor: col-resize;\n z-index: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.resize-handle:hover,\n.resize-handle:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.resize-grip {\n width: 2px;\n height: 32px;\n border-radius: 1px;\n background: var(--mj-border-strong);\n opacity: 0;\n transition: opacity 0.15s;\n}\n\n.resize-handle:hover .resize-grip,\n.resize-handle:active .resize-grip {\n opacity: 1;\n}\n\n/* Header */\n.panel-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.header-content {\n display: flex;\n flex-direction: column;\n gap: 4px;\n flex: 1;\n min-width: 0;\n}\n\n.header-title-row {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.header-icon {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.panel-title {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.agent-name {\n font-size: 13px;\n color: var(--mj-text-secondary);\n}\n\n.close-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 4px 8px;\n color: var(--mj-text-muted);\n font-size: 16px;\n border-radius: 4px;\n}\n\n.close-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.status-requested {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n border: 1px solid var(--mj-status-info-border);\n}\n\n.status-approved {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.status-rejected,\n.status-canceled {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n border: 1px solid var(--mj-status-error-border);\n}\n\n.status-responded {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n border: 1px solid var(--mj-status-success-border);\n}\n\n.status-expired {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border: 1px solid var(--mj-status-warning-border);\n}\n\n/* Priority Badge */\n.priority-badge {\n display: inline-block;\n padding: 2px 8px;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.priority-low {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.priority-normal {\n background: var(--mj-status-info-bg);\n color: var(--mj-status-info-text);\n}\n\n.priority-high {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n}\n\n.priority-critical {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n/* Body */\n.panel-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n.section {\n margin-bottom: 20px;\n}\n\n.section-title {\n margin: 0 0 8px 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.request-text {\n margin: 0;\n font-size: 14px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n}\n\n.response-text {\n margin: 0;\n font-size: 14px;\n line-height: 1.6;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n background: var(--mj-bg-surface-sunken);\n padding: 12px;\n border-radius: 6px;\n}\n\n/* Metadata */\n.metadata-section {\n background: var(--mj-bg-surface-card);\n padding: 12px;\n border-radius: 6px;\n}\n\n.metadata-grid {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.metadata-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.metadata-label {\n font-size: 12px;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n.metadata-value {\n font-size: 12px;\n color: var(--mj-text-primary);\n}\n\n.metadata-value.expired {\n color: var(--mj-status-error-text);\n}\n\n.expired-tag {\n display: inline-block;\n margin-left: 6px;\n padding: 1px 6px;\n border-radius: 8px;\n font-size: 10px;\n font-weight: 600;\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n/* Shared field styles (used by reassign dialog and free-text response) */\n.field-label {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.field-input,\n.field-textarea {\n padding: 8px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n transition: border-color 0.15s;\n}\n\n.field-input:focus,\n.field-textarea:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.field-textarea {\n resize: vertical;\n min-height: 60px;\n}\n\n.response-textarea {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 14px;\n font-family: inherit;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n resize: vertical;\n box-sizing: border-box;\n}\n\n.response-textarea:focus {\n outline: none;\n border-color: var(--mj-border-focus);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n/* Footer */\n.panel-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.btn {\n padding: 8px 16px;\n border: none;\n border-radius: 6px;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 6px;\n transition: background-color 0.15s;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-approve {\n background: var(--mj-status-success);\n color: var(--mj-text-inverse);\n}\n\n.btn-approve:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-success) 85%, black);\n}\n\n.btn-reject {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.btn-reject:hover:not(:disabled) {\n background: color-mix(in srgb, var(--mj-status-error) 85%, black);\n}\n\n.btn-respond {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n}\n\n.btn-respond:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.btn-cancel {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n margin-left: auto;\n}\n\n.btn-cancel:hover:not(:disabled) {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n.btn-reassign {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-default);\n}\n\n.btn-reassign:hover:not(:disabled) {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n border-color: var(--mj-border-strong);\n}\n\n.expired-notice {\n font-size: 13px;\n color: var(--mj-status-warning-text);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n/* Reassign Dialog */\n.reassign-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001;\n}\n\n.reassign-dialog {\n width: 420px;\n max-width: 90vw;\n max-height: 80vh;\n background: var(--mj-bg-surface);\n border-radius: 10px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.reassign-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.reassign-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.reassign-body {\n padding: 16px 20px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.reassign-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.search-input-wrapper {\n position: relative;\n}\n\n.search-icon {\n position: absolute;\n left: 10px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--mj-text-muted);\n font-size: 13px;\n}\n\n.search-input {\n padding-left: 32px;\n}\n\n.search-status {\n font-size: 13px;\n color: var(--mj-text-muted);\n padding: 8px 0;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.user-results {\n display: flex;\n flex-direction: column;\n gap: 2px;\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n}\n\n.user-result-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n background: none;\n border: none;\n cursor: pointer;\n text-align: left;\n width: 100%;\n transition: background-color 0.1s;\n}\n\n.user-result-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.user-result-item.selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.user-avatar {\n color: var(--mj-text-muted);\n font-size: 14px;\n width: 20px;\n text-align: center;\n}\n\n.user-info {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.user-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.user-email {\n font-size: 12px;\n color: var(--mj-text-muted);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.selected-check {\n color: var(--mj-brand-primary);\n font-size: 13px;\n}\n\n.reassign-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n"] }]
396
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.MJNotificationService }], propDecorators: { Request: [{
397
+ type: Input
398
+ }], RequestTypes: [{
399
+ type: Input
400
+ }], IsOpen: [{
401
+ type: Input
402
+ }], Close: [{
403
+ type: Output
404
+ }], dynamicForm: [{
405
+ type: ViewChild,
406
+ args: ['dynamicForm']
407
+ }], panelElement: [{
408
+ type: ViewChild,
409
+ args: ['panelElement']
410
+ }], OnEscapeKey: [{
411
+ type: HostListener,
412
+ args: ['document:keydown.escape']
413
+ }] } });
414
+ //# sourceMappingURL=agent-request-panel.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-request-panel.component.js","sourceRoot":"","sources":["../../../../src/lib/panels/agent-request-panel/agent-request-panel.component.ts","../../../../src/lib/panels/agent-request-panel/agent-request-panel.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview Agent Request Panel Component\n *\n * Slide-in panel for viewing and responding to AI agent feedback requests.\n * Displays request details, renders ResponseSchema as a dynamic form,\n * and submits ResponseData back to the request entity.\n */\n\nimport {\n Component, Input, Output, EventEmitter, ChangeDetectorRef,\n ChangeDetectionStrategy, OnInit, OnDestroy, HostListener, ViewChild, ElementRef\n} from '@angular/core';\nimport { MJAIAgentRequestEntity, MJAIAgentRequestTypeEntity } from '@memberjunction/core-entities';\nimport { RunView } from '@memberjunction/core';\nimport { UserInfoEngine } from '@memberjunction/core-entities';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { AgentResponseForm } from '@memberjunction/ai-core-plus';\nimport { DynamicFormComponent } from '@memberjunction/ng-forms';\n\nconst PANEL_WIDTH_SETTING_KEY = 'Explorer.AgentRequestPanelWidth';\nconst DEFAULT_PANEL_WIDTH = 520;\nconst MIN_PANEL_WIDTH = 360;\nconst MAX_PANEL_WIDTH = 900;\n\n/**\n * Result returned when the panel closes after an action\n */\nexport interface AgentRequestPanelResult {\n /** Whether an action was taken (responded, approved, rejected) */\n Success: boolean;\n /** The updated request entity */\n Request?: MJAIAgentRequestEntity;\n /** The action that was taken */\n Action: 'responded' | 'approved' | 'rejected' | 'cancelled';\n}\n\n@Component({\n standalone: false,\n selector: 'mj-agent-request-panel',\n templateUrl: './agent-request-panel.component.html',\n styleUrls: ['./agent-request-panel.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush\n})\nexport class AgentRequestPanelComponent implements OnInit, OnDestroy {\n @Input() Request: MJAIAgentRequestEntity | null = null;\n @Input() RequestTypes: MJAIAgentRequestTypeEntity[] = [];\n @Input() IsOpen = false;\n\n @Output() Close = new EventEmitter<AgentRequestPanelResult>();\n\n @ViewChild('dynamicForm') dynamicForm: DynamicFormComponent | undefined;\n @ViewChild('panelElement') panelElement: ElementRef<HTMLDivElement> | undefined;\n\n public IsLoading = false;\n public IsSaving = false;\n public ResponseText = '';\n public ResponseFormDefinition: AgentResponseForm | null = null;\n public PanelWidth = DEFAULT_PANEL_WIDTH;\n\n /** Tracks whether a response was submitted so the Close event carries the right result */\n private completedAction: 'responded' | 'approved' | 'rejected' | null = null;\n\n // Resize state\n public IsResizing = false;\n private resizeStartX = 0;\n private resizeStartWidth = 0;\n private boundOnResizeMove: ((e: MouseEvent) => void) | null = null;\n private boundOnResizeEnd: (() => void) | null = null;\n\n // Reassign state\n public ShowReassignDialog = false;\n public ReassignSearchTerm = '';\n public ReassignNote = '';\n public ReassignUsers: Array<{ ID: string; Name: string; Email: string }> = [];\n public SelectedReassignUser: { ID: string; Name: string; Email: string } | null = null;\n public IsSearchingUsers = false;\n public IsReassigning = false;\n\n private reassignSearchTimeout: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n private cdr: ChangeDetectorRef,\n private notificationService: MJNotificationService\n ) {}\n\n ngOnInit(): void {\n this.loadPanelWidth();\n if (this.Request) {\n this.initializeForm();\n }\n }\n\n ngOnDestroy(): void {\n this.cleanupResizeListeners();\n }\n\n @HostListener('document:keydown.escape')\n public OnEscapeKey(): void {\n if (this.IsOpen) {\n this.OnCancel();\n }\n }\n\n /** The request type entity for the current request */\n public get RequestType(): MJAIAgentRequestTypeEntity | null {\n if (!this.Request?.RequestTypeID) return null;\n return this.RequestTypes.find(t => UUIDsEqual(t.ID, this.Request!.RequestTypeID!)) ?? null;\n }\n\n /** Whether this request is still actionable (status = Requested) */\n public get IsActionable(): boolean {\n return this.Request?.Status === 'Requested';\n }\n\n /** Whether this is an approval-type request (show approve/reject buttons) */\n public get IsApprovalType(): boolean {\n const typeName = this.RequestType?.Name;\n return typeName === 'Approval';\n }\n\n /** Whether the request has a structured response form */\n public get HasResponseForm(): boolean {\n return this.ResponseFormDefinition != null && (this.ResponseFormDefinition.questions?.length ?? 0) > 0;\n }\n\n /** Priority display label */\n public get PriorityLabel(): string {\n if (!this.Request) return '';\n const p = this.Request.Priority;\n if (p <= 25) return 'Low';\n if (p <= 50) return 'Normal';\n if (p <= 75) return 'High';\n return 'Critical';\n }\n\n /** Priority CSS class */\n public get PriorityClass(): string {\n if (!this.Request) return '';\n const p = this.Request.Priority;\n if (p <= 25) return 'priority-low';\n if (p <= 50) return 'priority-normal';\n if (p <= 75) return 'priority-high';\n return 'priority-critical';\n }\n\n /** Whether the request is expired */\n public get IsExpired(): boolean {\n if (!this.Request?.ExpiresAt) return false;\n return new Date(this.Request.ExpiresAt) < new Date();\n }\n\n /**\n * Opens the panel with a specific request\n */\n public Open(request: MJAIAgentRequestEntity): void {\n this.Request = request;\n this.IsOpen = true;\n this.ResponseText = '';\n this.completedAction = null;\n this.initializeForm();\n this.cdr.markForCheck();\n }\n\n /**\n * Approve the request\n */\n public async OnApprove(): Promise<void> {\n await this.submitResponse('Approved', 'approved');\n }\n\n /**\n * Reject the request\n */\n public async OnReject(): Promise<void> {\n await this.submitResponse('Rejected', 'rejected');\n }\n\n /**\n * Submit a response (for non-approval types)\n */\n public async OnRespond(): Promise<void> {\n await this.submitResponse('Responded', 'responded');\n }\n\n /**\n * Cancel / close the panel without action\n */\n public OnCancel(): void {\n this.IsOpen = false;\n if (this.completedAction) {\n // A response was submitted — tell the parent about the action taken\n this.Close.emit({ Success: true, Request: this.Request!, Action: this.completedAction });\n this.completedAction = null;\n } else {\n this.Close.emit({ Success: false, Action: 'cancelled' });\n }\n this.cdr.markForCheck();\n }\n\n public OnBackdropClick(): void {\n this.OnCancel();\n }\n\n /** Open the reassign dialog */\n public OnReassignClick(): void {\n this.ShowReassignDialog = true;\n this.ReassignSearchTerm = '';\n this.ReassignNote = '';\n this.ReassignUsers = [];\n this.SelectedReassignUser = null;\n this.cdr.markForCheck();\n }\n\n /** Close the reassign dialog */\n public OnReassignCancel(): void {\n this.ShowReassignDialog = false;\n this.cdr.markForCheck();\n }\n\n /** Search users by name/email for reassignment */\n public OnReassignSearchChange(term: string): void {\n this.ReassignSearchTerm = term;\n if (this.reassignSearchTimeout) {\n clearTimeout(this.reassignSearchTimeout);\n }\n if (term.length < 2) {\n this.ReassignUsers = [];\n this.cdr.markForCheck();\n return;\n }\n this.reassignSearchTimeout = setTimeout(() => this.searchUsers(term), 300);\n }\n\n /** Select a user from the search results */\n public OnSelectReassignUser(user: { ID: string; Name: string; Email: string }): void {\n this.SelectedReassignUser = user;\n this.cdr.markForCheck();\n }\n\n /** Submit the reassignment */\n public async OnReassignSubmit(): Promise<void> {\n if (!this.Request || !this.SelectedReassignUser || this.IsReassigning) return;\n\n this.IsReassigning = true;\n this.cdr.markForCheck();\n\n try {\n this.Request.RequestForUserID = this.SelectedReassignUser.ID;\n\n // Append reassignment note to Comments\n const timestamp = new Date().toISOString();\n const reassignEntry = `[${timestamp}] Reassigned to ${this.SelectedReassignUser.Name || this.SelectedReassignUser.Email}${this.ReassignNote ? ` — \"${this.ReassignNote}\"` : ''}`;\n this.Request.Comments = this.Request.Comments\n ? `${this.Request.Comments}\\n${reassignEntry}`\n : reassignEntry;\n\n const saved = await this.Request.Save();\n if (saved) {\n this.notificationService.CreateSimpleNotification(\n `Request reassigned to ${this.SelectedReassignUser.Name || this.SelectedReassignUser.Email}`, 'success', 3000\n );\n this.ShowReassignDialog = false;\n this.IsOpen = false;\n this.Close.emit({ Success: true, Request: this.Request, Action: 'cancelled' });\n } else {\n this.notificationService.CreateSimpleNotification(\n 'Failed to reassign request. Please try again.', 'error', 5000\n );\n }\n } catch (error) {\n console.error('Error reassigning request:', error);\n this.notificationService.CreateSimpleNotification(\n 'An unexpected error occurred.', 'error', 5000\n );\n } finally {\n this.IsReassigning = false;\n this.cdr.markForCheck();\n }\n }\n\n private async searchUsers(term: string): Promise<void> {\n this.IsSearchingUsers = true;\n this.cdr.markForCheck();\n\n try {\n const rv = new RunView();\n const result = await rv.RunView<{ ID: string; Name: string; Email: string }>({\n EntityName: 'Users',\n ExtraFilter: `(Name LIKE '%${term.replace(/'/g, \"''\")}%' OR Email LIKE '%${term.replace(/'/g, \"''\")}%')`,\n OrderBy: 'Name',\n MaxRows: 10,\n Fields: ['ID', 'Name', 'Email'],\n ResultType: 'simple'\n });\n this.ReassignUsers = result.Success ? result.Results : [];\n } catch {\n this.ReassignUsers = [];\n } finally {\n this.IsSearchingUsers = false;\n this.cdr.markForCheck();\n }\n }\n\n // ========================================================================\n // Resize\n // ========================================================================\n\n /** Begin resizing when the user mousedowns on the resize handle */\n public OnResizeStart(event: MouseEvent): void {\n event.preventDefault();\n this.IsResizing = true;\n this.resizeStartX = event.clientX;\n this.resizeStartWidth = this.PanelWidth;\n\n this.boundOnResizeMove = (e: MouseEvent) => this.onResizeMove(e);\n this.boundOnResizeEnd = () => this.onResizeEnd();\n document.addEventListener('mousemove', this.boundOnResizeMove);\n document.addEventListener('mouseup', this.boundOnResizeEnd);\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n }\n\n private onResizeMove(event: MouseEvent): void {\n // Panel is on the right, so dragging left (negative deltaX) increases width\n const deltaX = this.resizeStartX - event.clientX;\n const newWidth = Math.max(MIN_PANEL_WIDTH, Math.min(MAX_PANEL_WIDTH, this.resizeStartWidth + deltaX));\n this.PanelWidth = newWidth;\n this.cdr.markForCheck();\n }\n\n private onResizeEnd(): void {\n this.IsResizing = false;\n this.cleanupResizeListeners();\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n this.savePanelWidth();\n this.cdr.markForCheck();\n }\n\n private cleanupResizeListeners(): void {\n if (this.boundOnResizeMove) {\n document.removeEventListener('mousemove', this.boundOnResizeMove);\n this.boundOnResizeMove = null;\n }\n if (this.boundOnResizeEnd) {\n document.removeEventListener('mouseup', this.boundOnResizeEnd);\n this.boundOnResizeEnd = null;\n }\n }\n\n private loadPanelWidth(): void {\n try {\n const saved = UserInfoEngine.Instance.GetSetting(PANEL_WIDTH_SETTING_KEY);\n if (saved) {\n const parsed = parseInt(saved, 10);\n if (!isNaN(parsed) && parsed >= MIN_PANEL_WIDTH && parsed <= MAX_PANEL_WIDTH) {\n this.PanelWidth = parsed;\n }\n }\n } catch {\n // UserInfoEngine may not be initialized yet — use default\n }\n }\n\n private savePanelWidth(): void {\n try {\n UserInfoEngine.Instance.SetSettingDebounced(PANEL_WIDTH_SETTING_KEY, String(this.PanelWidth));\n } catch {\n // Silently ignore save failures\n }\n }\n\n // ========================================================================\n // Form handling\n // ========================================================================\n\n /**\n * Parse the ResponseSchema JSON into an AgentResponseForm for the dynamic form component.\n */\n private initializeForm(): void {\n this.ResponseFormDefinition = null;\n\n if (!this.Request?.ResponseSchema) return;\n\n try {\n const schema = JSON.parse(this.Request.ResponseSchema) as AgentResponseForm;\n if (schema?.questions && Array.isArray(schema.questions)) {\n this.ResponseFormDefinition = schema;\n }\n } catch {\n // If schema is invalid JSON, ignore — user can still provide free-text response\n }\n }\n\n /**\n * Collect current form values from the DynamicFormComponent's reactive FormGroup.\n * This reads values directly so the user doesn't need to click the form's\n * internal submit button before clicking the panel's action buttons.\n */\n private collectFormData(): Record<string, unknown> | null {\n if (!this.dynamicForm?.FormGroup) return null;\n const values = this.dynamicForm.FormGroup.value;\n if (values && Object.keys(values).length > 0) {\n return values;\n }\n return null;\n }\n\n /**\n * Shared logic for submitting any response type\n */\n private async submitResponse(\n status: 'Approved' | 'Rejected' | 'Responded',\n action: 'approved' | 'rejected' | 'responded'\n ): Promise<void> {\n if (!this.Request || this.IsSaving) return;\n\n this.IsSaving = true;\n this.cdr.markForCheck();\n\n try {\n this.Request.Status = status;\n this.Request.Response = this.ResponseText || null;\n this.Request.RespondedAt = new Date();\n\n const formData = this.collectFormData();\n if (formData) {\n this.Request.ResponseData = JSON.stringify(formData);\n }\n\n const saved = await this.Request.Save();\n if (saved) {\n this.notificationService.CreateSimpleNotification(\n `Agent request has been ${action} successfully.`, 'success', 3000\n );\n // Keep panel open so the user can see their submitted response.\n // Template auto-switches to read-only mode since IsActionable becomes false.\n this.completedAction = action;\n } else {\n this.notificationService.CreateSimpleNotification(\n 'Failed to save response. Please try again.', 'error', 5000\n );\n }\n } catch (error) {\n console.error('Error submitting response:', error);\n this.notificationService.CreateSimpleNotification(\n 'An unexpected error occurred while saving the response.', 'error', 5000\n );\n } finally {\n this.IsSaving = false;\n this.cdr.markForCheck();\n }\n }\n}\n","@if (IsOpen && Request) {\n <div class=\"panel-backdrop\" (click)=\"OnBackdropClick()\">\n <div class=\"request-panel\" #panelElement [class.open]=\"IsOpen\" [class.resizing]=\"IsResizing\" [style.width.px]=\"PanelWidth\" (click)=\"$event.stopPropagation()\">\n <!-- Resize Handle (left edge) -->\n <div class=\"resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"resize-grip\"></div>\n </div>\n <!-- Header -->\n <div class=\"panel-header\">\n <div class=\"header-content\">\n <div class=\"header-title-row\">\n @if (RequestType?.Icon) {\n <i class=\"{{ RequestType!.Icon }} header-icon\"></i>\n }\n <h3 class=\"panel-title\">{{ RequestType?.Name || 'Agent Request' }}</h3>\n <span class=\"priority-badge\" [class]=\"PriorityClass\">{{ PriorityLabel }}</span>\n <span class=\"status-badge status-{{ Request.Status.toLowerCase() }}\">{{ Request.Status }}</span>\n </div>\n @if (Request.Agent) {\n <span class=\"agent-name\">\n <i class=\"fa-solid fa-robot\"></i> {{ Request.Agent }}\n </span>\n }\n </div>\n <button class=\"close-btn\" (click)=\"OnCancel()\" title=\"Close\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <!-- Body -->\n <div class=\"panel-body\">\n <!-- Request Details -->\n <div class=\"section\">\n <h4 class=\"section-title\">Request</h4>\n <p class=\"request-text\">{{ Request.Request }}</p>\n </div>\n\n <!-- Metadata -->\n <div class=\"section metadata-section\">\n <div class=\"metadata-grid\">\n @if (Request.ExpiresAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Expires</span>\n <span class=\"metadata-value\" [class.expired]=\"IsExpired\">\n {{ Request.ExpiresAt | date:'medium' }}\n @if (IsExpired) {\n <span class=\"expired-tag\">Expired</span>\n }\n </span>\n </div>\n }\n @if (Request.__mj_CreatedAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Created</span>\n <span class=\"metadata-value\">{{ Request.__mj_CreatedAt | date:'medium' }}</span>\n </div>\n }\n @if (Request.RespondedAt) {\n <div class=\"metadata-item\">\n <span class=\"metadata-label\">Responded</span>\n <span class=\"metadata-value\">{{ Request.RespondedAt | date:'medium' }}</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Dynamic Response Form (from ResponseSchema) -->\n @if (HasResponseForm && IsActionable && ResponseFormDefinition) {\n <div class=\"section\">\n <mj-dynamic-form #dynamicForm\n [FormDefinition]=\"ResponseFormDefinition\"\n [Disabled]=\"IsSaving\">\n </mj-dynamic-form>\n </div>\n }\n\n <!-- Free-text response (always available for actionable requests) -->\n @if (IsActionable) {\n <div class=\"section\">\n <h4 class=\"section-title\">\n @if (HasResponseForm) {\n Additional Comments\n } @else {\n Response\n }\n </h4>\n <textarea\n class=\"response-textarea\"\n [(ngModel)]=\"ResponseText\"\n placeholder=\"Type your response here...\"\n rows=\"4\">\n </textarea>\n </div>\n }\n\n <!-- Show existing response for already-handled requests -->\n @if (!IsActionable && (Request.Response || Request.ResponseData)) {\n @if (Request.ResponseData) {\n <div class=\"section\">\n <mj-dynamic-form-response\n [FormDefinition]=\"ResponseFormDefinition\"\n [ResponseData]=\"Request.ResponseData\"\n DisplayMode=\"card\">\n </mj-dynamic-form-response>\n </div>\n }\n @if (Request.Response) {\n <div class=\"section\">\n <h4 class=\"section-title\">\n @if (Request.ResponseData) {\n Additional Comments\n } @else {\n Response\n }\n </h4>\n <p class=\"response-text\">{{ Request.Response }}</p>\n </div>\n }\n }\n </div>\n\n <!-- Footer (action buttons) -->\n @if (IsActionable && !IsExpired) {\n <div class=\"panel-footer\">\n @if (IsApprovalType) {\n <button\n class=\"btn btn-approve\"\n (click)=\"OnApprove()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-check\"></i> Approve\n </button>\n <button\n class=\"btn btn-reject\"\n (click)=\"OnReject()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-times\"></i> Reject\n </button>\n } @else {\n <button\n class=\"btn btn-respond\"\n (click)=\"OnRespond()\"\n [disabled]=\"IsSaving\">\n <i class=\"fa-solid fa-paper-plane\"></i> Submit Response\n </button>\n }\n <button\n class=\"btn btn-reassign\"\n (click)=\"OnReassignClick()\"\n [disabled]=\"IsSaving\"\n title=\"Reassign to another user\">\n <i class=\"fa-solid fa-user-plus\"></i> Reassign\n </button>\n <button\n class=\"btn btn-cancel\"\n (click)=\"OnCancel()\"\n [disabled]=\"IsSaving\">\n Cancel\n </button>\n </div>\n } @else if (IsExpired) {\n <div class=\"panel-footer\">\n <div class=\"expired-notice\">\n <i class=\"fa-solid fa-clock\"></i> This request has expired and can no longer be responded to.\n </div>\n <button class=\"btn btn-cancel\" (click)=\"OnCancel()\">Close</button>\n </div>\n } @else {\n <div class=\"panel-footer\">\n <button class=\"btn btn-cancel\" (click)=\"OnCancel()\">Close</button>\n </div>\n }\n </div>\n </div>\n\n <!-- Reassign Dialog Overlay -->\n @if (ShowReassignDialog) {\n <div class=\"reassign-overlay\" (click)=\"OnReassignCancel()\">\n <div class=\"reassign-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"reassign-header\">\n <h4 class=\"reassign-title\">\n <i class=\"fa-solid fa-user-plus\"></i> Reassign Request\n </h4>\n <button class=\"close-btn\" (click)=\"OnReassignCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n\n <div class=\"reassign-body\">\n <!-- User Search -->\n <div class=\"reassign-field\">\n <label class=\"field-label\">Search User</label>\n <div class=\"search-input-wrapper\">\n <i class=\"fa-solid fa-search search-icon\"></i>\n <input\n type=\"text\"\n class=\"field-input search-input\"\n placeholder=\"Type a name or email...\"\n [value]=\"ReassignSearchTerm\"\n (input)=\"OnReassignSearchChange($any($event.target).value)\">\n </div>\n </div>\n\n <!-- Search Results -->\n @if (IsSearchingUsers) {\n <div class=\"search-status\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Searching...\n </div>\n }\n @if (ReassignUsers.length > 0) {\n <div class=\"user-results\">\n @for (user of ReassignUsers; track user.ID) {\n <button\n class=\"user-result-item\"\n [class.selected]=\"SelectedReassignUser?.ID === user.ID\"\n (click)=\"OnSelectReassignUser(user)\">\n <i class=\"fa-solid fa-user user-avatar\"></i>\n <div class=\"user-info\">\n <span class=\"user-name\">{{ user.Name }}</span>\n <span class=\"user-email\">{{ user.Email }}</span>\n </div>\n @if (SelectedReassignUser?.ID === user.ID) {\n <i class=\"fa-solid fa-check selected-check\"></i>\n }\n </button>\n }\n </div>\n } @else if (ReassignSearchTerm.length >= 2 && !IsSearchingUsers) {\n <div class=\"search-status\">No users found</div>\n }\n\n <!-- Note -->\n <div class=\"reassign-field\">\n <label class=\"field-label\">Note (optional)</label>\n <textarea\n class=\"field-textarea\"\n [(ngModel)]=\"ReassignNote\"\n placeholder=\"Add a reason for reassignment...\"\n rows=\"2\">\n </textarea>\n </div>\n </div>\n\n <div class=\"reassign-footer\">\n <button\n class=\"btn btn-respond\"\n (click)=\"OnReassignSubmit()\"\n [disabled]=\"!SelectedReassignUser || IsReassigning\">\n @if (IsReassigning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Reassigning...\n } @else {\n <i class=\"fa-solid fa-user-plus\"></i> Reassign\n }\n </button>\n <button\n class=\"btn btn-cancel\"\n (click)=\"OnReassignCancel()\"\n [disabled]=\"IsReassigning\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Public API Surface for @memberjunction/ng-agent-requests
3
+ *
4
+ * This package provides reusable Angular components for AI agent request management:
5
+ * - Panel component for viewing and responding to agent feedback requests
6
+ * - Dialog component for modal presentation of request details
7
+ */
8
+ export * from './lib/agent-requests.module';
9
+ export * from './lib/panels/agent-request-panel/agent-request-panel.component';
10
+ export * from './lib/dialogs/agent-request-dialog.component';
11
+ //# sourceMappingURL=public-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.d.ts","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,cAAc,6BAA6B,CAAC;AAG5C,cAAc,gEAAgE,CAAC;AAG/E,cAAc,8CAA8C,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Public API Surface for @memberjunction/ng-agent-requests
3
+ *
4
+ * This package provides reusable Angular components for AI agent request management:
5
+ * - Panel component for viewing and responding to agent feedback requests
6
+ * - Dialog component for modal presentation of request details
7
+ */
8
+ // Module
9
+ export * from './lib/agent-requests.module';
10
+ // Panel components
11
+ export * from './lib/panels/agent-request-panel/agent-request-panel.component';
12
+ // Dialog components
13
+ export * from './lib/dialogs/agent-request-dialog.component';
14
+ //# sourceMappingURL=public-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public-api.js","sourceRoot":"","sources":["../src/public-api.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,SAAS;AACT,cAAc,6BAA6B,CAAC;AAE5C,mBAAmB;AACnB,cAAc,gEAAgE,CAAC;AAE/E,oBAAoB;AACpB,cAAc,8CAA8C,CAAC","sourcesContent":["/**\n * Public API Surface for @memberjunction/ng-agent-requests\n *\n * This package provides reusable Angular components for AI agent request management:\n * - Panel component for viewing and responding to agent feedback requests\n * - Dialog component for modal presentation of request details\n */\n\n// Module\nexport * from './lib/agent-requests.module';\n\n// Panel components\nexport * from './lib/panels/agent-request-panel/agent-request-panel.component';\n\n// Dialog components\nexport * from './lib/dialogs/agent-request-dialog.component';\n"]}
package/package.json CHANGED
@@ -1,10 +1,55 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-agent-requests",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @memberjunction/ng-agent-requests",
3
+ "version": "5.12.0",
4
+ "description": "MemberJunction: Angular components for AI agent request management - panels and dialogs for viewing and responding to agent feedback requests",
5
+ "main": "./dist/public-api.js",
6
+ "typings": "./dist/public-api.d.ts",
7
+ "files": [
8
+ "/dist"
9
+ ],
10
+ "scripts": {
11
+ "test": "echo \"No tests configured yet\"",
12
+ "build": "ngc",
13
+ "test:watch": "vitest"
14
+ },
5
15
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
16
+ "memberjunction",
17
+ "agent-requests",
18
+ "angular",
19
+ "ai",
20
+ "human-in-the-loop"
21
+ ],
22
+ "author": "",
23
+ "license": "ISC",
24
+ "devDependencies": {
25
+ "@angular/compiler": "21.1.3",
26
+ "@angular/compiler-cli": "21.1.3",
27
+ "vitest": "^4.0.18"
28
+ },
29
+ "peerDependencies": {
30
+ "@angular/common": "21.1.3",
31
+ "@angular/core": "21.1.3",
32
+ "@angular/forms": "21.1.3",
33
+ "@progress/kendo-angular-dialog": "22.0.1",
34
+ "@progress/kendo-angular-buttons": "22.0.1",
35
+ "@progress/kendo-angular-dropdowns": "22.0.1",
36
+ "@progress/kendo-angular-inputs": "22.0.1",
37
+ "@progress/kendo-angular-dateinputs": "22.0.1"
38
+ },
39
+ "dependencies": {
40
+ "@memberjunction/core": "5.12.0",
41
+ "@memberjunction/core-entities": "5.12.0",
42
+ "@memberjunction/global": "5.12.0",
43
+ "@memberjunction/ai-core-plus": "5.12.0",
44
+ "@memberjunction/ng-forms": "5.12.0",
45
+ "@memberjunction/ng-notifications": "5.12.0",
46
+ "@memberjunction/ng-shared-generic": "5.12.0",
47
+ "tslib": "^2.8.1",
48
+ "rxjs": "^7.8.2"
49
+ },
50
+ "sideEffects": false,
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/MemberJunction/MJ"
54
+ }
10
55
  }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @memberjunction/ng-agent-requests
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@memberjunction/ng-agent-requests`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**