@memberjunction/ng-list-management 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/README.md +179 -85
- package/dist/lib/components/audience-source-picker/audience-source-picker.component.d.ts +74 -0
- package/dist/lib/components/audience-source-picker/audience-source-picker.component.d.ts.map +1 -0
- package/dist/lib/components/audience-source-picker/audience-source-picker.component.js +396 -0
- package/dist/lib/components/audience-source-picker/audience-source-picker.component.js.map +1 -0
- package/dist/lib/components/audience-source-summary/audience-source-summary.component.d.ts +34 -0
- package/dist/lib/components/audience-source-summary/audience-source-summary.component.d.ts.map +1 -0
- package/dist/lib/components/audience-source-summary/audience-source-summary.component.js +136 -0
- package/dist/lib/components/audience-source-summary/audience-source-summary.component.js.map +1 -0
- package/dist/lib/components/list-audit-log/list-audit-log.component.d.ts +55 -0
- package/dist/lib/components/list-audit-log/list-audit-log.component.d.ts.map +1 -0
- package/dist/lib/components/list-audit-log/list-audit-log.component.js +280 -0
- package/dist/lib/components/list-audit-log/list-audit-log.component.js.map +1 -0
- package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.d.ts +64 -0
- package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.d.ts.map +1 -0
- package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.js +323 -0
- package/dist/lib/components/list-delta-confirm/list-delta-confirm.component.js.map +1 -0
- package/dist/lib/components/list-invitations/list-invitations.component.d.ts +60 -0
- package/dist/lib/components/list-invitations/list-invitations.component.d.ts.map +1 -0
- package/dist/lib/components/list-invitations/list-invitations.component.js +367 -0
- package/dist/lib/components/list-invitations/list-invitations.component.js.map +1 -0
- package/dist/lib/components/list-management-dialog/list-management-dialog.component.d.ts.map +1 -1
- package/dist/lib/components/list-management-dialog/list-management-dialog.component.js +0 -4
- package/dist/lib/components/list-management-dialog/list-management-dialog.component.js.map +1 -1
- package/dist/lib/components/list-share-dialog/list-share-dialog.component.d.ts +18 -1
- package/dist/lib/components/list-share-dialog/list-share-dialog.component.d.ts.map +1 -1
- package/dist/lib/components/list-share-dialog/list-share-dialog.component.js +115 -66
- package/dist/lib/components/list-share-dialog/list-share-dialog.component.js.map +1 -1
- package/dist/lib/components/list-stats/list-stats.component.d.ts +52 -0
- package/dist/lib/components/list-stats/list-stats.component.d.ts.map +1 -0
- package/dist/lib/components/list-stats/list-stats.component.js +283 -0
- package/dist/lib/components/list-stats/list-stats.component.js.map +1 -0
- package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.d.ts +68 -0
- package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.d.ts.map +1 -0
- package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.js +316 -0
- package/dist/lib/components/save-view-as-list-dialog/save-view-as-list-dialog.component.js.map +1 -0
- package/dist/lib/components/shared-with-me/shared-with-me.component.d.ts +47 -0
- package/dist/lib/components/shared-with-me/shared-with-me.component.d.ts.map +1 -0
- package/dist/lib/components/shared-with-me/shared-with-me.component.js +231 -0
- package/dist/lib/components/shared-with-me/shared-with-me.component.js.map +1 -0
- package/dist/lib/components/tag-chips/tag-chips.component.d.ts +85 -0
- package/dist/lib/components/tag-chips/tag-chips.component.d.ts.map +1 -0
- package/dist/lib/components/tag-chips/tag-chips.component.js +392 -0
- package/dist/lib/components/tag-chips/tag-chips.component.js.map +1 -0
- package/dist/lib/models/list-sharing.models.d.ts +5 -0
- package/dist/lib/models/list-sharing.models.d.ts.map +1 -1
- package/dist/lib/models/list-sharing.models.js.map +1 -1
- package/dist/lib/module.d.ts +17 -8
- package/dist/lib/module.d.ts.map +1 -1
- package/dist/lib/module.js +51 -6
- package/dist/lib/module.js.map +1 -1
- package/dist/lib/services/list-management.service.d.ts.map +1 -1
- package/dist/lib/services/list-management.service.js +0 -9
- package/dist/lib/services/list-management.service.js.map +1 -1
- package/dist/lib/services/list-sharing.service.d.ts +8 -3
- package/dist/lib/services/list-sharing.service.d.ts.map +1 -1
- package/dist/lib/services/list-sharing.service.js +33 -15
- package/dist/lib/services/list-sharing.service.js.map +1 -1
- package/dist/public-api.d.ts +9 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +9 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +13 -11
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, inject } from '@angular/core';
|
|
2
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
3
|
+
import { RunView } from '@memberjunction/core';
|
|
4
|
+
import * as i0 from "@angular/core";
|
|
5
|
+
import * as i1 from "@angular/forms";
|
|
6
|
+
import * as i2 from "@memberjunction/ng-shared-generic";
|
|
7
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
8
|
+
function AudienceSourcePickerComponent_For_16_Template(rf, ctx) { if (rf & 1) {
|
|
9
|
+
i0.ɵɵelementStart(0, "option", 9);
|
|
10
|
+
i0.ɵɵtext(1);
|
|
11
|
+
i0.ɵɵelementEnd();
|
|
12
|
+
} if (rf & 2) {
|
|
13
|
+
const e_r1 = ctx.$implicit;
|
|
14
|
+
i0.ɵɵproperty("ngValue", e_r1.Name);
|
|
15
|
+
i0.ɵɵadvance();
|
|
16
|
+
i0.ɵɵtextInterpolate(e_r1.Name);
|
|
17
|
+
} }
|
|
18
|
+
function AudienceSourcePickerComponent_Conditional_17_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
19
|
+
i0.ɵɵelementStart(0, "div", 13);
|
|
20
|
+
i0.ɵɵelement(1, "mj-loading", 16);
|
|
21
|
+
i0.ɵɵelementEnd();
|
|
22
|
+
} }
|
|
23
|
+
function AudienceSourcePickerComponent_Conditional_17_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
24
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
25
|
+
i0.ɵɵtext(1, " No lists found for this entity. ");
|
|
26
|
+
i0.ɵɵelementEnd();
|
|
27
|
+
} }
|
|
28
|
+
function AudienceSourcePickerComponent_Conditional_17_Conditional_5_For_2_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
i0.ɵɵelementStart(0, "div", 22);
|
|
30
|
+
i0.ɵɵtext(1);
|
|
31
|
+
i0.ɵɵelementEnd();
|
|
32
|
+
} if (rf & 2) {
|
|
33
|
+
const l_r5 = i0.ɵɵnextContext().$implicit;
|
|
34
|
+
i0.ɵɵadvance();
|
|
35
|
+
i0.ɵɵtextInterpolate(l_r5.Description);
|
|
36
|
+
} }
|
|
37
|
+
function AudienceSourcePickerComponent_Conditional_17_Conditional_5_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
38
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
39
|
+
i0.ɵɵelementStart(0, "div", 18);
|
|
40
|
+
i0.ɵɵlistener("click", function AudienceSourcePickerComponent_Conditional_17_Conditional_5_For_2_Template_div_click_0_listener() { const l_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OnSelectList(l_r5)); });
|
|
41
|
+
i0.ɵɵelement(1, "i", 19);
|
|
42
|
+
i0.ɵɵelementStart(2, "div", 20)(3, "div", 21);
|
|
43
|
+
i0.ɵɵtext(4);
|
|
44
|
+
i0.ɵɵelementEnd();
|
|
45
|
+
i0.ɵɵconditionalCreate(5, AudienceSourcePickerComponent_Conditional_17_Conditional_5_For_2_Conditional_5_Template, 2, 1, "div", 22);
|
|
46
|
+
i0.ɵɵelementEnd()();
|
|
47
|
+
} if (rf & 2) {
|
|
48
|
+
const l_r5 = ctx.$implicit;
|
|
49
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
50
|
+
i0.ɵɵclassProp("audience-picker__item--selected", ctx_r2.selectedListId === l_r5.ID);
|
|
51
|
+
i0.ɵɵadvance();
|
|
52
|
+
i0.ɵɵclassProp("fa-circle-check", ctx_r2.selectedListId === l_r5.ID)("fa-circle", ctx_r2.selectedListId !== l_r5.ID);
|
|
53
|
+
i0.ɵɵadvance(3);
|
|
54
|
+
i0.ɵɵtextInterpolate(l_r5.Name);
|
|
55
|
+
i0.ɵɵadvance();
|
|
56
|
+
i0.ɵɵconditional(l_r5.Description ? 5 : -1);
|
|
57
|
+
} }
|
|
58
|
+
function AudienceSourcePickerComponent_Conditional_17_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
59
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
60
|
+
i0.ɵɵrepeaterCreate(1, AudienceSourcePickerComponent_Conditional_17_Conditional_5_For_2_Template, 6, 8, "div", 17, _forTrack0);
|
|
61
|
+
i0.ɵɵelementEnd();
|
|
62
|
+
} if (rf & 2) {
|
|
63
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
64
|
+
i0.ɵɵadvance();
|
|
65
|
+
i0.ɵɵrepeater(ctx_r2.filteredLists);
|
|
66
|
+
} }
|
|
67
|
+
function AudienceSourcePickerComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
68
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
69
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
70
|
+
i0.ɵɵelement(1, "i", 11);
|
|
71
|
+
i0.ɵɵelementStart(2, "input", 12);
|
|
72
|
+
i0.ɵɵtwoWayListener("ngModelChange", function AudienceSourcePickerComponent_Conditional_17_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.listSearch, $event) || (ctx_r2.listSearch = $event); return i0.ɵɵresetView($event); });
|
|
73
|
+
i0.ɵɵlistener("ngModelChange", function AudienceSourcePickerComponent_Conditional_17_Template_input_ngModelChange_2_listener() { i0.ɵɵrestoreView(_r2); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnListSearchChange()); });
|
|
74
|
+
i0.ɵɵelementEnd()();
|
|
75
|
+
i0.ɵɵconditionalCreate(3, AudienceSourcePickerComponent_Conditional_17_Conditional_3_Template, 2, 0, "div", 13)(4, AudienceSourcePickerComponent_Conditional_17_Conditional_4_Template, 2, 0, "div", 14)(5, AudienceSourcePickerComponent_Conditional_17_Conditional_5_Template, 3, 0, "div", 15);
|
|
76
|
+
} if (rf & 2) {
|
|
77
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
78
|
+
i0.ɵɵadvance(2);
|
|
79
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r2.listSearch);
|
|
80
|
+
i0.ɵɵadvance();
|
|
81
|
+
i0.ɵɵconditional(ctx_r2.loading ? 3 : ctx_r2.filteredLists.length === 0 ? 4 : 5);
|
|
82
|
+
} }
|
|
83
|
+
function AudienceSourcePickerComponent_Conditional_18_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
84
|
+
i0.ɵɵelementStart(0, "div", 13);
|
|
85
|
+
i0.ɵɵelement(1, "mj-loading", 24);
|
|
86
|
+
i0.ɵɵelementEnd();
|
|
87
|
+
} }
|
|
88
|
+
function AudienceSourcePickerComponent_Conditional_18_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
89
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
90
|
+
i0.ɵɵtext(1, " No views found for this entity. ");
|
|
91
|
+
i0.ɵɵelementEnd();
|
|
92
|
+
} }
|
|
93
|
+
function AudienceSourcePickerComponent_Conditional_18_Conditional_5_For_2_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
94
|
+
i0.ɵɵelementStart(0, "div", 22);
|
|
95
|
+
i0.ɵɵtext(1);
|
|
96
|
+
i0.ɵɵelementEnd();
|
|
97
|
+
} if (rf & 2) {
|
|
98
|
+
const v_r8 = i0.ɵɵnextContext().$implicit;
|
|
99
|
+
i0.ɵɵadvance();
|
|
100
|
+
i0.ɵɵtextInterpolate(v_r8.Description);
|
|
101
|
+
} }
|
|
102
|
+
function AudienceSourcePickerComponent_Conditional_18_Conditional_5_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
103
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
104
|
+
i0.ɵɵelementStart(0, "div", 18);
|
|
105
|
+
i0.ɵɵlistener("click", function AudienceSourcePickerComponent_Conditional_18_Conditional_5_For_2_Template_div_click_0_listener() { const v_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OnSelectView(v_r8)); });
|
|
106
|
+
i0.ɵɵelement(1, "i", 19);
|
|
107
|
+
i0.ɵɵelementStart(2, "div", 20)(3, "div", 21);
|
|
108
|
+
i0.ɵɵtext(4);
|
|
109
|
+
i0.ɵɵelementStart(5, "span", 27);
|
|
110
|
+
i0.ɵɵtext(6, "live");
|
|
111
|
+
i0.ɵɵelementEnd()();
|
|
112
|
+
i0.ɵɵconditionalCreate(7, AudienceSourcePickerComponent_Conditional_18_Conditional_5_For_2_Conditional_7_Template, 2, 1, "div", 22);
|
|
113
|
+
i0.ɵɵelementEnd()();
|
|
114
|
+
} if (rf & 2) {
|
|
115
|
+
const v_r8 = ctx.$implicit;
|
|
116
|
+
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
117
|
+
i0.ɵɵclassProp("audience-picker__item--selected", ctx_r2.selectedViewId === v_r8.ID);
|
|
118
|
+
i0.ɵɵadvance();
|
|
119
|
+
i0.ɵɵclassProp("fa-circle-check", ctx_r2.selectedViewId === v_r8.ID)("fa-circle", ctx_r2.selectedViewId !== v_r8.ID);
|
|
120
|
+
i0.ɵɵadvance(3);
|
|
121
|
+
i0.ɵɵtextInterpolate1(" ", v_r8.Name, " ");
|
|
122
|
+
i0.ɵɵadvance(3);
|
|
123
|
+
i0.ɵɵconditional(v_r8.Description ? 7 : -1);
|
|
124
|
+
} }
|
|
125
|
+
function AudienceSourcePickerComponent_Conditional_18_Conditional_5_Template(rf, ctx) { if (rf & 1) {
|
|
126
|
+
i0.ɵɵelementStart(0, "div", 15);
|
|
127
|
+
i0.ɵɵrepeaterCreate(1, AudienceSourcePickerComponent_Conditional_18_Conditional_5_For_2_Template, 8, 8, "div", 17, _forTrack0);
|
|
128
|
+
i0.ɵɵelementEnd();
|
|
129
|
+
i0.ɵɵelementStart(3, "div", 25);
|
|
130
|
+
i0.ɵɵelement(4, "i", 26);
|
|
131
|
+
i0.ɵɵtext(5, " A view is resolved live at execution \u2014 the record set may differ from what you see here if data changes. ");
|
|
132
|
+
i0.ɵɵelementEnd();
|
|
133
|
+
} if (rf & 2) {
|
|
134
|
+
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
135
|
+
i0.ɵɵadvance();
|
|
136
|
+
i0.ɵɵrepeater(ctx_r2.filteredViews);
|
|
137
|
+
} }
|
|
138
|
+
function AudienceSourcePickerComponent_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
139
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
140
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
141
|
+
i0.ɵɵelement(1, "i", 11);
|
|
142
|
+
i0.ɵɵelementStart(2, "input", 23);
|
|
143
|
+
i0.ɵɵtwoWayListener("ngModelChange", function AudienceSourcePickerComponent_Conditional_18_Template_input_ngModelChange_2_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.viewSearch, $event) || (ctx_r2.viewSearch = $event); return i0.ɵɵresetView($event); });
|
|
144
|
+
i0.ɵɵlistener("ngModelChange", function AudienceSourcePickerComponent_Conditional_18_Template_input_ngModelChange_2_listener() { i0.ɵɵrestoreView(_r6); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnViewSearchChange()); });
|
|
145
|
+
i0.ɵɵelementEnd()();
|
|
146
|
+
i0.ɵɵconditionalCreate(3, AudienceSourcePickerComponent_Conditional_18_Conditional_3_Template, 2, 0, "div", 13)(4, AudienceSourcePickerComponent_Conditional_18_Conditional_4_Template, 2, 0, "div", 14)(5, AudienceSourcePickerComponent_Conditional_18_Conditional_5_Template, 6, 0);
|
|
147
|
+
} if (rf & 2) {
|
|
148
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
149
|
+
i0.ɵɵadvance(2);
|
|
150
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r2.viewSearch);
|
|
151
|
+
i0.ɵɵadvance();
|
|
152
|
+
i0.ɵɵconditional(ctx_r2.loading ? 3 : ctx_r2.filteredViews.length === 0 ? 4 : 5);
|
|
153
|
+
} }
|
|
154
|
+
function AudienceSourcePickerComponent_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
155
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
156
|
+
i0.ɵɵelementStart(0, "div", 6)(1, "label", 7);
|
|
157
|
+
i0.ɵɵtext(2, "Filter");
|
|
158
|
+
i0.ɵɵelementEnd();
|
|
159
|
+
i0.ɵɵelementStart(3, "textarea", 28);
|
|
160
|
+
i0.ɵɵtwoWayListener("ngModelChange", function AudienceSourcePickerComponent_Conditional_19_Template_textarea_ngModelChange_3_listener($event) { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.adhocFilter, $event) || (ctx_r2.adhocFilter = $event); return i0.ɵɵresetView($event); });
|
|
161
|
+
i0.ɵɵlistener("ngModelChange", function AudienceSourcePickerComponent_Conditional_19_Template_textarea_ngModelChange_3_listener() { i0.ɵɵrestoreView(_r9); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnAdhocFilterChange()); });
|
|
162
|
+
i0.ɵɵelementEnd()();
|
|
163
|
+
i0.ɵɵelementStart(4, "div", 29);
|
|
164
|
+
i0.ɵɵelement(5, "i", 30);
|
|
165
|
+
i0.ɵɵtext(6, " Ad-hoc filters are evaluated server-side at execution time. Validate the filter on the server (via the resolve-audience action or send preview) before relying on it for production. ");
|
|
166
|
+
i0.ɵɵelementEnd();
|
|
167
|
+
} if (rf & 2) {
|
|
168
|
+
const ctx_r2 = i0.ɵɵnextContext();
|
|
169
|
+
i0.ɵɵadvance(3);
|
|
170
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r2.adhocFilter);
|
|
171
|
+
} }
|
|
172
|
+
export class AudienceSourcePickerComponent extends BaseAngularComponent {
|
|
173
|
+
cdr = inject(ChangeDetectorRef);
|
|
174
|
+
/**
|
|
175
|
+
* Pre-seed the picker with an existing audience. Useful when editing
|
|
176
|
+
* a previously-configured audience on a saved campaign.
|
|
177
|
+
*/
|
|
178
|
+
set Source(value) {
|
|
179
|
+
this._source = value;
|
|
180
|
+
if (value) {
|
|
181
|
+
// Pick the right tab + restore the saved field values.
|
|
182
|
+
this.activeTab = value.kind;
|
|
183
|
+
if (value.kind === 'list') {
|
|
184
|
+
this.selectedListId = value.listId;
|
|
185
|
+
}
|
|
186
|
+
else if (value.kind === 'view') {
|
|
187
|
+
this.selectedViewId = value.viewId;
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
this.adhocEntityName = value.entityName;
|
|
191
|
+
this.adhocFilter = value.extraFilter;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
get Source() {
|
|
196
|
+
return this._source;
|
|
197
|
+
}
|
|
198
|
+
_source = null;
|
|
199
|
+
/** When set, restrict the entity dropdown to this entity (lock it). */
|
|
200
|
+
LockedEntityName = null;
|
|
201
|
+
SourceChange = new EventEmitter();
|
|
202
|
+
activeTab = 'list';
|
|
203
|
+
entityOptions = [];
|
|
204
|
+
// List-tab state
|
|
205
|
+
selectedEntityName = null;
|
|
206
|
+
availableLists = [];
|
|
207
|
+
filteredLists = [];
|
|
208
|
+
listSearch = '';
|
|
209
|
+
selectedListId = null;
|
|
210
|
+
// View-tab state
|
|
211
|
+
availableViews = [];
|
|
212
|
+
filteredViews = [];
|
|
213
|
+
viewSearch = '';
|
|
214
|
+
selectedViewId = null;
|
|
215
|
+
// Adhoc-tab state
|
|
216
|
+
adhocEntityName = null;
|
|
217
|
+
adhocFilter = '';
|
|
218
|
+
loading = false;
|
|
219
|
+
async ngOnInit() {
|
|
220
|
+
this.entityOptions = this.ProviderToUse.Entities
|
|
221
|
+
.filter((e) => !e.SchemaName.startsWith('sys'))
|
|
222
|
+
.sort((a, b) => a.Name.localeCompare(b.Name));
|
|
223
|
+
if (this.LockedEntityName) {
|
|
224
|
+
this.selectedEntityName = this.LockedEntityName;
|
|
225
|
+
this.adhocEntityName = this.LockedEntityName;
|
|
226
|
+
}
|
|
227
|
+
else if (this.entityOptions.length > 0) {
|
|
228
|
+
this.selectedEntityName = this.entityOptions[0].Name;
|
|
229
|
+
this.adhocEntityName = this.selectedEntityName;
|
|
230
|
+
}
|
|
231
|
+
await this.refreshTabData();
|
|
232
|
+
}
|
|
233
|
+
async setTab(tab) {
|
|
234
|
+
this.activeTab = tab;
|
|
235
|
+
this.emitCurrentSource();
|
|
236
|
+
await this.refreshTabData();
|
|
237
|
+
}
|
|
238
|
+
async OnEntityChange(name) {
|
|
239
|
+
this.selectedEntityName = name;
|
|
240
|
+
this.adhocEntityName = name;
|
|
241
|
+
// Selecting a new entity invalidates any picked list/view.
|
|
242
|
+
this.selectedListId = null;
|
|
243
|
+
this.selectedViewId = null;
|
|
244
|
+
this.emitCurrentSource();
|
|
245
|
+
await this.refreshTabData();
|
|
246
|
+
}
|
|
247
|
+
OnListSearchChange() {
|
|
248
|
+
this.recomputeFilteredLists();
|
|
249
|
+
}
|
|
250
|
+
OnViewSearchChange() {
|
|
251
|
+
this.recomputeFilteredViews();
|
|
252
|
+
}
|
|
253
|
+
OnSelectList(list) {
|
|
254
|
+
this.selectedListId = list.ID;
|
|
255
|
+
this.emitCurrentSource();
|
|
256
|
+
}
|
|
257
|
+
OnSelectView(view) {
|
|
258
|
+
this.selectedViewId = view.ID;
|
|
259
|
+
this.emitCurrentSource();
|
|
260
|
+
}
|
|
261
|
+
OnAdhocFilterChange() {
|
|
262
|
+
this.emitCurrentSource();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Build the currently-selected `AudienceSource` from picker state, or
|
|
266
|
+
* `null` if the picker isn't yet in a complete state.
|
|
267
|
+
*/
|
|
268
|
+
get currentSource() {
|
|
269
|
+
if (this.activeTab === 'list') {
|
|
270
|
+
return this.selectedListId ? { kind: 'list', listId: this.selectedListId } : null;
|
|
271
|
+
}
|
|
272
|
+
if (this.activeTab === 'view') {
|
|
273
|
+
return this.selectedViewId ? { kind: 'view', viewId: this.selectedViewId } : null;
|
|
274
|
+
}
|
|
275
|
+
// adhoc
|
|
276
|
+
if (this.adhocEntityName && this.adhocFilter.trim().length > 0) {
|
|
277
|
+
return { kind: 'adhoc', entityName: this.adhocEntityName, extraFilter: this.adhocFilter.trim() };
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
emitCurrentSource() {
|
|
282
|
+
this._source = this.currentSource;
|
|
283
|
+
this.SourceChange.emit(this._source);
|
|
284
|
+
this.cdr.markForCheck();
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Pull lists or views for the current tab from the server. We
|
|
288
|
+
* intentionally batch the (list, view) pair on entity change so
|
|
289
|
+
* switching tabs doesn't re-query.
|
|
290
|
+
*/
|
|
291
|
+
async refreshTabData() {
|
|
292
|
+
if (!this.selectedEntityName)
|
|
293
|
+
return;
|
|
294
|
+
this.loading = true;
|
|
295
|
+
this.cdr.markForCheck();
|
|
296
|
+
try {
|
|
297
|
+
const entityInfo = this.ProviderToUse.EntityByName(this.selectedEntityName);
|
|
298
|
+
if (!entityInfo) {
|
|
299
|
+
this.availableLists = [];
|
|
300
|
+
this.availableViews = [];
|
|
301
|
+
this.recomputeFilteredLists();
|
|
302
|
+
this.recomputeFilteredViews();
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
306
|
+
const [lists, views] = await Promise.all([
|
|
307
|
+
rv.RunView({
|
|
308
|
+
EntityName: 'MJ: Lists',
|
|
309
|
+
ExtraFilter: `EntityID='${entityInfo.ID}'`,
|
|
310
|
+
OrderBy: 'Name',
|
|
311
|
+
ResultType: 'entity_object',
|
|
312
|
+
}),
|
|
313
|
+
rv.RunView({
|
|
314
|
+
EntityName: 'MJ: User Views',
|
|
315
|
+
ExtraFilter: `EntityID='${entityInfo.ID}'`,
|
|
316
|
+
OrderBy: 'Name',
|
|
317
|
+
ResultType: 'entity_object',
|
|
318
|
+
}),
|
|
319
|
+
]);
|
|
320
|
+
this.availableLists = lists.Success ? lists.Results ?? [] : [];
|
|
321
|
+
this.availableViews = views.Success ? views.Results ?? [] : [];
|
|
322
|
+
this.recomputeFilteredLists();
|
|
323
|
+
this.recomputeFilteredViews();
|
|
324
|
+
}
|
|
325
|
+
finally {
|
|
326
|
+
this.loading = false;
|
|
327
|
+
this.cdr.markForCheck();
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
recomputeFilteredLists() {
|
|
331
|
+
const term = this.listSearch.trim().toLowerCase();
|
|
332
|
+
this.filteredLists = term
|
|
333
|
+
? this.availableLists.filter((l) => l.Name.toLowerCase().includes(term))
|
|
334
|
+
: this.availableLists;
|
|
335
|
+
this.cdr.markForCheck();
|
|
336
|
+
}
|
|
337
|
+
recomputeFilteredViews() {
|
|
338
|
+
const term = this.viewSearch.trim().toLowerCase();
|
|
339
|
+
this.filteredViews = term
|
|
340
|
+
? this.availableViews.filter((v) => v.Name.toLowerCase().includes(term))
|
|
341
|
+
: this.availableViews;
|
|
342
|
+
this.cdr.markForCheck();
|
|
343
|
+
}
|
|
344
|
+
static ɵfac = /*@__PURE__*/ (() => { let ɵAudienceSourcePickerComponent_BaseFactory; return function AudienceSourcePickerComponent_Factory(__ngFactoryType__) { return (ɵAudienceSourcePickerComponent_BaseFactory || (ɵAudienceSourcePickerComponent_BaseFactory = i0.ɵɵgetInheritedFactory(AudienceSourcePickerComponent)))(__ngFactoryType__ || AudienceSourcePickerComponent); }; })();
|
|
345
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AudienceSourcePickerComponent, selectors: [["mj-audience-source-picker"]], inputs: { Source: "Source", LockedEntityName: "LockedEntityName" }, outputs: { SourceChange: "SourceChange" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 20, vars: 9, consts: [[1, "audience-picker"], [1, "audience-picker__tabs"], [1, "audience-picker__tab", 3, "click"], [1, "fa-solid", "fa-list"], [1, "fa-solid", "fa-eye"], [1, "fa-solid", "fa-filter"], [1, "audience-picker__field"], [1, "audience-picker__label"], [1, "mj-input", "mj-select", "audience-picker__entity", 3, "ngModelChange", "ngModel", "disabled"], [3, "ngValue"], [1, "audience-picker__search"], [1, "fa-solid", "fa-search"], ["placeholder", "Search lists by name\u2026", 1, "mj-input", 3, "ngModelChange", "ngModel"], [1, "audience-picker__state"], [1, "audience-picker__state", "audience-picker__state--empty"], [1, "audience-picker__items"], ["text", "Loading lists...", "size", "small"], [1, "audience-picker__item", 3, "audience-picker__item--selected"], [1, "audience-picker__item", 3, "click"], [1, "fa-solid"], [1, "audience-picker__item-body"], [1, "audience-picker__item-name"], [1, "audience-picker__item-desc"], ["placeholder", "Search views by name\u2026", 1, "mj-input", 3, "ngModelChange", "ngModel"], ["text", "Loading views...", "size", "small"], [1, "audience-picker__hint"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "audience-picker__live-badge"], ["rows", "3", "placeholder", "e.g. State IN ('CA','NY','TX') AND DonationCount >= 3", 1, "mj-input", "audience-picker__filter", 3, "ngModelChange", "ngModel"], [1, "audience-picker__hint", "audience-picker__hint--neutral"], [1, "fa-solid", "fa-circle-info"]], template: function AudienceSourcePickerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
346
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "button", 2);
|
|
347
|
+
i0.ɵɵlistener("click", function AudienceSourcePickerComponent_Template_button_click_2_listener() { return ctx.setTab("list"); });
|
|
348
|
+
i0.ɵɵelement(3, "i", 3);
|
|
349
|
+
i0.ɵɵtext(4, " List ");
|
|
350
|
+
i0.ɵɵelementEnd();
|
|
351
|
+
i0.ɵɵelementStart(5, "button", 2);
|
|
352
|
+
i0.ɵɵlistener("click", function AudienceSourcePickerComponent_Template_button_click_5_listener() { return ctx.setTab("view"); });
|
|
353
|
+
i0.ɵɵelement(6, "i", 4);
|
|
354
|
+
i0.ɵɵtext(7, " View ");
|
|
355
|
+
i0.ɵɵelementEnd();
|
|
356
|
+
i0.ɵɵelementStart(8, "button", 2);
|
|
357
|
+
i0.ɵɵlistener("click", function AudienceSourcePickerComponent_Template_button_click_8_listener() { return ctx.setTab("adhoc"); });
|
|
358
|
+
i0.ɵɵelement(9, "i", 5);
|
|
359
|
+
i0.ɵɵtext(10, " Ad-hoc Filter ");
|
|
360
|
+
i0.ɵɵelementEnd()();
|
|
361
|
+
i0.ɵɵelementStart(11, "div", 6)(12, "label", 7);
|
|
362
|
+
i0.ɵɵtext(13, "Entity");
|
|
363
|
+
i0.ɵɵelementEnd();
|
|
364
|
+
i0.ɵɵelementStart(14, "select", 8);
|
|
365
|
+
i0.ɵɵlistener("ngModelChange", function AudienceSourcePickerComponent_Template_select_ngModelChange_14_listener($event) { return ctx.OnEntityChange($event); });
|
|
366
|
+
i0.ɵɵrepeaterCreate(15, AudienceSourcePickerComponent_For_16_Template, 2, 2, "option", 9, _forTrack0);
|
|
367
|
+
i0.ɵɵelementEnd()();
|
|
368
|
+
i0.ɵɵconditionalCreate(17, AudienceSourcePickerComponent_Conditional_17_Template, 6, 2)(18, AudienceSourcePickerComponent_Conditional_18_Template, 6, 2)(19, AudienceSourcePickerComponent_Conditional_19_Template, 7, 1);
|
|
369
|
+
i0.ɵɵelementEnd();
|
|
370
|
+
} if (rf & 2) {
|
|
371
|
+
i0.ɵɵadvance(2);
|
|
372
|
+
i0.ɵɵclassProp("audience-picker__tab--active", ctx.activeTab === "list");
|
|
373
|
+
i0.ɵɵadvance(3);
|
|
374
|
+
i0.ɵɵclassProp("audience-picker__tab--active", ctx.activeTab === "view");
|
|
375
|
+
i0.ɵɵadvance(3);
|
|
376
|
+
i0.ɵɵclassProp("audience-picker__tab--active", ctx.activeTab === "adhoc");
|
|
377
|
+
i0.ɵɵadvance(6);
|
|
378
|
+
i0.ɵɵproperty("ngModel", ctx.selectedEntityName)("disabled", !!ctx.LockedEntityName);
|
|
379
|
+
i0.ɵɵadvance();
|
|
380
|
+
i0.ɵɵrepeater(ctx.entityOptions);
|
|
381
|
+
i0.ɵɵadvance(2);
|
|
382
|
+
i0.ɵɵconditional(ctx.activeTab === "list" ? 17 : ctx.activeTab === "view" ? 18 : 19);
|
|
383
|
+
} }, dependencies: [i1.NgSelectOption, i1.ɵNgSelectMultipleOption, i1.DefaultValueAccessor, i1.SelectControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.LoadingComponent], styles: [".audience-picker[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.audience-picker__tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n border-bottom: 1px solid var(--mj-border-default);\n padding-bottom: 6px;\n}\n\n.audience-picker__tab[_ngcontent-%COMP%] {\n background: none;\n border: 1px solid transparent;\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 12.5px;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n}\n\n.audience-picker__tab--active[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.audience-picker__field[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.audience-picker__label[_ngcontent-%COMP%] {\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n}\n\n.audience-picker__entity[_ngcontent-%COMP%] {\n max-width: 240px;\n}\n\n.audience-picker__search[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.audience-picker__search[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n position: absolute;\n left: 10px;\n color: var(--mj-text-muted);\n pointer-events: none;\n}\n\n.audience-picker__search[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n width: 100%;\n padding-left: 32px;\n}\n\n.audience-picker__filter[_ngcontent-%COMP%] {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n}\n\n.audience-picker__items[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n max-height: 240px;\n overflow-y: auto;\n}\n\n.audience-picker__item[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-subtle);\n transition: background 0.12s ease;\n}\n\n.audience-picker__item[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n}\n\n.audience-picker__item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.audience-picker__item--selected[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.audience-picker__item--selected[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n\n.audience-picker__item-body[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.audience-picker__item-name[_ngcontent-%COMP%] {\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.audience-picker__item-desc[_ngcontent-%COMP%] {\n font-size: 11.5px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.audience-picker__live-badge[_ngcontent-%COMP%] {\n font-size: 10px;\n padding: 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.audience-picker__state[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n padding: 24px;\n color: var(--mj-text-muted);\n font-size: 12.5px;\n}\n\n.audience-picker__hint[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n font-size: 11.5px;\n color: var(--mj-text-muted);\n padding: 6px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n}\n\n.audience-picker__hint--neutral[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}"], changeDetection: 0 });
|
|
384
|
+
}
|
|
385
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AudienceSourcePickerComponent, [{
|
|
386
|
+
type: Component,
|
|
387
|
+
args: [{ standalone: false, selector: 'mj-audience-source-picker', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"audience-picker\">\n <!-- Tab strip -->\n <div class=\"audience-picker__tabs\">\n <button\n class=\"audience-picker__tab\"\n [class.audience-picker__tab--active]=\"activeTab === 'list'\"\n (click)=\"setTab('list')\">\n <i class=\"fa-solid fa-list\"></i>\n List\n </button>\n <button\n class=\"audience-picker__tab\"\n [class.audience-picker__tab--active]=\"activeTab === 'view'\"\n (click)=\"setTab('view')\">\n <i class=\"fa-solid fa-eye\"></i>\n View\n </button>\n <button\n class=\"audience-picker__tab\"\n [class.audience-picker__tab--active]=\"activeTab === 'adhoc'\"\n (click)=\"setTab('adhoc')\">\n <i class=\"fa-solid fa-filter\"></i>\n Ad-hoc Filter\n </button>\n </div>\n\n <!-- Entity selector (shared across tabs) -->\n <div class=\"audience-picker__field\">\n <label class=\"audience-picker__label\">Entity</label>\n <select\n class=\"mj-input mj-select audience-picker__entity\"\n [ngModel]=\"selectedEntityName\"\n (ngModelChange)=\"OnEntityChange($event)\"\n [disabled]=\"!!LockedEntityName\">\n @for (e of entityOptions; track e.ID) {\n <option [ngValue]=\"e.Name\">{{ e.Name }}</option>\n }\n </select>\n </div>\n\n @if (activeTab === 'list') {\n <div class=\"audience-picker__search\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n class=\"mj-input\"\n [(ngModel)]=\"listSearch\"\n (ngModelChange)=\"OnListSearchChange()\"\n placeholder=\"Search lists by name\u2026\" />\n </div>\n @if (loading) {\n <div class=\"audience-picker__state\">\n <mj-loading text=\"Loading lists...\" size=\"small\"></mj-loading>\n </div>\n } @else if (filteredLists.length === 0) {\n <div class=\"audience-picker__state audience-picker__state--empty\">\n No lists found for this entity.\n </div>\n } @else {\n <div class=\"audience-picker__items\">\n @for (l of filteredLists; track l.ID) {\n <div\n class=\"audience-picker__item\"\n [class.audience-picker__item--selected]=\"selectedListId === l.ID\"\n (click)=\"OnSelectList(l)\">\n <i\n class=\"fa-solid\"\n [class.fa-circle-check]=\"selectedListId === l.ID\"\n [class.fa-circle]=\"selectedListId !== l.ID\"></i>\n <div class=\"audience-picker__item-body\">\n <div class=\"audience-picker__item-name\">{{ l.Name }}</div>\n @if (l.Description) {\n <div class=\"audience-picker__item-desc\">{{ l.Description }}</div>\n }\n </div>\n </div>\n }\n </div>\n }\n } @else if (activeTab === 'view') {\n <div class=\"audience-picker__search\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n class=\"mj-input\"\n [(ngModel)]=\"viewSearch\"\n (ngModelChange)=\"OnViewSearchChange()\"\n placeholder=\"Search views by name\u2026\" />\n </div>\n @if (loading) {\n <div class=\"audience-picker__state\">\n <mj-loading text=\"Loading views...\" size=\"small\"></mj-loading>\n </div>\n } @else if (filteredViews.length === 0) {\n <div class=\"audience-picker__state audience-picker__state--empty\">\n No views found for this entity.\n </div>\n } @else {\n <div class=\"audience-picker__items\">\n @for (v of filteredViews; track v.ID) {\n <div\n class=\"audience-picker__item\"\n [class.audience-picker__item--selected]=\"selectedViewId === v.ID\"\n (click)=\"OnSelectView(v)\">\n <i\n class=\"fa-solid\"\n [class.fa-circle-check]=\"selectedViewId === v.ID\"\n [class.fa-circle]=\"selectedViewId !== v.ID\"></i>\n <div class=\"audience-picker__item-body\">\n <div class=\"audience-picker__item-name\">\n {{ v.Name }}\n <span class=\"audience-picker__live-badge\">live</span>\n </div>\n @if (v.Description) {\n <div class=\"audience-picker__item-desc\">{{ v.Description }}</div>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"audience-picker__hint\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n A view is resolved live at execution \u2014 the record set may differ from\n what you see here if data changes.\n </div>\n }\n } @else {\n <!-- Ad-hoc filter tab -->\n <div class=\"audience-picker__field\">\n <label class=\"audience-picker__label\">Filter</label>\n <textarea\n class=\"mj-input audience-picker__filter\"\n rows=\"3\"\n [(ngModel)]=\"adhocFilter\"\n (ngModelChange)=\"OnAdhocFilterChange()\"\n placeholder=\"e.g. State IN ('CA','NY','TX') AND DonationCount >= 3\"></textarea>\n </div>\n <div class=\"audience-picker__hint audience-picker__hint--neutral\">\n <i class=\"fa-solid fa-circle-info\"></i>\n Ad-hoc filters are evaluated server-side at execution time. Validate the\n filter on the server (via the resolve-audience action or send preview)\n before relying on it for production.\n </div>\n }\n</div>\n", styles: [".audience-picker {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 16px;\n font-size: 13px;\n color: var(--mj-text-primary);\n}\n\n.audience-picker__tabs {\n display: flex;\n gap: 6px;\n border-bottom: 1px solid var(--mj-border-default);\n padding-bottom: 6px;\n}\n\n.audience-picker__tab {\n background: none;\n border: 1px solid transparent;\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 12.5px;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n color: var(--mj-text-secondary);\n}\n\n.audience-picker__tab--active {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.audience-picker__field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.audience-picker__label {\n font-size: 11px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--mj-text-muted);\n}\n\n.audience-picker__entity {\n max-width: 240px;\n}\n\n.audience-picker__search {\n position: relative;\n display: flex;\n align-items: center;\n}\n\n.audience-picker__search i {\n position: absolute;\n left: 10px;\n color: var(--mj-text-muted);\n pointer-events: none;\n}\n\n.audience-picker__search input {\n width: 100%;\n padding-left: 32px;\n}\n\n.audience-picker__filter {\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: 12px;\n}\n\n.audience-picker__items {\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n max-height: 240px;\n overflow-y: auto;\n}\n\n.audience-picker__item {\n display: flex;\n align-items: flex-start;\n gap: 10px;\n padding: 10px 12px;\n cursor: pointer;\n border-bottom: 1px solid var(--mj-border-subtle);\n transition: background 0.12s ease;\n}\n\n.audience-picker__item:last-child {\n border-bottom: none;\n}\n\n.audience-picker__item:hover {\n background: var(--mj-bg-surface-card);\n}\n\n.audience-picker__item--selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.audience-picker__item--selected i {\n color: var(--mj-brand-primary);\n}\n\n.audience-picker__item-body {\n flex: 1;\n min-width: 0;\n}\n\n.audience-picker__item-name {\n font-weight: 600;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n}\n\n.audience-picker__item-desc {\n font-size: 11.5px;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.audience-picker__live-badge {\n font-size: 10px;\n padding: 1px 6px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.audience-picker__state {\n display: flex;\n justify-content: center;\n padding: 24px;\n color: var(--mj-text-muted);\n font-size: 12.5px;\n}\n\n.audience-picker__hint {\n display: flex;\n align-items: flex-start;\n gap: 6px;\n font-size: 11.5px;\n color: var(--mj-text-muted);\n padding: 6px 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n}\n\n.audience-picker__hint--neutral i {\n color: var(--mj-brand-primary);\n}\n"] }]
|
|
388
|
+
}], null, { Source: [{
|
|
389
|
+
type: Input
|
|
390
|
+
}], LockedEntityName: [{
|
|
391
|
+
type: Input
|
|
392
|
+
}], SourceChange: [{
|
|
393
|
+
type: Output
|
|
394
|
+
}] }); })();
|
|
395
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AudienceSourcePickerComponent, { className: "AudienceSourcePickerComponent", filePath: "src/lib/components/audience-source-picker/audience-source-picker.component.ts", lineNumber: 32 }); })();
|
|
396
|
+
//# sourceMappingURL=audience-source-picker.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audience-source-picker.component.js","sourceRoot":"","sources":["../../../../src/lib/components/audience-source-picker/audience-source-picker.component.ts","../../../../src/lib/components/audience-source-picker/audience-source-picker.component.html"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core';\n\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\nimport { RunView, type EntityInfo } from '@memberjunction/core';\nimport type { MJListEntity, MJUserViewEntity } from '@memberjunction/core-entities';\nimport type { AudienceSource } from '@memberjunction/lists-base';\n\n/**\n * Reusable picker for an `AudienceSource` (mockup 20). Three tabs:\n *\n * - **List** — pick from saved `MJ: List`s for the chosen entity.\n * - **View** — pick from saved `MJ: User View`s for the chosen entity.\n * Marked \"live\" so the user knows the record set is re-resolved at\n * execution time.\n * - **Ad-hoc Filter** — entity + free-form `ExtraFilter`. Skips\n * server-side validation; the consumer can pre-flight via\n * `ResolveAudience` (Action) or `AudienceResolver.Resolve` if desired.\n *\n * Emits the typed `AudienceSource` via `SourceChange` on selection; the\n * sister `AudienceSourceSummaryComponent` renders the friendly summary.\n * Multi-provider safe via `BaseAngularComponent.ProviderToUse`.\n */\nexport type AudienceSourcePickerTab = 'list' | 'view' | 'adhoc';\n\n@Component({\n standalone: false,\n selector: 'mj-audience-source-picker',\n templateUrl: './audience-source-picker.component.html',\n styleUrls: ['./audience-source-picker.component.css'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AudienceSourcePickerComponent extends BaseAngularComponent implements OnInit {\n private readonly cdr = inject(ChangeDetectorRef);\n\n /**\n * Pre-seed the picker with an existing audience. Useful when editing\n * a previously-configured audience on a saved campaign.\n */\n @Input()\n set Source(value: AudienceSource | null) {\n this._source = value;\n if (value) {\n // Pick the right tab + restore the saved field values.\n this.activeTab = value.kind;\n if (value.kind === 'list') {\n this.selectedListId = value.listId;\n } else if (value.kind === 'view') {\n this.selectedViewId = value.viewId;\n } else {\n this.adhocEntityName = value.entityName;\n this.adhocFilter = value.extraFilter;\n }\n }\n }\n get Source(): AudienceSource | null {\n return this._source;\n }\n private _source: AudienceSource | null = null;\n\n /** When set, restrict the entity dropdown to this entity (lock it). */\n @Input() LockedEntityName: string | null = null;\n\n @Output() SourceChange = new EventEmitter<AudienceSource | null>();\n\n public activeTab: AudienceSourcePickerTab = 'list';\n public entityOptions: EntityInfo[] = [];\n\n // List-tab state\n public selectedEntityName: string | null = null;\n public availableLists: MJListEntity[] = [];\n public filteredLists: MJListEntity[] = [];\n public listSearch = '';\n public selectedListId: string | null = null;\n\n // View-tab state\n public availableViews: MJUserViewEntity[] = [];\n public filteredViews: MJUserViewEntity[] = [];\n public viewSearch = '';\n public selectedViewId: string | null = null;\n\n // Adhoc-tab state\n public adhocEntityName: string | null = null;\n public adhocFilter = '';\n\n public loading = false;\n\n async ngOnInit(): Promise<void> {\n this.entityOptions = this.ProviderToUse.Entities\n .filter((e) => !e.SchemaName.startsWith('sys'))\n .sort((a, b) => a.Name.localeCompare(b.Name));\n if (this.LockedEntityName) {\n this.selectedEntityName = this.LockedEntityName;\n this.adhocEntityName = this.LockedEntityName;\n } else if (this.entityOptions.length > 0) {\n this.selectedEntityName = this.entityOptions[0].Name;\n this.adhocEntityName = this.selectedEntityName;\n }\n await this.refreshTabData();\n }\n\n public async setTab(tab: AudienceSourcePickerTab): Promise<void> {\n this.activeTab = tab;\n this.emitCurrentSource();\n await this.refreshTabData();\n }\n\n public async OnEntityChange(name: string): Promise<void> {\n this.selectedEntityName = name;\n this.adhocEntityName = name;\n // Selecting a new entity invalidates any picked list/view.\n this.selectedListId = null;\n this.selectedViewId = null;\n this.emitCurrentSource();\n await this.refreshTabData();\n }\n\n public OnListSearchChange(): void {\n this.recomputeFilteredLists();\n }\n\n public OnViewSearchChange(): void {\n this.recomputeFilteredViews();\n }\n\n public OnSelectList(list: MJListEntity): void {\n this.selectedListId = list.ID;\n this.emitCurrentSource();\n }\n\n public OnSelectView(view: MJUserViewEntity): void {\n this.selectedViewId = view.ID;\n this.emitCurrentSource();\n }\n\n public OnAdhocFilterChange(): void {\n this.emitCurrentSource();\n }\n\n /**\n * Build the currently-selected `AudienceSource` from picker state, or\n * `null` if the picker isn't yet in a complete state.\n */\n public get currentSource(): AudienceSource | null {\n if (this.activeTab === 'list') {\n return this.selectedListId ? { kind: 'list', listId: this.selectedListId } : null;\n }\n if (this.activeTab === 'view') {\n return this.selectedViewId ? { kind: 'view', viewId: this.selectedViewId } : null;\n }\n // adhoc\n if (this.adhocEntityName && this.adhocFilter.trim().length > 0) {\n return { kind: 'adhoc', entityName: this.adhocEntityName, extraFilter: this.adhocFilter.trim() };\n }\n return null;\n }\n\n private emitCurrentSource(): void {\n this._source = this.currentSource;\n this.SourceChange.emit(this._source);\n this.cdr.markForCheck();\n }\n\n /**\n * Pull lists or views for the current tab from the server. We\n * intentionally batch the (list, view) pair on entity change so\n * switching tabs doesn't re-query.\n */\n private async refreshTabData(): Promise<void> {\n if (!this.selectedEntityName) return;\n this.loading = true;\n this.cdr.markForCheck();\n try {\n const entityInfo = this.ProviderToUse.EntityByName(this.selectedEntityName);\n if (!entityInfo) {\n this.availableLists = [];\n this.availableViews = [];\n this.recomputeFilteredLists();\n this.recomputeFilteredViews();\n return;\n }\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const [lists, views] = await Promise.all([\n rv.RunView<MJListEntity>({\n EntityName: 'MJ: Lists',\n ExtraFilter: `EntityID='${entityInfo.ID}'`,\n OrderBy: 'Name',\n ResultType: 'entity_object',\n }),\n rv.RunView<MJUserViewEntity>({\n EntityName: 'MJ: User Views',\n ExtraFilter: `EntityID='${entityInfo.ID}'`,\n OrderBy: 'Name',\n ResultType: 'entity_object',\n }),\n ]);\n this.availableLists = lists.Success ? lists.Results ?? [] : [];\n this.availableViews = views.Success ? views.Results ?? [] : [];\n this.recomputeFilteredLists();\n this.recomputeFilteredViews();\n } finally {\n this.loading = false;\n this.cdr.markForCheck();\n }\n }\n\n private recomputeFilteredLists(): void {\n const term = this.listSearch.trim().toLowerCase();\n this.filteredLists = term\n ? this.availableLists.filter((l) => l.Name.toLowerCase().includes(term))\n : this.availableLists;\n this.cdr.markForCheck();\n }\n\n private recomputeFilteredViews(): void {\n const term = this.viewSearch.trim().toLowerCase();\n this.filteredViews = term\n ? this.availableViews.filter((v) => v.Name.toLowerCase().includes(term))\n : this.availableViews;\n this.cdr.markForCheck();\n }\n}\n","<div class=\"audience-picker\">\n <!-- Tab strip -->\n <div class=\"audience-picker__tabs\">\n <button\n class=\"audience-picker__tab\"\n [class.audience-picker__tab--active]=\"activeTab === 'list'\"\n (click)=\"setTab('list')\">\n <i class=\"fa-solid fa-list\"></i>\n List\n </button>\n <button\n class=\"audience-picker__tab\"\n [class.audience-picker__tab--active]=\"activeTab === 'view'\"\n (click)=\"setTab('view')\">\n <i class=\"fa-solid fa-eye\"></i>\n View\n </button>\n <button\n class=\"audience-picker__tab\"\n [class.audience-picker__tab--active]=\"activeTab === 'adhoc'\"\n (click)=\"setTab('adhoc')\">\n <i class=\"fa-solid fa-filter\"></i>\n Ad-hoc Filter\n </button>\n </div>\n\n <!-- Entity selector (shared across tabs) -->\n <div class=\"audience-picker__field\">\n <label class=\"audience-picker__label\">Entity</label>\n <select\n class=\"mj-input mj-select audience-picker__entity\"\n [ngModel]=\"selectedEntityName\"\n (ngModelChange)=\"OnEntityChange($event)\"\n [disabled]=\"!!LockedEntityName\">\n @for (e of entityOptions; track e.ID) {\n <option [ngValue]=\"e.Name\">{{ e.Name }}</option>\n }\n </select>\n </div>\n\n @if (activeTab === 'list') {\n <div class=\"audience-picker__search\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n class=\"mj-input\"\n [(ngModel)]=\"listSearch\"\n (ngModelChange)=\"OnListSearchChange()\"\n placeholder=\"Search lists by name…\" />\n </div>\n @if (loading) {\n <div class=\"audience-picker__state\">\n <mj-loading text=\"Loading lists...\" size=\"small\"></mj-loading>\n </div>\n } @else if (filteredLists.length === 0) {\n <div class=\"audience-picker__state audience-picker__state--empty\">\n No lists found for this entity.\n </div>\n } @else {\n <div class=\"audience-picker__items\">\n @for (l of filteredLists; track l.ID) {\n <div\n class=\"audience-picker__item\"\n [class.audience-picker__item--selected]=\"selectedListId === l.ID\"\n (click)=\"OnSelectList(l)\">\n <i\n class=\"fa-solid\"\n [class.fa-circle-check]=\"selectedListId === l.ID\"\n [class.fa-circle]=\"selectedListId !== l.ID\"></i>\n <div class=\"audience-picker__item-body\">\n <div class=\"audience-picker__item-name\">{{ l.Name }}</div>\n @if (l.Description) {\n <div class=\"audience-picker__item-desc\">{{ l.Description }}</div>\n }\n </div>\n </div>\n }\n </div>\n }\n } @else if (activeTab === 'view') {\n <div class=\"audience-picker__search\">\n <i class=\"fa-solid fa-search\"></i>\n <input\n class=\"mj-input\"\n [(ngModel)]=\"viewSearch\"\n (ngModelChange)=\"OnViewSearchChange()\"\n placeholder=\"Search views by name…\" />\n </div>\n @if (loading) {\n <div class=\"audience-picker__state\">\n <mj-loading text=\"Loading views...\" size=\"small\"></mj-loading>\n </div>\n } @else if (filteredViews.length === 0) {\n <div class=\"audience-picker__state audience-picker__state--empty\">\n No views found for this entity.\n </div>\n } @else {\n <div class=\"audience-picker__items\">\n @for (v of filteredViews; track v.ID) {\n <div\n class=\"audience-picker__item\"\n [class.audience-picker__item--selected]=\"selectedViewId === v.ID\"\n (click)=\"OnSelectView(v)\">\n <i\n class=\"fa-solid\"\n [class.fa-circle-check]=\"selectedViewId === v.ID\"\n [class.fa-circle]=\"selectedViewId !== v.ID\"></i>\n <div class=\"audience-picker__item-body\">\n <div class=\"audience-picker__item-name\">\n {{ v.Name }}\n <span class=\"audience-picker__live-badge\">live</span>\n </div>\n @if (v.Description) {\n <div class=\"audience-picker__item-desc\">{{ v.Description }}</div>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"audience-picker__hint\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n A view is resolved live at execution — the record set may differ from\n what you see here if data changes.\n </div>\n }\n } @else {\n <!-- Ad-hoc filter tab -->\n <div class=\"audience-picker__field\">\n <label class=\"audience-picker__label\">Filter</label>\n <textarea\n class=\"mj-input audience-picker__filter\"\n rows=\"3\"\n [(ngModel)]=\"adhocFilter\"\n (ngModelChange)=\"OnAdhocFilterChange()\"\n placeholder=\"e.g. State IN ('CA','NY','TX') AND DonationCount >= 3\"></textarea>\n </div>\n <div class=\"audience-picker__hint audience-picker__hint--neutral\">\n <i class=\"fa-solid fa-circle-info\"></i>\n Ad-hoc filters are evaluated server-side at execution time. Validate the\n filter on the server (via the resolve-audience action or send preview)\n before relying on it for production.\n </div>\n }\n</div>\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
2
|
+
import type { AudienceSource } from '@memberjunction/lists-base';
|
|
3
|
+
import * as i0 from "@angular/core";
|
|
4
|
+
/**
|
|
5
|
+
* Compact, read-only summary of an `AudienceSource`. Resolves names
|
|
6
|
+
* (list / view → display name) so the user sees the audience as
|
|
7
|
+
* "VIP Donors Q4 2026 (List, 348 records)" rather than a raw UUID.
|
|
8
|
+
*
|
|
9
|
+
* Pairs naturally with `AudienceSourcePickerComponent`: bind the same
|
|
10
|
+
* `Source` to both, and the summary reflects the picker's current
|
|
11
|
+
* selection live.
|
|
12
|
+
*/
|
|
13
|
+
export declare class AudienceSourceSummaryComponent extends BaseAngularComponent {
|
|
14
|
+
private readonly cdr;
|
|
15
|
+
set Source(value: AudienceSource | null);
|
|
16
|
+
get Source(): AudienceSource | null;
|
|
17
|
+
private _source;
|
|
18
|
+
/** Optional pre-computed count to surface (e.g., from a prior resolve). */
|
|
19
|
+
RecordCount: number | null;
|
|
20
|
+
label: string | null;
|
|
21
|
+
icon: string;
|
|
22
|
+
get kindLabel(): string;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve a human-readable label for the source. Names are looked up
|
|
25
|
+
* one-at-a-time; cheap because each picker change fires at most once.
|
|
26
|
+
* Falls back to the underlying ID if the lookup fails — the summary
|
|
27
|
+
* is informational, never load-bearing.
|
|
28
|
+
*/
|
|
29
|
+
private resolveLabel;
|
|
30
|
+
private truncate;
|
|
31
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AudienceSourceSummaryComponent, never>;
|
|
32
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AudienceSourceSummaryComponent, "mj-audience-source-summary", never, { "Source": { "alias": "Source"; "required": false; }; "RecordCount": { "alias": "RecordCount"; "required": false; }; }, {}, never, never, false, never>;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=audience-source-summary.component.d.ts.map
|
package/dist/lib/components/audience-source-summary/audience-source-summary.component.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audience-source-summary.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/audience-source-summary/audience-source-summary.component.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAGrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;;AAEjE;;;;;;;;GAQG;AACH,qBAOa,8BAA+B,SAAQ,oBAAoB;IACtE,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IAEjD,IACI,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,EAItC;IACD,IAAI,MAAM,IAAI,cAAc,GAAG,IAAI,CAElC;IACD,OAAO,CAAC,OAAO,CAA+B;IAE9C,2EAA2E;IAClE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEpC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAQ;IAC5B,IAAI,SAA0B;IAErC,IAAW,SAAS,IAAI,MAAM,CAK7B;IAED;;;;;OAKG;YACW,YAAY;IAwB1B,OAAO,CAAC,QAAQ;yCAzDL,8BAA8B;2CAA9B,8BAA8B;CA4D1C"}
|