@memberjunction/ng-explorer-core 5.35.0 → 5.37.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/generated/lazy-feature-config.d.ts +1 -1
- package/dist/generated/lazy-feature-config.d.ts.map +1 -1
- package/dist/generated/lazy-feature-config.js +5 -3
- package/dist/generated/lazy-feature-config.js.map +1 -1
- package/dist/lib/conversation-feedback/conversation-feedback.d.ts +108 -0
- package/dist/lib/conversation-feedback/conversation-feedback.d.ts.map +1 -0
- package/dist/lib/conversation-feedback/conversation-feedback.js +809 -0
- package/dist/lib/conversation-feedback/conversation-feedback.js.map +1 -0
- package/dist/lib/conversation-feedback/index.d.ts +2 -0
- package/dist/lib/conversation-feedback/index.d.ts.map +1 -0
- package/dist/lib/conversation-feedback/index.js +2 -0
- package/dist/lib/conversation-feedback/index.js.map +1 -0
- package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/dashboard-resource.component.js +23 -1
- package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.d.ts +12 -0
- package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.js +107 -15
- package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
- package/dist/lib/single-list-detail/single-list-detail.component.d.ts +153 -3
- package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
- package/dist/lib/single-list-detail/single-list-detail.component.js +1479 -271
- package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
- package/dist/module.d.ts +32 -29
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +19 -6
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +45 -42
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Component, ChangeDetectorRef, HostListener, inject } from '@angular/core';
|
|
8
|
+
import { RegisterClass } from '@memberjunction/global';
|
|
9
|
+
import { BaseResourceComponent } from '@memberjunction/ng-shared';
|
|
10
|
+
import { RunQuery } from '@memberjunction/core';
|
|
11
|
+
import { ApplicationManager } from '@memberjunction/ng-base-application';
|
|
12
|
+
import * as i0 from "@angular/core";
|
|
13
|
+
import * as i1 from "@angular/common";
|
|
14
|
+
import * as i2 from "@angular/forms";
|
|
15
|
+
import * as i3 from "@memberjunction/ng-ui-components";
|
|
16
|
+
import * as i4 from "@memberjunction/ng-pagination";
|
|
17
|
+
function ConversationFeedbackResource_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
19
|
+
i0.ɵɵelement(1, "div", 4);
|
|
20
|
+
i0.ɵɵtext(2, " Loading...");
|
|
21
|
+
i0.ɵɵelementEnd();
|
|
22
|
+
} }
|
|
23
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
24
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
25
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
26
|
+
i0.ɵɵelement(1, "i", 44);
|
|
27
|
+
i0.ɵɵtext(2);
|
|
28
|
+
i0.ɵɵelementStart(3, "button", 45);
|
|
29
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Conditional_14_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.error = null); });
|
|
30
|
+
i0.ɵɵtext(4, "\u00D7");
|
|
31
|
+
i0.ɵɵelementEnd()();
|
|
32
|
+
} if (rf & 2) {
|
|
33
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
34
|
+
i0.ɵɵadvance(2);
|
|
35
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.error, " ");
|
|
36
|
+
} }
|
|
37
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_85_Template(rf, ctx) { if (rf & 1) {
|
|
38
|
+
i0.ɵɵelementStart(0, "div", 1);
|
|
39
|
+
i0.ɵɵelement(1, "div", 4);
|
|
40
|
+
i0.ɵɵtext(2, " Loading feedback...");
|
|
41
|
+
i0.ɵɵelementEnd();
|
|
42
|
+
} }
|
|
43
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
44
|
+
i0.ɵɵelementStart(0, "div", 46);
|
|
45
|
+
i0.ɵɵelement(1, "i", 47);
|
|
46
|
+
i0.ɵɵelementStart(2, "p");
|
|
47
|
+
i0.ɵɵtext(3, "No feedback matches the current filters.");
|
|
48
|
+
i0.ɵɵelementEnd()();
|
|
49
|
+
} }
|
|
50
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
51
|
+
i0.ɵɵelementStart(0, "div", 66);
|
|
52
|
+
i0.ɵɵtext(1);
|
|
53
|
+
i0.ɵɵelementEnd();
|
|
54
|
+
} if (rf & 2) {
|
|
55
|
+
const row_r6 = i0.ɵɵnextContext().$implicit;
|
|
56
|
+
i0.ɵɵadvance();
|
|
57
|
+
i0.ɵɵtextInterpolate(row_r6.comments);
|
|
58
|
+
} }
|
|
59
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
60
|
+
i0.ɵɵelementStart(0, "span", 67);
|
|
61
|
+
i0.ɵɵtext(1, "(no comment)");
|
|
62
|
+
i0.ɵɵelementEnd();
|
|
63
|
+
} }
|
|
64
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
65
|
+
i0.ɵɵelementStart(0, "span", 69);
|
|
66
|
+
i0.ɵɵtext(1);
|
|
67
|
+
i0.ɵɵelementEnd();
|
|
68
|
+
} if (rf & 2) {
|
|
69
|
+
const row_r6 = i0.ɵɵnextContext().$implicit;
|
|
70
|
+
i0.ɵɵadvance();
|
|
71
|
+
i0.ɵɵtextInterpolate(row_r6.messageSnippet);
|
|
72
|
+
} }
|
|
73
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
74
|
+
i0.ɵɵelementStart(0, "span", 67);
|
|
75
|
+
i0.ɵɵtext(1, "\u2014");
|
|
76
|
+
i0.ɵɵelementEnd();
|
|
77
|
+
} }
|
|
78
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
79
|
+
i0.ɵɵelementStart(0, "div", 71);
|
|
80
|
+
i0.ɵɵtext(1);
|
|
81
|
+
i0.ɵɵelementEnd();
|
|
82
|
+
} if (rf & 2) {
|
|
83
|
+
const row_r6 = i0.ɵɵnextContext().$implicit;
|
|
84
|
+
i0.ɵɵadvance();
|
|
85
|
+
i0.ɵɵtextInterpolate(row_r6.raterEmail);
|
|
86
|
+
} }
|
|
87
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Template(rf, ctx) { if (rf & 1) {
|
|
88
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
89
|
+
i0.ɵɵelementStart(0, "tr", 64);
|
|
90
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Template_tr_click_0_listener() { const row_r6 = i0.ɵɵrestoreView(_r5).$implicit; const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.openDrawer(row_r6)); });
|
|
91
|
+
i0.ɵɵelementStart(1, "td", 50)(2, "span", 65);
|
|
92
|
+
i0.ɵɵtext(3);
|
|
93
|
+
i0.ɵɵelementStart(4, "small");
|
|
94
|
+
i0.ɵɵtext(5, "/10");
|
|
95
|
+
i0.ɵɵelementEnd()()();
|
|
96
|
+
i0.ɵɵelementStart(6, "td", 51);
|
|
97
|
+
i0.ɵɵconditionalCreate(7, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_7_Template, 2, 1, "div", 66)(8, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_8_Template, 2, 0, "span", 67);
|
|
98
|
+
i0.ɵɵelementEnd();
|
|
99
|
+
i0.ɵɵelementStart(9, "td", 52)(10, "span", 68);
|
|
100
|
+
i0.ɵɵtext(11);
|
|
101
|
+
i0.ɵɵelementEnd()();
|
|
102
|
+
i0.ɵɵelementStart(12, "td", 53);
|
|
103
|
+
i0.ɵɵconditionalCreate(13, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_13_Template, 2, 1, "span", 69)(14, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_14_Template, 2, 0, "span", 67);
|
|
104
|
+
i0.ɵɵelementEnd();
|
|
105
|
+
i0.ɵɵelementStart(15, "td", 54)(16, "div", 70);
|
|
106
|
+
i0.ɵɵtext(17);
|
|
107
|
+
i0.ɵɵelementEnd();
|
|
108
|
+
i0.ɵɵconditionalCreate(18, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Conditional_18_Template, 2, 1, "div", 71);
|
|
109
|
+
i0.ɵɵelementEnd();
|
|
110
|
+
i0.ɵɵelementStart(19, "td", 55)(20, "span", 72);
|
|
111
|
+
i0.ɵɵtext(21);
|
|
112
|
+
i0.ɵɵpipe(22, "date");
|
|
113
|
+
i0.ɵɵelementEnd()()();
|
|
114
|
+
} if (rf & 2) {
|
|
115
|
+
const row_r6 = ctx.$implicit;
|
|
116
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
117
|
+
i0.ɵɵclassProp("expanded", (ctx_r1.selectedRow == null ? null : ctx_r1.selectedRow.ratingID) === row_r6.ratingID);
|
|
118
|
+
i0.ɵɵadvance(2);
|
|
119
|
+
i0.ɵɵproperty("ngClass", ctx_r1.bandClassFor(row_r6.rating));
|
|
120
|
+
i0.ɵɵadvance();
|
|
121
|
+
i0.ɵɵtextInterpolate1(" ", row_r6.rating);
|
|
122
|
+
i0.ɵɵadvance(4);
|
|
123
|
+
i0.ɵɵconditional(row_r6.comments ? 7 : 8);
|
|
124
|
+
i0.ɵɵadvance(4);
|
|
125
|
+
i0.ɵɵtextInterpolate(row_r6.agentName || "\u2014");
|
|
126
|
+
i0.ɵɵadvance(2);
|
|
127
|
+
i0.ɵɵconditional(row_r6.messageSnippet ? 13 : 14);
|
|
128
|
+
i0.ɵɵadvance(4);
|
|
129
|
+
i0.ɵɵtextInterpolate(row_r6.raterName || row_r6.raterEmail || "\u2014");
|
|
130
|
+
i0.ɵɵadvance();
|
|
131
|
+
i0.ɵɵconditional(row_r6.raterEmail && row_r6.raterName ? 18 : -1);
|
|
132
|
+
i0.ɵɵadvance(3);
|
|
133
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(22, 10, row_r6.createdAt, "short"));
|
|
134
|
+
} }
|
|
135
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
136
|
+
i0.ɵɵelementStart(0, "span", 58);
|
|
137
|
+
i0.ɵɵtext(1);
|
|
138
|
+
i0.ɵɵpipe(2, "number");
|
|
139
|
+
i0.ɵɵpipe(3, "number");
|
|
140
|
+
i0.ɵɵpipe(4, "number");
|
|
141
|
+
i0.ɵɵelementEnd();
|
|
142
|
+
} if (rf & 2) {
|
|
143
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
144
|
+
i0.ɵɵadvance();
|
|
145
|
+
i0.ɵɵtextInterpolate4(" ", i0.ɵɵpipeBind1(2, 4, ctx_r1.rangeStart), "-", i0.ɵɵpipeBind1(3, 6, ctx_r1.rangeEnd), " of ", i0.ɵɵpipeBind1(4, 8, ctx_r1.totalRowCount), " ", ctx_r1.totalRowCount === 1 ? "rating" : "ratings", " ");
|
|
146
|
+
} }
|
|
147
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
148
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
149
|
+
i0.ɵɵelementStart(0, "mj-pagination", 73);
|
|
150
|
+
i0.ɵɵlistener("PageChange", function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Conditional_21_Template_mj_pagination_PageChange_0_listener($event) { i0.ɵɵrestoreView(_r7); const ctx_r1 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r1.onPageChange($event)); });
|
|
151
|
+
i0.ɵɵelementEnd();
|
|
152
|
+
} if (rf & 2) {
|
|
153
|
+
const ctx_r1 = i0.ɵɵnextContext(4);
|
|
154
|
+
i0.ɵɵproperty("TotalRowCount", ctx_r1.totalRowCount)("PageNumber", ctx_r1.currentPage)("PageSize", ctx_r1.pageSize)("IsLoading", ctx_r1.loading);
|
|
155
|
+
} }
|
|
156
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_27_Template(rf, ctx) { if (rf & 1) {
|
|
157
|
+
i0.ɵɵelementStart(0, "option", 63);
|
|
158
|
+
i0.ɵɵtext(1);
|
|
159
|
+
i0.ɵɵelementEnd();
|
|
160
|
+
} if (rf & 2) {
|
|
161
|
+
const size_r8 = ctx.$implicit;
|
|
162
|
+
i0.ɵɵproperty("ngValue", size_r8);
|
|
163
|
+
i0.ɵɵadvance();
|
|
164
|
+
i0.ɵɵtextInterpolate(size_r8);
|
|
165
|
+
} }
|
|
166
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
167
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
168
|
+
i0.ɵɵelementStart(0, "div", 48)(1, "table", 49)(2, "thead")(3, "tr")(4, "th", 50);
|
|
169
|
+
i0.ɵɵtext(5, "Rating");
|
|
170
|
+
i0.ɵɵelementEnd();
|
|
171
|
+
i0.ɵɵelementStart(6, "th", 51);
|
|
172
|
+
i0.ɵɵtext(7, "Comments");
|
|
173
|
+
i0.ɵɵelementEnd();
|
|
174
|
+
i0.ɵɵelementStart(8, "th", 52);
|
|
175
|
+
i0.ɵɵtext(9, "Agent");
|
|
176
|
+
i0.ɵɵelementEnd();
|
|
177
|
+
i0.ɵɵelementStart(10, "th", 53);
|
|
178
|
+
i0.ɵɵtext(11, "Message");
|
|
179
|
+
i0.ɵɵelementEnd();
|
|
180
|
+
i0.ɵɵelementStart(12, "th", 54);
|
|
181
|
+
i0.ɵɵtext(13, "Rater");
|
|
182
|
+
i0.ɵɵelementEnd();
|
|
183
|
+
i0.ɵɵelementStart(14, "th", 55);
|
|
184
|
+
i0.ɵɵtext(15, "When");
|
|
185
|
+
i0.ɵɵelementEnd()()();
|
|
186
|
+
i0.ɵɵelementStart(16, "tbody");
|
|
187
|
+
i0.ɵɵrepeaterCreate(17, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_18_Template, 23, 13, "tr", 56, i0.ɵɵcomponentInstance().trackByRatingID, true);
|
|
188
|
+
i0.ɵɵelementEnd()()();
|
|
189
|
+
i0.ɵɵelementStart(19, "div", 57);
|
|
190
|
+
i0.ɵɵconditionalCreate(20, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Conditional_20_Template, 5, 10, "span", 58)(21, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Conditional_21_Template, 1, 4, "mj-pagination", 59);
|
|
191
|
+
i0.ɵɵelementStart(22, "label", 60)(23, "span", 61);
|
|
192
|
+
i0.ɵɵtext(24, "Rows");
|
|
193
|
+
i0.ɵɵelementEnd();
|
|
194
|
+
i0.ɵɵelementStart(25, "select", 62);
|
|
195
|
+
i0.ɵɵlistener("ngModelChange", function ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Template_select_ngModelChange_25_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.onPageSizeChange($event)); });
|
|
196
|
+
i0.ɵɵrepeaterCreate(26, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_For_27_Template, 2, 2, "option", 63, i0.ɵɵrepeaterTrackByIdentity);
|
|
197
|
+
i0.ɵɵelementEnd()()();
|
|
198
|
+
} if (rf & 2) {
|
|
199
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
200
|
+
i0.ɵɵadvance(17);
|
|
201
|
+
i0.ɵɵrepeater(ctx_r1.rows);
|
|
202
|
+
i0.ɵɵadvance(3);
|
|
203
|
+
i0.ɵɵconditional(ctx_r1.isSinglePage ? 20 : 21);
|
|
204
|
+
i0.ɵɵadvance(5);
|
|
205
|
+
i0.ɵɵproperty("ngModel", ctx_r1.pageSize);
|
|
206
|
+
i0.ɵɵadvance();
|
|
207
|
+
i0.ɵɵrepeater(ctx_r1.pageSizeOptions);
|
|
208
|
+
} }
|
|
209
|
+
function ConversationFeedbackResource_Conditional_2_Conditional_86_Template(rf, ctx) { if (rf & 1) {
|
|
210
|
+
i0.ɵɵconditionalCreate(0, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_0_Template, 4, 0, "div", 46)(1, ConversationFeedbackResource_Conditional_2_Conditional_86_Conditional_1_Template, 28, 2);
|
|
211
|
+
} if (rf & 2) {
|
|
212
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
213
|
+
i0.ɵɵconditional(ctx_r1.rows.length === 0 ? 0 : 1);
|
|
214
|
+
} }
|
|
215
|
+
function ConversationFeedbackResource_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
216
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
217
|
+
i0.ɵɵelementStart(0, "div", 5)(1, "div", 6)(2, "div", 7)(3, "div", 8);
|
|
218
|
+
i0.ɵɵelement(4, "i", 9);
|
|
219
|
+
i0.ɵɵelementEnd();
|
|
220
|
+
i0.ɵɵelementStart(5, "div")(6, "h1", 10);
|
|
221
|
+
i0.ɵɵtext(7, "Agent Feedback");
|
|
222
|
+
i0.ɵɵelementEnd();
|
|
223
|
+
i0.ɵɵelementStart(8, "p", 11);
|
|
224
|
+
i0.ɵɵtext(9, "User ratings and comments on AI messages");
|
|
225
|
+
i0.ɵɵelementEnd()()();
|
|
226
|
+
i0.ɵɵelementStart(10, "div", 12)(11, "button", 13);
|
|
227
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.loadFeedback()); });
|
|
228
|
+
i0.ɵɵelement(12, "i", 14);
|
|
229
|
+
i0.ɵɵtext(13, " Refresh ");
|
|
230
|
+
i0.ɵɵelementEnd()()();
|
|
231
|
+
i0.ɵɵconditionalCreate(14, ConversationFeedbackResource_Conditional_2_Conditional_14_Template, 5, 1, "div", 15);
|
|
232
|
+
i0.ɵɵelementStart(15, "div", 16)(16, "div", 17)(17, "div", 18);
|
|
233
|
+
i0.ɵɵelement(18, "i", 9);
|
|
234
|
+
i0.ɵɵelementEnd();
|
|
235
|
+
i0.ɵɵelementStart(19, "div", 19)(20, "div", 20);
|
|
236
|
+
i0.ɵɵtext(21);
|
|
237
|
+
i0.ɵɵelementEnd();
|
|
238
|
+
i0.ɵɵelementStart(22, "div", 21);
|
|
239
|
+
i0.ɵɵtext(23, "Total Ratings");
|
|
240
|
+
i0.ɵɵelementEnd()()();
|
|
241
|
+
i0.ɵɵelementStart(24, "div", 17)(25, "div", 22);
|
|
242
|
+
i0.ɵɵelement(26, "i", 23);
|
|
243
|
+
i0.ɵɵelementEnd();
|
|
244
|
+
i0.ɵɵelementStart(27, "div", 19)(28, "div", 20);
|
|
245
|
+
i0.ɵɵtext(29);
|
|
246
|
+
i0.ɵɵelementStart(30, "span", 24);
|
|
247
|
+
i0.ɵɵtext(31, "/10");
|
|
248
|
+
i0.ɵɵelementEnd()();
|
|
249
|
+
i0.ɵɵelementStart(32, "div", 21);
|
|
250
|
+
i0.ɵɵtext(33, "Avg Rating");
|
|
251
|
+
i0.ɵɵelementEnd()()();
|
|
252
|
+
i0.ɵɵelementStart(34, "div", 17)(35, "div", 25);
|
|
253
|
+
i0.ɵɵelement(36, "i", 26);
|
|
254
|
+
i0.ɵɵelementEnd();
|
|
255
|
+
i0.ɵɵelementStart(37, "div", 19)(38, "div", 27);
|
|
256
|
+
i0.ɵɵtext(39);
|
|
257
|
+
i0.ɵɵelementEnd();
|
|
258
|
+
i0.ɵɵelementStart(40, "div", 21);
|
|
259
|
+
i0.ɵɵtext(41, "Positive (8-10)");
|
|
260
|
+
i0.ɵɵelementEnd()()();
|
|
261
|
+
i0.ɵɵelementStart(42, "div", 17)(43, "div", 28);
|
|
262
|
+
i0.ɵɵelement(44, "i", 29);
|
|
263
|
+
i0.ɵɵelementEnd();
|
|
264
|
+
i0.ɵɵelementStart(45, "div", 19)(46, "div", 30);
|
|
265
|
+
i0.ɵɵtext(47);
|
|
266
|
+
i0.ɵɵelementEnd();
|
|
267
|
+
i0.ɵɵelementStart(48, "div", 21);
|
|
268
|
+
i0.ɵɵtext(49, "Negative (1-3)");
|
|
269
|
+
i0.ɵɵelementEnd()()()();
|
|
270
|
+
i0.ɵɵelementStart(50, "div", 31)(51, "div", 32)(52, "div", 33)(53, "span", 34);
|
|
271
|
+
i0.ɵɵtext(54, "Date Range");
|
|
272
|
+
i0.ɵɵelementEnd();
|
|
273
|
+
i0.ɵɵelementStart(55, "div", 35)(56, "button", 36);
|
|
274
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_56_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setDateRange("1d")); });
|
|
275
|
+
i0.ɵɵtext(57, "1d");
|
|
276
|
+
i0.ɵɵelementEnd();
|
|
277
|
+
i0.ɵɵelementStart(58, "button", 36);
|
|
278
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_58_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setDateRange("7d")); });
|
|
279
|
+
i0.ɵɵtext(59, "7d");
|
|
280
|
+
i0.ɵɵelementEnd();
|
|
281
|
+
i0.ɵɵelementStart(60, "button", 36);
|
|
282
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_60_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setDateRange("30d")); });
|
|
283
|
+
i0.ɵɵtext(61, "30d");
|
|
284
|
+
i0.ɵɵelementEnd();
|
|
285
|
+
i0.ɵɵelementStart(62, "button", 36);
|
|
286
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_62_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setDateRange("90d")); });
|
|
287
|
+
i0.ɵɵtext(63, "90d");
|
|
288
|
+
i0.ɵɵelementEnd();
|
|
289
|
+
i0.ɵɵelementStart(64, "button", 36);
|
|
290
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_64_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setDateRange("all")); });
|
|
291
|
+
i0.ɵɵtext(65, "All");
|
|
292
|
+
i0.ɵɵelementEnd()()();
|
|
293
|
+
i0.ɵɵelementStart(66, "div", 33)(67, "span", 34);
|
|
294
|
+
i0.ɵɵtext(68, "Rating Band");
|
|
295
|
+
i0.ɵɵelementEnd();
|
|
296
|
+
i0.ɵɵelementStart(69, "div", 35)(70, "button", 36);
|
|
297
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_70_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setBand("all")); });
|
|
298
|
+
i0.ɵɵtext(71, "All");
|
|
299
|
+
i0.ɵɵelementEnd();
|
|
300
|
+
i0.ɵɵelementStart(72, "button", 37);
|
|
301
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_72_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setBand("high")); });
|
|
302
|
+
i0.ɵɵtext(73, "High 8-10");
|
|
303
|
+
i0.ɵɵelementEnd();
|
|
304
|
+
i0.ɵɵelementStart(74, "button", 38);
|
|
305
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_74_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setBand("mid")); });
|
|
306
|
+
i0.ɵɵtext(75, "Mid 4-7");
|
|
307
|
+
i0.ɵɵelementEnd();
|
|
308
|
+
i0.ɵɵelementStart(76, "button", 39);
|
|
309
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_2_Template_button_click_76_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.setBand("low")); });
|
|
310
|
+
i0.ɵɵtext(77, "Low 1-3");
|
|
311
|
+
i0.ɵɵelementEnd()()();
|
|
312
|
+
i0.ɵɵelementStart(78, "div", 40)(79, "span", 34);
|
|
313
|
+
i0.ɵɵtext(80, "Search");
|
|
314
|
+
i0.ɵɵelementEnd();
|
|
315
|
+
i0.ɵɵelementStart(81, "input", 41);
|
|
316
|
+
i0.ɵɵtwoWayListener("ngModelChange", function ConversationFeedbackResource_Conditional_2_Template_input_ngModelChange_81_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.searchTerm, $event) || (ctx_r1.searchTerm = $event); return i0.ɵɵresetView($event); });
|
|
317
|
+
i0.ɵɵlistener("input", function ConversationFeedbackResource_Conditional_2_Template_input_input_81_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onSearchChange()); });
|
|
318
|
+
i0.ɵɵelementEnd()()();
|
|
319
|
+
i0.ɵɵelementStart(82, "div", 42);
|
|
320
|
+
i0.ɵɵtext(83);
|
|
321
|
+
i0.ɵɵelementEnd()()();
|
|
322
|
+
i0.ɵɵelementStart(84, "div", 43);
|
|
323
|
+
i0.ɵɵconditionalCreate(85, ConversationFeedbackResource_Conditional_2_Conditional_85_Template, 3, 0, "div", 1);
|
|
324
|
+
i0.ɵɵconditionalCreate(86, ConversationFeedbackResource_Conditional_2_Conditional_86_Template, 2, 1);
|
|
325
|
+
i0.ɵɵelementEnd();
|
|
326
|
+
} if (rf & 2) {
|
|
327
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
328
|
+
i0.ɵɵadvance(11);
|
|
329
|
+
i0.ɵɵproperty("disabled", ctx_r1.loading);
|
|
330
|
+
i0.ɵɵadvance();
|
|
331
|
+
i0.ɵɵclassProp("fa-spin", ctx_r1.loading);
|
|
332
|
+
i0.ɵɵadvance(2);
|
|
333
|
+
i0.ɵɵconditional(ctx_r1.error ? 14 : -1);
|
|
334
|
+
i0.ɵɵadvance(7);
|
|
335
|
+
i0.ɵɵtextInterpolate(ctx_r1.stats.total);
|
|
336
|
+
i0.ɵɵadvance(8);
|
|
337
|
+
i0.ɵɵtextInterpolate(ctx_r1.stats.avgRating);
|
|
338
|
+
i0.ɵɵadvance(10);
|
|
339
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.stats.percentPositive, "%");
|
|
340
|
+
i0.ɵɵadvance(8);
|
|
341
|
+
i0.ɵɵtextInterpolate1("", ctx_r1.stats.percentNegative, "%");
|
|
342
|
+
i0.ɵɵadvance(9);
|
|
343
|
+
i0.ɵɵclassProp("active", ctx_r1.dateRange === "1d");
|
|
344
|
+
i0.ɵɵadvance(2);
|
|
345
|
+
i0.ɵɵclassProp("active", ctx_r1.dateRange === "7d");
|
|
346
|
+
i0.ɵɵadvance(2);
|
|
347
|
+
i0.ɵɵclassProp("active", ctx_r1.dateRange === "30d");
|
|
348
|
+
i0.ɵɵadvance(2);
|
|
349
|
+
i0.ɵɵclassProp("active", ctx_r1.dateRange === "90d");
|
|
350
|
+
i0.ɵɵadvance(2);
|
|
351
|
+
i0.ɵɵclassProp("active", ctx_r1.dateRange === "all");
|
|
352
|
+
i0.ɵɵadvance(6);
|
|
353
|
+
i0.ɵɵclassProp("active", ctx_r1.ratingBand === "all");
|
|
354
|
+
i0.ɵɵadvance(2);
|
|
355
|
+
i0.ɵɵclassProp("active", ctx_r1.ratingBand === "high");
|
|
356
|
+
i0.ɵɵadvance(2);
|
|
357
|
+
i0.ɵɵclassProp("active", ctx_r1.ratingBand === "mid");
|
|
358
|
+
i0.ɵɵadvance(2);
|
|
359
|
+
i0.ɵɵclassProp("active", ctx_r1.ratingBand === "low");
|
|
360
|
+
i0.ɵɵadvance(5);
|
|
361
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r1.searchTerm);
|
|
362
|
+
i0.ɵɵadvance(2);
|
|
363
|
+
i0.ɵɵtextInterpolate2(" ", ctx_r1.totalRowCount, " ", ctx_r1.totalRowCount === 1 ? "rating" : "ratings", " match the current filters ");
|
|
364
|
+
i0.ɵɵadvance(2);
|
|
365
|
+
i0.ɵɵconditional(ctx_r1.loading ? 85 : -1);
|
|
366
|
+
i0.ɵɵadvance();
|
|
367
|
+
i0.ɵɵconditional(!ctx_r1.loading ? 86 : -1);
|
|
368
|
+
} }
|
|
369
|
+
function ConversationFeedbackResource_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
370
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
371
|
+
i0.ɵɵelementStart(0, "div", 74);
|
|
372
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_3_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r9); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeDrawer()); });
|
|
373
|
+
i0.ɵɵelementEnd();
|
|
374
|
+
} }
|
|
375
|
+
function ConversationFeedbackResource_Conditional_5_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
376
|
+
i0.ɵɵelementStart(0, "div", 85);
|
|
377
|
+
i0.ɵɵtext(1);
|
|
378
|
+
i0.ɵɵelementEnd();
|
|
379
|
+
} if (rf & 2) {
|
|
380
|
+
const r_r11 = i0.ɵɵnextContext();
|
|
381
|
+
i0.ɵɵadvance();
|
|
382
|
+
i0.ɵɵtextInterpolate(r_r11.comments);
|
|
383
|
+
} }
|
|
384
|
+
function ConversationFeedbackResource_Conditional_5_Conditional_24_Template(rf, ctx) { if (rf & 1) {
|
|
385
|
+
i0.ɵɵelementStart(0, "div", 86);
|
|
386
|
+
i0.ɵɵtext(1, "(no comment was provided)");
|
|
387
|
+
i0.ɵɵelementEnd();
|
|
388
|
+
} }
|
|
389
|
+
function ConversationFeedbackResource_Conditional_5_Conditional_48_Template(rf, ctx) { if (rf & 1) {
|
|
390
|
+
i0.ɵɵtext(0);
|
|
391
|
+
} if (rf & 2) {
|
|
392
|
+
const r_r11 = i0.ɵɵnextContext();
|
|
393
|
+
i0.ɵɵtextInterpolate1(" ", r_r11.messageText, " ");
|
|
394
|
+
} }
|
|
395
|
+
function ConversationFeedbackResource_Conditional_5_Conditional_49_Template(rf, ctx) { if (rf & 1) {
|
|
396
|
+
i0.ɵɵelementStart(0, "span", 67);
|
|
397
|
+
i0.ɵɵtext(1, "(message text unavailable)");
|
|
398
|
+
i0.ɵɵelementEnd();
|
|
399
|
+
} }
|
|
400
|
+
function ConversationFeedbackResource_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
401
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
402
|
+
i0.ɵɵelementStart(0, "div", 75)(1, "div", 76);
|
|
403
|
+
i0.ɵɵelement(2, "i", 9);
|
|
404
|
+
i0.ɵɵelementStart(3, "div", 77)(4, "h2", 78);
|
|
405
|
+
i0.ɵɵtext(5);
|
|
406
|
+
i0.ɵɵelementEnd();
|
|
407
|
+
i0.ɵɵelementStart(6, "div", 79)(7, "span", 65);
|
|
408
|
+
i0.ɵɵtext(8);
|
|
409
|
+
i0.ɵɵelementStart(9, "small");
|
|
410
|
+
i0.ɵɵtext(10, "/10");
|
|
411
|
+
i0.ɵɵelementEnd()();
|
|
412
|
+
i0.ɵɵelementStart(11, "span");
|
|
413
|
+
i0.ɵɵtext(12, " Rated by ");
|
|
414
|
+
i0.ɵɵelementStart(13, "strong");
|
|
415
|
+
i0.ɵɵtext(14);
|
|
416
|
+
i0.ɵɵelementEnd();
|
|
417
|
+
i0.ɵɵtext(15);
|
|
418
|
+
i0.ɵɵpipe(16, "date");
|
|
419
|
+
i0.ɵɵelementEnd()()()();
|
|
420
|
+
i0.ɵɵelementStart(17, "button", 80);
|
|
421
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_5_Template_button_click_17_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeDrawer()); });
|
|
422
|
+
i0.ɵɵelement(18, "i", 81);
|
|
423
|
+
i0.ɵɵelementEnd()();
|
|
424
|
+
i0.ɵɵelementStart(19, "div", 82)(20, "div", 83)(21, "div", 84);
|
|
425
|
+
i0.ɵɵtext(22, "Reviewer Comment");
|
|
426
|
+
i0.ɵɵelementEnd();
|
|
427
|
+
i0.ɵɵconditionalCreate(23, ConversationFeedbackResource_Conditional_5_Conditional_23_Template, 2, 1, "div", 85)(24, ConversationFeedbackResource_Conditional_5_Conditional_24_Template, 2, 0, "div", 86);
|
|
428
|
+
i0.ɵɵelementEnd();
|
|
429
|
+
i0.ɵɵelementStart(25, "div", 83)(26, "div", 87)(27, "div", 88)(28, "div", 89);
|
|
430
|
+
i0.ɵɵtext(29, "Agent");
|
|
431
|
+
i0.ɵɵelementEnd();
|
|
432
|
+
i0.ɵɵelementStart(30, "div", 90);
|
|
433
|
+
i0.ɵɵtext(31);
|
|
434
|
+
i0.ɵɵelementEnd()();
|
|
435
|
+
i0.ɵɵelementStart(32, "div", 88)(33, "div", 89);
|
|
436
|
+
i0.ɵɵtext(34, "Conversation");
|
|
437
|
+
i0.ɵɵelementEnd();
|
|
438
|
+
i0.ɵɵelementStart(35, "div", 90);
|
|
439
|
+
i0.ɵɵtext(36);
|
|
440
|
+
i0.ɵɵelementEnd()();
|
|
441
|
+
i0.ɵɵelementStart(37, "div", 88)(38, "div", 89);
|
|
442
|
+
i0.ɵɵtext(39, "Message Role");
|
|
443
|
+
i0.ɵɵelementEnd();
|
|
444
|
+
i0.ɵɵelementStart(40, "div", 90);
|
|
445
|
+
i0.ɵɵtext(41);
|
|
446
|
+
i0.ɵɵelementEnd()()()();
|
|
447
|
+
i0.ɵɵelementStart(42, "div", 83)(43, "div", 91)(44, "div", 92);
|
|
448
|
+
i0.ɵɵelement(45, "i", 93);
|
|
449
|
+
i0.ɵɵtext(46, " Rated Message ");
|
|
450
|
+
i0.ɵɵelementEnd();
|
|
451
|
+
i0.ɵɵelementStart(47, "div", 94);
|
|
452
|
+
i0.ɵɵconditionalCreate(48, ConversationFeedbackResource_Conditional_5_Conditional_48_Template, 1, 1)(49, ConversationFeedbackResource_Conditional_5_Conditional_49_Template, 2, 0, "span", 67);
|
|
453
|
+
i0.ɵɵelementEnd()()()();
|
|
454
|
+
i0.ɵɵelementStart(50, "div", 95)(51, "button", 96);
|
|
455
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_5_Template_button_click_51_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.closeDrawer()); });
|
|
456
|
+
i0.ɵɵtext(52, "Close");
|
|
457
|
+
i0.ɵɵelementEnd();
|
|
458
|
+
i0.ɵɵelementStart(53, "button", 97);
|
|
459
|
+
i0.ɵɵlistener("click", function ConversationFeedbackResource_Conditional_5_Template_button_click_53_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.openConversation()); });
|
|
460
|
+
i0.ɵɵelement(54, "i", 98);
|
|
461
|
+
i0.ɵɵtext(55, " Open Conversation ");
|
|
462
|
+
i0.ɵɵelementEnd()();
|
|
463
|
+
} if (rf & 2) {
|
|
464
|
+
const r_r11 = ctx;
|
|
465
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
466
|
+
i0.ɵɵadvance(5);
|
|
467
|
+
i0.ɵɵtextInterpolate1(" Feedback on ", r_r11.agentName || "AI message", " ");
|
|
468
|
+
i0.ɵɵadvance(2);
|
|
469
|
+
i0.ɵɵproperty("ngClass", ctx_r1.bandClassFor(r_r11.rating));
|
|
470
|
+
i0.ɵɵadvance();
|
|
471
|
+
i0.ɵɵtextInterpolate1(" ", r_r11.rating);
|
|
472
|
+
i0.ɵɵadvance(6);
|
|
473
|
+
i0.ɵɵtextInterpolate(r_r11.raterName || r_r11.raterEmail || "Unknown user");
|
|
474
|
+
i0.ɵɵadvance();
|
|
475
|
+
i0.ɵɵtextInterpolate1(" \u00B7 ", i0.ɵɵpipeBind2(16, 12, r_r11.createdAt, "medium"), " ");
|
|
476
|
+
i0.ɵɵadvance(8);
|
|
477
|
+
i0.ɵɵconditional(r_r11.comments ? 23 : 24);
|
|
478
|
+
i0.ɵɵadvance(8);
|
|
479
|
+
i0.ɵɵtextInterpolate(r_r11.agentName || "\u2014");
|
|
480
|
+
i0.ɵɵadvance(5);
|
|
481
|
+
i0.ɵɵtextInterpolate(r_r11.conversationName || "\u2014");
|
|
482
|
+
i0.ɵɵadvance(5);
|
|
483
|
+
i0.ɵɵtextInterpolate(r_r11.messageRole || "\u2014");
|
|
484
|
+
i0.ɵɵadvance(7);
|
|
485
|
+
i0.ɵɵconditional(r_r11.messageText ? 48 : 49);
|
|
486
|
+
i0.ɵɵadvance(5);
|
|
487
|
+
i0.ɵɵproperty("disabled", !r_r11.conversationID)("title", r_r11.conversationID ? "Open the source conversation" : "No conversation linked");
|
|
488
|
+
} }
|
|
489
|
+
let ConversationFeedbackResource = class ConversationFeedbackResource extends BaseResourceComponent {
|
|
490
|
+
isInitializing = true;
|
|
491
|
+
loading = false;
|
|
492
|
+
statsLoading = false;
|
|
493
|
+
error = null;
|
|
494
|
+
ratingBand = 'all';
|
|
495
|
+
dateRange = '7d';
|
|
496
|
+
searchTerm = '';
|
|
497
|
+
rows = [];
|
|
498
|
+
totalRowCount = 0;
|
|
499
|
+
pageSize = 10;
|
|
500
|
+
currentPage = 1;
|
|
501
|
+
pageSizeOptions = [10, 25, 50, 100];
|
|
502
|
+
stats = { total: 0, avgRating: 0, percentPositive: 0, percentNegative: 0 };
|
|
503
|
+
selectedRow = null;
|
|
504
|
+
searchDebounceTimer = null;
|
|
505
|
+
cdr = inject(ChangeDetectorRef);
|
|
506
|
+
appManager = inject(ApplicationManager);
|
|
507
|
+
async GetResourceDisplayName() {
|
|
508
|
+
return 'Agent Feedback';
|
|
509
|
+
}
|
|
510
|
+
async GetResourceIconClass() {
|
|
511
|
+
return 'fa-solid fa-comment-dots';
|
|
512
|
+
}
|
|
513
|
+
async ngOnInit() {
|
|
514
|
+
this.isInitializing = false;
|
|
515
|
+
this.cdr.detectChanges();
|
|
516
|
+
await this.applyFilters();
|
|
517
|
+
this.NotifyLoadComplete();
|
|
518
|
+
}
|
|
519
|
+
/** Manual refresh from the page header — same path as a filter change. */
|
|
520
|
+
async loadFeedback() {
|
|
521
|
+
await this.applyFilters();
|
|
522
|
+
}
|
|
523
|
+
setBand(band) {
|
|
524
|
+
if (this.ratingBand === band)
|
|
525
|
+
return;
|
|
526
|
+
this.ratingBand = band;
|
|
527
|
+
this.currentPage = 1;
|
|
528
|
+
void this.applyFilters();
|
|
529
|
+
}
|
|
530
|
+
setDateRange(range) {
|
|
531
|
+
if (this.dateRange === range)
|
|
532
|
+
return;
|
|
533
|
+
this.dateRange = range;
|
|
534
|
+
this.currentPage = 1;
|
|
535
|
+
void this.applyFilters();
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Search input fires on every keystroke; debounce so we don't slam the
|
|
539
|
+
* server while the user is typing. 250ms is enough to feel responsive
|
|
540
|
+
* without firing on intermediate states.
|
|
541
|
+
*/
|
|
542
|
+
onSearchChange() {
|
|
543
|
+
if (this.searchDebounceTimer)
|
|
544
|
+
clearTimeout(this.searchDebounceTimer);
|
|
545
|
+
this.searchDebounceTimer = setTimeout(() => {
|
|
546
|
+
this.currentPage = 1;
|
|
547
|
+
void this.applyFilters();
|
|
548
|
+
}, 250);
|
|
549
|
+
}
|
|
550
|
+
onPageChange(event) {
|
|
551
|
+
if (event.PageNumber === this.currentPage)
|
|
552
|
+
return;
|
|
553
|
+
this.currentPage = event.PageNumber;
|
|
554
|
+
void this.loadRows();
|
|
555
|
+
}
|
|
556
|
+
onPageSizeChange(size) {
|
|
557
|
+
const next = Number(size);
|
|
558
|
+
if (!next || next === this.pageSize)
|
|
559
|
+
return;
|
|
560
|
+
this.pageSize = next;
|
|
561
|
+
this.currentPage = 1;
|
|
562
|
+
void this.loadRows();
|
|
563
|
+
}
|
|
564
|
+
openDrawer(row) {
|
|
565
|
+
this.selectedRow = row;
|
|
566
|
+
this.cdr.detectChanges();
|
|
567
|
+
}
|
|
568
|
+
closeDrawer() {
|
|
569
|
+
this.selectedRow = null;
|
|
570
|
+
this.cdr.detectChanges();
|
|
571
|
+
}
|
|
572
|
+
async openConversation() {
|
|
573
|
+
if (!this.selectedRow?.conversationID)
|
|
574
|
+
return;
|
|
575
|
+
const conversationID = this.selectedRow.conversationID;
|
|
576
|
+
// The Chat app owns the Conversations nav item; the active app (Agent
|
|
577
|
+
// Feedback) does not, so we must target Chat explicitly. Fall back to
|
|
578
|
+
// scanning all apps for a nav item whose DriverClass is the chat resource.
|
|
579
|
+
let appId = this.appManager.GetAppByName('Chat')?.ID;
|
|
580
|
+
let navItemLabel = 'Conversations';
|
|
581
|
+
if (!appId) {
|
|
582
|
+
for (const app of this.appManager.GetAllApps()) {
|
|
583
|
+
const navItems = await app.GetNavItems();
|
|
584
|
+
const match = navItems.find(n => n.DriverClass === 'ChatConversationsResource');
|
|
585
|
+
if (match) {
|
|
586
|
+
appId = app.ID;
|
|
587
|
+
navItemLabel = match.Label;
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (!appId) {
|
|
593
|
+
this.error = 'Could not find a Chat application to open this conversation in.';
|
|
594
|
+
this.cdr.detectChanges();
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
this.closeDrawer();
|
|
598
|
+
await this.navigationService.OpenNavItemByName(navItemLabel, { conversationId: conversationID }, appId);
|
|
599
|
+
}
|
|
600
|
+
onEscape() {
|
|
601
|
+
if (this.selectedRow)
|
|
602
|
+
this.closeDrawer();
|
|
603
|
+
}
|
|
604
|
+
bandClassFor(rating) {
|
|
605
|
+
if (rating >= 8)
|
|
606
|
+
return 'rating-high';
|
|
607
|
+
if (rating >= 4)
|
|
608
|
+
return 'rating-mid';
|
|
609
|
+
return 'rating-low';
|
|
610
|
+
}
|
|
611
|
+
trackByRatingID = (_, row) => row.ratingID;
|
|
612
|
+
/** 1-based index of the first row on the current page (0 when empty). */
|
|
613
|
+
get rangeStart() {
|
|
614
|
+
if (this.totalRowCount === 0)
|
|
615
|
+
return 0;
|
|
616
|
+
return (this.currentPage - 1) * this.pageSize + 1;
|
|
617
|
+
}
|
|
618
|
+
/** 1-based index of the last row on the current page (clamped to total). */
|
|
619
|
+
get rangeEnd() {
|
|
620
|
+
return Math.min(this.currentPage * this.pageSize, this.totalRowCount);
|
|
621
|
+
}
|
|
622
|
+
/**
|
|
623
|
+
* True when the filtered set fits on a single page — in this case
|
|
624
|
+
* mj-pagination hides itself, so the footer renders a fallback summary
|
|
625
|
+
* line. When false we let mj-pagination own the bar (it has its own
|
|
626
|
+
* `1-N of M` summary on the left + nav controls on the right).
|
|
627
|
+
*/
|
|
628
|
+
get isSinglePage() {
|
|
629
|
+
if (this.totalRowCount <= 0)
|
|
630
|
+
return false;
|
|
631
|
+
return Math.ceil(this.totalRowCount / this.pageSize) <= 1;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Run both the rows-query (paged) and the stats-query (aggregate) against
|
|
635
|
+
* the current filter state. Called when filters change or on initial load.
|
|
636
|
+
*/
|
|
637
|
+
async applyFilters() {
|
|
638
|
+
await Promise.all([this.loadRows(), this.loadStats()]);
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Fetch the current page of rows from the server. The mj-pagination
|
|
642
|
+
* footer reads `totalRowCount` to derive the page count.
|
|
643
|
+
*/
|
|
644
|
+
async loadRows() {
|
|
645
|
+
this.loading = true;
|
|
646
|
+
this.error = null;
|
|
647
|
+
this.cdr.detectChanges();
|
|
648
|
+
try {
|
|
649
|
+
const rq = new RunQuery();
|
|
650
|
+
const result = await rq.RunQuery({
|
|
651
|
+
QueryName: 'ListConversationDetailFeedback',
|
|
652
|
+
CategoryPath: '/MJ/Conversations',
|
|
653
|
+
Parameters: this.buildQueryParameters(),
|
|
654
|
+
StartRow: (this.currentPage - 1) * this.pageSize,
|
|
655
|
+
MaxRows: this.pageSize
|
|
656
|
+
});
|
|
657
|
+
if (!result.Success) {
|
|
658
|
+
this.error = result.ErrorMessage || 'Failed to load feedback';
|
|
659
|
+
this.rows = [];
|
|
660
|
+
this.totalRowCount = 0;
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
this.rows = (result.Results ?? []).map((r) => this.mapRow(r));
|
|
664
|
+
this.totalRowCount = result.TotalRowCount ?? this.rows.length;
|
|
665
|
+
}
|
|
666
|
+
catch (e) {
|
|
667
|
+
this.error = e?.message || 'Failed to load feedback';
|
|
668
|
+
this.rows = [];
|
|
669
|
+
this.totalRowCount = 0;
|
|
670
|
+
}
|
|
671
|
+
finally {
|
|
672
|
+
this.loading = false;
|
|
673
|
+
this.cdr.detectChanges();
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Aggregate counts over the same filter universe as `loadRows()`. Runs in
|
|
678
|
+
* parallel with the rows query so the stat cards and the table stay in
|
|
679
|
+
* sync after every filter change.
|
|
680
|
+
*/
|
|
681
|
+
async loadStats() {
|
|
682
|
+
this.statsLoading = true;
|
|
683
|
+
this.cdr.detectChanges();
|
|
684
|
+
try {
|
|
685
|
+
const rq = new RunQuery();
|
|
686
|
+
const result = await rq.RunQuery({
|
|
687
|
+
QueryName: 'ListConversationDetailFeedbackStats',
|
|
688
|
+
CategoryPath: '/MJ/Conversations',
|
|
689
|
+
Parameters: this.buildQueryParameters()
|
|
690
|
+
});
|
|
691
|
+
const row = result.Results?.[0] || {};
|
|
692
|
+
const total = Number(row.Total ?? 0);
|
|
693
|
+
const avg = Number(row.AvgRating ?? 0);
|
|
694
|
+
const pos = Number(row.PositiveCount ?? 0);
|
|
695
|
+
const neg = Number(row.NegativeCount ?? 0);
|
|
696
|
+
this.stats = {
|
|
697
|
+
total,
|
|
698
|
+
avgRating: total ? Math.round(avg * 10) / 10 : 0,
|
|
699
|
+
percentPositive: total ? Math.round((pos / total) * 100) : 0,
|
|
700
|
+
percentNegative: total ? Math.round((neg / total) * 100) : 0
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
catch (e) {
|
|
704
|
+
// Stats are non-critical — the table is the primary surface. Log and
|
|
705
|
+
// leave the previous stats values in place.
|
|
706
|
+
console.warn('[Feedback] stats query failed', e);
|
|
707
|
+
}
|
|
708
|
+
finally {
|
|
709
|
+
this.statsLoading = false;
|
|
710
|
+
this.cdr.detectChanges();
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Build the Nunjucks-bound parameter set passed to both queries. Server-side
|
|
715
|
+
* filtering: emit only the keys that actually have a value so the SQL's
|
|
716
|
+
* `{% if X %}` blocks stay inert when filters are at their default.
|
|
717
|
+
*/
|
|
718
|
+
buildQueryParameters() {
|
|
719
|
+
const params = {};
|
|
720
|
+
if (this.ratingBand === 'high') {
|
|
721
|
+
params.MinRating = 8;
|
|
722
|
+
}
|
|
723
|
+
else if (this.ratingBand === 'mid') {
|
|
724
|
+
params.MinRating = 4;
|
|
725
|
+
params.MaxRating = 7;
|
|
726
|
+
}
|
|
727
|
+
else if (this.ratingBand === 'low') {
|
|
728
|
+
params.MaxRating = 3;
|
|
729
|
+
}
|
|
730
|
+
const cutoffIso = this.dateRangeCutoffIso();
|
|
731
|
+
if (cutoffIso) {
|
|
732
|
+
params.StartDate = cutoffIso;
|
|
733
|
+
}
|
|
734
|
+
const term = this.searchTerm.trim();
|
|
735
|
+
if (term) {
|
|
736
|
+
params.SearchText = term;
|
|
737
|
+
}
|
|
738
|
+
return params;
|
|
739
|
+
}
|
|
740
|
+
dateRangeCutoffIso() {
|
|
741
|
+
if (this.dateRange === 'all')
|
|
742
|
+
return null;
|
|
743
|
+
const days = this.dateRange === '1d' ? 1
|
|
744
|
+
: this.dateRange === '7d' ? 7
|
|
745
|
+
: this.dateRange === '30d' ? 30
|
|
746
|
+
: 90;
|
|
747
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
|
|
748
|
+
return cutoff.toISOString();
|
|
749
|
+
}
|
|
750
|
+
mapRow(r) {
|
|
751
|
+
const message = r.MessageText ?? null;
|
|
752
|
+
const snippet = message
|
|
753
|
+
? (message.length > 160 ? message.slice(0, 160) + '…' : message)
|
|
754
|
+
: null;
|
|
755
|
+
return {
|
|
756
|
+
ratingID: r.RatingID,
|
|
757
|
+
rating: r.Rating,
|
|
758
|
+
comments: r.Comments ?? null,
|
|
759
|
+
createdAt: r.RatedAt ?? '',
|
|
760
|
+
raterName: r.RaterName ?? null,
|
|
761
|
+
raterEmail: r.RaterEmail ?? null,
|
|
762
|
+
conversationDetailID: r.ConversationDetailID,
|
|
763
|
+
conversationID: r.ConversationID ?? null,
|
|
764
|
+
conversationName: r.ConversationName ?? null,
|
|
765
|
+
messageText: message,
|
|
766
|
+
messageSnippet: snippet,
|
|
767
|
+
messageRole: r.MessageRole ?? null,
|
|
768
|
+
agentID: r.AgentID ?? null,
|
|
769
|
+
agentName: r.AgentName ?? null
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵConversationFeedbackResource_BaseFactory; return function ConversationFeedbackResource_Factory(__ngFactoryType__) { return (ɵConversationFeedbackResource_BaseFactory || (ɵConversationFeedbackResource_BaseFactory = i0.ɵɵgetInheritedFactory(ConversationFeedbackResource)))(__ngFactoryType__ || ConversationFeedbackResource); }; })();
|
|
773
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ConversationFeedbackResource, selectors: [["mj-conversation-feedback"]], hostBindings: function ConversationFeedbackResource_HostBindings(rf, ctx) { if (rf & 1) {
|
|
774
|
+
i0.ɵɵlistener("keydown.escape", function ConversationFeedbackResource_keydown_escape_HostBindingHandler() { return ctx.onEscape(); }, i0.ɵɵresolveDocument);
|
|
775
|
+
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 6, vars: 6, consts: [[1, "feedback-container"], [1, "loading-overlay"], [1, "slideout-backdrop"], ["role", "dialog", "aria-modal", "true", "aria-labelledby", "slideout-title", 1, "slideout-panel"], [1, "spinner"], [1, "sticky-header"], [1, "page-header"], [1, "page-title-area"], [1, "page-icon"], [1, "fa-solid", "fa-comment-dots"], [1, "page-title"], [1, "page-subtitle"], [1, "page-actions"], [1, "btn-action", 3, "click", "disabled"], [1, "fa-solid", "fa-arrows-rotate"], [1, "error-banner"], [1, "stats-grid"], [1, "stat-card"], [1, "stat-icon", "stat-icon-total"], [1, "stat-content"], [1, "stat-value"], [1, "stat-label"], [1, "stat-icon", "stat-icon-avg"], [1, "fa-solid", "fa-star"], [1, "stat-unit"], [1, "stat-icon", "stat-icon-positive"], [1, "fa-solid", "fa-thumbs-up"], [1, "stat-value", "rating-high-text"], [1, "stat-icon", "stat-icon-negative"], [1, "fa-solid", "fa-thumbs-down"], [1, "stat-value", "rating-low-text"], [1, "filter-bar"], [1, "filter-row"], [1, "filter-group"], [1, "filter-label"], [1, "btn-group"], [1, "btn-filter", 3, "click"], [1, "btn-filter", "band-high", 3, "click"], [1, "btn-filter", "band-mid", 3, "click"], [1, "btn-filter", "band-low", 3, "click"], [1, "filter-group", "filter-search"], ["type", "text", "placeholder", "Search comments, message, agent, rater...", 1, "input-search", 3, "ngModelChange", "input", "ngModel"], [1, "diagnostic-line"], [1, "scrollable-content"], [1, "fa-solid", "fa-triangle-exclamation"], ["aria-label", "Dismiss error", 3, "click"], [1, "empty-state"], [1, "fa-solid", "fa-comment-slash"], [1, "feedback-table-wrapper"], [1, "feedback-table"], [1, "col-rating"], [1, "col-comment"], [1, "col-agent"], [1, "col-message"], [1, "col-rater"], [1, "col-when"], [1, "feedback-row", 3, "expanded"], [1, "pagination-bar"], [1, "pagination-summary"], [1, "feedback-pagination", 3, "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "page-size-control"], [1, "page-size-label"], [1, "page-size-select", 3, "ngModelChange", "ngModel"], [3, "ngValue"], [1, "feedback-row", 3, "click"], [1, "rating-badge", 3, "ngClass"], [1, "comment-text", "clamped"], [1, "muted"], [1, "agent-name"], [1, "message-snippet"], [1, "rater-name"], [1, "rater-email"], [1, "when"], [1, "feedback-pagination", 3, "PageChange", "TotalRowCount", "PageNumber", "PageSize", "IsLoading"], [1, "slideout-backdrop", 3, "click"], [1, "slideout-header"], [1, "slideout-title"], [1, "slideout-title-text"], ["id", "slideout-title", 1, "slideout-title-line"], [1, "slideout-subtitle"], ["title", "Close (Esc)", "aria-label", "Close", 1, "slideout-close", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "slideout-content"], [1, "slideout-section"], [1, "section-label"], [1, "comment-body"], [1, "comment-body", "muted"], [1, "meta-grid"], [1, "meta-card"], [1, "meta-label"], [1, "meta-value"], [1, "message-card"], [1, "message-card-header"], [1, "fa-solid", "fa-message"], [1, "message-card-body"], [1, "slideout-footer"], ["mjButton", "", "variant", "flat", "size", "lg", 3, "click"], ["mjButton", "", "variant", "primary", "size", "lg", 3, "click", "disabled", "title"], [1, "fa-solid", "fa-arrow-up-right-from-square"]], template: function ConversationFeedbackResource_Template(rf, ctx) { if (rf & 1) {
|
|
776
|
+
i0.ɵɵelementStart(0, "div", 0);
|
|
777
|
+
i0.ɵɵconditionalCreate(1, ConversationFeedbackResource_Conditional_1_Template, 3, 0, "div", 1);
|
|
778
|
+
i0.ɵɵconditionalCreate(2, ConversationFeedbackResource_Conditional_2_Template, 87, 31);
|
|
779
|
+
i0.ɵɵconditionalCreate(3, ConversationFeedbackResource_Conditional_3_Template, 1, 0, "div", 2);
|
|
780
|
+
i0.ɵɵelementStart(4, "aside", 3);
|
|
781
|
+
i0.ɵɵconditionalCreate(5, ConversationFeedbackResource_Conditional_5_Template, 56, 15);
|
|
782
|
+
i0.ɵɵelementEnd()();
|
|
783
|
+
} if (rf & 2) {
|
|
784
|
+
let tmp_4_0;
|
|
785
|
+
i0.ɵɵadvance();
|
|
786
|
+
i0.ɵɵconditional(ctx.isInitializing ? 1 : -1);
|
|
787
|
+
i0.ɵɵadvance();
|
|
788
|
+
i0.ɵɵconditional(!ctx.isInitializing ? 2 : -1);
|
|
789
|
+
i0.ɵɵadvance();
|
|
790
|
+
i0.ɵɵconditional(ctx.selectedRow ? 3 : -1);
|
|
791
|
+
i0.ɵɵadvance();
|
|
792
|
+
i0.ɵɵclassProp("open", !!ctx.selectedRow);
|
|
793
|
+
i0.ɵɵadvance();
|
|
794
|
+
i0.ɵɵconditional((tmp_4_0 = ctx.selectedRow) ? 5 : -1, tmp_4_0);
|
|
795
|
+
} }, dependencies: [i1.NgClass, i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.DefaultValueAccessor, i2.SelectControlValueAccessor, i2.NgControlStatus, i2.NgModel, i3.MJButtonDirective, i4.PaginationComponent, i1.DecimalPipe, i1.DatePipe], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n}\n\n.feedback-container[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n overflow: hidden;\n}\n\n\n\n\n\n.sticky-header[_ngcontent-%COMP%] {\n flex-shrink: 0;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n}\n\n\n\n\n\n.page-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n padding: 1rem 1.5rem;\n}\n\n.page-title-area[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 0.75rem; }\n\n.page-icon[_ngcontent-%COMP%] {\n width: 40px; height: 40px;\n display: flex; align-items: center; justify-content: center;\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n font-size: 1.125rem;\n}\n\n.page-title[_ngcontent-%COMP%] {\n font-size: 1.25rem;\n font-weight: 700;\n margin: 0;\n letter-spacing: -0.02em;\n color: var(--mj-text-primary);\n}\n\n.page-subtitle[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.page-actions[_ngcontent-%COMP%] { display: flex; align-items: center; gap: 0.5rem; }\n\n.btn-action[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.875rem;\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 180ms, border-color 180ms, color 180ms;\n}\n.btn-action[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n.btn-action[_ngcontent-%COMP%]:disabled { opacity: 0.5; cursor: default; }\n\n\n\n\n\n.stats-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.75rem;\n padding: 0 1.5rem 1rem;\n}\n\n@media (min-width: 768px) {\n .stats-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n }\n}\n\n.stat-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n padding: 1rem 1.25rem;\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.stat-icon[_ngcontent-%COMP%] {\n width: 44px;\n height: 44px;\n border-radius: var(--mj-radius-lg);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.125rem;\n flex-shrink: 0;\n}\n\n.stat-icon-total[_ngcontent-%COMP%] {\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n}\n.stat-icon-avg[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-tertiary) 16%, transparent);\n color: var(--mj-brand-tertiary);\n}\n.stat-icon-positive[_ngcontent-%COMP%] {\n background: var(--rating-high-bg, #ecfdf5);\n color: var(--rating-high, #10b981);\n}\n.stat-icon-negative[_ngcontent-%COMP%] {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.stat-content[_ngcontent-%COMP%] { flex: 1; min-width: 0; }\n\n.stat-value[_ngcontent-%COMP%] {\n font-size: 1.5rem;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: var(--mj-text-primary);\n line-height: 1.1;\n}\n.stat-unit[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n font-weight: 500;\n margin-left: 2px;\n}\n.stat-label[_ngcontent-%COMP%] {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n margin-top: 0.125rem;\n}\n\n.rating-high-text[_ngcontent-%COMP%] { color: var(--rating-high, #10b981); }\n.rating-low-text[_ngcontent-%COMP%] { color: var(--mj-status-error); }\n\n\n\n\n\n.filter-bar[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.75rem 1.5rem 1rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.filter-row[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 1rem;\n align-items: flex-end;\n}\n\n.filter-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.3rem;\n}\n\n.filter-search[_ngcontent-%COMP%] {\n flex: 0 1 auto;\n width: 280px;\n min-width: 200px;\n max-width: 360px;\n}\n\n.filter-label[_ngcontent-%COMP%] {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n}\n\n.btn-group[_ngcontent-%COMP%] { display: flex; gap: 0; }\n.btn-group[_ngcontent-%COMP%] .btn-filter[_ngcontent-%COMP%]:first-child { border-radius: var(--mj-radius-sm) 0 0 var(--mj-radius-sm); }\n.btn-group[_ngcontent-%COMP%] .btn-filter[_ngcontent-%COMP%]:last-child { border-radius: 0 var(--mj-radius-sm) var(--mj-radius-sm) 0; }\n.btn-group[_ngcontent-%COMP%] .btn-filter[_ngcontent-%COMP%]:not(:first-child) { margin-left: -1px; }\n\n.btn-filter[_ngcontent-%COMP%] {\n padding: 0.4rem 0.75rem;\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 180ms, color 180ms, border-color 180ms;\n white-space: nowrap;\n}\n.btn-filter[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-hover); color: var(--mj-text-primary); }\n.btn-filter.active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n z-index: 1;\n position: relative;\n}\n.btn-filter.band-high.active[_ngcontent-%COMP%] {\n background: var(--rating-high, #10b981);\n border-color: var(--rating-high, #10b981);\n color: var(--mj-text-inverse);\n}\n.btn-filter.band-mid.active[_ngcontent-%COMP%] {\n background: var(--rating-mid, #f59e0b);\n border-color: var(--rating-mid, #f59e0b);\n color: var(--mj-text-inverse);\n}\n.btn-filter.band-low.active[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.input-search[_ngcontent-%COMP%] {\n padding: 0.45rem 0.75rem;\n font-size: 0.875rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n width: 100%;\n}\n.input-search[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n.diagnostic-line[_ngcontent-%COMP%] {\n font-size: 0.6875rem;\n color: var(--mj-text-secondary);\n letter-spacing: 0.02em;\n}\n\n\n\n\n\n.error-banner[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n margin: 0 1.5rem 0.75rem;\n padding: 0.75rem 1rem;\n background: var(--mj-status-error-bg);\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 35%, transparent);\n border-radius: var(--mj-radius-md);\n color: var(--mj-status-error-text);\n font-size: 0.8125rem;\n font-weight: 500;\n}\n.error-banner[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n font-size: 1.125rem;\n color: var(--mj-status-error);\n padding: 0 0.25rem;\n}\n\n\n\n\n\n.scrollable-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 1.25rem 1.5rem 1.5rem;\n}\n\n.loading-overlay[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.625rem;\n padding: 3rem;\n color: var(--mj-text-secondary);\n font-size: 0.8125rem;\n font-weight: 500;\n}\n.spinner[_ngcontent-%COMP%] {\n width: 20px; height: 20px;\n border: 2px solid var(--mj-border-default);\n border-top-color: var(--mj-brand-primary);\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spin 0.6s linear infinite;\n}\n@keyframes _ngcontent-%COMP%_spin { to { transform: rotate(360deg); } }\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 1.25rem;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n}\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 2.25rem;\n margin-bottom: 0.875rem;\n opacity: 0.5;\n}\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 0.875rem;\n font-weight: 500;\n margin: 0;\n}\n\n\n\n\n\n.feedback-table-wrapper[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n overflow: auto;\n}\n\n.feedback-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8125rem;\n table-layout: fixed;\n}\n\n.feedback-table[_ngcontent-%COMP%] thead[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n.feedback-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n text-align: left;\n font-size: 0.6875rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n padding: 0.75rem 0.875rem;\n}\n.feedback-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 0.875rem;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-primary);\n vertical-align: top;\n}\n\n.feedback-row[_ngcontent-%COMP%] {\n cursor: pointer;\n transition: background 180ms;\n}\n.feedback-row[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-hover); }\n.feedback-row.expanded[_ngcontent-%COMP%] { background: var(--mj-brand-accent-subtle); }\n\n.col-rating[_ngcontent-%COMP%] { width: 90px; }\n.col-comment[_ngcontent-%COMP%] { width: 28%; }\n.col-agent[_ngcontent-%COMP%] { width: 14%; }\n.col-message[_ngcontent-%COMP%] { width: 26%; }\n.col-rater[_ngcontent-%COMP%] { width: 16%; }\n.col-when[_ngcontent-%COMP%] { width: 12%; }\n\n.rating-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: baseline;\n gap: 2px;\n padding: 0.35rem 0.625rem;\n border-radius: var(--mj-radius-md);\n font-size: 0.875rem;\n font-weight: 700;\n letter-spacing: -0.02em;\n}\n.rating-badge[_ngcontent-%COMP%] small[_ngcontent-%COMP%] {\n font-size: 0.625rem;\n font-weight: 500;\n opacity: 0.7;\n}\n.rating-badge.rating-high[_ngcontent-%COMP%] { background: var(--rating-high-bg, #ecfdf5); color: var(--rating-high, #10b981); }\n.rating-badge.rating-mid[_ngcontent-%COMP%] { background: var(--rating-mid-bg, #fffbeb); color: var(--rating-mid, #f59e0b); }\n.rating-badge.rating-low[_ngcontent-%COMP%] { background: var(--mj-status-error-bg); color: var(--mj-status-error); }\n\n.comment-text[_ngcontent-%COMP%] {\n white-space: pre-wrap;\n word-break: break-word;\n line-height: 1.5;\n}\n.comment-text.clamped[_ngcontent-%COMP%] {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n line-clamp: 2;\n overflow: hidden;\n}\n.message-snippet[_ngcontent-%COMP%] {\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n line-clamp: 2;\n overflow: hidden;\n}\n\n\n\n.pagination-bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-top: 0.5rem;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n.pagination-summary[_ngcontent-%COMP%] {\n flex: 1;\n font-weight: 500;\n color: var(--mj-text-secondary);\n padding: 0.5rem 0;\n}\n.feedback-pagination[_ngcontent-%COMP%] {\n display: block;\n flex: 1;\n min-width: 0;\n}\n\n.page-size-control[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.page-size-label[_ngcontent-%COMP%] {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n}\n.page-size-select[_ngcontent-%COMP%] {\n padding: 0.3rem 0.5rem;\n font-size: 0.8125rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.page-size-select[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n.agent-name[_ngcontent-%COMP%] { font-weight: 500; color: var(--mj-text-primary); }\n.rater-name[_ngcontent-%COMP%] { font-weight: 500; color: var(--mj-text-primary); }\n.rater-email[_ngcontent-%COMP%] { font-size: 0.6875rem; color: var(--mj-text-secondary); margin-top: 2px; }\n.when[_ngcontent-%COMP%] { color: var(--mj-text-secondary); font-size: 0.75rem; white-space: nowrap; }\n.muted[_ngcontent-%COMP%] { color: var(--mj-text-secondary); font-style: italic; }\n\n\n\n\n\n\n\n\n\n.slideout-backdrop[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n background: var(--mj-bg-overlay, rgba(28, 28, 34, 0.45));\n z-index: 100;\n animation: _ngcontent-%COMP%_feedback-fadeIn 0.2s ease;\n}\n@keyframes _ngcontent-%COMP%_feedback-fadeIn { from { opacity: 0; } to { opacity: 1; } }\n\n.slideout-panel[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n width: 560px;\n max-width: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay, rgba(0, 0, 0, 0.25));\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(100%);\n overflow: hidden;\n}\n.slideout-panel.open[_ngcontent-%COMP%] { transform: translateX(0); }\n\n\n\n.slideout-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n padding: 20px 24px;\n background: var(--mj-brand-accent-subtle);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n.slideout-title[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n.slideout-title[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 22px;\n flex-shrink: 0;\n line-height: 1.2;\n}\n.slideout-title-text[_ngcontent-%COMP%] { min-width: 0; }\n.slideout-title-line[_ngcontent-%COMP%] {\n font-weight: 700;\n font-size: 17px;\n color: var(--mj-text-primary);\n margin: 0 0 6px;\n line-height: 1.3;\n word-break: break-word;\n}\n.slideout-subtitle[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n flex-wrap: wrap;\n}\n.slideout-subtitle[_ngcontent-%COMP%] strong[_ngcontent-%COMP%] { color: var(--mj-text-primary); font-weight: 600; }\n\n.slideout-close[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-hover);\n border: none;\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.slideout-close[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n\n\n.slideout-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 0;\n background: var(--mj-bg-surface);\n}\n.slideout-section[_ngcontent-%COMP%] {\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.slideout-section[_ngcontent-%COMP%]:last-child { border-bottom: none; }\n\n.section-label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n margin-bottom: 0.5rem;\n}\n.comment-body[_ngcontent-%COMP%] {\n font-size: 14px;\n line-height: 1.55;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n.comment-body.muted[_ngcontent-%COMP%] { font-style: italic; color: var(--mj-text-secondary); }\n\n.meta-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 0.75rem;\n}\n.meta-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.75rem 0.875rem;\n}\n.meta-label[_ngcontent-%COMP%] {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n font-weight: 600;\n margin-bottom: 0.25rem;\n}\n.meta-value[_ngcontent-%COMP%] {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n word-break: break-word;\n}\n\n.message-card[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n}\n.message-card-header[_ngcontent-%COMP%] {\n padding: 0.75rem 1.125rem;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n font-size: 0.8125rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.message-card-body[_ngcontent-%COMP%] {\n padding: 1rem 1.125rem;\n font-size: 0.875rem;\n line-height: 1.55;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n max-height: 320px;\n overflow-y: auto;\n}\n\n\n\n.slideout-footer[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n.slideout-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%] { min-width: 96px; }\n.slideout-footer[_ngcontent-%COMP%] button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { margin-right: 6px; }"] });
|
|
796
|
+
};
|
|
797
|
+
ConversationFeedbackResource = __decorate([
|
|
798
|
+
RegisterClass(BaseResourceComponent, 'ConversationFeedbackResource')
|
|
799
|
+
], ConversationFeedbackResource);
|
|
800
|
+
export { ConversationFeedbackResource };
|
|
801
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ConversationFeedbackResource, [{
|
|
802
|
+
type: Component,
|
|
803
|
+
args: [{ standalone: false, selector: 'mj-conversation-feedback', template: "<div class=\"feedback-container\">\n\n @if (isInitializing) {\n <div class=\"loading-overlay\"><div class=\"spinner\"></div> Loading...</div>\n }\n\n @if (!isInitializing) {\n\n <div class=\"sticky-header\">\n <div class=\"page-header\">\n <div class=\"page-title-area\">\n <div class=\"page-icon\"><i class=\"fa-solid fa-comment-dots\"></i></div>\n <div>\n <h1 class=\"page-title\">Agent Feedback</h1>\n <p class=\"page-subtitle\">User ratings and comments on AI messages</p>\n </div>\n </div>\n <div class=\"page-actions\">\n <button class=\"btn-action\" (click)=\"loadFeedback()\" [disabled]=\"loading\">\n <i class=\"fa-solid fa-arrows-rotate\" [class.fa-spin]=\"loading\"></i> Refresh\n </button>\n </div>\n </div>\n\n @if (error) {\n <div class=\"error-banner\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n {{ error }}\n <button (click)=\"error = null\" aria-label=\"Dismiss error\">\u00D7</button>\n </div>\n }\n\n <div class=\"stats-grid\">\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-total\"><i class=\"fa-solid fa-comment-dots\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ stats.total }}</div>\n <div class=\"stat-label\">Total Ratings</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-avg\"><i class=\"fa-solid fa-star\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value\">{{ stats.avgRating }}<span class=\"stat-unit\">/10</span></div>\n <div class=\"stat-label\">Avg Rating</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-positive\"><i class=\"fa-solid fa-thumbs-up\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value rating-high-text\">{{ stats.percentPositive }}%</div>\n <div class=\"stat-label\">Positive (8-10)</div>\n </div>\n </div>\n <div class=\"stat-card\">\n <div class=\"stat-icon stat-icon-negative\"><i class=\"fa-solid fa-thumbs-down\"></i></div>\n <div class=\"stat-content\">\n <div class=\"stat-value rating-low-text\">{{ stats.percentNegative }}%</div>\n <div class=\"stat-label\">Negative (1-3)</div>\n </div>\n </div>\n </div>\n\n <div class=\"filter-bar\">\n <div class=\"filter-row\">\n <div class=\"filter-group\">\n <span class=\"filter-label\">Date Range</span>\n <div class=\"btn-group\">\n <button class=\"btn-filter\" [class.active]=\"dateRange === '1d'\" (click)=\"setDateRange('1d')\">1d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === '7d'\" (click)=\"setDateRange('7d')\">7d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === '30d'\" (click)=\"setDateRange('30d')\">30d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === '90d'\" (click)=\"setDateRange('90d')\">90d</button>\n <button class=\"btn-filter\" [class.active]=\"dateRange === 'all'\" (click)=\"setDateRange('all')\">All</button>\n </div>\n </div>\n <div class=\"filter-group\">\n <span class=\"filter-label\">Rating Band</span>\n <div class=\"btn-group\">\n <button class=\"btn-filter\" [class.active]=\"ratingBand === 'all'\" (click)=\"setBand('all')\">All</button>\n <button class=\"btn-filter band-high\" [class.active]=\"ratingBand === 'high'\" (click)=\"setBand('high')\">High 8-10</button>\n <button class=\"btn-filter band-mid\" [class.active]=\"ratingBand === 'mid'\" (click)=\"setBand('mid')\">Mid 4-7</button>\n <button class=\"btn-filter band-low\" [class.active]=\"ratingBand === 'low'\" (click)=\"setBand('low')\">Low 1-3</button>\n </div>\n </div>\n <div class=\"filter-group filter-search\">\n <span class=\"filter-label\">Search</span>\n <input\n type=\"text\"\n class=\"input-search\"\n [(ngModel)]=\"searchTerm\"\n (input)=\"onSearchChange()\"\n placeholder=\"Search comments, message, agent, rater...\" />\n </div>\n </div>\n <div class=\"diagnostic-line\">\n {{ totalRowCount }} {{ totalRowCount === 1 ? 'rating' : 'ratings' }} match the current filters\n </div>\n </div>\n </div>\n\n <div class=\"scrollable-content\">\n @if (loading) {\n <div class=\"loading-overlay\"><div class=\"spinner\"></div> Loading feedback...</div>\n }\n\n @if (!loading) {\n @if (rows.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-comment-slash\"></i>\n <p>No feedback matches the current filters.</p>\n </div>\n } @else {\n <div class=\"feedback-table-wrapper\">\n <table class=\"feedback-table\">\n <thead>\n <tr>\n <th class=\"col-rating\">Rating</th>\n <th class=\"col-comment\">Comments</th>\n <th class=\"col-agent\">Agent</th>\n <th class=\"col-message\">Message</th>\n <th class=\"col-rater\">Rater</th>\n <th class=\"col-when\">When</th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows; track trackByRatingID($index, row)) {\n <tr\n class=\"feedback-row\"\n [class.expanded]=\"selectedRow?.ratingID === row.ratingID\"\n (click)=\"openDrawer(row)\">\n <td class=\"col-rating\">\n <span class=\"rating-badge\" [ngClass]=\"bandClassFor(row.rating)\">\n {{ row.rating }}<small>/10</small>\n </span>\n </td>\n <td class=\"col-comment\">\n @if (row.comments) {\n <div class=\"comment-text clamped\">{{ row.comments }}</div>\n } @else {\n <span class=\"muted\">(no comment)</span>\n }\n </td>\n <td class=\"col-agent\">\n <span class=\"agent-name\">{{ row.agentName || '\u2014' }}</span>\n </td>\n <td class=\"col-message\">\n @if (row.messageSnippet) {\n <span class=\"message-snippet\">{{ row.messageSnippet }}</span>\n } @else {\n <span class=\"muted\">\u2014</span>\n }\n </td>\n <td class=\"col-rater\">\n <div class=\"rater-name\">{{ row.raterName || row.raterEmail || '\u2014' }}</div>\n @if (row.raterEmail && row.raterName) {\n <div class=\"rater-email\">{{ row.raterEmail }}</div>\n }\n </td>\n <td class=\"col-when\">\n <span class=\"when\">{{ row.createdAt | date:'short' }}</span>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n\n <!--\n Bottom bar. When the filtered set spans multiple pages mj-pagination\n renders its own full-width `1-N of M | controls` row. When it fits\n on a single page mj-pagination self-hides, so the footer renders\n a single fallback summary line in its place.\n -->\n <!--\n Bottom bar: mj-pagination on the left (it has its own internal\n `1-N of M | nav controls` layout) + page-size selector hugging the\n right edge. The selector lives outside mj-pagination because the\n shared component doesn't expose a size-change output; the parent\n owns PageSize.\n -->\n <div class=\"pagination-bar\">\n @if (isSinglePage) {\n <span class=\"pagination-summary\">\n {{ rangeStart | number }}-{{ rangeEnd | number }} of {{ totalRowCount | number }}\n {{ totalRowCount === 1 ? 'rating' : 'ratings' }}\n </span>\n } @else {\n <mj-pagination\n class=\"feedback-pagination\"\n [TotalRowCount]=\"totalRowCount\"\n [PageNumber]=\"currentPage\"\n [PageSize]=\"pageSize\"\n [IsLoading]=\"loading\"\n (PageChange)=\"onPageChange($event)\">\n </mj-pagination>\n }\n\n <label class=\"page-size-control\">\n <span class=\"page-size-label\">Rows</span>\n <select\n class=\"page-size-select\"\n [ngModel]=\"pageSize\"\n (ngModelChange)=\"onPageSizeChange($event)\">\n @for (size of pageSizeOptions; track size) {\n <option [ngValue]=\"size\">{{ size }}</option>\n }\n </select>\n </label>\n </div>\n }\n }\n </div>\n }\n\n <!-- ===== Detail slide-out panel ===== -->\n @if (selectedRow) {\n <div class=\"slideout-backdrop\" (click)=\"closeDrawer()\"></div>\n }\n\n <aside class=\"slideout-panel\"\n [class.open]=\"!!selectedRow\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"slideout-title\">\n @if (selectedRow; as r) {\n <div class=\"slideout-header\">\n <div class=\"slideout-title\">\n <i class=\"fa-solid fa-comment-dots\"></i>\n <div class=\"slideout-title-text\">\n <h2 id=\"slideout-title\" class=\"slideout-title-line\">\n Feedback on {{ r.agentName || 'AI message' }}\n </h2>\n <div class=\"slideout-subtitle\">\n <span class=\"rating-badge\" [ngClass]=\"bandClassFor(r.rating)\">\n {{ r.rating }}<small>/10</small>\n </span>\n <span>\n Rated by <strong>{{ r.raterName || r.raterEmail || 'Unknown user' }}</strong>\n \u00B7 {{ r.createdAt | date:'medium' }}\n </span>\n </div>\n </div>\n </div>\n <button class=\"slideout-close\" (click)=\"closeDrawer()\" title=\"Close (Esc)\" aria-label=\"Close\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"slideout-content\">\n <div class=\"slideout-section\">\n <div class=\"section-label\">Reviewer Comment</div>\n @if (r.comments) {\n <div class=\"comment-body\">{{ r.comments }}</div>\n } @else {\n <div class=\"comment-body muted\">(no comment was provided)</div>\n }\n </div>\n\n <div class=\"slideout-section\">\n <div class=\"meta-grid\">\n <div class=\"meta-card\">\n <div class=\"meta-label\">Agent</div>\n <div class=\"meta-value\">{{ r.agentName || '\u2014' }}</div>\n </div>\n <div class=\"meta-card\">\n <div class=\"meta-label\">Conversation</div>\n <div class=\"meta-value\">{{ r.conversationName || '\u2014' }}</div>\n </div>\n <div class=\"meta-card\">\n <div class=\"meta-label\">Message Role</div>\n <div class=\"meta-value\">{{ r.messageRole || '\u2014' }}</div>\n </div>\n </div>\n </div>\n\n <div class=\"slideout-section\">\n <div class=\"message-card\">\n <div class=\"message-card-header\">\n <i class=\"fa-solid fa-message\"></i> Rated Message\n </div>\n <div class=\"message-card-body\">\n @if (r.messageText) {\n {{ r.messageText }}\n } @else {\n <span class=\"muted\">(message text unavailable)</span>\n }\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"slideout-footer\">\n <button mjButton variant=\"flat\" size=\"lg\" (click)=\"closeDrawer()\">Close</button>\n <button mjButton variant=\"primary\" size=\"lg\"\n (click)=\"openConversation()\"\n [disabled]=\"!r.conversationID\"\n [title]=\"r.conversationID ? 'Open the source conversation' : 'No conversation linked'\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i>\n Open Conversation\n </button>\n </div>\n }\n </aside>\n</div>\n", styles: [":host {\n display: block;\n height: 100%;\n}\n\n.feedback-container {\n position: relative;\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n overflow: hidden;\n}\n\n/* -----------------------------------------------------------------------------\n Sticky Header\n ----------------------------------------------------------------------------- */\n.sticky-header {\n flex-shrink: 0;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n}\n\n/* -----------------------------------------------------------------------------\n Page Header\n ----------------------------------------------------------------------------- */\n.page-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 1rem;\n padding: 1rem 1.5rem;\n}\n\n.page-title-area { display: flex; align-items: center; gap: 0.75rem; }\n\n.page-icon {\n width: 40px; height: 40px;\n display: flex; align-items: center; justify-content: center;\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n border-radius: var(--mj-radius-md);\n font-size: 1.125rem;\n}\n\n.page-title {\n font-size: 1.25rem;\n font-weight: 700;\n margin: 0;\n letter-spacing: -0.02em;\n color: var(--mj-text-primary);\n}\n\n.page-subtitle {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n margin: 0;\n}\n\n.page-actions { display: flex; align-items: center; gap: 0.5rem; }\n\n.btn-action {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.875rem;\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 180ms, border-color 180ms, color 180ms;\n}\n.btn-action:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n.btn-action:disabled { opacity: 0.5; cursor: default; }\n\n/* -----------------------------------------------------------------------------\n Stats Grid\n ----------------------------------------------------------------------------- */\n.stats-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 0.75rem;\n padding: 0 1.5rem 1rem;\n}\n\n@media (min-width: 768px) {\n .stats-grid {\n grid-template-columns: repeat(4, 1fr);\n gap: 1rem;\n }\n}\n\n.stat-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n padding: 1rem 1.25rem;\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\n.stat-icon {\n width: 44px;\n height: 44px;\n border-radius: var(--mj-radius-lg);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.125rem;\n flex-shrink: 0;\n}\n\n.stat-icon-total {\n background: var(--mj-brand-accent-subtle);\n color: var(--mj-brand-primary);\n}\n.stat-icon-avg {\n background: color-mix(in srgb, var(--mj-brand-tertiary) 16%, transparent);\n color: var(--mj-brand-tertiary);\n}\n.stat-icon-positive {\n background: var(--rating-high-bg, #ecfdf5);\n color: var(--rating-high, #10b981);\n}\n.stat-icon-negative {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error);\n}\n\n.stat-content { flex: 1; min-width: 0; }\n\n.stat-value {\n font-size: 1.5rem;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: var(--mj-text-primary);\n line-height: 1.1;\n}\n.stat-unit {\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n font-weight: 500;\n margin-left: 2px;\n}\n.stat-label {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n margin-top: 0.125rem;\n}\n\n.rating-high-text { color: var(--rating-high, #10b981); }\n.rating-low-text { color: var(--mj-status-error); }\n\n/* -----------------------------------------------------------------------------\n Filter Bar\n ----------------------------------------------------------------------------- */\n.filter-bar {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n padding: 0.75rem 1.5rem 1rem;\n background: var(--mj-bg-surface);\n border-top: 1px solid var(--mj-border-default);\n}\n\n.filter-row {\n display: flex;\n flex-wrap: wrap;\n gap: 1rem;\n align-items: flex-end;\n}\n\n.filter-group {\n display: flex;\n flex-direction: column;\n gap: 0.3rem;\n}\n\n.filter-search {\n flex: 0 1 auto;\n width: 280px;\n min-width: 200px;\n max-width: 360px;\n}\n\n.filter-label {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n}\n\n.btn-group { display: flex; gap: 0; }\n.btn-group .btn-filter:first-child { border-radius: var(--mj-radius-sm) 0 0 var(--mj-radius-sm); }\n.btn-group .btn-filter:last-child { border-radius: 0 var(--mj-radius-sm) var(--mj-radius-sm) 0; }\n.btn-group .btn-filter:not(:first-child) { margin-left: -1px; }\n\n.btn-filter {\n padding: 0.4rem 0.75rem;\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: background 180ms, color 180ms, border-color 180ms;\n white-space: nowrap;\n}\n.btn-filter:hover { background: var(--mj-bg-surface-hover); color: var(--mj-text-primary); }\n.btn-filter.active {\n background: var(--mj-brand-primary);\n color: var(--mj-brand-on-primary);\n border-color: var(--mj-brand-primary);\n z-index: 1;\n position: relative;\n}\n.btn-filter.band-high.active {\n background: var(--rating-high, #10b981);\n border-color: var(--rating-high, #10b981);\n color: var(--mj-text-inverse);\n}\n.btn-filter.band-mid.active {\n background: var(--rating-mid, #f59e0b);\n border-color: var(--rating-mid, #f59e0b);\n color: var(--mj-text-inverse);\n}\n.btn-filter.band-low.active {\n background: var(--mj-status-error);\n border-color: var(--mj-status-error);\n color: var(--mj-text-inverse);\n}\n\n.input-search {\n padding: 0.45rem 0.75rem;\n font-size: 0.875rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n width: 100%;\n}\n.input-search:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n.diagnostic-line {\n font-size: 0.6875rem;\n color: var(--mj-text-secondary);\n letter-spacing: 0.02em;\n}\n\n/* -----------------------------------------------------------------------------\n Error banner\n ----------------------------------------------------------------------------- */\n.error-banner {\n display: flex;\n align-items: center;\n gap: 0.625rem;\n margin: 0 1.5rem 0.75rem;\n padding: 0.75rem 1rem;\n background: var(--mj-status-error-bg);\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 35%, transparent);\n border-radius: var(--mj-radius-md);\n color: var(--mj-status-error-text);\n font-size: 0.8125rem;\n font-weight: 500;\n}\n.error-banner button {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n font-size: 1.125rem;\n color: var(--mj-status-error);\n padding: 0 0.25rem;\n}\n\n/* -----------------------------------------------------------------------------\n Scrollable Content\n ----------------------------------------------------------------------------- */\n.scrollable-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n padding: 1.25rem 1.5rem 1.5rem;\n}\n\n.loading-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.625rem;\n padding: 3rem;\n color: var(--mj-text-secondary);\n font-size: 0.8125rem;\n font-weight: 500;\n}\n.spinner {\n width: 20px; height: 20px;\n border: 2px solid var(--mj-border-default);\n border-top-color: var(--mj-brand-primary);\n border-radius: 50%;\n animation: spin 0.6s linear infinite;\n}\n@keyframes spin { to { transform: rotate(360deg); } }\n\n/* -----------------------------------------------------------------------------\n Empty State\n ----------------------------------------------------------------------------- */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 4rem 1.25rem;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n}\n.empty-state i {\n font-size: 2.25rem;\n margin-bottom: 0.875rem;\n opacity: 0.5;\n}\n.empty-state p {\n font-size: 0.875rem;\n font-weight: 500;\n margin: 0;\n}\n\n/* -----------------------------------------------------------------------------\n Feedback Table\n ----------------------------------------------------------------------------- */\n.feedback-table-wrapper {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n overflow: auto;\n}\n\n.feedback-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.8125rem;\n table-layout: fixed;\n}\n\n.feedback-table thead {\n background: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n.feedback-table th {\n text-align: left;\n font-size: 0.6875rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n padding: 0.75rem 0.875rem;\n}\n.feedback-table td {\n padding: 0.875rem;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-primary);\n vertical-align: top;\n}\n\n.feedback-row {\n cursor: pointer;\n transition: background 180ms;\n}\n.feedback-row:hover { background: var(--mj-bg-surface-hover); }\n.feedback-row.expanded { background: var(--mj-brand-accent-subtle); }\n\n.col-rating { width: 90px; }\n.col-comment { width: 28%; }\n.col-agent { width: 14%; }\n.col-message { width: 26%; }\n.col-rater { width: 16%; }\n.col-when { width: 12%; }\n\n.rating-badge {\n display: inline-flex;\n align-items: baseline;\n gap: 2px;\n padding: 0.35rem 0.625rem;\n border-radius: var(--mj-radius-md);\n font-size: 0.875rem;\n font-weight: 700;\n letter-spacing: -0.02em;\n}\n.rating-badge small {\n font-size: 0.625rem;\n font-weight: 500;\n opacity: 0.7;\n}\n.rating-badge.rating-high { background: var(--rating-high-bg, #ecfdf5); color: var(--rating-high, #10b981); }\n.rating-badge.rating-mid { background: var(--rating-mid-bg, #fffbeb); color: var(--rating-mid, #f59e0b); }\n.rating-badge.rating-low { background: var(--mj-status-error-bg); color: var(--mj-status-error); }\n\n.comment-text {\n white-space: pre-wrap;\n word-break: break-word;\n line-height: 1.5;\n}\n.comment-text.clamped {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n line-clamp: 2;\n overflow: hidden;\n}\n.message-snippet {\n color: var(--mj-text-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n line-clamp: 2;\n overflow: hidden;\n}\n/* Bottom row: mj-pagination (or the single-page summary) takes flex:1; the\n page-size selector hugs the right edge next to the nav controls. */\n.pagination-bar {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n margin-top: 0.5rem;\n font-size: 0.8125rem;\n color: var(--mj-text-secondary);\n}\n.pagination-summary {\n flex: 1;\n font-weight: 500;\n color: var(--mj-text-secondary);\n padding: 0.5rem 0;\n}\n.feedback-pagination {\n display: block;\n flex: 1;\n min-width: 0;\n}\n\n.page-size-control {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.page-size-label {\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n}\n.page-size-select {\n padding: 0.3rem 0.5rem;\n font-size: 0.8125rem;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.page-size-select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n\n.agent-name { font-weight: 500; color: var(--mj-text-primary); }\n.rater-name { font-weight: 500; color: var(--mj-text-primary); }\n.rater-email { font-size: 0.6875rem; color: var(--mj-text-secondary); margin-top: 2px; }\n.when { color: var(--mj-text-secondary); font-size: 0.75rem; white-space: nowrap; }\n.muted { color: var(--mj-text-secondary); font-style: italic; }\n\n/* -----------------------------------------------------------------------------\n Slide-out Panel \u2014 matches the MJ slideout pattern used elsewhere in Explorer\n (see api-key-edit-panel.component.css). Scoped to the feedback-container\n via `position: absolute` so the MJ Explorer chrome (tab bar, app nav) sits\n above it correctly \u2014 `position: fixed` would otherwise be re-anchored by\n any ancestor with a `transform` set.\n ----------------------------------------------------------------------------- */\n.slideout-backdrop {\n position: absolute;\n inset: 0;\n background: var(--mj-bg-overlay, rgba(28, 28, 34, 0.45));\n z-index: 100;\n animation: feedback-fadeIn 0.2s ease;\n}\n@keyframes feedback-fadeIn { from { opacity: 0; } to { opacity: 1; } }\n\n.slideout-panel {\n position: absolute;\n top: 0;\n right: 0;\n height: 100%;\n width: 560px;\n max-width: 100%;\n background: var(--mj-bg-surface);\n box-shadow: -8px 0 32px var(--mj-bg-overlay, rgba(0, 0, 0, 0.25));\n z-index: 101;\n display: flex;\n flex-direction: column;\n transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n transform: translateX(100%);\n overflow: hidden;\n}\n.slideout-panel.open { transform: translateX(0); }\n\n/* Header */\n.slideout-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 1rem;\n padding: 20px 24px;\n background: var(--mj-brand-accent-subtle);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n.slideout-title {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n.slideout-title > i {\n color: var(--mj-brand-primary);\n font-size: 22px;\n flex-shrink: 0;\n line-height: 1.2;\n}\n.slideout-title-text { min-width: 0; }\n.slideout-title-line {\n font-weight: 700;\n font-size: 17px;\n color: var(--mj-text-primary);\n margin: 0 0 6px;\n line-height: 1.3;\n word-break: break-word;\n}\n.slideout-subtitle {\n display: flex;\n align-items: center;\n gap: 10px;\n font-size: 12px;\n color: var(--mj-text-secondary);\n flex-wrap: wrap;\n}\n.slideout-subtitle strong { color: var(--mj-text-primary); font-weight: 600; }\n\n.slideout-close {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-hover);\n border: none;\n border-radius: 8px;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.2s ease;\n flex-shrink: 0;\n}\n.slideout-close:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-primary);\n}\n\n/* Content */\n.slideout-content {\n flex: 1;\n overflow-y: auto;\n padding: 0;\n background: var(--mj-bg-surface);\n}\n.slideout-section {\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.slideout-section:last-child { border-bottom: none; }\n\n.section-label {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n margin-bottom: 0.5rem;\n}\n.comment-body {\n font-size: 14px;\n line-height: 1.55;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n}\n.comment-body.muted { font-style: italic; color: var(--mj-text-secondary); }\n\n.meta-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 0.75rem;\n}\n.meta-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: 0.75rem 0.875rem;\n}\n.meta-label {\n font-size: 0.625rem;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: var(--mj-text-secondary);\n font-weight: 600;\n margin-bottom: 0.25rem;\n}\n.meta-value {\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n word-break: break-word;\n}\n\n.message-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n overflow: hidden;\n}\n.message-card-header {\n padding: 0.75rem 1.125rem;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-sunken);\n font-size: 0.8125rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.message-card-body {\n padding: 1rem 1.125rem;\n font-size: 0.875rem;\n line-height: 1.55;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n max-height: 320px;\n overflow-y: auto;\n}\n\n/* Footer */\n.slideout-footer {\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n padding: 16px 24px;\n background: var(--mj-bg-page);\n border-top: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n.slideout-footer button { min-width: 96px; }\n.slideout-footer button i { margin-right: 6px; }\n"] }]
|
|
804
|
+
}], null, { onEscape: [{
|
|
805
|
+
type: HostListener,
|
|
806
|
+
args: ['document:keydown.escape']
|
|
807
|
+
}] }); })();
|
|
808
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ConversationFeedbackResource, { className: "ConversationFeedbackResource", filePath: "src/lib/conversation-feedback/conversation-feedback.ts", lineNumber: 34 }); })();
|
|
809
|
+
//# sourceMappingURL=conversation-feedback.js.map
|