@memberjunction/ng-explorer-core 5.4.1 → 5.6.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.
Files changed (36) hide show
  1. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.d.ts.map +1 -1
  2. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js +7 -6
  3. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js.map +1 -1
  4. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +2 -2
  5. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
  6. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
  7. package/dist/lib/resource-wrappers/dashboard-resource.component.js +16 -5
  8. package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
  9. package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
  10. package/dist/lib/resource-wrappers/view-resource.component.js +13 -3
  11. package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
  12. package/dist/lib/shell/components/header/app-switcher.component.d.ts +4 -0
  13. package/dist/lib/shell/components/header/app-switcher.component.d.ts.map +1 -1
  14. package/dist/lib/shell/components/header/app-switcher.component.js +15 -6
  15. package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
  16. package/dist/lib/shell/shell.component.d.ts +2 -0
  17. package/dist/lib/shell/shell.component.d.ts.map +1 -1
  18. package/dist/lib/shell/shell.component.js +28 -15
  19. package/dist/lib/shell/shell.component.js.map +1 -1
  20. package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
  21. package/dist/lib/single-list-detail/single-list-detail.component.js +4 -3
  22. package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
  23. package/dist/lib/single-record/single-record.component.d.ts +1 -0
  24. package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
  25. package/dist/lib/single-record/single-record.component.js +3 -1
  26. package/dist/lib/single-record/single-record.component.js.map +1 -1
  27. package/dist/lib/single-search-result/single-search-result.component.d.ts +41 -6
  28. package/dist/lib/single-search-result/single-search-result.component.d.ts.map +1 -1
  29. package/dist/lib/single-search-result/single-search-result.component.js +291 -13
  30. package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
  31. package/dist/lib/user-notifications/user-notifications.component.js +3 -3
  32. package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
  33. package/dist/lib/user-profile/user-profile.component.d.ts.map +1 -1
  34. package/dist/lib/user-profile/user-profile.component.js +3 -2
  35. package/dist/lib/user-profile/user-profile.component.js.map +1 -1
  36. package/package.json +34 -34
@@ -1,29 +1,307 @@
1
1
  import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { RunView, CompositeKey, EntityFieldTSType, Metadata } from '@memberjunction/core';
2
3
  import * as i0 from "@angular/core";
4
+ import * as i1 from "@memberjunction/ng-shared";
5
+ import * as i2 from "@memberjunction/ng-shared-generic";
6
+ import * as i3 from "@memberjunction/ng-entity-viewer";
7
+ import * as i4 from "@angular/common";
8
+ function SingleSearchResultComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
9
+ i0.ɵɵelementStart(0, "span", 5);
10
+ i0.ɵɵtext(1);
11
+ i0.ɵɵelementEnd();
12
+ } if (rf & 2) {
13
+ const ctx_r0 = i0.ɵɵnextContext();
14
+ i0.ɵɵadvance();
15
+ i0.ɵɵtextInterpolate1("Searching for \"", ctx_r0.searchInput, "\"...");
16
+ } }
17
+ function SingleSearchResultComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
18
+ i0.ɵɵelementStart(0, "span", 5);
19
+ i0.ɵɵtext(1);
20
+ i0.ɵɵelementEnd();
21
+ } if (rf & 2) {
22
+ const ctx_r0 = i0.ɵɵnextContext();
23
+ i0.ɵɵadvance();
24
+ i0.ɵɵtextInterpolate1("No results for \"", ctx_r0.searchInput, "\"");
25
+ } }
26
+ function SingleSearchResultComponent_Conditional_8_Template(rf, ctx) { if (rf & 1) {
27
+ i0.ɵɵelementStart(0, "span", 5);
28
+ i0.ɵɵtext(1);
29
+ i0.ɵɵpipe(2, "number");
30
+ i0.ɵɵelementEnd();
31
+ } if (rf & 2) {
32
+ const ctx_r0 = i0.ɵɵnextContext();
33
+ i0.ɵɵadvance();
34
+ i0.ɵɵtextInterpolate3("", i0.ɵɵpipeBind1(2, 3, ctx_r0.ResultCount), " ", ctx_r0.ResultCount === 1 ? "result" : "results", " for \"", ctx_r0.searchInput, "\"");
35
+ } }
36
+ function SingleSearchResultComponent_Conditional_9_Conditional_8_Template(rf, ctx) { if (rf & 1) {
37
+ const _r3 = i0.ɵɵgetCurrentView();
38
+ i0.ɵɵelementStart(0, "button", 19);
39
+ i0.ɵɵlistener("click", function SingleSearchResultComponent_Conditional_9_Conditional_8_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.SetViewMode("timeline")); });
40
+ i0.ɵɵelement(1, "i", 20);
41
+ i0.ɵɵelementEnd();
42
+ } if (rf & 2) {
43
+ const ctx_r0 = i0.ɵɵnextContext(2);
44
+ i0.ɵɵclassProp("active", ctx_r0.CurrentViewMode === "timeline");
45
+ } }
46
+ function SingleSearchResultComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
47
+ const _r2 = i0.ɵɵgetCurrentView();
48
+ i0.ɵɵelementStart(0, "div", 6)(1, "button", 11);
49
+ i0.ɵɵlistener("click", function SingleSearchResultComponent_Conditional_9_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnCreateNewRecord()); });
50
+ i0.ɵɵelement(2, "i", 12);
51
+ i0.ɵɵelementEnd();
52
+ i0.ɵɵelementStart(3, "div", 13)(4, "button", 14);
53
+ i0.ɵɵlistener("click", function SingleSearchResultComponent_Conditional_9_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.SetViewMode("grid")); });
54
+ i0.ɵɵelement(5, "i", 15);
55
+ i0.ɵɵelementEnd();
56
+ i0.ɵɵelementStart(6, "button", 16);
57
+ i0.ɵɵlistener("click", function SingleSearchResultComponent_Conditional_9_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.SetViewMode("cards")); });
58
+ i0.ɵɵelement(7, "i", 17);
59
+ i0.ɵɵelementEnd();
60
+ i0.ɵɵconditionalCreate(8, SingleSearchResultComponent_Conditional_9_Conditional_8_Template, 2, 2, "button", 18);
61
+ i0.ɵɵelementEnd()();
62
+ } if (rf & 2) {
63
+ const ctx_r0 = i0.ɵɵnextContext();
64
+ i0.ɵɵadvance(4);
65
+ i0.ɵɵclassProp("active", ctx_r0.CurrentViewMode === "grid");
66
+ i0.ɵɵadvance(2);
67
+ i0.ɵɵclassProp("active", ctx_r0.CurrentViewMode === "cards");
68
+ i0.ɵɵadvance(2);
69
+ i0.ɵɵconditional(ctx_r0.HasDateFields ? 8 : -1);
70
+ } }
71
+ function SingleSearchResultComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
72
+ i0.ɵɵelementStart(0, "div", 7);
73
+ i0.ɵɵelement(1, "mj-loading", 21);
74
+ i0.ɵɵelementEnd();
75
+ } }
76
+ function SingleSearchResultComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
77
+ i0.ɵɵelementStart(0, "div", 8);
78
+ i0.ɵɵelement(1, "i", 22);
79
+ i0.ɵɵelementStart(2, "p");
80
+ i0.ɵɵtext(3, "No results found for \"");
81
+ i0.ɵɵelementStart(4, "strong");
82
+ i0.ɵɵtext(5);
83
+ i0.ɵɵelementEnd();
84
+ i0.ɵɵtext(6);
85
+ i0.ɵɵelementEnd();
86
+ i0.ɵɵelementStart(7, "p", 23);
87
+ i0.ɵɵtext(8, "Try a different search term or entity");
88
+ i0.ɵɵelementEnd()();
89
+ } if (rf & 2) {
90
+ const ctx_r0 = i0.ɵɵnextContext();
91
+ i0.ɵɵadvance(5);
92
+ i0.ɵɵtextInterpolate(ctx_r0.searchInput);
93
+ i0.ɵɵadvance();
94
+ i0.ɵɵtextInterpolate1("\" in ", ctx_r0.entity);
95
+ } }
96
+ function SingleSearchResultComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
97
+ i0.ɵɵelementStart(0, "div", 9);
98
+ i0.ɵɵelement(1, "i", 24);
99
+ i0.ɵɵelementStart(2, "p");
100
+ i0.ɵɵtext(3, "Found 1 result \u2014 opening record...");
101
+ i0.ɵɵelementEnd()();
102
+ } }
103
+ function SingleSearchResultComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
104
+ const _r4 = i0.ɵɵgetCurrentView();
105
+ i0.ɵɵelementStart(0, "div", 10)(1, "mj-entity-viewer", 25);
106
+ i0.ɵɵlistener("viewModeChange", function SingleSearchResultComponent_Conditional_13_Template_mj_entity_viewer_viewModeChange_1_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnViewModeChanged($event)); })("dataLoaded", function SingleSearchResultComponent_Conditional_13_Template_mj_entity_viewer_dataLoaded_1_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnDataLoaded($event)); })("recordOpened", function SingleSearchResultComponent_Conditional_13_Template_mj_entity_viewer_recordOpened_1_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnRecordOpened($event)); })("addRequested", function SingleSearchResultComponent_Conditional_13_Template_mj_entity_viewer_addRequested_1_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnCreateNewRecord()); });
107
+ i0.ɵɵelementEnd()();
108
+ } if (rf & 2) {
109
+ const ctx_r0 = i0.ɵɵnextContext();
110
+ i0.ɵɵadvance();
111
+ i0.ɵɵproperty("entity", ctx_r0.EntityInfo)("viewMode", ctx_r0.CurrentViewMode)("filterText", ctx_r0.searchInput)("config", ctx_r0.ViewerConfig)("showGridToolbar", true)("gridToolbarConfig", ctx_r0.GridToolbarConfig);
112
+ } }
3
113
  export class SingleSearchResultComponent {
114
+ navigationService;
115
+ cdr;
4
116
  entity = '';
5
117
  searchInput = '';
6
118
  loadComplete = new EventEmitter();
7
119
  loadStarted = new EventEmitter();
8
- get params() {
9
- const p = {
120
+ SearchState = 'loading';
121
+ ResultCount = 0;
122
+ EntityInfo = null;
123
+ CurrentViewMode = 'grid';
124
+ /** Whether the current entity has date fields (enables timeline view toggle) */
125
+ get HasDateFields() {
126
+ if (!this.EntityInfo)
127
+ return false;
128
+ return this.EntityInfo.Fields.some(f => f.TSType === EntityFieldTSType.Date);
129
+ }
130
+ /** Resolved icon class for the entity, matching Data Explorer's format */
131
+ get EntityIcon() {
132
+ if (this.EntityInfo?.Icon) {
133
+ return this.formatEntityIcon(this.EntityInfo.Icon);
134
+ }
135
+ return 'fa-solid fa-table';
136
+ }
137
+ /** Configuration for the entity-viewer — matches the Data Explorer's setup */
138
+ ViewerConfig = {
139
+ showFilter: false,
140
+ showViewModeToggle: false,
141
+ showRecordCount: false,
142
+ showPagination: true,
143
+ serverSideFiltering: true,
144
+ serverSideSorting: true,
145
+ height: '100%'
146
+ };
147
+ /** Grid toolbar config — minimal action bar (no search, no column chooser) */
148
+ GridToolbarConfig = {
149
+ showSearch: false,
150
+ showRefresh: true,
151
+ showAdd: true,
152
+ showExport: true,
153
+ showDelete: false,
154
+ showColumnChooser: false,
155
+ showRowCount: false,
156
+ showSelectionCount: false
157
+ };
158
+ constructor(navigationService, cdr) {
159
+ this.navigationService = navigationService;
160
+ this.cdr = cdr;
161
+ }
162
+ ngOnChanges(changes) {
163
+ if ((changes['entity'] || changes['searchInput']) && this.entity && this.searchInput) {
164
+ this.ExecuteSearch();
165
+ }
166
+ }
167
+ /**
168
+ * Runs a lightweight pre-query (MaxRows: 2, PK fields only) to determine result count,
169
+ * then routes to the appropriate state: no-results, single-result auto-nav, or viewer.
170
+ */
171
+ async ExecuteSearch() {
172
+ this.SearchState = 'loading';
173
+ this.ResultCount = 0;
174
+ this.CurrentViewMode = 'grid';
175
+ this.loadStarted.emit(true);
176
+ // Resolve EntityInfo early so the header can show icon + display name during loading
177
+ const entityInfo = this.findEntityInfo();
178
+ this.EntityInfo = entityInfo ?? null;
179
+ this.cdr.detectChanges();
180
+ if (!entityInfo) {
181
+ this.setNoResults();
182
+ return;
183
+ }
184
+ const result = await this.runPreQuery(entityInfo);
185
+ if (!result.Success || result.Results.length === 0) {
186
+ this.setNoResults();
187
+ return;
188
+ }
189
+ if (result.Results.length === 1) {
190
+ this.navigateToSingleResult(entityInfo, result.Results[0]);
191
+ return;
192
+ }
193
+ // 2+ results — show the entity-viewer
194
+ this.SearchState = 'viewer';
195
+ this.cdr.detectChanges();
196
+ }
197
+ findEntityInfo() {
198
+ const md = new Metadata();
199
+ return md.Entities.find(e => e.Name.trim().toLowerCase() === this.entity.trim().toLowerCase());
200
+ }
201
+ async runPreQuery(entityInfo) {
202
+ const rv = new RunView();
203
+ const pkFields = entityInfo.PrimaryKeys.map(pk => pk.Name);
204
+ return rv.RunView({
10
205
  EntityName: this.entity,
11
- ExtraFilter: "ID IS NOT NULL", // temporary hack as ExtraFilter is required for dynamic views
206
+ ExtraFilter: 'ID IS NOT NULL',
12
207
  UserSearchString: this.searchInput,
13
- };
14
- return p;
208
+ Fields: pkFields,
209
+ ResultType: 'simple',
210
+ MaxRows: 2,
211
+ });
212
+ }
213
+ setNoResults() {
214
+ this.SearchState = 'no-results';
215
+ this.ResultCount = 0;
216
+ this.loadComplete.emit(true);
217
+ this.cdr.detectChanges();
218
+ }
219
+ navigateToSingleResult(entityInfo, record) {
220
+ this.SearchState = 'single-result';
221
+ this.ResultCount = 1;
222
+ this.cdr.detectChanges();
223
+ const compositeKey = new CompositeKey();
224
+ compositeKey.LoadFromEntityInfoAndRecord(entityInfo, record);
225
+ this.navigationService.OpenEntityRecord(entityInfo.Name, compositeKey);
226
+ this.loadComplete.emit(true);
227
+ }
228
+ // ── Header action handlers ──
229
+ SetViewMode(mode) {
230
+ this.CurrentViewMode = mode;
231
+ this.cdr.detectChanges();
15
232
  }
16
- static ɵfac = function SingleSearchResultComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SingleSearchResultComponent)(); };
17
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SingleSearchResultComponent, selectors: [["mj-single-search-result"]], inputs: { entity: "entity", searchInput: "searchInput" }, outputs: { loadComplete: "loadComplete", loadStarted: "loadStarted" }, standalone: false, decls: 2, vars: 0, template: function SingleSearchResultComponent_Template(rf, ctx) { if (rf & 1) {
18
- i0.ɵɵelementStart(0, "div");
19
- i0.ɵɵtext(1, " Needs to be implemented!\n");
233
+ OnViewModeChanged(mode) {
234
+ this.CurrentViewMode = mode;
235
+ this.cdr.detectChanges();
236
+ }
237
+ OnCreateNewRecord() {
238
+ if (this.EntityInfo) {
239
+ this.navigationService.OpenNewEntityRecord(this.EntityInfo.Name);
240
+ }
241
+ }
242
+ // ── Entity-viewer event handlers ──
243
+ OnDataLoaded(event) {
244
+ this.ResultCount = event.totalRowCount;
245
+ this.loadComplete.emit(true);
246
+ this.cdr.detectChanges();
247
+ }
248
+ OnRecordOpened(event) {
249
+ this.navigationService.OpenEntityRecord(event.entity.Name, event.compositeKey);
250
+ }
251
+ /** Format entity icon to ensure proper Font Awesome class format */
252
+ formatEntityIcon(icon) {
253
+ if (!icon) {
254
+ return 'fa-solid fa-table';
255
+ }
256
+ if (icon.startsWith('fa-solid') || icon.startsWith('fa-regular') ||
257
+ icon.startsWith('fa-light') || icon.startsWith('fa-brands') ||
258
+ icon.startsWith('fa ')) {
259
+ return icon;
260
+ }
261
+ if (icon.startsWith('fa-')) {
262
+ return `fa-solid ${icon}`;
263
+ }
264
+ return `fa-solid fa-${icon}`;
265
+ }
266
+ static ɵfac = function SingleSearchResultComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || SingleSearchResultComponent)(i0.ɵɵdirectiveInject(i1.NavigationService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
267
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SingleSearchResultComponent, selectors: [["mj-single-search-result"]], inputs: { entity: "entity", searchInput: "searchInput" }, outputs: { loadComplete: "loadComplete", loadStarted: "loadStarted" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 14, vars: 9, consts: [[1, "search-result-container"], [1, "content-header"], [1, "header-left"], [1, "entity-icon"], [1, "entity-title"], [1, "record-count"], [1, "header-right"], [1, "loading-container"], [1, "empty-state"], [1, "single-result-state"], [1, "viewer-container"], ["title", "New Record", 1, "header-action-btn", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "view-mode-toggle"], ["title", "Grid View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["title", "Cards View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-grip"], ["title", "Timeline View", 1, "toggle-btn", 3, "active"], ["title", "Timeline View", 1, "toggle-btn", 3, "click"], [1, "fa-solid", "fa-timeline"], ["text", "Searching..."], [1, "fa-solid", "fa-magnifying-glass", "empty-icon"], [1, "empty-hint"], [1, "fa-solid", "fa-arrow-right", "redirect-icon"], [3, "viewModeChange", "dataLoaded", "recordOpened", "addRequested", "entity", "viewMode", "filterText", "config", "showGridToolbar", "gridToolbarConfig"]], template: function SingleSearchResultComponent_Template(rf, ctx) { if (rf & 1) {
268
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
269
+ i0.ɵɵelement(3, "i", 3);
270
+ i0.ɵɵelementStart(4, "h2", 4);
271
+ i0.ɵɵtext(5);
272
+ i0.ɵɵelementEnd();
273
+ i0.ɵɵconditionalCreate(6, SingleSearchResultComponent_Conditional_6_Template, 2, 1, "span", 5)(7, SingleSearchResultComponent_Conditional_7_Template, 2, 1, "span", 5)(8, SingleSearchResultComponent_Conditional_8_Template, 3, 5, "span", 5);
274
+ i0.ɵɵelementEnd();
275
+ i0.ɵɵconditionalCreate(9, SingleSearchResultComponent_Conditional_9_Template, 9, 5, "div", 6);
276
+ i0.ɵɵelementEnd();
277
+ i0.ɵɵconditionalCreate(10, SingleSearchResultComponent_Conditional_10_Template, 2, 0, "div", 7);
278
+ i0.ɵɵconditionalCreate(11, SingleSearchResultComponent_Conditional_11_Template, 9, 2, "div", 8);
279
+ i0.ɵɵconditionalCreate(12, SingleSearchResultComponent_Conditional_12_Template, 4, 0, "div", 9);
280
+ i0.ɵɵconditionalCreate(13, SingleSearchResultComponent_Conditional_13_Template, 2, 6, "div", 10);
20
281
  i0.ɵɵelementEnd();
21
- } }, encapsulation: 2 });
282
+ } if (rf & 2) {
283
+ i0.ɵɵadvance(3);
284
+ i0.ɵɵclassMap(ctx.EntityIcon);
285
+ i0.ɵɵadvance(2);
286
+ i0.ɵɵtextInterpolate((ctx.EntityInfo == null ? null : ctx.EntityInfo.DisplayNameOrName) || ctx.entity);
287
+ i0.ɵɵadvance();
288
+ i0.ɵɵconditional(ctx.SearchState === "loading" ? 6 : ctx.SearchState === "no-results" ? 7 : ctx.ResultCount > 0 ? 8 : -1);
289
+ i0.ɵɵadvance(3);
290
+ i0.ɵɵconditional(ctx.SearchState === "viewer" && ctx.EntityInfo ? 9 : -1);
291
+ i0.ɵɵadvance();
292
+ i0.ɵɵconditional(ctx.SearchState === "loading" ? 10 : -1);
293
+ i0.ɵɵadvance();
294
+ i0.ɵɵconditional(ctx.SearchState === "no-results" ? 11 : -1);
295
+ i0.ɵɵadvance();
296
+ i0.ɵɵconditional(ctx.SearchState === "single-result" ? 12 : -1);
297
+ i0.ɵɵadvance();
298
+ i0.ɵɵconditional(ctx.SearchState === "viewer" && ctx.EntityInfo ? 13 : -1);
299
+ } }, dependencies: [i2.LoadingComponent, i3.EntityViewerComponent, i4.DecimalPipe], styles: [".search-result-container[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n\n\n\n\n\n.content-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n background: white;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n gap: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n\n.entity-icon[_ngcontent-%COMP%] {\n font-size: 20px;\n color: #1976d2;\n}\n\n.entity-title[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.record-count[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #666;\n font-weight: 500;\n}\n\n\n\n\n\n\n.header-right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-shrink: 0;\n}\n\n\n\n.header-action-btn[_ngcontent-%COMP%] {\n position: relative;\n width: 36px;\n height: 36px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n transition: all 0.15s ease;\n}\n.header-action-btn[_ngcontent-%COMP%]:hover {\n border-color: #1976d2;\n color: #1976d2;\n background: #f5f9ff;\n}\n.header-action-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n\n\n.view-mode-toggle[_ngcontent-%COMP%] {\n display: flex;\n background: #f0f0f0;\n border-radius: 8px;\n padding: 3px;\n}\n\n.toggle-btn[_ngcontent-%COMP%] {\n width: 36px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n transition: all 0.15s ease;\n}\n.toggle-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n}\n.toggle-btn.active[_ngcontent-%COMP%] {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n\n\n\n\n\n.loading-container[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 48px 0;\n}\n\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: 64px 0;\n color: #666;\n}\n\n.empty-icon[_ngcontent-%COMP%] {\n font-size: 48px;\n color: #ccc;\n margin-bottom: 16px;\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 4px 0;\n font-size: 15px;\n}\n\n.empty-hint[_ngcontent-%COMP%] {\n color: #999;\n font-size: 13px !important;\n}\n\n\n\n\n\n\n.single-result-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px 0;\n color: #666;\n}\n\n.redirect-icon[_ngcontent-%COMP%] {\n font-size: 32px;\n color: #4a90d9;\n margin-bottom: 16px;\n}\n\n.single-result-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 15px;\n margin: 4px 0;\n}\n\n\n\n\n\n\n.viewer-container[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n}"] });
22
300
  }
23
301
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SingleSearchResultComponent, [{
24
302
  type: Component,
25
- args: [{ standalone: false, selector: 'mj-single-search-result', template: "<div>\n Needs to be implemented!\n</div>" }]
26
- }], null, { entity: [{
303
+ args: [{ standalone: false, selector: 'mj-single-search-result', template: "<div class=\"search-result-container\">\n <!-- Content Header \u2014 matches Data Explorer layout -->\n <div class=\"content-header\">\n <div class=\"header-left\">\n <i [class]=\"EntityIcon\" class=\"entity-icon\"></i>\n <h2 class=\"entity-title\">{{ EntityInfo?.DisplayNameOrName || entity }}</h2>\n @if (SearchState === 'loading') {\n <span class=\"record-count\">Searching for \"{{ searchInput }}\"...</span>\n } @else if (SearchState === 'no-results') {\n <span class=\"record-count\">No results for \"{{ searchInput }}\"</span>\n } @else if (ResultCount > 0) {\n <span class=\"record-count\">{{ ResultCount | number }} {{ ResultCount === 1 ? 'result' : 'results' }} for \"{{ searchInput }}\"</span>\n }\n </div>\n\n @if (SearchState === 'viewer' && EntityInfo) {\n <div class=\"header-right\">\n <!-- New Record -->\n <button\n class=\"header-action-btn\"\n (click)=\"OnCreateNewRecord()\"\n title=\"New Record\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <!-- View Mode Toggle -->\n <div class=\"view-mode-toggle\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"CurrentViewMode === 'grid'\"\n (click)=\"SetViewMode('grid')\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"CurrentViewMode === 'cards'\"\n (click)=\"SetViewMode('cards')\"\n title=\"Cards View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n @if (HasDateFields) {\n <button\n class=\"toggle-btn\"\n [class.active]=\"CurrentViewMode === 'timeline'\"\n (click)=\"SetViewMode('timeline')\"\n title=\"Timeline View\">\n <i class=\"fa-solid fa-timeline\"></i>\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Loading state -->\n @if (SearchState === 'loading') {\n <div class=\"loading-container\">\n <mj-loading text=\"Searching...\"></mj-loading>\n </div>\n }\n\n <!-- No results -->\n @if (SearchState === 'no-results') {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-magnifying-glass empty-icon\"></i>\n <p>No results found for \"<strong>{{ searchInput }}</strong>\" in {{ entity }}</p>\n <p class=\"empty-hint\">Try a different search term or entity</p>\n </div>\n }\n\n <!-- Single result \u2014 auto-navigating to entity form -->\n @if (SearchState === 'single-result') {\n <div class=\"single-result-state\">\n <i class=\"fa-solid fa-arrow-right redirect-icon\"></i>\n <p>Found 1 result &mdash; opening record...</p>\n </div>\n }\n\n <!-- Entity Viewer (2+ results) -->\n @if (SearchState === 'viewer' && EntityInfo) {\n <div class=\"viewer-container\">\n <mj-entity-viewer\n [entity]=\"EntityInfo\"\n [viewMode]=\"CurrentViewMode\"\n [filterText]=\"searchInput\"\n [config]=\"ViewerConfig\"\n [showGridToolbar]=\"true\"\n [gridToolbarConfig]=\"GridToolbarConfig\"\n (viewModeChange)=\"OnViewModeChanged($event)\"\n (dataLoaded)=\"OnDataLoaded($event)\"\n (recordOpened)=\"OnRecordOpened($event)\"\n (addRequested)=\"OnCreateNewRecord()\">\n </mj-entity-viewer>\n </div>\n }\n</div>\n", styles: [".search-result-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n/* ============================================\n CONTENT HEADER \u2014 matches Data Explorer layout\n ============================================ */\n\n.content-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 24px;\n background: white;\n border-bottom: 1px solid #e0e0e0;\n flex-shrink: 0;\n gap: 24px;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);\n}\n\n.header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n\n.entity-icon {\n font-size: 20px;\n color: #1976d2;\n}\n\n.entity-title {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.record-count {\n font-size: 13px;\n color: #666;\n font-weight: 500;\n}\n\n/* ============================================\n HEADER RIGHT \u2014 action buttons, view mode toggle\n ============================================ */\n\n.header-right {\n display: flex;\n align-items: center;\n gap: 16px;\n flex-shrink: 0;\n}\n\n/* Action Button (New Record, Export, etc.) */\n.header-action-btn {\n position: relative;\n width: 36px;\n height: 36px;\n border: 1px solid #e0e0e0;\n background: white;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n transition: all 0.15s ease;\n}\n.header-action-btn:hover {\n border-color: #1976d2;\n color: #1976d2;\n background: #f5f9ff;\n}\n.header-action-btn i {\n font-size: 14px;\n}\n\n/* View Mode Toggle */\n.view-mode-toggle {\n display: flex;\n background: #f0f0f0;\n border-radius: 8px;\n padding: 3px;\n}\n\n.toggle-btn {\n width: 36px;\n height: 32px;\n border: none;\n background: transparent;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #666;\n transition: all 0.15s ease;\n}\n.toggle-btn:hover {\n color: #333;\n}\n.toggle-btn.active {\n background: white;\n color: #1976d2;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n}\n\n/* ============================================\n LOADING STATE\n ============================================ */\n\n.loading-container {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 48px 0;\n}\n\n/* ============================================\n EMPTY STATE\n ============================================ */\n\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px 0;\n color: #666;\n}\n\n.empty-icon {\n font-size: 48px;\n color: #ccc;\n margin-bottom: 16px;\n}\n\n.empty-state p {\n margin: 4px 0;\n font-size: 15px;\n}\n\n.empty-hint {\n color: #999;\n font-size: 13px !important;\n}\n\n/* ============================================\n SINGLE RESULT \u2014 auto-nav state\n ============================================ */\n\n.single-result-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 64px 0;\n color: #666;\n}\n\n.redirect-icon {\n font-size: 32px;\n color: #4a90d9;\n margin-bottom: 16px;\n}\n\n.single-result-state p {\n font-size: 15px;\n margin: 4px 0;\n}\n\n/* ============================================\n VIEWER CONTAINER\n ============================================ */\n\n.viewer-container {\n flex: 1;\n min-height: 0;\n}\n"] }]
304
+ }], () => [{ type: i1.NavigationService }, { type: i0.ChangeDetectorRef }], { entity: [{
27
305
  type: Input
28
306
  }], searchInput: [{
29
307
  type: Input
@@ -32,5 +310,5 @@ export class SingleSearchResultComponent {
32
310
  }], loadStarted: [{
33
311
  type: Output
34
312
  }] }); })();
35
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SingleSearchResultComponent, { className: "SingleSearchResultComponent", filePath: "src/lib/single-search-result/single-search-result.component.ts", lineNumber: 10 }); })();
313
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SingleSearchResultComponent, { className: "SingleSearchResultComponent", filePath: "src/lib/single-search-result/single-search-result.component.ts", lineNumber: 14 }); })();
36
314
  //# sourceMappingURL=single-search-result.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"single-search-result.component.js","sourceRoot":"","sources":["../../../src/lib/single-search-result/single-search-result.component.ts","../../../src/lib/single-search-result/single-search-result.component.html"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;;AASvE,MAAM,OAAO,2BAA2B;IACtB,MAAM,GAAW,EAAE,CAAC;IACpB,WAAW,GAAW,EAAE,CAAC;IACxB,YAAY,GAAsB,IAAI,YAAY,EAAO,CAAC;IAC1D,WAAW,GAAsB,IAAI,YAAY,EAAO,CAAC;IAE1E,IAAW,MAAM;QACf,MAAM,CAAC,GAAkB;YACvB,UAAU,EAAE,IAAI,CAAC,MAAM;YACvB,WAAW,EAAE,gBAAgB,EAAE,8DAA8D;YAC7F,gBAAgB,EAAE,IAAI,CAAC,WAAW;SACnC,CAAA;QACD,OAAO,CAAC,CAAC;IACX,CAAC;qHAbU,2BAA2B;6DAA3B,2BAA2B;YCTxC,2BAAK;YACD,2CACJ;YAAA,iBAAM;;;iFDOO,2BAA2B;cANvC,SAAS;6BACI,KAAK,YACP,yBAAyB;;kBAKlC,KAAK;;kBACL,KAAK;;kBACL,MAAM;;kBACN,MAAM;;kFAJI,2BAA2B","sourcesContent":["import { Component, EventEmitter, Input, Output } from '@angular/core';\nimport { RunViewParams } from '@memberjunction/core';\n\n@Component({\n standalone: false,\n selector: 'mj-single-search-result',\n templateUrl: './single-search-result.component.html',\n styleUrls: ['./single-search-result.component.css']\n})\nexport class SingleSearchResultComponent {\n @Input() public entity: string = '';\n @Input() public searchInput: string = '';\n @Output() public loadComplete: EventEmitter<any> = new EventEmitter<any>();\n @Output() public loadStarted: EventEmitter<any> = new EventEmitter<any>();\n\n public get params(): RunViewParams {\n const p: RunViewParams = {\n EntityName: this.entity,\n ExtraFilter: \"ID IS NOT NULL\", // temporary hack as ExtraFilter is required for dynamic views\n UserSearchString: this.searchInput,\n }\n return p;\n }\n \n\n}\n","<div>\n Needs to be implemented!\n</div>"]}
1
+ {"version":3,"file":"single-search-result.component.js","sourceRoot":"","sources":["../../../src/lib/single-search-result/single-search-result.component.ts","../../../src/lib/single-search-result/single-search-result.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ChangeDetectorRef, OnChanges, SimpleChanges } from '@angular/core';\nimport { RunView, CompositeKey, EntityInfo, EntityFieldTSType, Metadata } from '@memberjunction/core';\nimport { NavigationService } from '@memberjunction/ng-shared';\nimport { DataLoadedEvent, RecordOpenedEvent, EntityViewerConfig, EntityViewMode, GridToolbarConfig } from '@memberjunction/ng-entity-viewer';\n\ntype SearchState = 'loading' | 'no-results' | 'single-result' | 'viewer';\n\n@Component({\n standalone: false,\n selector: 'mj-single-search-result',\n templateUrl: './single-search-result.component.html',\n styleUrls: ['./single-search-result.component.css']\n})\nexport class SingleSearchResultComponent implements OnChanges {\n @Input() public entity: string = '';\n @Input() public searchInput: string = '';\n @Output() public loadComplete = new EventEmitter<boolean>();\n @Output() public loadStarted = new EventEmitter<boolean>();\n\n public SearchState: SearchState = 'loading';\n public ResultCount = 0;\n public EntityInfo: EntityInfo | null = null;\n public CurrentViewMode: EntityViewMode = 'grid';\n\n /** Whether the current entity has date fields (enables timeline view toggle) */\n public get HasDateFields(): boolean {\n if (!this.EntityInfo) return false;\n return this.EntityInfo.Fields.some(f => f.TSType === EntityFieldTSType.Date);\n }\n\n /** Resolved icon class for the entity, matching Data Explorer's format */\n public get EntityIcon(): string {\n if (this.EntityInfo?.Icon) {\n return this.formatEntityIcon(this.EntityInfo.Icon);\n }\n return 'fa-solid fa-table';\n }\n\n /** Configuration for the entity-viewer — matches the Data Explorer's setup */\n public ViewerConfig: Partial<EntityViewerConfig> = {\n showFilter: false,\n showViewModeToggle: false,\n showRecordCount: false,\n showPagination: true,\n serverSideFiltering: true,\n serverSideSorting: true,\n height: '100%'\n };\n\n /** Grid toolbar config — minimal action bar (no search, no column chooser) */\n public GridToolbarConfig: Partial<GridToolbarConfig> = {\n showSearch: false,\n showRefresh: true,\n showAdd: true,\n showExport: true,\n showDelete: false,\n showColumnChooser: false,\n showRowCount: false,\n showSelectionCount: false\n };\n\n constructor(\n private navigationService: NavigationService,\n private cdr: ChangeDetectorRef\n ) {}\n\n ngOnChanges(changes: SimpleChanges): void {\n if ((changes['entity'] || changes['searchInput']) && this.entity && this.searchInput) {\n this.ExecuteSearch();\n }\n }\n\n /**\n * Runs a lightweight pre-query (MaxRows: 2, PK fields only) to determine result count,\n * then routes to the appropriate state: no-results, single-result auto-nav, or viewer.\n */\n private async ExecuteSearch(): Promise<void> {\n this.SearchState = 'loading';\n this.ResultCount = 0;\n this.CurrentViewMode = 'grid';\n this.loadStarted.emit(true);\n\n // Resolve EntityInfo early so the header can show icon + display name during loading\n const entityInfo = this.findEntityInfo();\n this.EntityInfo = entityInfo ?? null;\n this.cdr.detectChanges();\n\n if (!entityInfo) {\n this.setNoResults();\n return;\n }\n\n const result = await this.runPreQuery(entityInfo);\n if (!result.Success || result.Results.length === 0) {\n this.setNoResults();\n return;\n }\n\n if (result.Results.length === 1) {\n this.navigateToSingleResult(entityInfo, result.Results[0]);\n return;\n }\n\n // 2+ results — show the entity-viewer\n this.SearchState = 'viewer';\n this.cdr.detectChanges();\n }\n\n private findEntityInfo(): EntityInfo | undefined {\n const md = new Metadata();\n return md.Entities.find(e =>\n e.Name.trim().toLowerCase() === this.entity.trim().toLowerCase()\n );\n }\n\n private async runPreQuery(entityInfo: EntityInfo) {\n const rv = new RunView();\n const pkFields = entityInfo.PrimaryKeys.map(pk => pk.Name);\n return rv.RunView<Record<string, unknown>>({\n EntityName: this.entity,\n ExtraFilter: 'ID IS NOT NULL',\n UserSearchString: this.searchInput,\n Fields: pkFields,\n ResultType: 'simple',\n MaxRows: 2,\n });\n }\n\n private setNoResults(): void {\n this.SearchState = 'no-results';\n this.ResultCount = 0;\n this.loadComplete.emit(true);\n this.cdr.detectChanges();\n }\n\n private navigateToSingleResult(entityInfo: EntityInfo, record: Record<string, unknown>): void {\n this.SearchState = 'single-result';\n this.ResultCount = 1;\n this.cdr.detectChanges();\n\n const compositeKey = new CompositeKey();\n compositeKey.LoadFromEntityInfoAndRecord(entityInfo, record);\n this.navigationService.OpenEntityRecord(entityInfo.Name, compositeKey);\n this.loadComplete.emit(true);\n }\n\n // ── Header action handlers ──\n\n public SetViewMode(mode: EntityViewMode): void {\n this.CurrentViewMode = mode;\n this.cdr.detectChanges();\n }\n\n public OnViewModeChanged(mode: EntityViewMode): void {\n this.CurrentViewMode = mode;\n this.cdr.detectChanges();\n }\n\n public OnCreateNewRecord(): void {\n if (this.EntityInfo) {\n this.navigationService.OpenNewEntityRecord(this.EntityInfo.Name);\n }\n }\n\n // ── Entity-viewer event handlers ──\n\n public OnDataLoaded(event: DataLoadedEvent): void {\n this.ResultCount = event.totalRowCount;\n this.loadComplete.emit(true);\n this.cdr.detectChanges();\n }\n\n public OnRecordOpened(event: RecordOpenedEvent): void {\n this.navigationService.OpenEntityRecord(event.entity.Name, event.compositeKey);\n }\n\n /** Format entity icon to ensure proper Font Awesome class format */\n private formatEntityIcon(icon: string): string {\n if (!icon) {\n return 'fa-solid fa-table';\n }\n if (icon.startsWith('fa-solid') || icon.startsWith('fa-regular') ||\n icon.startsWith('fa-light') || icon.startsWith('fa-brands') ||\n icon.startsWith('fa ')) {\n return icon;\n }\n if (icon.startsWith('fa-')) {\n return `fa-solid ${icon}`;\n }\n return `fa-solid fa-${icon}`;\n }\n}\n","<div class=\"search-result-container\">\n <!-- Content Header — matches Data Explorer layout -->\n <div class=\"content-header\">\n <div class=\"header-left\">\n <i [class]=\"EntityIcon\" class=\"entity-icon\"></i>\n <h2 class=\"entity-title\">{{ EntityInfo?.DisplayNameOrName || entity }}</h2>\n @if (SearchState === 'loading') {\n <span class=\"record-count\">Searching for \"{{ searchInput }}\"...</span>\n } @else if (SearchState === 'no-results') {\n <span class=\"record-count\">No results for \"{{ searchInput }}\"</span>\n } @else if (ResultCount > 0) {\n <span class=\"record-count\">{{ ResultCount | number }} {{ ResultCount === 1 ? 'result' : 'results' }} for \"{{ searchInput }}\"</span>\n }\n </div>\n\n @if (SearchState === 'viewer' && EntityInfo) {\n <div class=\"header-right\">\n <!-- New Record -->\n <button\n class=\"header-action-btn\"\n (click)=\"OnCreateNewRecord()\"\n title=\"New Record\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n\n <!-- View Mode Toggle -->\n <div class=\"view-mode-toggle\">\n <button\n class=\"toggle-btn\"\n [class.active]=\"CurrentViewMode === 'grid'\"\n (click)=\"SetViewMode('grid')\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n class=\"toggle-btn\"\n [class.active]=\"CurrentViewMode === 'cards'\"\n (click)=\"SetViewMode('cards')\"\n title=\"Cards View\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n @if (HasDateFields) {\n <button\n class=\"toggle-btn\"\n [class.active]=\"CurrentViewMode === 'timeline'\"\n (click)=\"SetViewMode('timeline')\"\n title=\"Timeline View\">\n <i class=\"fa-solid fa-timeline\"></i>\n </button>\n }\n </div>\n </div>\n }\n </div>\n\n <!-- Loading state -->\n @if (SearchState === 'loading') {\n <div class=\"loading-container\">\n <mj-loading text=\"Searching...\"></mj-loading>\n </div>\n }\n\n <!-- No results -->\n @if (SearchState === 'no-results') {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-magnifying-glass empty-icon\"></i>\n <p>No results found for \"<strong>{{ searchInput }}</strong>\" in {{ entity }}</p>\n <p class=\"empty-hint\">Try a different search term or entity</p>\n </div>\n }\n\n <!-- Single result — auto-navigating to entity form -->\n @if (SearchState === 'single-result') {\n <div class=\"single-result-state\">\n <i class=\"fa-solid fa-arrow-right redirect-icon\"></i>\n <p>Found 1 result &mdash; opening record...</p>\n </div>\n }\n\n <!-- Entity Viewer (2+ results) -->\n @if (SearchState === 'viewer' && EntityInfo) {\n <div class=\"viewer-container\">\n <mj-entity-viewer\n [entity]=\"EntityInfo\"\n [viewMode]=\"CurrentViewMode\"\n [filterText]=\"searchInput\"\n [config]=\"ViewerConfig\"\n [showGridToolbar]=\"true\"\n [gridToolbarConfig]=\"GridToolbarConfig\"\n (viewModeChange)=\"OnViewModeChanged($event)\"\n (dataLoaded)=\"OnDataLoaded($event)\"\n (recordOpened)=\"OnRecordOpened($event)\"\n (addRequested)=\"OnCreateNewRecord()\">\n </mj-entity-viewer>\n </div>\n }\n</div>\n"]}
@@ -2,7 +2,7 @@ import { Component, ViewChild } from '@angular/core';
2
2
  import { SharedService } from '@memberjunction/ng-shared';
3
3
  import { MJUserNotificationEntity, UserInfoEngine } from '@memberjunction/core-entities';
4
4
  import { Metadata, TransactionVariable } from '@memberjunction/core';
5
- import { SafeJSONParse } from '@memberjunction/global';
5
+ import { SafeJSONParse, UUIDsEqual } from '@memberjunction/global';
6
6
  import { MJNotificationService } from '@memberjunction/ng-notifications';
7
7
  import * as i0 from "@angular/core";
8
8
  import * as i1 from "@memberjunction/ng-shared";
@@ -325,7 +325,7 @@ export class UserNotificationsComponent {
325
325
  }
326
326
  // Apply type filter if selected
327
327
  if (this.selectedTypeFilter) {
328
- temp = temp.filter(n => n.NotificationTypeID === this.selectedTypeFilter);
328
+ temp = temp.filter(n => UUIDsEqual(n.NotificationTypeID, this.selectedTypeFilter));
329
329
  }
330
330
  // Apply text filter if it is not empty
331
331
  if (this.currentFilter.trim().length > 0) {
@@ -541,7 +541,7 @@ export class UserNotificationsComponent {
541
541
  getNotificationType(typeId) {
542
542
  if (!typeId)
543
543
  return null;
544
- return this.notificationTypes.find(t => t.ID === typeId) || null;
544
+ return this.notificationTypes.find(t => UUIDsEqual(t.ID, typeId)) || null;
545
545
  }
546
546
  getTypeIcon(notification) {
547
547
  const type = this.getNotificationType(notification.NotificationTypeID);
@@ -1 +1 @@
1
- {"version":3,"file":"user-notifications.component.js","sourceRoot":"","sources":["../../../src/lib/user-notifications/user-notifications.component.ts","../../../src/lib/user-notifications/user-notifications.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewChild, ElementRef, AfterViewInit, OnInit } from '@angular/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { MJConversationDetailEntity, MJConversationEntity, MJUserNotificationEntity, MJUserNotificationTypeEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { Metadata, TransactionGroupBase, TransactionVariable } from '@memberjunction/core';\nimport { Router } from '@angular/router';\nimport { SafeJSONParse } from '@memberjunction/global';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\n\n/**\n * Radio button filter options for notification read status\n */\ntype ReadFilterOption = 'All' | 'Unread' | 'Read';\n\n/**\n * Configuration for record-type resource navigation\n */\ninterface RecordResourceConfig {\n Entity?: string;\n}\n\n/**\n * Configuration for conversation-type resource navigation\n */\ninterface ConversationResourceConfig {\n type: 'conversation';\n conversationId?: string;\n messageId?: string;\n artifactId?: string;\n versionNumber?: string;\n taskId?: string;\n}\n\n/**\n * Result of parsing a notification URL\n */\ninterface NotificationUrlInfo {\n urlParts: string[];\n queryString: string;\n}\n\n@Component({\n standalone: false,\n selector: 'app-user-notifications',\n templateUrl: './user-notifications.component.html',\n styleUrls: ['./user-notifications.component.css']\n})\nexport class UserNotificationsComponent implements OnInit, AfterViewInit {\n @ViewChild('allRadio') allRadio!: ElementRef<HTMLInputElement>;\n @ViewChild('unreadRadio') unreadRadio!: ElementRef<HTMLInputElement>;\n @ViewChild('readRadio') readRadio!: ElementRef<HTMLInputElement>;\n\n public radioSelected: ReadFilterOption = 'All';\n public currentFilter: string = '';\n public notificationTypes: MJUserNotificationTypeEntity[] = [];\n public selectedTypeFilter: string | null = null;\n public loadingTypes: boolean = true;\n\n constructor (public sharedService: SharedService, private router: Router) {}\n\n async ngOnInit() {\n this.loadNotificationTypes();\n }\n\n ngAfterViewInit(): void {\n this.sharedService.InvokeManualResize(); // make sure the notifications component is sized correctly\n }\n\n private loadNotificationTypes() {\n // Get notification types from UserInfoEngine cache, sorted client-side\n // UserInfoEngine is auto-configured via @RegisterForStartup()\n this.notificationTypes = [...UserInfoEngine.Instance.NotificationTypes].sort((a, b) => {\n const priorityA = a.Priority ?? 999;\n const priorityB = b.Priority ?? 999;\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n return a.Name.localeCompare(b.Name);\n });\n this.loadingTypes = false;\n }\n\n public get NotificationsToShow(): MJUserNotificationEntity[] {\n let temp: MJUserNotificationEntity[] = [];\n switch (this.radioSelected) {\n case 'All':\n temp = this.AllNotifications;\n break;\n case 'Unread':\n temp = this.AllNotifications.filter(n => n.Unread);\n break;\n case 'Read':\n temp = this.AllNotifications.filter(n => !n.Unread);\n break;\n }\n\n // Apply type filter if selected\n if (this.selectedTypeFilter) {\n temp = temp.filter(n => n.NotificationTypeID === this.selectedTypeFilter);\n }\n\n // Apply text filter if it is not empty\n if (this.currentFilter.trim().length > 0) {\n // check for inclusion of filter value in title or message\n temp = temp.filter(n => n.Title?.toLowerCase().includes(this.currentFilter.trim().toLowerCase()) ||\n n.Message?.toLowerCase().includes(this.currentFilter.trim().toLowerCase())\n );\n }\n\n return temp;\n }\n\n public isNotificationClickable(notification: MJUserNotificationEntity): boolean {\n const info = this.notificationUrl(notification);\n return (info !== null && info.urlParts && info.urlParts.length > 0);\n }\n\n public notificationUrl(notification: MJUserNotificationEntity): NotificationUrlInfo {\n const url: string[] = [];\n let queryString = '';\n if (notification.ResourceRecordID && notification.ResourceRecordID.length > 0 &&\n notification.ResourceTypeID && notification.ResourceTypeID.length > 0) {\n // we have a resource here, like a Report, Dashboard, etc\n // we can generate a url to navigate to it\n const rt = this.sharedService.ResourceTypeByID(notification.ResourceTypeID);\n let routeSegment: string | null | undefined;\n if (rt)\n routeSegment = this.sharedService.mapResourceTypeNameToRouteSegment(rt.Name);\n\n if (rt && routeSegment && routeSegment.trim().length > 0) {\n url.push('resource');\n url.push(routeSegment);\n url.push(notification.ResourceRecordID.toString());\n if (notification.ResourceConfiguration && notification.ResourceConfiguration.trim().length > 0) {\n if (rt.Name.trim().toLowerCase() === 'records') {\n // special handling for records\n const config = SafeJSONParse<RecordResourceConfig>(notification.ResourceConfiguration);\n if (config && config.Entity)\n queryString = `Entity=${config.Entity}`;\n }\n else\n queryString = notification.ResourceConfiguration;\n }\n }\n }\n else if (notification.ResourceConfiguration && notification.ResourceConfiguration.trim().length > 0) {\n // we do NOT have a resource type or resource record id, but we do have a ResourceConfiguration\n // string, which means we might have information on how to navigate to what we want if we parse the config\n // HOME screen stuff is done this way\n\n const config = SafeJSONParse<ConversationResourceConfig>(notification.ResourceConfiguration);\n if (config && config.type?.trim().toLowerCase() === 'conversation') {\n url.push('chat');\n // Build query string with conversation and artifact navigation\n const queryParams: string[] = [];\n if (config.conversationId) queryParams.push(`conversationId=${config.conversationId}`);\n if (config.messageId) queryParams.push(`messageId=${config.messageId}`);\n if (config.artifactId) queryParams.push(`artifactId=${config.artifactId}`);\n if (config.versionNumber) queryParams.push(`versionNumber=${config.versionNumber}`);\n if (config.taskId) queryParams.push(`taskId=${config.taskId}`);\n queryString = queryParams.join('&');\n }\n }\n\n return { urlParts: url, queryString };\n }\n\n public get AllNotifications(): MJUserNotificationEntity[] {\n return SharedService.UserNotifications;\n }\n\n public get UnreadNotifications(): MJUserNotificationEntity[] {\n return this.AllNotifications.filter(n => n.Unread);\n }\n\n public get ReadNotifications(): MJUserNotificationEntity[] {\n return this.AllNotifications.filter(n => !n.Unread);\n }\n\n selectReadOption(option: ReadFilterOption): void {\n this.radioSelected = option;\n // now update the radio button group in the UI\n switch (option) {\n case 'All':\n this.allRadio.nativeElement.checked = true;\n break;\n case 'Unread':\n this.unreadRadio.nativeElement.checked = true;\n break;\n case 'Read':\n this.readRadio.nativeElement.checked = true;\n break;\n }\n }\n\n onReadRadioChanged(event: Event): void {\n if (event.target instanceof HTMLInputElement) {\n this.radioSelected = event.target.value as ReadFilterOption;\n }\n }\n\n onFilterChanged(value: string): void {\n this.currentFilter = value;\n }\n\n getItemTitleClass(notification: MJUserNotificationEntity): string {\n if (notification.Unread) {\n return 'notification-title notification-title-unread';\n }\n return 'notification-title';\n }\n\n getItemWrapperClass(notification: MJUserNotificationEntity): string {\n let classInfo = 'notification-wrap';\n\n if (this.isNotificationClickable(notification))\n classInfo += ' notification-wrap-clickable';\n\n if (notification.Unread)\n classInfo += ' notification-wrap-unread';\n\n return classInfo;\n }\n\n async markAsRead(notification: MJUserNotificationEntity, bRead: boolean, transGroup: TransactionGroupBase | null): Promise<boolean> {\n if (notification) {\n const notificationId = notification.ID;\n notification.Unread = !bRead;\n let notificationEntity: MJUserNotificationEntity;\n if (notification instanceof MJUserNotificationEntity) {\n // the passed in param truly is a MJUserNotificationEntity or subclass, so just use it, saves a DB round trip\n notificationEntity = notification;\n }\n else {\n // the passed in param is just a plain object, so we need to load the entity\n const md = new Metadata();\n notificationEntity = await md.GetEntityObject<MJUserNotificationEntity>('MJ: User Notifications');\n await notificationEntity.Load(notificationId); \n notificationEntity.Unread = !bRead; \n }\n\n // part of a transaction group, if so, add it as that will defer the actual network traffic/save\n if (transGroup) {\n notificationEntity.TransactionGroup = transGroup;\n await notificationEntity.Save()\n return true;\n }\n else {\n // Save the notification (not part of transaction group)\n await notificationEntity.Save();\n // Update the observables so badge count refreshes immediately\n MJNotificationService.UpdateNotificationObservables();\n return true;\n }\n }\n else {\n return false;\n }\n }\n\n public async markAllAsRead() {\n await this.markAll(true);\n\n // test harness for creating Conversations and Conversation Details record in a single transaction using variables\n await this.TestTransactionGroupVariables();\n }\n\n public async TestTransactionGroupVariables() {\n const md = new Metadata();\n const transGroup = await md.CreateTransactionGroup();\n\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations');\n conversation.UserID = md.CurrentUser.ID;\n conversation.Description = 'Test Conversation';\n conversation.TransactionGroup = transGroup;\n if (!await conversation.Save()) {\n this.sharedService.CreateSimpleNotification('Unable to create conversation', 'error', 5000);\n }\n\n const tvDefine = new TransactionVariable('NewConvoID', conversation, 'ID', 'Define')\n transGroup.AddVariable(tvDefine);\n\n const conversationDetail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details');\n conversationDetail.Message = 'Test Message';\n conversationDetail.Role = 'User';\n conversationDetail.ConversationID = 'x'; // fake UUID must be non-null to pass validation, this will be replaced by the variable, since we're part of a TG, not a real save, so doesn't validate it as a true fkey\n conversationDetail.TransactionGroup = transGroup;\n if (!await conversationDetail.Save()) {\n this.sharedService.CreateSimpleNotification('Unable to create conversation detail', 'error', 500);\n } \n const tvUse = new TransactionVariable('NewConvoID', conversationDetail, 'ConversationID', 'Use')\n transGroup.AddVariable(tvUse);\n\n if (await transGroup.Submit()) {\n this.sharedService.CreateSimpleNotification('Transaction Group with Variables worked', 'success', 5000);\n }\n else {\n this.sharedService.CreateSimpleNotification('Transaction Group with Variables failed', 'error', 5000);\n }\n }\n\n public async markAllAsUnread() {\n await this.markAll(false);\n }\n\n public async markAll(bRead: boolean) {\n // Use transaction group for batching - all saves are queued and sent in one round-trip\n const md = new Metadata();\n const transGroup = await md.CreateTransactionGroup();\n\n // Queue all saves - no need to await individual saves since transaction group queues them\n for (const notification of this.AllNotifications) {\n if (notification.Unread && bRead || !notification.Unread && !bRead) {\n // Don't await - Save() with transaction group queues the operation immediately\n this.markAsRead(notification, bRead, transGroup);\n }\n }\n\n // Submit transaction group - this is where the actual network call happens\n if (!await transGroup.Submit())\n this.sharedService.CreateSimpleNotification('Unable to mark all notifications as read', 'error', 5000);\n else\n SharedService.RefreshUserNotifications();\n }\n \n notificationClicked(notification: MJUserNotificationEntity): void {\n if (this.isNotificationClickable(notification)) {\n // also mark this as read when we click it\n this.markAsRead(notification, true, null);\n\n const info = this.notificationUrl(notification);\n if (info.queryString && info.queryString.trim().length > 0) {\n const fullUrl = `${info.urlParts.join('/')}${info.queryString ? '?' + info.queryString : ''}`;\n this.router.navigateByUrl(fullUrl);\n }\n else {\n this.router.navigate(info.urlParts);\n }\n }\n }\n\n public getNotificationType(typeId: string | null): MJUserNotificationTypeEntity | null {\n if (!typeId) return null;\n return this.notificationTypes.find(t => t.ID === typeId) || null;\n }\n\n public getTypeIcon(notification: MJUserNotificationEntity): string {\n const type = this.getNotificationType(notification.NotificationTypeID);\n return type?.Icon || 'fa-bell';\n }\n\n public getTypeColor(notification: MJUserNotificationEntity): string {\n const type = this.getNotificationType(notification.NotificationTypeID);\n return type?.Color || '#999';\n }\n\n public getTypeName(notification: MJUserNotificationEntity): string {\n const type = this.getNotificationType(notification.NotificationTypeID);\n return type ? type.Name : 'Notification';\n }\n\n public onTypeFilterChange(typeId: string | null): void {\n this.selectedTypeFilter = typeId;\n }\n}\n","<div class=\"notifications-container\">\n <!-- Header Section -->\n <div class=\"notifications-header\">\n <div class=\"header-top\">\n <div class=\"header-title\">\n <div class=\"title-icon\">\n <i class=\"fa-solid fa-bell\"></i>\n </div>\n <h1>Notifications</h1>\n </div>\n\n @if (AllNotifications.length > 0) {\n <div class=\"header-actions\">\n @if (UnreadNotifications.length > 0) {\n <button class=\"action-btn action-btn-ghost\"\n type=\"button\"\n (click)=\"markAllAsRead()\"\n aria-label=\"Mark all notifications as read\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Read</span>\n </button>\n }\n @if (ReadNotifications.length > 0) {\n <button class=\"action-btn action-btn-secondary\"\n type=\"button\"\n (click)=\"markAllAsUnread()\"\n aria-label=\"Mark all notifications as unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Unread</span>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Filters Section -->\n @if (AllNotifications.length > 0) {\n <div class=\"filters-section\">\n <!-- Horizontal Slider for Filters (Mobile) -->\n <div class=\"filters-slider\">\n <div class=\"slider-content\">\n <!-- Stats Pills (Filter Tabs) -->\n <div class=\"header-stats\" role=\"group\" aria-label=\"Filter notifications by read status\">\n <input #allRadio type=\"radio\" name=\"UnreadStatus\" value=\"All\"\n (change)=\"onReadRadioChanged($event)\" checked class=\"hidden-radio\"\n id=\"filter-all\" />\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'All'\"\n (click)=\"selectReadOption('All')\"\n [attr.aria-pressed]=\"radioSelected === 'All'\"\n aria-label=\"Show all notifications\">\n <span class=\"stat-label\">All</span>\n <span class=\"stat-count\">{{AllNotifications.length}}</span>\n </button>\n\n <input #unreadRadio type=\"radio\" name=\"UnreadStatus\" value=\"Unread\"\n (change)=\"onReadRadioChanged($event)\" class=\"hidden-radio\"\n id=\"filter-unread\" />\n @if (UnreadNotifications.length > 0 || ReadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Unread'\"\n [class.has-unread]=\"UnreadNotifications.length > 0\"\n (click)=\"selectReadOption('Unread')\"\n [attr.aria-pressed]=\"radioSelected === 'Unread'\"\n aria-label=\"Show unread notifications\">\n <span class=\"stat-label\">Unread</span>\n <span class=\"stat-count\" [class.unread-count]=\"UnreadNotifications.length > 0\">\n {{UnreadNotifications.length}}\n </span>\n </button>\n }\n\n <input #readRadio type=\"radio\" name=\"UnreadStatus\" value=\"Read\"\n (change)=\"onReadRadioChanged($event)\" class=\"hidden-radio\"\n id=\"filter-read\" />\n @if (ReadNotifications.length > 0 || UnreadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Read'\"\n (click)=\"selectReadOption('Read')\"\n [attr.aria-pressed]=\"radioSelected === 'Read'\"\n aria-label=\"Show read notifications\">\n <span class=\"stat-label\">Read</span>\n <span class=\"stat-count\">{{ReadNotifications.length}}</span>\n </button>\n }\n </div>\n\n <!-- Native Type Filter Dropdown (in slider on mobile) -->\n @if (notificationTypes.length > 0) {\n <div class=\"filter-item type-filter-slider\">\n <select class=\"type-select\"\n [(ngModel)]=\"selectedTypeFilter\"\n (change)=\"onTypeFilterChange(selectedTypeFilter)\"\n aria-label=\"Filter by notification type\">\n <option [value]=\"null\">All Types</option>\n @for (type of notificationTypes; track type.ID) {\n <option [value]=\"type.ID\">{{type.Name}}</option>\n }\n </select>\n </div>\n }\n </div>\n </div>\n\n <!-- Search Filter (separate, full-width on mobile) -->\n <div class=\"search-section\">\n <div class=\"search-container\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n class=\"search-input\"\n placeholder=\"Search notifications...\"\n [value]=\"currentFilter\"\n (input)=\"onFilterChanged($any($event.target).value)\"\n aria-label=\"Search notifications\" />\n </div>\n </div>\n\n <!-- Filter Info Badge -->\n @if (NotificationsToShow.length !== AllNotifications.length) {\n <div class=\"filter-info\">\n <span class=\"filter-badge\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n Showing {{NotificationsToShow.length}} of {{AllNotifications.length}}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Notifications Content -->\n <div class=\"notifications-content\">\n @if (NotificationsToShow.length === 0 && AllNotifications.length === 0) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-bell-slash\"></i>\n </div>\n <h3>No Notifications</h3>\n <p>You're all caught up! New notifications will appear here.</p>\n </div>\n } @else if (NotificationsToShow.length === 0) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-filter-circle-xmark\"></i>\n </div>\n <h3>No Matching Notifications</h3>\n <p>Try adjusting your filters to see more notifications.</p>\n </div>\n } @else {\n <!-- Notifications List -->\n <div class=\"notifications-list\">\n @for (notification of NotificationsToShow; track notification.ID) {\n <article class=\"notification-card\"\n [class.unread]=\"notification.Unread\"\n [class.clickable]=\"isNotificationClickable(notification)\"\n role=\"article\"\n [attr.aria-label]=\"notification.Title\">\n\n <!-- Unread Indicator Bar -->\n <div class=\"card-indicator\" aria-hidden=\"true\"></div>\n\n <!-- Type Icon -->\n <div class=\"card-icon\"\n [style.background-color]=\"getTypeColor(notification) + '15'\"\n aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + getTypeIcon(notification)\"\n [style.color]=\"getTypeColor(notification)\"></i>\n </div>\n\n <!-- Content -->\n <div class=\"card-content\" (click)=\"notificationClicked(notification)\">\n <div class=\"card-header\">\n <h3 class=\"card-title\" [class.card-title-unread]=\"notification.Unread\">\n {{notification.Title}}\n </h3>\n <span class=\"card-time\">\n <i class=\"fa-regular fa-clock\" aria-hidden=\"true\"></i>\n <time [attr.datetime]=\"notification.__mj_CreatedAt\">\n {{notification.__mj_CreatedAt | date:'MMM d, h:mm a'}}\n </time>\n </span>\n </div>\n\n <div class=\"card-meta\">\n <span class=\"card-type\"\n [style.background-color]=\"getTypeColor(notification) + '15'\"\n [style.color]=\"getTypeColor(notification)\">\n {{getTypeName(notification)}}\n </span>\n </div>\n\n <p class=\"card-message\">{{notification.Message}}</p>\n </div>\n\n <!-- Actions -->\n <div class=\"card-actions\">\n @if (notification.Unread) {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(notification, true, null)\"\n [attr.aria-label]=\"'Mark ' + notification.Title + ' as read'\"\n title=\"Mark as Read\">\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Read</span>\n </button>\n } @else {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(notification, false, null)\"\n [attr.aria-label]=\"'Mark ' + notification.Title + ' as unread'\"\n title=\"Mark as Unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Unread</span>\n </button>\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n</div>\n"]}
1
+ {"version":3,"file":"user-notifications.component.js","sourceRoot":"","sources":["../../../src/lib/user-notifications/user-notifications.component.ts","../../../src/lib/user-notifications/user-notifications.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewChild, ElementRef, AfterViewInit, OnInit } from '@angular/core';\nimport { SharedService } from '@memberjunction/ng-shared';\nimport { MJConversationDetailEntity, MJConversationEntity, MJUserNotificationEntity, MJUserNotificationTypeEntity, UserInfoEngine } from '@memberjunction/core-entities';\nimport { Metadata, TransactionGroupBase, TransactionVariable } from '@memberjunction/core';\nimport { Router } from '@angular/router';\nimport { SafeJSONParse , UUIDsEqual } from '@memberjunction/global';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\n\n/**\n * Radio button filter options for notification read status\n */\ntype ReadFilterOption = 'All' | 'Unread' | 'Read';\n\n/**\n * Configuration for record-type resource navigation\n */\ninterface RecordResourceConfig {\n Entity?: string;\n}\n\n/**\n * Configuration for conversation-type resource navigation\n */\ninterface ConversationResourceConfig {\n type: 'conversation';\n conversationId?: string;\n messageId?: string;\n artifactId?: string;\n versionNumber?: string;\n taskId?: string;\n}\n\n/**\n * Result of parsing a notification URL\n */\ninterface NotificationUrlInfo {\n urlParts: string[];\n queryString: string;\n}\n\n@Component({\n standalone: false,\n selector: 'app-user-notifications',\n templateUrl: './user-notifications.component.html',\n styleUrls: ['./user-notifications.component.css']\n})\nexport class UserNotificationsComponent implements OnInit, AfterViewInit {\n @ViewChild('allRadio') allRadio!: ElementRef<HTMLInputElement>;\n @ViewChild('unreadRadio') unreadRadio!: ElementRef<HTMLInputElement>;\n @ViewChild('readRadio') readRadio!: ElementRef<HTMLInputElement>;\n\n public radioSelected: ReadFilterOption = 'All';\n public currentFilter: string = '';\n public notificationTypes: MJUserNotificationTypeEntity[] = [];\n public selectedTypeFilter: string | null = null;\n public loadingTypes: boolean = true;\n\n constructor (public sharedService: SharedService, private router: Router) {}\n\n async ngOnInit() {\n this.loadNotificationTypes();\n }\n\n ngAfterViewInit(): void {\n this.sharedService.InvokeManualResize(); // make sure the notifications component is sized correctly\n }\n\n private loadNotificationTypes() {\n // Get notification types from UserInfoEngine cache, sorted client-side\n // UserInfoEngine is auto-configured via @RegisterForStartup()\n this.notificationTypes = [...UserInfoEngine.Instance.NotificationTypes].sort((a, b) => {\n const priorityA = a.Priority ?? 999;\n const priorityB = b.Priority ?? 999;\n if (priorityA !== priorityB) {\n return priorityA - priorityB;\n }\n return a.Name.localeCompare(b.Name);\n });\n this.loadingTypes = false;\n }\n\n public get NotificationsToShow(): MJUserNotificationEntity[] {\n let temp: MJUserNotificationEntity[] = [];\n switch (this.radioSelected) {\n case 'All':\n temp = this.AllNotifications;\n break;\n case 'Unread':\n temp = this.AllNotifications.filter(n => n.Unread);\n break;\n case 'Read':\n temp = this.AllNotifications.filter(n => !n.Unread);\n break;\n }\n\n // Apply type filter if selected\n if (this.selectedTypeFilter) {\n temp = temp.filter(n => UUIDsEqual(n.NotificationTypeID, this.selectedTypeFilter));\n }\n\n // Apply text filter if it is not empty\n if (this.currentFilter.trim().length > 0) {\n // check for inclusion of filter value in title or message\n temp = temp.filter(n => n.Title?.toLowerCase().includes(this.currentFilter.trim().toLowerCase()) ||\n n.Message?.toLowerCase().includes(this.currentFilter.trim().toLowerCase())\n );\n }\n\n return temp;\n }\n\n public isNotificationClickable(notification: MJUserNotificationEntity): boolean {\n const info = this.notificationUrl(notification);\n return (info !== null && info.urlParts && info.urlParts.length > 0);\n }\n\n public notificationUrl(notification: MJUserNotificationEntity): NotificationUrlInfo {\n const url: string[] = [];\n let queryString = '';\n if (notification.ResourceRecordID && notification.ResourceRecordID.length > 0 &&\n notification.ResourceTypeID && notification.ResourceTypeID.length > 0) {\n // we have a resource here, like a Report, Dashboard, etc\n // we can generate a url to navigate to it\n const rt = this.sharedService.ResourceTypeByID(notification.ResourceTypeID);\n let routeSegment: string | null | undefined;\n if (rt)\n routeSegment = this.sharedService.mapResourceTypeNameToRouteSegment(rt.Name);\n\n if (rt && routeSegment && routeSegment.trim().length > 0) {\n url.push('resource');\n url.push(routeSegment);\n url.push(notification.ResourceRecordID.toString());\n if (notification.ResourceConfiguration && notification.ResourceConfiguration.trim().length > 0) {\n if (rt.Name.trim().toLowerCase() === 'records') {\n // special handling for records\n const config = SafeJSONParse<RecordResourceConfig>(notification.ResourceConfiguration);\n if (config && config.Entity)\n queryString = `Entity=${config.Entity}`;\n }\n else\n queryString = notification.ResourceConfiguration;\n }\n }\n }\n else if (notification.ResourceConfiguration && notification.ResourceConfiguration.trim().length > 0) {\n // we do NOT have a resource type or resource record id, but we do have a ResourceConfiguration\n // string, which means we might have information on how to navigate to what we want if we parse the config\n // HOME screen stuff is done this way\n\n const config = SafeJSONParse<ConversationResourceConfig>(notification.ResourceConfiguration);\n if (config && config.type?.trim().toLowerCase() === 'conversation') {\n url.push('chat');\n // Build query string with conversation and artifact navigation\n const queryParams: string[] = [];\n if (config.conversationId) queryParams.push(`conversationId=${config.conversationId}`);\n if (config.messageId) queryParams.push(`messageId=${config.messageId}`);\n if (config.artifactId) queryParams.push(`artifactId=${config.artifactId}`);\n if (config.versionNumber) queryParams.push(`versionNumber=${config.versionNumber}`);\n if (config.taskId) queryParams.push(`taskId=${config.taskId}`);\n queryString = queryParams.join('&');\n }\n }\n\n return { urlParts: url, queryString };\n }\n\n public get AllNotifications(): MJUserNotificationEntity[] {\n return SharedService.UserNotifications;\n }\n\n public get UnreadNotifications(): MJUserNotificationEntity[] {\n return this.AllNotifications.filter(n => n.Unread);\n }\n\n public get ReadNotifications(): MJUserNotificationEntity[] {\n return this.AllNotifications.filter(n => !n.Unread);\n }\n\n selectReadOption(option: ReadFilterOption): void {\n this.radioSelected = option;\n // now update the radio button group in the UI\n switch (option) {\n case 'All':\n this.allRadio.nativeElement.checked = true;\n break;\n case 'Unread':\n this.unreadRadio.nativeElement.checked = true;\n break;\n case 'Read':\n this.readRadio.nativeElement.checked = true;\n break;\n }\n }\n\n onReadRadioChanged(event: Event): void {\n if (event.target instanceof HTMLInputElement) {\n this.radioSelected = event.target.value as ReadFilterOption;\n }\n }\n\n onFilterChanged(value: string): void {\n this.currentFilter = value;\n }\n\n getItemTitleClass(notification: MJUserNotificationEntity): string {\n if (notification.Unread) {\n return 'notification-title notification-title-unread';\n }\n return 'notification-title';\n }\n\n getItemWrapperClass(notification: MJUserNotificationEntity): string {\n let classInfo = 'notification-wrap';\n\n if (this.isNotificationClickable(notification))\n classInfo += ' notification-wrap-clickable';\n\n if (notification.Unread)\n classInfo += ' notification-wrap-unread';\n\n return classInfo;\n }\n\n async markAsRead(notification: MJUserNotificationEntity, bRead: boolean, transGroup: TransactionGroupBase | null): Promise<boolean> {\n if (notification) {\n const notificationId = notification.ID;\n notification.Unread = !bRead;\n let notificationEntity: MJUserNotificationEntity;\n if (notification instanceof MJUserNotificationEntity) {\n // the passed in param truly is a MJUserNotificationEntity or subclass, so just use it, saves a DB round trip\n notificationEntity = notification;\n }\n else {\n // the passed in param is just a plain object, so we need to load the entity\n const md = new Metadata();\n notificationEntity = await md.GetEntityObject<MJUserNotificationEntity>('MJ: User Notifications');\n await notificationEntity.Load(notificationId); \n notificationEntity.Unread = !bRead; \n }\n\n // part of a transaction group, if so, add it as that will defer the actual network traffic/save\n if (transGroup) {\n notificationEntity.TransactionGroup = transGroup;\n await notificationEntity.Save()\n return true;\n }\n else {\n // Save the notification (not part of transaction group)\n await notificationEntity.Save();\n // Update the observables so badge count refreshes immediately\n MJNotificationService.UpdateNotificationObservables();\n return true;\n }\n }\n else {\n return false;\n }\n }\n\n public async markAllAsRead() {\n await this.markAll(true);\n\n // test harness for creating Conversations and Conversation Details record in a single transaction using variables\n await this.TestTransactionGroupVariables();\n }\n\n public async TestTransactionGroupVariables() {\n const md = new Metadata();\n const transGroup = await md.CreateTransactionGroup();\n\n const conversation = await md.GetEntityObject<MJConversationEntity>('MJ: Conversations');\n conversation.UserID = md.CurrentUser.ID;\n conversation.Description = 'Test Conversation';\n conversation.TransactionGroup = transGroup;\n if (!await conversation.Save()) {\n this.sharedService.CreateSimpleNotification('Unable to create conversation', 'error', 5000);\n }\n\n const tvDefine = new TransactionVariable('NewConvoID', conversation, 'ID', 'Define')\n transGroup.AddVariable(tvDefine);\n\n const conversationDetail = await md.GetEntityObject<MJConversationDetailEntity>('MJ: Conversation Details');\n conversationDetail.Message = 'Test Message';\n conversationDetail.Role = 'User';\n conversationDetail.ConversationID = 'x'; // fake UUID must be non-null to pass validation, this will be replaced by the variable, since we're part of a TG, not a real save, so doesn't validate it as a true fkey\n conversationDetail.TransactionGroup = transGroup;\n if (!await conversationDetail.Save()) {\n this.sharedService.CreateSimpleNotification('Unable to create conversation detail', 'error', 500);\n } \n const tvUse = new TransactionVariable('NewConvoID', conversationDetail, 'ConversationID', 'Use')\n transGroup.AddVariable(tvUse);\n\n if (await transGroup.Submit()) {\n this.sharedService.CreateSimpleNotification('Transaction Group with Variables worked', 'success', 5000);\n }\n else {\n this.sharedService.CreateSimpleNotification('Transaction Group with Variables failed', 'error', 5000);\n }\n }\n\n public async markAllAsUnread() {\n await this.markAll(false);\n }\n\n public async markAll(bRead: boolean) {\n // Use transaction group for batching - all saves are queued and sent in one round-trip\n const md = new Metadata();\n const transGroup = await md.CreateTransactionGroup();\n\n // Queue all saves - no need to await individual saves since transaction group queues them\n for (const notification of this.AllNotifications) {\n if (notification.Unread && bRead || !notification.Unread && !bRead) {\n // Don't await - Save() with transaction group queues the operation immediately\n this.markAsRead(notification, bRead, transGroup);\n }\n }\n\n // Submit transaction group - this is where the actual network call happens\n if (!await transGroup.Submit())\n this.sharedService.CreateSimpleNotification('Unable to mark all notifications as read', 'error', 5000);\n else\n SharedService.RefreshUserNotifications();\n }\n \n notificationClicked(notification: MJUserNotificationEntity): void {\n if (this.isNotificationClickable(notification)) {\n // also mark this as read when we click it\n this.markAsRead(notification, true, null);\n\n const info = this.notificationUrl(notification);\n if (info.queryString && info.queryString.trim().length > 0) {\n const fullUrl = `${info.urlParts.join('/')}${info.queryString ? '?' + info.queryString : ''}`;\n this.router.navigateByUrl(fullUrl);\n }\n else {\n this.router.navigate(info.urlParts);\n }\n }\n }\n\n public getNotificationType(typeId: string | null): MJUserNotificationTypeEntity | null {\n if (!typeId) return null;\n return this.notificationTypes.find(t => UUIDsEqual(t.ID, typeId)) || null;\n }\n\n public getTypeIcon(notification: MJUserNotificationEntity): string {\n const type = this.getNotificationType(notification.NotificationTypeID);\n return type?.Icon || 'fa-bell';\n }\n\n public getTypeColor(notification: MJUserNotificationEntity): string {\n const type = this.getNotificationType(notification.NotificationTypeID);\n return type?.Color || '#999';\n }\n\n public getTypeName(notification: MJUserNotificationEntity): string {\n const type = this.getNotificationType(notification.NotificationTypeID);\n return type ? type.Name : 'Notification';\n }\n\n public onTypeFilterChange(typeId: string | null): void {\n this.selectedTypeFilter = typeId;\n }\n}\n","<div class=\"notifications-container\">\n <!-- Header Section -->\n <div class=\"notifications-header\">\n <div class=\"header-top\">\n <div class=\"header-title\">\n <div class=\"title-icon\">\n <i class=\"fa-solid fa-bell\"></i>\n </div>\n <h1>Notifications</h1>\n </div>\n\n @if (AllNotifications.length > 0) {\n <div class=\"header-actions\">\n @if (UnreadNotifications.length > 0) {\n <button class=\"action-btn action-btn-ghost\"\n type=\"button\"\n (click)=\"markAllAsRead()\"\n aria-label=\"Mark all notifications as read\">\n <i class=\"fa-solid fa-check-double\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Read</span>\n </button>\n }\n @if (ReadNotifications.length > 0) {\n <button class=\"action-btn action-btn-secondary\"\n type=\"button\"\n (click)=\"markAllAsUnread()\"\n aria-label=\"Mark all notifications as unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"btn-text\">Mark All Unread</span>\n </button>\n }\n </div>\n }\n </div>\n\n <!-- Filters Section -->\n @if (AllNotifications.length > 0) {\n <div class=\"filters-section\">\n <!-- Horizontal Slider for Filters (Mobile) -->\n <div class=\"filters-slider\">\n <div class=\"slider-content\">\n <!-- Stats Pills (Filter Tabs) -->\n <div class=\"header-stats\" role=\"group\" aria-label=\"Filter notifications by read status\">\n <input #allRadio type=\"radio\" name=\"UnreadStatus\" value=\"All\"\n (change)=\"onReadRadioChanged($event)\" checked class=\"hidden-radio\"\n id=\"filter-all\" />\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'All'\"\n (click)=\"selectReadOption('All')\"\n [attr.aria-pressed]=\"radioSelected === 'All'\"\n aria-label=\"Show all notifications\">\n <span class=\"stat-label\">All</span>\n <span class=\"stat-count\">{{AllNotifications.length}}</span>\n </button>\n\n <input #unreadRadio type=\"radio\" name=\"UnreadStatus\" value=\"Unread\"\n (change)=\"onReadRadioChanged($event)\" class=\"hidden-radio\"\n id=\"filter-unread\" />\n @if (UnreadNotifications.length > 0 || ReadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Unread'\"\n [class.has-unread]=\"UnreadNotifications.length > 0\"\n (click)=\"selectReadOption('Unread')\"\n [attr.aria-pressed]=\"radioSelected === 'Unread'\"\n aria-label=\"Show unread notifications\">\n <span class=\"stat-label\">Unread</span>\n <span class=\"stat-count\" [class.unread-count]=\"UnreadNotifications.length > 0\">\n {{UnreadNotifications.length}}\n </span>\n </button>\n }\n\n <input #readRadio type=\"radio\" name=\"UnreadStatus\" value=\"Read\"\n (change)=\"onReadRadioChanged($event)\" class=\"hidden-radio\"\n id=\"filter-read\" />\n @if (ReadNotifications.length > 0 || UnreadNotifications.length === 0) {\n <button class=\"stat-pill\"\n type=\"button\"\n [class.active]=\"radioSelected === 'Read'\"\n (click)=\"selectReadOption('Read')\"\n [attr.aria-pressed]=\"radioSelected === 'Read'\"\n aria-label=\"Show read notifications\">\n <span class=\"stat-label\">Read</span>\n <span class=\"stat-count\">{{ReadNotifications.length}}</span>\n </button>\n }\n </div>\n\n <!-- Native Type Filter Dropdown (in slider on mobile) -->\n @if (notificationTypes.length > 0) {\n <div class=\"filter-item type-filter-slider\">\n <select class=\"type-select\"\n [(ngModel)]=\"selectedTypeFilter\"\n (change)=\"onTypeFilterChange(selectedTypeFilter)\"\n aria-label=\"Filter by notification type\">\n <option [value]=\"null\">All Types</option>\n @for (type of notificationTypes; track type.ID) {\n <option [value]=\"type.ID\">{{type.Name}}</option>\n }\n </select>\n </div>\n }\n </div>\n </div>\n\n <!-- Search Filter (separate, full-width on mobile) -->\n <div class=\"search-section\">\n <div class=\"search-container\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\" aria-hidden=\"true\"></i>\n <input type=\"text\"\n class=\"search-input\"\n placeholder=\"Search notifications...\"\n [value]=\"currentFilter\"\n (input)=\"onFilterChanged($any($event.target).value)\"\n aria-label=\"Search notifications\" />\n </div>\n </div>\n\n <!-- Filter Info Badge -->\n @if (NotificationsToShow.length !== AllNotifications.length) {\n <div class=\"filter-info\">\n <span class=\"filter-badge\">\n <i class=\"fa-solid fa-filter\" aria-hidden=\"true\"></i>\n Showing {{NotificationsToShow.length}} of {{AllNotifications.length}}\n </span>\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Notifications Content -->\n <div class=\"notifications-content\">\n @if (NotificationsToShow.length === 0 && AllNotifications.length === 0) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-bell-slash\"></i>\n </div>\n <h3>No Notifications</h3>\n <p>You're all caught up! New notifications will appear here.</p>\n </div>\n } @else if (NotificationsToShow.length === 0) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-filter-circle-xmark\"></i>\n </div>\n <h3>No Matching Notifications</h3>\n <p>Try adjusting your filters to see more notifications.</p>\n </div>\n } @else {\n <!-- Notifications List -->\n <div class=\"notifications-list\">\n @for (notification of NotificationsToShow; track notification.ID) {\n <article class=\"notification-card\"\n [class.unread]=\"notification.Unread\"\n [class.clickable]=\"isNotificationClickable(notification)\"\n role=\"article\"\n [attr.aria-label]=\"notification.Title\">\n\n <!-- Unread Indicator Bar -->\n <div class=\"card-indicator\" aria-hidden=\"true\"></div>\n\n <!-- Type Icon -->\n <div class=\"card-icon\"\n [style.background-color]=\"getTypeColor(notification) + '15'\"\n aria-hidden=\"true\">\n <i [class]=\"'fa-solid ' + getTypeIcon(notification)\"\n [style.color]=\"getTypeColor(notification)\"></i>\n </div>\n\n <!-- Content -->\n <div class=\"card-content\" (click)=\"notificationClicked(notification)\">\n <div class=\"card-header\">\n <h3 class=\"card-title\" [class.card-title-unread]=\"notification.Unread\">\n {{notification.Title}}\n </h3>\n <span class=\"card-time\">\n <i class=\"fa-regular fa-clock\" aria-hidden=\"true\"></i>\n <time [attr.datetime]=\"notification.__mj_CreatedAt\">\n {{notification.__mj_CreatedAt | date:'MMM d, h:mm a'}}\n </time>\n </span>\n </div>\n\n <div class=\"card-meta\">\n <span class=\"card-type\"\n [style.background-color]=\"getTypeColor(notification) + '15'\"\n [style.color]=\"getTypeColor(notification)\">\n {{getTypeName(notification)}}\n </span>\n </div>\n\n <p class=\"card-message\">{{notification.Message}}</p>\n </div>\n\n <!-- Actions -->\n <div class=\"card-actions\">\n @if (notification.Unread) {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(notification, true, null)\"\n [attr.aria-label]=\"'Mark ' + notification.Title + ' as read'\"\n title=\"Mark as Read\">\n <i class=\"fa-solid fa-check\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Read</span>\n </button>\n } @else {\n <button class=\"card-action-btn\"\n type=\"button\"\n (click)=\"markAsRead(notification, false, null)\"\n [attr.aria-label]=\"'Mark ' + notification.Title + ' as unread'\"\n title=\"Mark as Unread\">\n <i class=\"fa-solid fa-envelope\" aria-hidden=\"true\"></i>\n <span class=\"action-text\">Unread</span>\n </button>\n }\n </div>\n </article>\n }\n </div>\n }\n </div>\n</div>\n"]}