@memberjunction/ng-user-view-grid 0.9.38 → 0.9.40

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,667 +0,0 @@
1
- import { Component, ViewChild, ElementRef, Output, EventEmitter, OnInit, Input, AfterViewInit } from '@angular/core';
2
- import { FormBuilder, FormGroup } from '@angular/forms';
3
- import { Router } from '@angular/router'
4
-
5
- import { Metadata, BaseEntity, RunView, RunViewParams, EntityFieldInfo, EntityFieldTSType, EntityInfo, LogError } from '@memberjunction/core';
6
- import { ViewInfo, ViewGridState, ViewColumnInfo, UserViewEntityExtended } from '@memberjunction/core-entities';
7
-
8
- import { CellClickEvent, GridDataResult, PageChangeEvent, GridComponent, CellCloseEvent,
9
- ColumnReorderEvent, ColumnResizeArgs, ColumnComponent, SelectionEvent, SelectableSettings} from "@progress/kendo-angular-grid";
10
- import { Keys } from '@progress/kendo-angular-common';
11
-
12
-
13
- import { Subject } from 'rxjs';
14
- import { ExcelExportComponent } from '@progress/kendo-angular-excel-export';
15
- import { DisplaySimpleNotificationRequestData, MJEventType, MJGlobal } from '@memberjunction/global';
16
-
17
-
18
- export type GridRowClickedEvent = {
19
- entityId: number;
20
- entityName: string;
21
- recordId: number;
22
- }
23
-
24
- export type GridRowEditedEvent = {
25
- record: BaseEntity;
26
- row: number;
27
- saved: boolean;
28
- }
29
-
30
- export type GridPendingRecordItem = {
31
- record: BaseEntity;
32
- row: number;
33
- dataItem: any;
34
- }
35
-
36
- @Component({
37
- selector: 'mj-user-view-grid',
38
- templateUrl: './ng-user-view-grid.component.html',
39
- styleUrls: ['./ng-user-view-grid.component.css']
40
- })
41
- export class UserViewGridComponent implements OnInit, AfterViewInit {
42
- title = 'UserViewGrid';
43
- @Input() Params: RunViewParams | undefined;
44
- @Input() BottomMargin: number = 0;
45
- @Input() InEditMode: boolean = false;
46
- @Input() EditMode: "None" | "Save" | "Queue" = "None"
47
- @Input() AutoNavigate: boolean = true;
48
-
49
- @Output() rowClicked = new EventEmitter<GridRowClickedEvent>();
50
- @Output() rowEdited = new EventEmitter<GridRowEditedEvent>();
51
-
52
- @ViewChild('kendoGrid', { read: GridComponent }) kendoGridElement: GridComponent | null = null;
53
- @ViewChild('kendoGrid', { read: ElementRef }) kendoGridElementRef: ElementRef | null = null;
54
- @ViewChild('excelExport', { read: ExcelExportComponent }) kendoExcelExport: ExcelExportComponent | null = null;
55
-
56
- private _pendingRecords: GridPendingRecordItem[] = [];
57
-
58
- public viewData: [] = [];
59
- public totalRowCount: number = 0;
60
- public formattedData: { [key: string]: any }[] = [];
61
- public viewColumns: ViewColumnInfo[] = [];
62
- public visibleColumns: ViewColumnInfo[] = [];
63
- public sortSettings: any[] = [];
64
- public entityRecord: BaseEntity | null = null;
65
- public skip: number = 0;
66
- public pageSize: number = 40;
67
- public isLoading: boolean = false;
68
- public gridView: GridDataResult = { data: [], total: 0 };
69
- public gridHeight: number = 750;
70
-
71
- public _viewEntity: UserViewEntityExtended | undefined
72
- public _entityInfo: EntityInfo | undefined;
73
- private _newGridState: ViewGridState = {};
74
-
75
- private editModeEnded = new Subject<void>();
76
- public compareMode: boolean = false;
77
- public compareRecords: BaseEntity[] = [];
78
- public selectableSettings: SelectableSettings = {
79
- enabled: false
80
- };
81
- public selectedKeys: any[] = [];
82
- public isDialogOpened: boolean = false;
83
- public showRefreshButton: boolean = true;
84
-
85
- public viewExecutionTime: number = 0;
86
-
87
- public get PendingRecords(): GridPendingRecordItem[] {
88
- return this._pendingRecords;
89
- }
90
-
91
- protected StartEditMode() {
92
- this.InEditMode = true;
93
- }
94
- protected EndEditMode() {
95
- this.InEditMode = false;
96
- this.editModeEnded.next();
97
- }
98
- public EditingComplete(): Promise<boolean> {
99
- if (this.InEditMode) {
100
- // we need to wait for edit mode to end before we can return true
101
- return new Promise((resolve, reject) => {
102
- const subscription = this.editModeEnded.subscribe(() => {
103
- resolve(true);
104
- subscription.unsubscribe();
105
- });
106
- });
107
- }
108
- else
109
- return Promise.resolve(true); // not in edit mode, so editing is complete!
110
- }
111
-
112
- public IsDynamicView(): boolean {
113
- return !this._viewEntity; // if we have a viewEntity it is a stored view
114
- }
115
-
116
- public pageChange(event: PageChangeEvent): void {
117
- this.skip = event.skip;
118
- this.virtualLoadData();
119
- }
120
-
121
- private virtualLoadData(): void {
122
- // check to see if we have already formatted the slice of the data we need right now
123
- // we are storing the formattted data in the formattedData array and it has same set
124
- // of indexes as the viewData array (raw unformatted data). When we first get viewData
125
- // from the server we create an array of the same length as viewData, but have nulls in all of the
126
- // indexes. As we format each row of viewData we store the formatted row in the same index
127
- // in the formattedData array. So if we have already formatted the data we need for the current
128
- // page, we just use that data, otherwise we format the data we need for the current page
129
-
130
- try {
131
- // check to see if we have already formatted the data we need for the current page
132
- for (let i = this.skip; (i < (this.skip + this.pageSize)) && (i < this.viewData.length); i++) {
133
- if (!this.formattedData[i]) {
134
- // we have not formatted this row yet, so format it
135
- const r = this.viewColumns.map((c) => {
136
- if (c && c.EntityField && this.viewData[i] && this.viewData[i][c.EntityField.Name]) {
137
- if (!c.hidden && c.EntityField.Name !== 'ID') {
138
- const ef = c.EntityField;
139
- return {field: c.EntityField.Name, value: ef.FormatValue(this.viewData[i][c.EntityField.Name], 0, undefined, 300)}
140
- }
141
- else
142
- return {field: c.EntityField.Name, value: this.viewData[i][c.EntityField.Name]} // hidden column, so just return the value, don't bother formatting
143
- }
144
- else
145
- return {field: c.Name, value: null};
146
- });
147
- // now r is an array of {field: string, value: any} objects, so we need to convert it to an object
148
- // with the field names as the keys and the values as the values
149
- const row: { [key: string]: any } = {};
150
- for (let j = 0; j < r.length; j++) {
151
- if (r[j] && r[j].field && r[j].field.length > 0)
152
- row[r[j].field] = r[j].value;
153
- }
154
- this.formattedData[i] = row;
155
- }
156
- }
157
-
158
- // now that we have made sure current page of data is formatted, we can return it
159
- this.gridView = {
160
- data: this.formattedData.slice(this.skip, this.skip + this.pageSize),
161
- total: this.viewData.length,
162
- };
163
- }
164
- catch (e) {
165
- LogError(e);
166
- }
167
- }
168
-
169
- constructor(private formBuilder: FormBuilder,
170
- private router: Router) {
171
-
172
- }
173
-
174
- private _saveTimeout: any;
175
- private SaveView() {
176
- // debounced outer function...
177
- clearTimeout(this._saveTimeout);
178
- this._saveTimeout = setTimeout(async ()=> {
179
- // when we actually call inner save view we do await
180
- await this.innerSaveView()
181
- }, 5000); // 5 seconds delay
182
- };
183
-
184
-
185
- private _viewDirty: boolean = false;
186
- public async innerSaveView() {
187
- if (this._viewDirty) {
188
- const md = new Metadata()
189
- if (this._viewEntity &&
190
- this._viewEntity.Get('UserID') === md.CurrentUser.ID) {
191
- // this view is a saved view, AND it belongs to the current user
192
-
193
- // update the grid state if we have settings updates for columns and/or sorts
194
- const tempGridState: ViewGridState = JSON.parse(this._viewEntity.Get('GridState'));
195
- const tempColSettings = this._newGridState.columnSettings ? this._newGridState.columnSettings : tempGridState.columnSettings;
196
- tempColSettings.forEach((col: ViewColumnInfo) => {col.DisplayName, col.ID, col.Name, col.hidden, col.orderIndex, col.width}); // remove EntityFieldInfo from the column settings
197
- tempGridState.columnSettings = tempColSettings;
198
- tempGridState.sortSettings = this._newGridState.sortSettings ? this._newGridState.sortSettings : tempGridState.sortSettings;
199
-
200
- // now stringify the grid state and save it
201
- this._viewEntity.Set('GridState', JSON.stringify(tempGridState));
202
- const newSortState = tempGridState.sortSettings.map((s: any) => {return {field: s.field, direction: s.dir === 'asc' ? 1 : 2}})
203
- const oldSortState = JSON.parse(this._viewEntity.Get('SortState'));
204
- this._viewEntity.Set('SortState', JSON.stringify(newSortState));
205
-
206
- if (await this._viewEntity.Save()) {
207
- // check to see if sort state changed and if so, refresh the grid
208
- if (JSON.stringify(newSortState) !== JSON.stringify(oldSortState)) {
209
- if (this.Params) // makes sure we have params before we refresh
210
- this.Refresh(this.Params);
211
- }
212
- this._viewDirty = false;
213
- }
214
- else {
215
- this.CreateSimpleNotification('Unable to save view settings', 'error', 5000)
216
- }
217
- }
218
- }
219
- }
220
-
221
- protected CreateSimpleNotification(message: string, style: "none" | "success" | "error" | "warning" | "info", duration: number) {
222
- const data: DisplaySimpleNotificationRequestData = {
223
- message: message,
224
- style: style,
225
- DisplayDuration: duration
226
- }
227
- MJGlobal.Instance.RaiseEvent({
228
- component: this,
229
- event: MJEventType.DisplaySimpleNotificationRequest,
230
- eventCode: "",
231
- args: data
232
- })
233
- }
234
-
235
- public async columnReorder(args: ColumnReorderEvent) {
236
- // Remove the column from the original position
237
- // need to find the column in the viewColumns array because args.old/new Indexes are from the visibleColumns array
238
- const fieldName = (<any>args.column).field;
239
- if (fieldName) {
240
- const vcOldIndex = this.viewColumns.findIndex((vc: ViewColumnInfo) => vc.Name === fieldName);
241
- const vcNewIndex = this.viewColumns.findIndex((vc: ViewColumnInfo) => vc.orderIndex === args.newIndex);
242
- if (vcOldIndex >= 0) {
243
- // got the index, now remove the element
244
- const element = this.viewColumns.splice(vcOldIndex, 1)[0];
245
-
246
- // Insert it at the new position
247
- this.viewColumns.splice(vcNewIndex, 0, element);
248
-
249
- // go through all of the columns and set orderIndex as that isn't done automatically
250
- let visColIndex = 0;
251
- for (let i = 0; i < this.viewColumns.length; i++) {
252
- if (!this.viewColumns[i].hidden) {
253
- this.viewColumns[i].orderIndex = visColIndex;
254
- visColIndex++;
255
- }
256
- }
257
- // now loop through all of the HIDDEN columns and set their orderIndex, done in second loop because we want first loop to give us total number of visible columns
258
- for (let i = 0; i < this.viewColumns.length; i++) {
259
- if (this.viewColumns[i].hidden) {
260
- this.viewColumns[i].orderIndex = visColIndex;
261
- visColIndex++;
262
- }
263
- }
264
-
265
- // make sure that _newGridState.columnSettings is set
266
- this._newGridState.columnSettings = this.viewColumns;
267
-
268
- this._viewDirty = true;
269
- this.SaveView();
270
- }
271
- }
272
-
273
-
274
- }
275
-
276
- public async columnResize(args: ColumnResizeArgs[]) {
277
- for (const col of args) {
278
- const c = col.column as ColumnComponent;
279
- const viewCol = this.viewColumns.find(vc => vc.Name === c.field);
280
- const visCol = this.visibleColumns.find(vc => vc.Name === c.field);
281
- const visCols = this.visibleColumns;
282
- if (viewCol)
283
- viewCol.width = col.newWidth;
284
- }
285
-
286
- this._newGridState.columnSettings = this.viewColumns.map(vc => {
287
- return { // only pass back the stuff that should persist, we don't want EntityField persisting, waste of space!
288
- Name: vc.Name,
289
- DisplayName: vc.DisplayName,
290
- width: vc.width,
291
- orderIndex: vc.orderIndex,
292
- hidden: vc.hidden
293
- }
294
- });
295
- this._viewDirty = true;
296
- this.SaveView();
297
- }
298
-
299
- public async sortChanged(sort: any) {
300
- this._newGridState.sortSettings = sort;
301
- this.sortSettings = sort; // for the UI display - grid binding to this shows that the sort is applied via arrows in the column headers
302
-
303
- if (this.IsDynamicView()) {
304
- // Dynamic View, we have this.Params and can add an OrderBy and then just Refresh() the entire component
305
- // that will result in going to the server for a refreshed set of data
306
- if (this.Params) {
307
- this.Params.OrderBy = sort[0].field + ' ' + (sort[0].dir === 'asc' ? 'ASC' : 'DESC');
308
- this.Refresh(this.Params);
309
- }
310
- else {
311
- LogError("sortChanged() called but this.Params is null or undefined") // should never get here
312
- }
313
- }
314
- else {
315
- // Saved view - we do this on the server side only
316
- this._viewDirty = true;
317
- this.innerSaveView(); // for sort changes we call innerSaveView() directly, not through SaveView() which is debounced
318
- }
319
- }
320
-
321
- public async cellClickHandler(args: CellClickEvent) {
322
- if(this.compareMode) return;
323
-
324
- if (this._entityInfo) {
325
- this.rowClicked.emit({
326
- entityId: this._entityInfo.ID,
327
- entityName: this._entityInfo.Name,
328
- recordId: args.dataItem.ID
329
- })
330
-
331
- if (this._entityInfo.AllowUpdateAPI &&
332
- this.EditMode !== "None" ) {
333
- const perm = this._entityInfo.GetUserPermisions(new Metadata().CurrentUser)
334
- if (perm.CanUpdate) {
335
- this.StartEditMode();
336
- args.sender.editCell(args.rowIndex, args.columnIndex, this.createFormGroup(args.dataItem));
337
- }
338
- }
339
-
340
- if (!this.InEditMode && this.AutoNavigate) {
341
- // tell app router to go to this record
342
- this.router.navigate(['resource', 'record', args.dataItem.ID], { queryParams: { Entity: this._entityInfo.Name } })
343
- }
344
- }
345
- }
346
-
347
- public createFormGroup(dataItem: any): FormGroup {
348
- const groupFields: any = {};
349
- this.viewColumns.forEach((vc: ViewColumnInfo) => {
350
- if (vc.EntityField.AllowUpdateAPI &&
351
- vc.EntityField.IsVirtual === false &&
352
- vc.EntityField.AllowUpdateInView)
353
- groupFields[vc.Name] = dataItem[vc.Name];
354
- });
355
-
356
-
357
- return this.formBuilder.group(groupFields);
358
- }
359
-
360
- public getEditor(ef: EntityFieldInfo): "boolean" | "text" | "numeric" | "date" {
361
- switch (ef.TSType) {
362
- case EntityFieldTSType.Boolean:
363
- return "boolean";
364
- case EntityFieldTSType.Date:
365
- return "date";
366
- case EntityFieldTSType.Number:
367
- return "numeric";
368
- default:
369
- return "text";
370
- }
371
- }
372
-
373
- public async cellCloseHandler(args: CellCloseEvent) {
374
- try {
375
- if (this._entityInfo && this.EditMode !== "None") {
376
- const { formGroup, dataItem } = args;
377
-
378
- if (!formGroup.valid) {
379
- // prevent closing the edited cell if there are invalid values.
380
- args.preventDefault();
381
- }
382
- else if (formGroup.dirty) {
383
- if (args.originalEvent && args.originalEvent.keyCode === Keys.Escape)
384
- return; // user hit escape, so don't save their changes
385
-
386
- // update the data item with the new values - this drives UI refresh while we save the record...
387
-
388
- Object.assign(dataItem, formGroup.value);
389
-
390
- const md = new Metadata();
391
- let record: BaseEntity | undefined;
392
- let bSaved: boolean = false;
393
- if (this.EditMode === "Save") {
394
- record = await md.GetEntityObject(this._entityInfo.Name);
395
- await record.Load(dataItem.ID);
396
- record.SetMany(formGroup.value);
397
- bSaved = await record.Save();
398
- if (!bSaved)
399
- this.CreateSimpleNotification("Error saving record: " + record.ID, 'error', 5000)
400
- }
401
- else {
402
- record = this._pendingRecords.find((r: GridPendingRecordItem) => r.record.ID === dataItem.ID)?.record;
403
- if (!record) { // haven't edited this one before
404
- record = await md.GetEntityObject(this._viewEntity!.Get('Entity'));
405
- await record.Load(dataItem.ID);
406
- this._pendingRecords.push({record,
407
- row: args.rowIndex,
408
- dataItem}); // don't save - put the changed record on a queue for saving later by our container
409
- }
410
- // go through the formGroup and only set the values that exist as columns in the grid
411
- const keys = Object.keys(formGroup.value);
412
- keys.forEach((k: string) => {
413
- const vc = this.viewColumns.find((vc: ViewColumnInfo) => vc.Name === k && vc.hidden === false);
414
- if (vc) {
415
- record!.Set(k, formGroup.value[k]);
416
- }
417
- })
418
-
419
- //record.SetMany(formGroup.value);
420
- }
421
-
422
- this.rowEdited.emit({
423
- record: record,
424
- row: args.rowIndex,
425
- saved: bSaved
426
- })
427
-
428
- }
429
- }
430
- }
431
- catch (e) {
432
- console.error(e);
433
- }
434
- finally {
435
- this.EndEditMode();
436
- }
437
-
438
- }
439
-
440
- // this handles reverting pending cahnges to records WITHIN the grid, not the user view settings, unrelated to that.
441
- public RevertPendingChanges(): void {
442
- if (this._pendingRecords && this._pendingRecords.length > 0) {
443
- this._pendingRecords.forEach((r: GridPendingRecordItem) => {
444
- r.record!.Revert();
445
- Object.assign(r.dataItem, r.record!.GetAll()); // copy the original values back to the data Item which gets the grid to display the old values again...
446
- })
447
- this._pendingRecords = [];
448
- if (this.Params)
449
- this.Refresh(this.Params);
450
- }
451
- }
452
-
453
-
454
- ngOnInit(): void {
455
-
456
- }
457
-
458
- ngAfterViewInit(): void {
459
- //this.setGridHeight();
460
- if (this.Params)
461
- this.Refresh(this.Params)
462
- }
463
-
464
- private _deferLoadCount: number = 0;
465
- private _allowLoad: boolean = true;
466
- @Input() public get AllowLoad(): boolean {
467
- return this._allowLoad;
468
- }
469
- public set AllowLoad(value: boolean) {
470
- this._allowLoad = value
471
- if (value === true && this._deferLoadCount === 0) {
472
- this._deferLoadCount++; // only do this one time
473
- if (this.Params)
474
- this.Refresh(this.Params)
475
- return;
476
- }
477
- }
478
-
479
- async RefreshFromSavedParams() {
480
- if (this.Params)
481
- this.Refresh(this.Params)
482
- }
483
- async Refresh(params: RunViewParams) {
484
- this.Params = params;
485
-
486
- if (this.AllowLoad === false) {
487
- return;
488
- }
489
- if (params && (params.ViewEntity || params.ViewID || params.ViewName || (params.EntityName && params.ExtraFilter))) {
490
- const startTime = new Date().getTime();
491
- this.isLoading = true
492
-
493
- const md = new Metadata();
494
- const rv = new RunView();
495
-
496
- // get the view entity first so we can pass it in, otherwise it will end up getting loaded inside of RunView() which is inefficient as we need it too
497
- // this is done for performance purposes
498
- if (!params.ViewEntity && (params.ViewID || params.ViewName)) {
499
- if (params.ViewID && params.ViewID > 0) {
500
- this._viewEntity = <UserViewEntityExtended>await ViewInfo.GetViewEntity(params.ViewID);
501
- this._entityInfo = md.Entities.find(x => x.ID === this._viewEntity?.Get('EntityID'));
502
- }
503
- else if (params.ViewName) {
504
- this._viewEntity = <UserViewEntityExtended>await ViewInfo.GetViewEntityByName(params.ViewName);
505
- this._entityInfo = md.Entities.find(x => x.ID === this._viewEntity?.Get('EntityID'));
506
- }
507
- params.ViewEntity = this._viewEntity;
508
- }
509
- else
510
- this._viewEntity = <UserViewEntityExtended>params.ViewEntity; //passed in via params in the Refresh() function
511
-
512
- const rvResult = await rv.RunView(params);
513
- if (!rvResult.Success) {
514
- // it failed
515
- this.CreateSimpleNotification("Error running view:\n\n" + rvResult.ErrorMessage, 'error', 5000)
516
- }
517
- else {
518
- // it worked
519
- this.viewData = rvResult.Results;
520
-
521
- this.totalRowCount = rvResult.TotalRowCount;
522
- this.formattedData = new Array(this.viewData.length);
523
-
524
- if (this._viewEntity) {
525
- // we were passed the view entity for efficiency, no need to load it again!
526
- this._entityInfo = md.Entities.find(x => x.ID === this._viewEntity?.Get('EntityID'));
527
- }
528
- else
529
- this._entityInfo = md.Entities.find(x => x.Name === params.EntityName);
530
-
531
- let cols: ViewColumnInfo[] | undefined;
532
- if (this._viewEntity)
533
- cols = this._viewEntity.Columns
534
- else
535
- cols = this._entityInfo?.Fields.filter((f: EntityFieldInfo) => f.DefaultInView ).map((f: EntityFieldInfo) => {
536
- return {
537
- ID: f.ID,
538
- Name: f.Name,
539
- DisplayName: f.DisplayName,
540
- EntityField: f,
541
- hidden: false,
542
- orderIndex: f.Sequence,
543
- width: f.DefaultColumnWidth ? f.DefaultColumnWidth : 100,
544
- } as ViewColumnInfo
545
- });
546
- if (cols) {
547
- this.viewColumns = cols
548
- const tempCols = cols.filter(x => x.hidden === false).sort((a,b) => {
549
- const aOrder = a.orderIndex != null ? a.orderIndex : 9999;
550
- const bOrder = b.orderIndex != null ? b.orderIndex : 9999;
551
- return aOrder - bOrder;
552
- });
553
-
554
- this.visibleColumns = tempCols
555
- }
556
-
557
- // sorting setup
558
- if (this._viewEntity) {
559
- const temp = this._viewEntity.ViewSortInfo;
560
- const kendoSortSettings = temp.map((s: any) => {return {field: s.field, dir: s.direction === 1 ? 'asc' : 'desc'}})
561
- this.sortSettings = kendoSortSettings;
562
- }
563
-
564
- this.skip = 0;
565
- this.virtualLoadData();
566
-
567
- }
568
- this.viewExecutionTime = (new Date().getTime() - startTime) / 1000; // in seconds
569
- this.isLoading = false
570
- }
571
- else {
572
- LogError("Refresh(params) must have ViewID or ViewName or (EntityName and ExtraFilter)")
573
- }
574
- }
575
-
576
- GetColumnTitle(col: ViewColumnInfo) {
577
- if (col.DisplayName)
578
- return col.DisplayName; // use view's display name first if it exists
579
- else if (col.EntityField.DisplayName )
580
- return col.EntityField.DisplayName; // then use entity display name, if that exist
581
- else
582
- return col.Name; // otherwise just use the column name
583
- }
584
-
585
- GetColumnCellStyle(col: ViewColumnInfo) {
586
- switch (col.EntityField.Type.trim().toLowerCase()) {
587
- case "money":
588
- case 'decimal':
589
- case 'real':
590
- case 'float':
591
- case 'int':
592
- return {'text-align': 'right', 'vertical-align': 'top'}; // right align numbers,
593
- default:
594
- return {'text-align': 'left', 'vertical-align': 'top'}; // left align everything else
595
- }
596
- }
597
-
598
- selectionChange(args: SelectionEvent) {
599
- if(this.compareRecords.length && args?.deselectedRows?.length){
600
- const tempRow = args?.deselectedRows[0]?.dataItem;
601
- this.compareRecords = this.compareRecords.filter(record => record.ID !== tempRow?.ID);
602
- } else if(args?.selectedRows?.length){
603
- const tempRow = args?.selectedRows[0]?.dataItem;
604
- this.compareRecords.push(tempRow);
605
- }
606
- }
607
-
608
- enableCompare(cancel: boolean = false){
609
- if(!cancel && this.selectedKeys.length){
610
- this.isDialogOpened = true;
611
- }
612
- this.compareMode = !this.compareMode;
613
- this.selectedKeys = [];
614
- }
615
-
616
- closeDialog(){
617
- this.isDialogOpened = false;
618
- this.compareRecords = [];
619
- }
620
-
621
-
622
- // Export Functionality
623
- public exportColumns: ViewColumnInfo[] = [];
624
- public exportData: any[] = [];
625
- public async doExcelExport() {
626
- if (this.kendoExcelExport === null)
627
- throw new Error("kendoExcelExport is null, cannot export data");
628
-
629
- try {
630
- this.CreateSimpleNotification("Working on the export, will notify you when it is complete...", 'info', 2000)
631
- const data = await this.getExportData();
632
- // we have the data.
633
- const cols = this.viewColumns.filter((vc: ViewColumnInfo) => vc.hidden === false)
634
- this.exportColumns = cols;
635
- this.exportData = data;
636
- // before we call the save, we need to let Angular do its thing that will result in the kendoExcelExport component binding properly to
637
- // the exportColumns and exportData arrays. So we wait for the next tick before we call save()
638
- setTimeout(() => {
639
- this.kendoExcelExport!.save();
640
- this.CreateSimpleNotification("Excel Export Complete", 'success', 2000)
641
- }, 100);
642
- }
643
- catch (e) {
644
- this.CreateSimpleNotification("Error exporting data", 'error', 5000)
645
- LogError(e)
646
- }
647
- }
648
-
649
- protected async getExportData(): Promise<any[]> {
650
- // Get the data for the ENTIRE view, not just the current page
651
- const md = new Metadata();
652
- const rv = new RunView();
653
- const p = {
654
- ...this.Params!,
655
- IgnoreMaxRows: true,
656
- ForceAuditLog: true,
657
- AuditLogDescription: `Export of Data From ${this._viewEntity ? '"' + this._viewEntity.Get('Name') + '"' : ''} View for User ${md.CurrentUser.Email}`
658
- }
659
- const result = await rv.RunView(p);
660
- if (result && result.Success) {
661
- return result.Results;
662
- }
663
- else
664
- throw new Error("Unable to get export data");
665
- }
666
-
667
- }
@@ -1,36 +0,0 @@
1
- import { NgModule } from '@angular/core';
2
- import { UserViewGridComponent } from './ng-user-view-grid.component';
3
- import { CommonModule } from '@angular/common';
4
- import { FormsModule, ReactiveFormsModule } from '@angular/forms';
5
- import { RouterModule } from '@angular/router';
6
-
7
- // Kendo UI Angular imports
8
- import { GridModule } from '@progress/kendo-angular-grid';
9
- import { ExcelExportModule } from '@progress/kendo-angular-excel-export';
10
- import { DialogsModule } from "@progress/kendo-angular-dialog";
11
- import { ButtonsModule } from '@progress/kendo-angular-buttons';
12
-
13
- import { CompareRecordsModule } from '@memberjunction/ng-compare-records';
14
- import { ContainerDirectivesModule } from '@memberjunction/ng-container-directives';
15
-
16
- @NgModule({
17
- declarations: [
18
- UserViewGridComponent
19
- ],
20
- imports: [
21
- CommonModule,
22
- FormsModule,
23
- ReactiveFormsModule,
24
- RouterModule,
25
- GridModule,
26
- DialogsModule,
27
- ExcelExportModule,
28
- ButtonsModule,
29
- CompareRecordsModule,
30
- ContainerDirectivesModule
31
- ],
32
- exports: [
33
- UserViewGridComponent
34
- ]
35
- })
36
- export class UserViewGridModule { }