@memberjunction/ng-record-changes 0.9.54 → 0.9.64

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.
@@ -1,32 +1,32 @@
1
- import { EventEmitter, OnInit, Renderer2, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';
2
- import { DomSanitizer } from '@angular/platform-browser';
3
- import { SortDescriptor } from '@progress/kendo-data-query';
4
- import { BaseEntity } from '@memberjunction/core';
5
- import * as i0 from "@angular/core";
6
- export declare class RecordChangesComponent implements OnInit, AfterViewInit, OnDestroy {
7
- private sanitizer;
8
- private renderer;
9
- showloader: boolean;
10
- dialogClosed: EventEmitter<any>;
11
- record: BaseEntity;
12
- wrapper: ElementRef;
13
- viewData: any;
14
- visibleColumns: any;
15
- sortSettings: SortDescriptor[];
16
- constructor(sanitizer: DomSanitizer, renderer: Renderer2);
17
- ngOnInit(): void;
18
- ngAfterViewInit(): void;
19
- ngOnDestroy(): void;
20
- LoadRecordChanges(primaryKeyValue: any, appName: string, entityName: string): Promise<void>;
21
- prepareColumns(): void;
22
- closePropertiesDialog(): void;
23
- FormatColumnValue(col: any, value: any, dataItem: any, maxLength?: number, trailingChars?: string): any;
24
- protected fieldMarkup(fieldName: string): string;
25
- protected valueMarkup(changeInfo: {
26
- field: string;
27
- oldValue: any;
28
- newValue: any;
29
- }, isOldValue: boolean): string;
30
- static ɵfac: i0.ɵɵFactoryDeclaration<RecordChangesComponent, never>;
31
- static ɵcmp: i0.ɵɵComponentDeclaration<RecordChangesComponent, "mj-record-changes", never, { "record": "record"; }, { "dialogClosed": "dialogClosed"; }, never, never, false, never>;
32
- }
1
+ import { EventEmitter, OnInit, Renderer2, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';
2
+ import { DomSanitizer } from '@angular/platform-browser';
3
+ import { SortDescriptor } from '@progress/kendo-data-query';
4
+ import { BaseEntity } from '@memberjunction/core';
5
+ import * as i0 from "@angular/core";
6
+ export declare class RecordChangesComponent implements OnInit, AfterViewInit, OnDestroy {
7
+ private sanitizer;
8
+ private renderer;
9
+ showloader: boolean;
10
+ dialogClosed: EventEmitter<any>;
11
+ record: BaseEntity;
12
+ wrapper: ElementRef;
13
+ viewData: any;
14
+ visibleColumns: any;
15
+ sortSettings: SortDescriptor[];
16
+ constructor(sanitizer: DomSanitizer, renderer: Renderer2);
17
+ ngOnInit(): void;
18
+ ngAfterViewInit(): void;
19
+ ngOnDestroy(): void;
20
+ LoadRecordChanges(primaryKeyValue: any, appName: string, entityName: string): Promise<void>;
21
+ prepareColumns(): void;
22
+ closePropertiesDialog(): void;
23
+ FormatColumnValue(col: any, value: any, dataItem: any, maxLength?: number, trailingChars?: string): any;
24
+ protected fieldMarkup(fieldName: string): string;
25
+ protected valueMarkup(changeInfo: {
26
+ field: string;
27
+ oldValue: any;
28
+ newValue: any;
29
+ }, isOldValue: boolean): string;
30
+ static ɵfac: i0.ɵɵFactoryDeclaration<RecordChangesComponent, never>;
31
+ static ɵcmp: i0.ɵɵComponentDeclaration<RecordChangesComponent, "mj-record-changes", never, { "record": { "alias": "record"; "required": false; }; }, { "dialogClosed": "dialogClosed"; }, never, never, false, never>;
32
+ }
@@ -1,215 +1,215 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
- import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
11
- import { EntityFieldTSType, LogError, RunView } from '@memberjunction/core';
12
- import * as i0 from "@angular/core";
13
- import * as i1 from "@angular/platform-browser";
14
- import * as i2 from "@angular/common";
15
- import * as i3 from "@progress/kendo-angular-grid";
16
- import * as i4 from "@progress/kendo-angular-dialog";
17
- import * as i5 from "@progress/kendo-angular-indicators";
18
- const _c0 = ["recordChangesWrapper"];
19
- function RecordChangesComponent_kendo_loader_4_Template(rf, ctx) { if (rf & 1) {
20
- i0.ɵɵelement(0, "kendo-loader", 5);
21
- } }
22
- function RecordChangesComponent_div_5_kendo_grid_column_4_ng_template_1_Template(rf, ctx) { if (rf & 1) {
23
- i0.ɵɵelement(0, "span", 13);
24
- } if (rf & 2) {
25
- const dataItem_r8 = ctx.$implicit;
26
- const item_r5 = i0.ɵɵnextContext().$implicit;
27
- const ctx_r7 = i0.ɵɵnextContext(2);
28
- i0.ɵɵproperty("innerHTML", ctx_r7.FormatColumnValue(item_r5, dataItem_r8[item_r5.field], dataItem_r8), i0.ɵɵsanitizeHtml);
29
- } }
30
- const _c1 = function () { return { "font-weight": "bold", "background-color": "#a9c2af" }; };
31
- function RecordChangesComponent_div_5_kendo_grid_column_4_Template(rf, ctx) { if (rf & 1) {
32
- i0.ɵɵelementStart(0, "kendo-grid-column", 11);
33
- i0.ɵɵtemplate(1, RecordChangesComponent_div_5_kendo_grid_column_4_ng_template_1_Template, 1, 1, "ng-template", 12);
34
- i0.ɵɵelementEnd();
35
- } if (rf & 2) {
36
- const item_r5 = ctx.$implicit;
37
- i0.ɵɵpropertyInterpolate("field", item_r5.field);
38
- i0.ɵɵpropertyInterpolate("title", item_r5.title);
39
- i0.ɵɵproperty("width", item_r5.width)("headerStyle", i0.ɵɵpureFunction0(4, _c1));
40
- } }
41
- function RecordChangesComponent_div_5_Template(rf, ctx) { if (rf & 1) {
42
- i0.ɵɵelementStart(0, "div", 6, 7)(2, "div", 8)(3, "kendo-grid", 9);
43
- i0.ɵɵtemplate(4, RecordChangesComponent_div_5_kendo_grid_column_4_Template, 2, 5, "kendo-grid-column", 10);
44
- i0.ɵɵelementEnd()()();
45
- } if (rf & 2) {
46
- const ctx_r2 = i0.ɵɵnextContext();
47
- i0.ɵɵadvance(3);
48
- i0.ɵɵproperty("kendoGridBinding", ctx_r2.viewData)("pageSize", 100)("sortable", true)("sort", ctx_r2.sortSettings)("loading", ctx_r2.showloader)("resizable", true)("navigable", true);
49
- i0.ɵɵadvance(1);
50
- i0.ɵɵproperty("ngForOf", ctx_r2.visibleColumns);
51
- } }
52
- export class RecordChangesComponent {
53
- constructor(sanitizer, renderer) {
54
- this.sanitizer = sanitizer;
55
- this.renderer = renderer;
56
- this.showloader = false;
57
- this.dialogClosed = new EventEmitter();
58
- this.viewData = [];
59
- this.visibleColumns = [];
60
- this.sortSettings = [
61
- {
62
- field: "ChangedAt",
63
- dir: "desc",
64
- },
65
- ];
66
- }
67
- ngOnInit() {
68
- if (this.record) {
69
- this.showloader = true;
70
- this.LoadRecordChanges(this.record.PrimaryKey.Value, '', this.record.EntityInfo.Name);
71
- this.prepareColumns();
72
- }
73
- }
74
- ngAfterViewInit() {
75
- // Move the wrapper to the body when the component is initialized
76
- if (this.renderer && this.wrapper && this.wrapper.nativeElement)
77
- this.renderer.appendChild(document.body, this.wrapper.nativeElement);
78
- }
79
- ngOnDestroy() {
80
- // Remove the wrapper from the body when the component is destroyed
81
- if (this.renderer && this.wrapper && this.wrapper.nativeElement)
82
- this.renderer.removeChild(document.body, this.wrapper.nativeElement);
83
- }
84
- LoadRecordChanges(primaryKeyValue, appName, entityName) {
85
- return __awaiter(this, void 0, void 0, function* () {
86
- // Perform any necessary actions with the ViewID, such as fetching data
87
- if (primaryKeyValue && entityName) {
88
- const rv = new RunView();
89
- const response = yield rv.RunView({ EntityName: "Record Changes", ExtraFilter: `Entity='${entityName}' AND RecordID='${primaryKeyValue}'` });
90
- if (response.Success) {
91
- this.viewData = response.Results;
92
- this.showloader = false;
93
- }
94
- else {
95
- LogError(response.ErrorMessage);
96
- this.showloader = false;
97
- }
98
- }
99
- });
100
- }
101
- prepareColumns() {
102
- this.visibleColumns = [{
103
- field: 'ChangedAt',
104
- title: 'Date',
105
- type: 'datetime',
106
- width: 175,
107
- },
108
- {
109
- field: 'ChangesDescription',
110
- title: 'Changes',
111
- type: 'description',
112
- }];
113
- }
114
- closePropertiesDialog() {
115
- this.dialogClosed.emit();
116
- }
117
- FormatColumnValue(col, value, dataItem, maxLength = 0, trailingChars = "...") {
118
- if (value === null)
119
- return null;
120
- try {
121
- switch (col.type.toLowerCase()) {
122
- case 'datetime':
123
- let utcDate = new Date(value); // UTC date
124
- // Calculate the local timezone offset and adjust the date
125
- const localDate = new Date(utcDate.getTime() - utcDate.getTimezoneOffset() * 60000);
126
- return new Intl.DateTimeFormat('en-US', {
127
- year: 'numeric',
128
- month: 'short',
129
- day: 'numeric',
130
- hour: 'numeric',
131
- minute: 'numeric',
132
- hour12: true,
133
- }).format(localDate);
134
- case 'int':
135
- return new Intl.NumberFormat(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
136
- case 'description':
137
- // while the database stores a description, it is easier to use the ChangesJSON field to get the structured data and then generate our HTML
138
- const json = JSON.parse(dataItem.ChangesJSON);
139
- if (!json)
140
- return value;
141
- else {
142
- const fields = Object.keys(json); // each field that was changed is a key in the JSON object
143
- let formattedDescription = '<div>';
144
- for (let i = 0; i < fields.length; i++) {
145
- const changeInfo = json[fields[i]];
146
- const field = this.record.EntityInfo.Fields.find((f) => { var _a; return f.Name.trim().toLowerCase() === ((_a = changeInfo.field) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase()); });
147
- let innerDescription = "";
148
- if (field) {
149
- // we have field metadata, so we can use that
150
- if (field.TSType === EntityFieldTSType.Boolean) {
151
- // for boolean fields, it is cleaner to just show new value, no need to show old value as it is always the opposite
152
- innerDescription = `${this.fieldMarkup(field.DisplayNameOrName)} set to ${this.valueMarkup(changeInfo, false)}`;
153
- }
154
- else {
155
- innerDescription = `${this.fieldMarkup(field.DisplayNameOrName)} changed from ${this.valueMarkup(changeInfo, true)} to ${this.valueMarkup(changeInfo, false)}`;
156
- }
157
- }
158
- else {
159
- // we don't have field metadata - this could happen if a field was removed or renamed after a record change was recorded in the database, so just use what we have
160
- innerDescription = `${this.fieldMarkup(changeInfo.field)} changed from ${this.valueMarkup(changeInfo, true)}} to ${this.valueMarkup(changeInfo, false)}`;
161
- }
162
- formattedDescription += `<div>${innerDescription}</div>`;
163
- }
164
- formattedDescription += '</div>';
165
- return this.sanitizer.bypassSecurityTrustHtml(formattedDescription);
166
- }
167
- default:
168
- return value;
169
- }
170
- }
171
- catch (e) {
172
- LogError(e);
173
- return value;
174
- }
175
- }
176
- fieldMarkup(fieldName) {
177
- return `<b>${fieldName}</b>`;
178
- }
179
- valueMarkup(changeInfo, isOldValue) {
180
- return `<span style="color: ${isOldValue ? 'darkgray' : 'blue'};">${isOldValue ? changeInfo.oldValue : changeInfo.newValue}</span>`;
181
- }
182
- }
183
- RecordChangesComponent.ɵfac = function RecordChangesComponent_Factory(t) { return new (t || RecordChangesComponent)(i0.ɵɵdirectiveInject(i1.DomSanitizer), i0.ɵɵdirectiveInject(i0.Renderer2)); };
184
- RecordChangesComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RecordChangesComponent, selectors: [["mj-record-changes"]], viewQuery: function RecordChangesComponent_Query(rf, ctx) { if (rf & 1) {
185
- i0.ɵɵviewQuery(_c0, 7);
186
- } if (rf & 2) {
187
- let _t;
188
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.wrapper = _t.first);
189
- } }, inputs: { record: "record" }, outputs: { dialogClosed: "dialogClosed" }, decls: 6, vars: 7, consts: [["recordChangesWrapper", ""], [1, "k-overlay"], ["title", "Record Changes", 1, "kendo-window-custom", 3, "width", "height", "minHeight", "minWidth", "resizable", "close"], ["type", "converging-spinner", 4, "ngIf"], ["class", "k-d-flex k-flex-col k-justify-content-between k-h-full", 4, "ngIf"], ["type", "converging-spinner"], [1, "k-d-flex", "k-flex-col", "k-justify-content-between", "k-h-full"], ["dialogContainer", ""], [1, "kendo-grid-container"], ["scrollable", "virtual", 3, "kendoGridBinding", "pageSize", "sortable", "sort", "loading", "resizable", "navigable"], [3, "field", "title", "width", "headerStyle", 4, "ngFor", "ngForOf"], [3, "field", "title", "width", "headerStyle"], ["kendoGridCellTemplate", ""], [3, "innerHTML"]], template: function RecordChangesComponent_Template(rf, ctx) { if (rf & 1) {
190
- i0.ɵɵelementStart(0, "div", null, 0);
191
- i0.ɵɵelement(2, "div", 1);
192
- i0.ɵɵelementStart(3, "kendo-window", 2);
193
- i0.ɵɵlistener("close", function RecordChangesComponent_Template_kendo_window_close_3_listener() { return ctx.closePropertiesDialog(); });
194
- i0.ɵɵtemplate(4, RecordChangesComponent_kendo_loader_4_Template, 1, 0, "kendo-loader", 3);
195
- i0.ɵɵtemplate(5, RecordChangesComponent_div_5_Template, 5, 8, "div", 4);
196
- i0.ɵɵelementEnd()();
197
- } if (rf & 2) {
198
- i0.ɵɵadvance(3);
199
- i0.ɵɵproperty("width", 700)("height", 525)("minHeight", 300)("minWidth", 400)("resizable", true);
200
- i0.ɵɵadvance(1);
201
- i0.ɵɵproperty("ngIf", ctx.showloader);
202
- i0.ɵɵadvance(1);
203
- i0.ɵɵproperty("ngIf", !ctx.showloader);
204
- } }, dependencies: [i2.NgForOf, i2.NgIf, i3.GridComponent, i3.DataBindingDirective, i3.ColumnComponent, i3.CellTemplateDirective, i4.WindowComponent, i5.LoaderComponent] });
205
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordChangesComponent, [{
206
- type: Component,
207
- args: [{ selector: 'mj-record-changes', template: "<div #recordChangesWrapper>\r\n <div class=\"k-overlay\"></div>\r\n <kendo-window \r\n class=\"kendo-window-custom\"\r\n [width]=\"700\"\r\n [height]=\"525\"\r\n [minHeight]=\"300\"\r\n [minWidth]=\"400\"\r\n [resizable]=\"true\"\r\n title=\"Record Changes\"\r\n (close)=\"closePropertiesDialog()\"\r\n >\r\n <kendo-loader *ngIf=\"showloader\" type=\"converging-spinner\" ></kendo-loader>\r\n <div class=\"k-d-flex k-flex-col k-justify-content-between k-h-full\" #dialogContainer *ngIf=\"!showloader\">\r\n <div class=\"kendo-grid-container\">\r\n <kendo-grid \r\n [kendoGridBinding]=\"viewData\" \r\n scrollable=\"virtual\" \r\n [pageSize]=\"100\" \r\n [sortable]=\"true\"\r\n [sort]=\"sortSettings\" \r\n [loading]=\"showloader\" \r\n [resizable]=\"true\" \r\n [navigable]=\"true\"\r\n >\r\n <kendo-grid-column \r\n *ngFor=\"let item of visibleColumns; let i = index\" \r\n field=\"{{item.field}}\"\r\n title=\"{{item.title}}\" \r\n [width]=\"item.width\"\r\n [headerStyle]=\"{ 'font-weight' : 'bold', 'background-color': '#a9c2af' }\"\r\n >\r\n <ng-template kendoGridCellTemplate let-dataItem>\r\n <span [innerHTML]=\"FormatColumnValue(item, dataItem[item.field], dataItem)\"></span>\r\n </ng-template>\r\n </kendo-grid-column>\r\n </kendo-grid>\r\n </div>\r\n </div>\r\n </kendo-window>\r\n</div>" }]
208
- }], function () { return [{ type: i1.DomSanitizer }, { type: i0.Renderer2 }]; }, { dialogClosed: [{
209
- type: Output
210
- }], record: [{
211
- type: Input
212
- }], wrapper: [{
213
- type: ViewChild,
214
- args: ['recordChangesWrapper', { static: true }]
215
- }] }); })();
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';
11
+ import { EntityFieldTSType, LogError, RunView } from '@memberjunction/core';
12
+ import * as i0 from "@angular/core";
13
+ import * as i1 from "@angular/platform-browser";
14
+ import * as i2 from "@angular/common";
15
+ import * as i3 from "@progress/kendo-angular-grid";
16
+ import * as i4 from "@progress/kendo-angular-dialog";
17
+ import * as i5 from "@progress/kendo-angular-indicators";
18
+ const _c0 = ["recordChangesWrapper"];
19
+ function RecordChangesComponent_kendo_loader_4_Template(rf, ctx) { if (rf & 1) {
20
+ i0.ɵɵelement(0, "kendo-loader", 5);
21
+ } }
22
+ function RecordChangesComponent_div_5_kendo_grid_column_4_ng_template_1_Template(rf, ctx) { if (rf & 1) {
23
+ i0.ɵɵelement(0, "span", 13);
24
+ } if (rf & 2) {
25
+ const dataItem_r8 = ctx.$implicit;
26
+ const item_r5 = i0.ɵɵnextContext().$implicit;
27
+ const ctx_r7 = i0.ɵɵnextContext(2);
28
+ i0.ɵɵproperty("innerHTML", ctx_r7.FormatColumnValue(item_r5, dataItem_r8[item_r5.field], dataItem_r8), i0.ɵɵsanitizeHtml);
29
+ } }
30
+ const _c1 = () => ({ "font-weight": "bold", "background-color": "#a9c2af" });
31
+ function RecordChangesComponent_div_5_kendo_grid_column_4_Template(rf, ctx) { if (rf & 1) {
32
+ i0.ɵɵelementStart(0, "kendo-grid-column", 11);
33
+ i0.ɵɵtemplate(1, RecordChangesComponent_div_5_kendo_grid_column_4_ng_template_1_Template, 1, 1, "ng-template", 12);
34
+ i0.ɵɵelementEnd();
35
+ } if (rf & 2) {
36
+ const item_r5 = ctx.$implicit;
37
+ i0.ɵɵpropertyInterpolate("field", item_r5.field);
38
+ i0.ɵɵpropertyInterpolate("title", item_r5.title);
39
+ i0.ɵɵproperty("width", item_r5.width)("headerStyle", i0.ɵɵpureFunction0(4, _c1));
40
+ } }
41
+ function RecordChangesComponent_div_5_Template(rf, ctx) { if (rf & 1) {
42
+ i0.ɵɵelementStart(0, "div", 6, 7)(2, "div", 8)(3, "kendo-grid", 9);
43
+ i0.ɵɵtemplate(4, RecordChangesComponent_div_5_kendo_grid_column_4_Template, 2, 5, "kendo-grid-column", 10);
44
+ i0.ɵɵelementEnd()()();
45
+ } if (rf & 2) {
46
+ const ctx_r2 = i0.ɵɵnextContext();
47
+ i0.ɵɵadvance(3);
48
+ i0.ɵɵproperty("kendoGridBinding", ctx_r2.viewData)("pageSize", 100)("sortable", true)("sort", ctx_r2.sortSettings)("loading", ctx_r2.showloader)("resizable", true)("navigable", true);
49
+ i0.ɵɵadvance();
50
+ i0.ɵɵproperty("ngForOf", ctx_r2.visibleColumns);
51
+ } }
52
+ export class RecordChangesComponent {
53
+ constructor(sanitizer, renderer) {
54
+ this.sanitizer = sanitizer;
55
+ this.renderer = renderer;
56
+ this.showloader = false;
57
+ this.dialogClosed = new EventEmitter();
58
+ this.viewData = [];
59
+ this.visibleColumns = [];
60
+ this.sortSettings = [
61
+ {
62
+ field: "ChangedAt",
63
+ dir: "desc",
64
+ },
65
+ ];
66
+ }
67
+ ngOnInit() {
68
+ if (this.record) {
69
+ this.showloader = true;
70
+ this.LoadRecordChanges(this.record.PrimaryKey.Value, '', this.record.EntityInfo.Name);
71
+ this.prepareColumns();
72
+ }
73
+ }
74
+ ngAfterViewInit() {
75
+ // Move the wrapper to the body when the component is initialized
76
+ if (this.renderer && this.wrapper && this.wrapper.nativeElement)
77
+ this.renderer.appendChild(document.body, this.wrapper.nativeElement);
78
+ }
79
+ ngOnDestroy() {
80
+ // Remove the wrapper from the body when the component is destroyed
81
+ if (this.renderer && this.wrapper && this.wrapper.nativeElement)
82
+ this.renderer.removeChild(document.body, this.wrapper.nativeElement);
83
+ }
84
+ LoadRecordChanges(primaryKeyValue, appName, entityName) {
85
+ return __awaiter(this, void 0, void 0, function* () {
86
+ // Perform any necessary actions with the ViewID, such as fetching data
87
+ if (primaryKeyValue && entityName) {
88
+ const rv = new RunView();
89
+ const response = yield rv.RunView({ EntityName: "Record Changes", ExtraFilter: `Entity='${entityName}' AND RecordID='${primaryKeyValue}'` });
90
+ if (response.Success) {
91
+ this.viewData = response.Results;
92
+ this.showloader = false;
93
+ }
94
+ else {
95
+ LogError(response.ErrorMessage);
96
+ this.showloader = false;
97
+ }
98
+ }
99
+ });
100
+ }
101
+ prepareColumns() {
102
+ this.visibleColumns = [{
103
+ field: 'ChangedAt',
104
+ title: 'Date',
105
+ type: 'datetime',
106
+ width: 175,
107
+ },
108
+ {
109
+ field: 'ChangesDescription',
110
+ title: 'Changes',
111
+ type: 'description',
112
+ }];
113
+ }
114
+ closePropertiesDialog() {
115
+ this.dialogClosed.emit();
116
+ }
117
+ FormatColumnValue(col, value, dataItem, maxLength = 0, trailingChars = "...") {
118
+ if (value === null)
119
+ return null;
120
+ try {
121
+ switch (col.type.toLowerCase()) {
122
+ case 'datetime':
123
+ let utcDate = new Date(value); // UTC date
124
+ // Calculate the local timezone offset and adjust the date
125
+ const localDate = new Date(utcDate.getTime() - utcDate.getTimezoneOffset() * 60000);
126
+ return new Intl.DateTimeFormat('en-US', {
127
+ year: 'numeric',
128
+ month: 'short',
129
+ day: 'numeric',
130
+ hour: 'numeric',
131
+ minute: 'numeric',
132
+ hour12: true,
133
+ }).format(localDate);
134
+ case 'int':
135
+ return new Intl.NumberFormat(undefined, { minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(value);
136
+ case 'description':
137
+ // while the database stores a description, it is easier to use the ChangesJSON field to get the structured data and then generate our HTML
138
+ const json = JSON.parse(dataItem.ChangesJSON);
139
+ if (!json)
140
+ return value;
141
+ else {
142
+ const fields = Object.keys(json); // each field that was changed is a key in the JSON object
143
+ let formattedDescription = '<div>';
144
+ for (let i = 0; i < fields.length; i++) {
145
+ const changeInfo = json[fields[i]];
146
+ const field = this.record.EntityInfo.Fields.find((f) => { var _a; return f.Name.trim().toLowerCase() === ((_a = changeInfo.field) === null || _a === void 0 ? void 0 : _a.trim().toLowerCase()); });
147
+ let innerDescription = "";
148
+ if (field) {
149
+ // we have field metadata, so we can use that
150
+ if (field.TSType === EntityFieldTSType.Boolean) {
151
+ // for boolean fields, it is cleaner to just show new value, no need to show old value as it is always the opposite
152
+ innerDescription = `${this.fieldMarkup(field.DisplayNameOrName)} set to ${this.valueMarkup(changeInfo, false)}`;
153
+ }
154
+ else {
155
+ innerDescription = `${this.fieldMarkup(field.DisplayNameOrName)} changed from ${this.valueMarkup(changeInfo, true)} to ${this.valueMarkup(changeInfo, false)}`;
156
+ }
157
+ }
158
+ else {
159
+ // we don't have field metadata - this could happen if a field was removed or renamed after a record change was recorded in the database, so just use what we have
160
+ innerDescription = `${this.fieldMarkup(changeInfo.field)} changed from ${this.valueMarkup(changeInfo, true)}} to ${this.valueMarkup(changeInfo, false)}`;
161
+ }
162
+ formattedDescription += `<div>${innerDescription}</div>`;
163
+ }
164
+ formattedDescription += '</div>';
165
+ return this.sanitizer.bypassSecurityTrustHtml(formattedDescription);
166
+ }
167
+ default:
168
+ return value;
169
+ }
170
+ }
171
+ catch (e) {
172
+ LogError(e);
173
+ return value;
174
+ }
175
+ }
176
+ fieldMarkup(fieldName) {
177
+ return `<b>${fieldName}</b>`;
178
+ }
179
+ valueMarkup(changeInfo, isOldValue) {
180
+ return `<span style="color: ${isOldValue ? 'darkgray' : 'blue'};">${isOldValue ? changeInfo.oldValue : changeInfo.newValue}</span>`;
181
+ }
182
+ }
183
+ RecordChangesComponent.ɵfac = function RecordChangesComponent_Factory(t) { return new (t || RecordChangesComponent)(i0.ɵɵdirectiveInject(i1.DomSanitizer), i0.ɵɵdirectiveInject(i0.Renderer2)); };
184
+ RecordChangesComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RecordChangesComponent, selectors: [["mj-record-changes"]], viewQuery: function RecordChangesComponent_Query(rf, ctx) { if (rf & 1) {
185
+ i0.ɵɵviewQuery(_c0, 7);
186
+ } if (rf & 2) {
187
+ let _t;
188
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.wrapper = _t.first);
189
+ } }, inputs: { record: "record" }, outputs: { dialogClosed: "dialogClosed" }, decls: 6, vars: 7, consts: [["recordChangesWrapper", ""], [1, "k-overlay"], ["title", "Record Changes", 1, "kendo-window-custom", 3, "width", "height", "minHeight", "minWidth", "resizable", "close"], ["type", "converging-spinner", 4, "ngIf"], ["class", "k-d-flex k-flex-col k-justify-content-between k-h-full", 4, "ngIf"], ["type", "converging-spinner"], [1, "k-d-flex", "k-flex-col", "k-justify-content-between", "k-h-full"], ["dialogContainer", ""], [1, "kendo-grid-container"], ["scrollable", "virtual", 3, "kendoGridBinding", "pageSize", "sortable", "sort", "loading", "resizable", "navigable"], [3, "field", "title", "width", "headerStyle", 4, "ngFor", "ngForOf"], [3, "field", "title", "width", "headerStyle"], ["kendoGridCellTemplate", ""], [3, "innerHTML"]], template: function RecordChangesComponent_Template(rf, ctx) { if (rf & 1) {
190
+ i0.ɵɵelementStart(0, "div", null, 0);
191
+ i0.ɵɵelement(2, "div", 1);
192
+ i0.ɵɵelementStart(3, "kendo-window", 2);
193
+ i0.ɵɵlistener("close", function RecordChangesComponent_Template_kendo_window_close_3_listener() { return ctx.closePropertiesDialog(); });
194
+ i0.ɵɵtemplate(4, RecordChangesComponent_kendo_loader_4_Template, 1, 0, "kendo-loader", 3)(5, RecordChangesComponent_div_5_Template, 5, 8, "div", 4);
195
+ i0.ɵɵelementEnd()();
196
+ } if (rf & 2) {
197
+ i0.ɵɵadvance(3);
198
+ i0.ɵɵproperty("width", 700)("height", 525)("minHeight", 300)("minWidth", 400)("resizable", true);
199
+ i0.ɵɵadvance();
200
+ i0.ɵɵproperty("ngIf", ctx.showloader);
201
+ i0.ɵɵadvance();
202
+ i0.ɵɵproperty("ngIf", !ctx.showloader);
203
+ } }, dependencies: [i2.NgForOf, i2.NgIf, i3.GridComponent, i3.DataBindingDirective, i3.ColumnComponent, i3.CellTemplateDirective, i4.WindowComponent, i5.LoaderComponent] });
204
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordChangesComponent, [{
205
+ type: Component,
206
+ args: [{ selector: 'mj-record-changes', template: "<div #recordChangesWrapper>\r\n <div class=\"k-overlay\"></div>\r\n <kendo-window \r\n class=\"kendo-window-custom\"\r\n [width]=\"700\"\r\n [height]=\"525\"\r\n [minHeight]=\"300\"\r\n [minWidth]=\"400\"\r\n [resizable]=\"true\"\r\n title=\"Record Changes\"\r\n (close)=\"closePropertiesDialog()\"\r\n >\r\n <kendo-loader *ngIf=\"showloader\" type=\"converging-spinner\" ></kendo-loader>\r\n <div class=\"k-d-flex k-flex-col k-justify-content-between k-h-full\" #dialogContainer *ngIf=\"!showloader\">\r\n <div class=\"kendo-grid-container\">\r\n <kendo-grid \r\n [kendoGridBinding]=\"viewData\" \r\n scrollable=\"virtual\" \r\n [pageSize]=\"100\" \r\n [sortable]=\"true\"\r\n [sort]=\"sortSettings\" \r\n [loading]=\"showloader\" \r\n [resizable]=\"true\" \r\n [navigable]=\"true\"\r\n >\r\n <kendo-grid-column \r\n *ngFor=\"let item of visibleColumns; let i = index\" \r\n field=\"{{item.field}}\"\r\n title=\"{{item.title}}\" \r\n [width]=\"item.width\"\r\n [headerStyle]=\"{ 'font-weight' : 'bold', 'background-color': '#a9c2af' }\"\r\n >\r\n <ng-template kendoGridCellTemplate let-dataItem>\r\n <span [innerHTML]=\"FormatColumnValue(item, dataItem[item.field], dataItem)\"></span>\r\n </ng-template>\r\n </kendo-grid-column>\r\n </kendo-grid>\r\n </div>\r\n </div>\r\n </kendo-window>\r\n</div>" }]
207
+ }], () => [{ type: i1.DomSanitizer }, { type: i0.Renderer2 }], { dialogClosed: [{
208
+ type: Output
209
+ }], record: [{
210
+ type: Input
211
+ }], wrapper: [{
212
+ type: ViewChild,
213
+ args: ['recordChangesWrapper', { static: true }]
214
+ }] }); })();
215
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RecordChangesComponent, { className: "RecordChangesComponent", filePath: "src\\lib\\ng-record-changes.component.ts", lineNumber: 11 }); })();
@@ -1,17 +1,17 @@
1
- import * as i0 from "@angular/core";
2
- import * as i1 from "./ng-record-changes.component";
3
- import * as i2 from "@angular/common";
4
- import * as i3 from "@angular/forms";
5
- import * as i4 from "@angular/router";
6
- import * as i5 from "@progress/kendo-angular-grid";
7
- import * as i6 from "@progress/kendo-angular-dialog";
8
- import * as i7 from "@progress/kendo-angular-excel-export";
9
- import * as i8 from "@progress/kendo-angular-buttons";
10
- import * as i9 from "@memberjunction/ng-compare-records";
11
- import * as i10 from "@memberjunction/ng-container-directives";
12
- import * as i11 from "@progress/kendo-angular-indicators";
13
- export declare class RecordChangesModule {
14
- static ɵfac: i0.ɵɵFactoryDeclaration<RecordChangesModule, never>;
15
- static ɵmod: i0.ɵɵNgModuleDeclaration<RecordChangesModule, [typeof i1.RecordChangesComponent], [typeof i2.CommonModule, typeof i3.FormsModule, typeof i3.ReactiveFormsModule, typeof i4.RouterModule, typeof i5.GridModule, typeof i6.DialogsModule, typeof i7.ExcelExportModule, typeof i8.ButtonsModule, typeof i9.CompareRecordsModule, typeof i10.ContainerDirectivesModule, typeof i11.IndicatorsModule], [typeof i1.RecordChangesComponent]>;
16
- static ɵinj: i0.ɵɵInjectorDeclaration<RecordChangesModule>;
17
- }
1
+ import * as i0 from "@angular/core";
2
+ import * as i1 from "./ng-record-changes.component";
3
+ import * as i2 from "@angular/common";
4
+ import * as i3 from "@angular/forms";
5
+ import * as i4 from "@angular/router";
6
+ import * as i5 from "@progress/kendo-angular-grid";
7
+ import * as i6 from "@progress/kendo-angular-dialog";
8
+ import * as i7 from "@progress/kendo-angular-excel-export";
9
+ import * as i8 from "@progress/kendo-angular-buttons";
10
+ import * as i9 from "@memberjunction/ng-compare-records";
11
+ import * as i10 from "@memberjunction/ng-container-directives";
12
+ import * as i11 from "@progress/kendo-angular-indicators";
13
+ export declare class RecordChangesModule {
14
+ static ɵfac: i0.ɵɵFactoryDeclaration<RecordChangesModule, never>;
15
+ static ɵmod: i0.ɵɵNgModuleDeclaration<RecordChangesModule, [typeof i1.RecordChangesComponent], [typeof i2.CommonModule, typeof i3.FormsModule, typeof i3.ReactiveFormsModule, typeof i4.RouterModule, typeof i5.GridModule, typeof i6.DialogsModule, typeof i7.ExcelExportModule, typeof i8.ButtonsModule, typeof i9.CompareRecordsModule, typeof i10.ContainerDirectivesModule, typeof i11.IndicatorsModule], [typeof i1.RecordChangesComponent]>;
16
+ static ɵinj: i0.ɵɵInjectorDeclaration<RecordChangesModule>;
17
+ }
@@ -1,64 +1,64 @@
1
- import { NgModule } from '@angular/core';
2
- import { RecordChangesComponent } from './ng-record-changes.component';
3
- import { CommonModule } from '@angular/common';
4
- import { FormsModule, ReactiveFormsModule } from '@angular/forms';
5
- import { RouterModule } from '@angular/router';
6
- // Kendo UI Angular imports
7
- import { GridModule } from '@progress/kendo-angular-grid';
8
- import { ExcelExportModule } from '@progress/kendo-angular-excel-export';
9
- import { DialogsModule } from "@progress/kendo-angular-dialog";
10
- import { ButtonsModule } from '@progress/kendo-angular-buttons';
11
- import { IndicatorsModule } from '@progress/kendo-angular-indicators';
12
- import { CompareRecordsModule } from '@memberjunction/ng-compare-records';
13
- import { ContainerDirectivesModule } from '@memberjunction/ng-container-directives';
14
- import * as i0 from "@angular/core";
15
- export class RecordChangesModule {
16
- }
17
- RecordChangesModule.ɵfac = function RecordChangesModule_Factory(t) { return new (t || RecordChangesModule)(); };
18
- RecordChangesModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: RecordChangesModule });
19
- RecordChangesModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule,
20
- FormsModule,
21
- ReactiveFormsModule,
22
- RouterModule,
23
- GridModule,
24
- DialogsModule,
25
- ExcelExportModule,
26
- ButtonsModule,
27
- CompareRecordsModule,
28
- ContainerDirectivesModule,
29
- IndicatorsModule] });
30
- (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordChangesModule, [{
31
- type: NgModule,
32
- args: [{
33
- declarations: [
34
- RecordChangesComponent
35
- ],
36
- imports: [
37
- CommonModule,
38
- FormsModule,
39
- ReactiveFormsModule,
40
- RouterModule,
41
- GridModule,
42
- DialogsModule,
43
- ExcelExportModule,
44
- ButtonsModule,
45
- CompareRecordsModule,
46
- ContainerDirectivesModule,
47
- IndicatorsModule
48
- ],
49
- exports: [
50
- RecordChangesComponent
51
- ]
52
- }]
53
- }], null, null); })();
54
- (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(RecordChangesModule, { declarations: [RecordChangesComponent], imports: [CommonModule,
55
- FormsModule,
56
- ReactiveFormsModule,
57
- RouterModule,
58
- GridModule,
59
- DialogsModule,
60
- ExcelExportModule,
61
- ButtonsModule,
62
- CompareRecordsModule,
63
- ContainerDirectivesModule,
64
- IndicatorsModule], exports: [RecordChangesComponent] }); })();
1
+ import { NgModule } from '@angular/core';
2
+ import { RecordChangesComponent } from './ng-record-changes.component';
3
+ import { CommonModule } from '@angular/common';
4
+ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
5
+ import { RouterModule } from '@angular/router';
6
+ // Kendo UI Angular imports
7
+ import { GridModule } from '@progress/kendo-angular-grid';
8
+ import { ExcelExportModule } from '@progress/kendo-angular-excel-export';
9
+ import { DialogsModule } from "@progress/kendo-angular-dialog";
10
+ import { ButtonsModule } from '@progress/kendo-angular-buttons';
11
+ import { IndicatorsModule } from '@progress/kendo-angular-indicators';
12
+ import { CompareRecordsModule } from '@memberjunction/ng-compare-records';
13
+ import { ContainerDirectivesModule } from '@memberjunction/ng-container-directives';
14
+ import * as i0 from "@angular/core";
15
+ export class RecordChangesModule {
16
+ }
17
+ RecordChangesModule.ɵfac = function RecordChangesModule_Factory(t) { return new (t || RecordChangesModule)(); };
18
+ RecordChangesModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: RecordChangesModule });
19
+ RecordChangesModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule,
20
+ FormsModule,
21
+ ReactiveFormsModule,
22
+ RouterModule,
23
+ GridModule,
24
+ DialogsModule,
25
+ ExcelExportModule,
26
+ ButtonsModule,
27
+ CompareRecordsModule,
28
+ ContainerDirectivesModule,
29
+ IndicatorsModule] });
30
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RecordChangesModule, [{
31
+ type: NgModule,
32
+ args: [{
33
+ declarations: [
34
+ RecordChangesComponent
35
+ ],
36
+ imports: [
37
+ CommonModule,
38
+ FormsModule,
39
+ ReactiveFormsModule,
40
+ RouterModule,
41
+ GridModule,
42
+ DialogsModule,
43
+ ExcelExportModule,
44
+ ButtonsModule,
45
+ CompareRecordsModule,
46
+ ContainerDirectivesModule,
47
+ IndicatorsModule
48
+ ],
49
+ exports: [
50
+ RecordChangesComponent
51
+ ]
52
+ }]
53
+ }], null, null); })();
54
+ (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(RecordChangesModule, { declarations: [RecordChangesComponent], imports: [CommonModule,
55
+ FormsModule,
56
+ ReactiveFormsModule,
57
+ RouterModule,
58
+ GridModule,
59
+ DialogsModule,
60
+ ExcelExportModule,
61
+ ButtonsModule,
62
+ CompareRecordsModule,
63
+ ContainerDirectivesModule,
64
+ IndicatorsModule], exports: [RecordChangesComponent] }); })();
@@ -1,2 +1,2 @@
1
- export * from './lib/ng-record-changes.component';
2
- export * from './lib/ng-record-changes.module';
1
+ export * from './lib/ng-record-changes.component';
2
+ export * from './lib/ng-record-changes.module';
@@ -1,5 +1,5 @@
1
- /*
2
- * Public API Surface of ng-user-view-grid
3
- */
4
- export * from './lib/ng-record-changes.component';
5
- export * from './lib/ng-record-changes.module';
1
+ /*
2
+ * Public API Surface of ng-user-view-grid
3
+ */
4
+ export * from './lib/ng-record-changes.component';
5
+ export * from './lib/ng-record-changes.module';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-record-changes",
3
- "version": "0.9.54",
3
+ "version": "0.9.64",
4
4
  "description": "MemberJunction: Angular pop-up window and grid to show changes made to a specific individual record",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -15,19 +15,19 @@
15
15
  "author": "",
16
16
  "license": "ISC",
17
17
  "devDependencies": {
18
- "@angular/compiler": "^15.2.0",
19
- "@angular/compiler-cli": "^15.2.0"
18
+ "@angular/compiler": "^17.1.2",
19
+ "@angular/compiler-cli": "^17.1.2"
20
20
  },
21
21
  "peerDependencies": {
22
- "@angular/common": "^15.2.0",
23
- "@angular/core": "^15.2.0",
24
- "@angular/forms": "^15.2.0",
25
- "@angular/router": "^15.2.0",
26
- "@progress/kendo-angular-grid": "^12.1.0"
22
+ "@angular/common": "^17.1.2",
23
+ "@angular/core": "^17.1.2",
24
+ "@angular/forms": "^17.1.2",
25
+ "@angular/router": "^17.1.2",
26
+ "@progress/kendo-angular-grid": "^15.0.1"
27
27
  },
28
28
  "dependencies": {
29
- "@memberjunction/core": "^0.9.116",
30
- "@memberjunction/global": "^0.9.108",
29
+ "@memberjunction/core": "^0.9.134",
30
+ "@memberjunction/global": "^0.9.123",
31
31
  "@memberjunction/ng-compare-records": "^0.9.69",
32
32
  "@memberjunction/ng-container-directives": "^0.9.45",
33
33
  "tslib": "^2.3.0"