@memberjunction/ng-agent-requests 0.0.1 → 5.13.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.
- package/dist/lib/agent-requests.module.d.ts +18 -0
- package/dist/lib/agent-requests.module.d.ts.map +1 -0
- package/dist/lib/agent-requests.module.js +72 -0
- package/dist/lib/agent-requests.module.js.map +1 -0
- package/dist/lib/dialogs/agent-request-dialog.component.d.ts +27 -0
- package/dist/lib/dialogs/agent-request-dialog.component.d.ts.map +1 -0
- package/dist/lib/dialogs/agent-request-dialog.component.js +117 -0
- package/dist/lib/dialogs/agent-request-dialog.component.js.map +1 -0
- package/dist/lib/panels/agent-request-panel/agent-request-panel.component.d.ts +140 -0
- package/dist/lib/panels/agent-request-panel/agent-request-panel.component.d.ts.map +1 -0
- package/dist/lib/panels/agent-request-panel/agent-request-panel.component.js +414 -0
- package/dist/lib/panels/agent-request-panel/agent-request-panel.component.js.map +1 -0
- package/dist/public-api.d.ts +11 -0
- package/dist/public-api.d.ts.map +1 -0
- package/dist/public-api.js +14 -0
- package/dist/public-api.js.map +1 -0
- package/package.json +51 -6
- package/README.md +0 -45
|
@@ -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":"AAAA;;;;;;GAMG;AAEH,OAAO,EACuB,YAAY,EAAE,iBAAiB,EAChC,MAAM,EAAE,SAAS,EAA2B,UAAU,EAClF,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAInG,OAAO,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;;AAOhE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC,gCAAgC;IAChC,MAAM,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,GAAG,WAAW,CAAC;CAC/D;AAED,qBAOa,0BAA2B,YAAW,MAAM,EAAE,SAAS;IAsC5D,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,mBAAmB;IAtCtB,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAC9C,YAAY,EAAE,0BAA0B,EAAE,CAAM;IAChD,MAAM,UAAS;IAEd,KAAK,wCAA+C;IAEpC,WAAW,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC7C,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,GAAG,SAAS,CAAC;IAEzE,SAAS,UAAS;IAClB,QAAQ,UAAS;IACjB,YAAY,SAAM;IAClB,sBAAsB,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IACxD,UAAU,SAAuB;IAExC,0FAA0F;IAC1F,OAAO,CAAC,eAAe,CAAsD;IAGtE,UAAU,UAAS;IAC1B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,iBAAiB,CAA0C;IACnE,OAAO,CAAC,gBAAgB,CAA6B;IAG9C,kBAAkB,UAAS;IAC3B,kBAAkB,SAAM;IACxB,YAAY,SAAM;IAClB,aAAa,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAM;IACvE,oBAAoB,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAQ;IAChF,gBAAgB,UAAS;IACzB,aAAa,UAAS;IAE7B,OAAO,CAAC,qBAAqB,CAA8C;gBAG/D,GAAG,EAAE,iBAAiB,EACtB,mBAAmB,EAAE,qBAAqB;IAGtD,QAAQ,IAAI,IAAI;IAOhB,WAAW,IAAI,IAAI;IAKZ,WAAW,IAAI,IAAI;IAM1B,sDAAsD;IACtD,IAAW,WAAW,IAAI,0BAA0B,GAAG,IAAI,CAG1D;IAED,oEAAoE;IACpE,IAAW,YAAY,IAAI,OAAO,CAEjC;IAED,6EAA6E;IAC7E,IAAW,cAAc,IAAI,OAAO,CAGnC;IAED,yDAAyD;IACzD,IAAW,eAAe,IAAI,OAAO,CAEpC;IAED,6BAA6B;IAC7B,IAAW,aAAa,IAAI,MAAM,CAOjC;IAED,yBAAyB;IACzB,IAAW,aAAa,IAAI,MAAM,CAOjC;IAED,qCAAqC;IACrC,IAAW,SAAS,IAAI,OAAO,CAG9B;IAED;;OAEG;IACI,IAAI,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI;IASlD;;OAEG;IACU,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC;;OAEG;IACU,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAItC;;OAEG;IACU,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAIvC;;OAEG;IACI,QAAQ,IAAI,IAAI;IAYhB,eAAe,IAAI,IAAI;IAI9B,+BAA+B;IACxB,eAAe,IAAI,IAAI;IAS9B,gCAAgC;IACzB,gBAAgB,IAAI,IAAI;IAK/B,kDAAkD;IAC3C,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAajD,4CAA4C;IACrC,oBAAoB,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKpF,8BAA8B;IACjB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;YAwChC,WAAW;IA2BzB,mEAAmE;IAC5D,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAc7C,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,cAAc;IAYtB;;OAEG;IACH,OAAO,CAAC,cAAc;IAetB;;;;OAIG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;YACW,cAAc;yCAhXnB,0BAA0B;2CAA1B,0BAA0B;CA0ZtC"}
|
|
@@ -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":"AAAA;;;;;;GAMG;AAEH,OAAO,EACH,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EACtC,uBAAuB,EAAqB,YAAY,EAAE,SAAS,EACtE,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;;;;;;AAKpD,MAAM,uBAAuB,GAAG,iCAAiC,CAAC;AAClE,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAChC,MAAM,eAAe,GAAG,GAAG,CAAC;AAC5B,MAAM,eAAe,GAAG,GAAG,CAAC;AAqB5B,MAAM,OAAO,0BAA0B;IAsCvB;IACA;IAtCH,OAAO,GAAkC,IAAI,CAAC;IAC9C,YAAY,GAAiC,EAAE,CAAC;IAChD,MAAM,GAAG,KAAK,CAAC;IAEd,KAAK,GAAG,IAAI,YAAY,EAA2B,CAAC;IAEpC,WAAW,CAAmC;IAC7C,YAAY,CAAyC;IAEzE,SAAS,GAAG,KAAK,CAAC;IAClB,QAAQ,GAAG,KAAK,CAAC;IACjB,YAAY,GAAG,EAAE,CAAC;IAClB,sBAAsB,GAA6B,IAAI,CAAC;IACxD,UAAU,GAAG,mBAAmB,CAAC;IAExC,0FAA0F;IAClF,eAAe,GAAiD,IAAI,CAAC;IAE7E,eAAe;IACR,UAAU,GAAG,KAAK,CAAC;IAClB,YAAY,GAAG,CAAC,CAAC;IACjB,gBAAgB,GAAG,CAAC,CAAC;IACrB,iBAAiB,GAAqC,IAAI,CAAC;IAC3D,gBAAgB,GAAwB,IAAI,CAAC;IAErD,iBAAiB;IACV,kBAAkB,GAAG,KAAK,CAAC;IAC3B,kBAAkB,GAAG,EAAE,CAAC;IACxB,YAAY,GAAG,EAAE,CAAC;IAClB,aAAa,GAAuD,EAAE,CAAC;IACvE,oBAAoB,GAAuD,IAAI,CAAC;IAChF,gBAAgB,GAAG,KAAK,CAAC;IACzB,aAAa,GAAG,KAAK,CAAC;IAErB,qBAAqB,GAAyC,IAAI,CAAC;IAE3E,YACY,GAAsB,EACtB,mBAA0C;QAD1C,QAAG,GAAH,GAAG,CAAmB;QACtB,wBAAmB,GAAnB,mBAAmB,CAAuB;IACnD,CAAC;IAEJ,QAAQ;QACJ,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1B,CAAC;IACL,CAAC;IAED,WAAW;QACP,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAClC,CAAC;IAGM,WAAW;QACd,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACd,IAAI,CAAC,QAAQ,EAAE,CAAC;QACpB,CAAC;IACL,CAAC;IAED,sDAAsD;IACtD,IAAW,WAAW;QAClB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa;YAAE,OAAO,IAAI,CAAC;QAC9C,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,OAAQ,CAAC,aAAc,CAAC,CAAC,IAAI,IAAI,CAAC;IAC/F,CAAC;IAED,oEAAoE;IACpE,IAAW,YAAY;QACnB,OAAO,IAAI,CAAC,OAAO,EAAE,MAAM,KAAK,WAAW,CAAC;IAChD,CAAC;IAED,6EAA6E;IAC7E,IAAW,cAAc;QACrB,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC;QACxC,OAAO,QAAQ,KAAK,UAAU,CAAC;IACnC,CAAC;IAED,yDAAyD;IACzD,IAAW,eAAe;QACtB,OAAO,IAAI,CAAC,sBAAsB,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC3G,CAAC;IAED,6BAA6B;IAC7B,IAAW,aAAa;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,KAAK,CAAC;QAC1B,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,QAAQ,CAAC;QAC7B,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,MAAM,CAAC;QAC3B,OAAO,UAAU,CAAC;IACtB,CAAC;IAED,yBAAyB;IACzB,IAAW,aAAa;QACpB,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,cAAc,CAAC;QACnC,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,iBAAiB,CAAC;QACtC,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,eAAe,CAAC;QACpC,OAAO,mBAAmB,CAAC;IAC/B,CAAC;IAED,qCAAqC;IACrC,IAAW,SAAS;QAChB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS;YAAE,OAAO,KAAK,CAAC;QAC3C,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,IAAI,EAAE,CAAC;IACzD,CAAC;IAED;;OAEG;IACI,IAAI,CAAC,OAA+B;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,SAAS;QAClB,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,QAAQ;QACjB,MAAM,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACtD,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,SAAS;QAClB,MAAM,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACxD,CAAC;IAED;;OAEG;IACI,QAAQ;QACX,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,oEAAoE;YACpE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,eAAe,EAAE,CAAC,CAAC;YACzF,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAChC,CAAC;aAAM,CAAC;YACJ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEM,eAAe;QAClB,IAAI,CAAC,QAAQ,EAAE,CAAC;IACpB,CAAC;IAED,+BAA+B;IACxB,eAAe;QAClB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;QAC7B,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QACxB,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAED,gCAAgC;IACzB,gBAAgB;QACnB,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;QAChC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAED,kDAAkD;IAC3C,sBAAsB,CAAC,IAAY;QACtC,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;QAC/B,IAAI,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC7B,YAAY,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC7C,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;YACxB,OAAO;QACX,CAAC;QACD,IAAI,CAAC,qBAAqB,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/E,CAAC;IAED,4CAA4C;IACrC,oBAAoB,CAAC,IAAiD;QACzE,IAAI,CAAC,oBAAoB,GAAG,IAAI,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAED,8BAA8B;IACvB,KAAK,CAAC,gBAAgB;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAE9E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAExB,IAAI,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC;YAE7D,uCAAuC;YACvC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,aAAa,GAAG,IAAI,SAAS,mBAAmB,IAAI,CAAC,oBAAoB,CAAC,IAAI,IAAI,IAAI,CAAC,oBAAoB,CAAC,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YACjL,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ;gBACzC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,KAAK,aAAa,EAAE;gBAC9C,CAAC,CAAC,aAAa,CAAC;YAEpB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,KAAK,EAAE,CAAC;gBACR,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAC7C,yBAAyB,IAAI,CAAC,oBAAoB,CAAC,IAAI,IAAI,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,CAChH,CAAC;gBACF,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC;gBAChC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;YACnF,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAC7C,+CAA+C,EAAE,OAAO,EAAE,IAAI,CACjE,CAAC;YACN,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAC7C,+BAA+B,EAAE,OAAO,EAAE,IAAI,CACjD,CAAC;QACN,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,IAAY;QAClC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,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,CAA8C;gBACzE,UAAU,EAAE,OAAO;gBACnB,WAAW,EAAE,gBAAgB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,sBAAsB,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK;gBACxG,OAAO,EAAE,MAAM;gBACf,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC;gBAC/B,UAAU,EAAE,QAAQ;aACvB,CAAC,CAAC;YACH,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACL,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QAC5B,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;YAC9B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,SAAS;IACT,2EAA2E;IAE3E,mEAAmE;IAC5D,aAAa,CAAC,KAAiB;QAClC,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;QAClC,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC;QAExC,IAAI,CAAC,iBAAiB,GAAG,CAAC,CAAa,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjE,IAAI,CAAC,gBAAgB,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjD,QAAQ,CAAC,gBAAgB,CAAC,WAAW,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC/D,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;QAC5D,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,YAAY,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC;IAC5C,CAAC;IAEO,YAAY,CAAC,KAAiB;QAClC,4EAA4E;QAC5E,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;QACjD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC;QACtG,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,WAAW;QACf,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC;QACxB,IAAI,CAAC,sBAAsB,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC;QACpC,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;IAC5B,CAAC;IAEO,sBAAsB;QAC1B,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzB,QAAQ,CAAC,mBAAmB,CAAC,WAAW,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAClE,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAClC,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;YAC/D,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QACjC,CAAC;IACL,CAAC;IAEO,cAAc;QAClB,IAAI,CAAC;YACD,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,uBAAuB,CAAC,CAAC;YAC1E,IAAI,KAAK,EAAE,CAAC;gBACR,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACnC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,eAAe,IAAI,MAAM,IAAI,eAAe,EAAE,CAAC;oBAC3E,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC;gBAC7B,CAAC;YACL,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,0DAA0D;QAC9D,CAAC;IACL,CAAC;IAEO,cAAc;QAClB,IAAI,CAAC;YACD,cAAc,CAAC,QAAQ,CAAC,mBAAmB,CAAC,uBAAuB,EAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAClG,CAAC;QAAC,MAAM,CAAC;YACL,gCAAgC;QACpC,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,gBAAgB;IAChB,2EAA2E;IAE3E;;OAEG;IACK,cAAc;QAClB,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;QAEnC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc;YAAE,OAAO;QAE1C,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAsB,CAAC;YAC5E,IAAI,MAAM,EAAE,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBACvD,IAAI,CAAC,sBAAsB,GAAG,MAAM,CAAC;YACzC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,gFAAgF;QACpF,CAAC;IACL,CAAC;IAED;;;;OAIG;IACK,eAAe;QACnB,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS;YAAE,OAAO,IAAI,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC;QAChD,IAAI,MAAM,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,OAAO,MAAM,CAAC;QAClB,CAAC;QACD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CACxB,MAA6C,EAC7C,MAA6C;QAE7C,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAE3C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAExB,IAAI,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;YAC7B,IAAI,CAAC,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,IAAI,IAAI,CAAC;YAClD,IAAI,CAAC,OAAO,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;YAEtC,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YACxC,IAAI,QAAQ,EAAE,CAAC;gBACX,IAAI,CAAC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;YACzD,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,KAAK,EAAE,CAAC;gBACR,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAC7C,0BAA0B,MAAM,gBAAgB,EAAE,SAAS,EAAE,IAAI,CACpE,CAAC;gBACF,gEAAgE;gBAChE,6EAA6E;gBAC7E,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;YAClC,CAAC;iBAAM,CAAC;gBACJ,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAC7C,4CAA4C,EAAE,OAAO,EAAE,IAAI,CAC9D,CAAC;YACN,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,IAAI,CAAC,mBAAmB,CAAC,wBAAwB,CAC7C,yDAAyD,EAAE,OAAO,EAAE,IAAI,CAC3E,CAAC;QACN,CAAC;gBAAS,CAAC;YACP,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;YACtB,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC;QAC5B,CAAC;IACL,CAAC;uGAzZQ,0BAA0B;2FAA1B,0BAA0B,gdC5CvC,0oZAwQA;;2FD5Na,0BAA0B;kBAPtC,SAAS;iCACM,KAAK,YACP,wBAAwB,mBAGjB,uBAAuB,CAAC,MAAM;;sBAG9C,KAAK;;sBACL,KAAK;;sBACL,KAAK;;sBAEL,MAAM;;sBAEN,SAAS;uBAAC,aAAa;;sBACvB,SAAS;uBAAC,cAAc;;sBA6CxB,YAAY;uBAAC,yBAAyB","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": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.13.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
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
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.13.0",
|
|
41
|
+
"@memberjunction/core-entities": "5.13.0",
|
|
42
|
+
"@memberjunction/global": "5.13.0",
|
|
43
|
+
"@memberjunction/ai-core-plus": "5.13.0",
|
|
44
|
+
"@memberjunction/ng-forms": "5.13.0",
|
|
45
|
+
"@memberjunction/ng-notifications": "5.13.0",
|
|
46
|
+
"@memberjunction/ng-shared-generic": "5.13.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**
|