@memberjunction/ng-base-forms 0.9.2

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.
@@ -0,0 +1,76 @@
1
+ import { AfterViewInit, OnInit, OnDestroy, QueryList, ElementRef } from '@angular/core';
2
+ import { EntityInfo, ValidationResult, BaseEntity, EntityPermissionType, EntityRelationshipInfo, RunViewParams } from '@memberjunction/core';
3
+ import { UserViewGridComponent } from '@memberjunction/ng-user-view-grid';
4
+ import { BaseRecordComponent } from './base-record-component';
5
+ import { SharedService } from '@memberjunction/ng-shared';
6
+ import { ActivatedRoute, Router } from '@angular/router';
7
+ import { TabStripComponent } from '@progress/kendo-angular-layout';
8
+ import * as i0 from "@angular/core";
9
+ export declare abstract class BaseFormComponent extends BaseRecordComponent implements AfterViewInit, OnInit, OnDestroy {
10
+ protected elementRef: ElementRef;
11
+ protected sharedService: SharedService;
12
+ protected router: Router;
13
+ protected route: ActivatedRoute;
14
+ activeTabIndex: number;
15
+ selectedTab: number;
16
+ EditMode: boolean;
17
+ BottomMargin: number;
18
+ TabHeight: string;
19
+ GridBottomMargin: number;
20
+ FavoriteInitDone: boolean;
21
+ isHistoryDialogOpen: boolean;
22
+ private splitterLayoutChangeSubject;
23
+ private _pendingRecords;
24
+ private _updatingBrowserUrl;
25
+ constructor(elementRef: ElementRef, sharedService: SharedService, router: Router, route: ActivatedRoute);
26
+ tabComponent: TabStripComponent;
27
+ ngOnInit(): Promise<void>;
28
+ ngAfterViewInit(): void;
29
+ private resizeSub;
30
+ ngOnDestroy(): void;
31
+ userViewGridComponents: QueryList<UserViewGridComponent>;
32
+ protected get PendingRecords(): BaseEntity[];
33
+ onTabSelect(e: any): void;
34
+ StartEditMode(): void;
35
+ handleHistoryDialog(): void;
36
+ RemoveFavorite(): Promise<void>;
37
+ MakeFavorite(): Promise<void>;
38
+ SetFavoriteStatus(isFavorite: boolean): Promise<void>;
39
+ private _isFavorite;
40
+ get IsFavorite(): boolean;
41
+ CheckUserPermission(type: EntityPermissionType): boolean;
42
+ get UserCanEdit(): boolean;
43
+ get UserCanRead(): boolean;
44
+ get UserCanCreate(): boolean;
45
+ get UserCanDelete(): boolean;
46
+ GridEditMode(): "None" | "Save" | "Queue";
47
+ private _tabIndexes;
48
+ GetTabIndex(tabName: string): number;
49
+ RegisterTabs(tabs: string[]): void;
50
+ RegisterTab(tabName: string): number;
51
+ IsCurrentTab(tabName: string): boolean;
52
+ RegisterAndCheckIfCurrentTab(tabName: string): boolean;
53
+ protected BuildRelationshipViewParams(item: EntityRelationshipInfo): RunViewParams;
54
+ BuildRelationshipViewParamsByEntityName(relatedEntityName: string): RunViewParams;
55
+ GetRelatedEntityTabDisplayName(relatedEntityName: string): string;
56
+ protected ValidatePendingRecords(): ValidationResult[];
57
+ Validate(): ValidationResult;
58
+ SaveRecord(StopEditModeAfterSave: boolean): Promise<boolean>;
59
+ Wait(duration: number): Promise<void>;
60
+ CancelEdit(): void;
61
+ protected PendingRecordsDirty(): boolean;
62
+ protected PopulatePendingRecords(): Promise<void>;
63
+ protected InternalSaveRecord(): Promise<boolean>;
64
+ protected GetTabTopPosition(): number;
65
+ private _tabMargin;
66
+ setTabHeight(): void;
67
+ protected get ContainerObjectHeight(): number;
68
+ protected ResizeTab(): void;
69
+ protected setupSplitterLayoutDebounce(): void;
70
+ splitterLayoutChange(): void;
71
+ protected ResizeGrids(): void;
72
+ ShowDependencies(): Promise<void>;
73
+ get EntityInfo(): EntityInfo | undefined;
74
+ static ɵfac: i0.ɵɵFactoryDeclaration<BaseFormComponent, never>;
75
+ static ɵdir: i0.ɵɵDirectiveDeclaration<BaseFormComponent, never, never, {}, {}, never, never, false, never>;
76
+ }
@@ -0,0 +1,436 @@
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 { Directive, ViewChildren, ViewChild } from '@angular/core';
11
+ import { Subject, debounceTime, fromEvent } from 'rxjs';
12
+ import { EntityInfo, EntityPermissionType, Metadata, LogError } from '@memberjunction/core';
13
+ import { UserViewGridComponent } from '@memberjunction/ng-user-view-grid';
14
+ import { BaseRecordComponent } from './base-record-component';
15
+ import { TabStripComponent } from '@progress/kendo-angular-layout';
16
+ import * as i0 from "@angular/core";
17
+ import * as i1 from "@memberjunction/ng-shared";
18
+ import * as i2 from "@angular/router";
19
+ export class BaseFormComponent extends BaseRecordComponent {
20
+ constructor(elementRef, sharedService, router, route) {
21
+ super();
22
+ this.elementRef = elementRef;
23
+ this.sharedService = sharedService;
24
+ this.router = router;
25
+ this.route = route;
26
+ this.activeTabIndex = 0;
27
+ this.selectedTab = 0;
28
+ this.EditMode = false;
29
+ this.BottomMargin = 53;
30
+ this.TabHeight = '500px';
31
+ this.GridBottomMargin = 100;
32
+ this.FavoriteInitDone = false;
33
+ this.isHistoryDialogOpen = false;
34
+ this.splitterLayoutChangeSubject = new Subject();
35
+ this._pendingRecords = [];
36
+ this._updatingBrowserUrl = false;
37
+ this.resizeSub = null;
38
+ this._isFavorite = false;
39
+ this._tabIndexes = [];
40
+ this._tabMargin = 10;
41
+ this.setupSplitterLayoutDebounce();
42
+ }
43
+ ngOnInit() {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ if (this.record) {
46
+ const md = new Metadata();
47
+ this._isFavorite = yield md.GetRecordFavoriteStatus(md.CurrentUser.ID, this.record.EntityInfo.Name, this.record.PrimaryKeys.map(pk => { return { FieldName: pk.Name, Value: pk.Value }; }));
48
+ this.FavoriteInitDone = true;
49
+ }
50
+ });
51
+ }
52
+ ngAfterViewInit() {
53
+ this.setTabHeight();
54
+ this.route.queryParams.subscribe(params => {
55
+ if (!this._updatingBrowserUrl) {
56
+ // only do this if WE didn't invoke the browser URL update
57
+ const tabName = params['tab'];
58
+ if (tabName && tabName.length > 0) {
59
+ // Select the proper tab based on the tabName
60
+ const tabIndex = this.GetTabIndex(tabName);
61
+ if (tabIndex >= 0) {
62
+ // found the tab index, if we don't find it, do nothing
63
+ this.activeTabIndex = tabIndex;
64
+ this.tabComponent.selectTab(tabIndex);
65
+ // now resize after a pause to allow the UI to settle
66
+ setTimeout(() => {
67
+ this.sharedService.InvokeManualResize();
68
+ }, 100);
69
+ }
70
+ }
71
+ }
72
+ });
73
+ }
74
+ ngOnDestroy() {
75
+ if (this.resizeSub) {
76
+ this.resizeSub.unsubscribe();
77
+ }
78
+ }
79
+ get PendingRecords() {
80
+ return this._pendingRecords;
81
+ }
82
+ onTabSelect(e) {
83
+ this.activeTabIndex = e.index;
84
+ this.sharedService.InvokeManualResize();
85
+ // now that we've updated our state and re-sized, also update the browser URL to add the tab name as a query parameter to the URL
86
+ const tabName = this._tabIndexes[this.activeTabIndex];
87
+ this._updatingBrowserUrl = true;
88
+ this.router.navigate([], { queryParams: { tab: tabName }, queryParamsHandling: 'merge' });
89
+ this._updatingBrowserUrl = true;
90
+ }
91
+ StartEditMode() {
92
+ this.EditMode = true;
93
+ }
94
+ handleHistoryDialog() {
95
+ this.isHistoryDialogOpen = !this.isHistoryDialogOpen;
96
+ }
97
+ RemoveFavorite() {
98
+ return __awaiter(this, void 0, void 0, function* () {
99
+ return this.SetFavoriteStatus(false);
100
+ });
101
+ }
102
+ MakeFavorite() {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ return this.SetFavoriteStatus(true);
105
+ });
106
+ }
107
+ SetFavoriteStatus(isFavorite) {
108
+ return __awaiter(this, void 0, void 0, function* () {
109
+ const md = new Metadata();
110
+ yield md.SetRecordFavoriteStatus(md.CurrentUser.ID, this.record.EntityInfo.Name, this.record.PrimaryKeys.map(pk => { return { FieldName: pk.Name, Value: pk.Value }; }), isFavorite);
111
+ this._isFavorite = isFavorite;
112
+ });
113
+ }
114
+ get IsFavorite() {
115
+ return this._isFavorite;
116
+ }
117
+ CheckUserPermission(type) {
118
+ try {
119
+ if (this.record)
120
+ return this.record.CheckPermissions(type, false);
121
+ else
122
+ return false;
123
+ }
124
+ catch (e) {
125
+ LogError(e);
126
+ return false;
127
+ }
128
+ }
129
+ get UserCanEdit() {
130
+ return this.CheckUserPermission(EntityPermissionType.Update);
131
+ }
132
+ get UserCanRead() {
133
+ return this.CheckUserPermission(EntityPermissionType.Read);
134
+ }
135
+ get UserCanCreate() {
136
+ return this.CheckUserPermission(EntityPermissionType.Create);
137
+ }
138
+ get UserCanDelete() {
139
+ return this.CheckUserPermission(EntityPermissionType.Delete);
140
+ }
141
+ GridEditMode() {
142
+ return this.EditMode ? "Queue" : "None";
143
+ }
144
+ GetTabIndex(tabName) {
145
+ return this._tabIndexes.indexOf(tabName);
146
+ }
147
+ RegisterTabs(tabs) {
148
+ // copy the array, clear existing array and copy all elements
149
+ this._tabIndexes.splice(0, this._tabIndexes.length);
150
+ for (let i = 0; i < tabs.length; i++)
151
+ this.RegisterTab(tabs[i]);
152
+ }
153
+ RegisterTab(tabName) {
154
+ const currentIndex = this._tabIndexes.indexOf(tabName);
155
+ if (currentIndex === -1) {
156
+ this._tabIndexes.push(tabName);
157
+ return this._tabIndexes.length - 1;
158
+ }
159
+ else
160
+ return currentIndex;
161
+ }
162
+ IsCurrentTab(tabName) {
163
+ if (this._tabIndexes.length === 0 || this._tabIndexes.indexOf(tabName) === -1)
164
+ return false;
165
+ else
166
+ return this.activeTabIndex === this.GetTabIndex(tabName);
167
+ }
168
+ RegisterAndCheckIfCurrentTab(tabName) {
169
+ this.RegisterTab(tabName);
170
+ return this.IsCurrentTab(tabName);
171
+ }
172
+ BuildRelationshipViewParams(item) {
173
+ return EntityInfo.BuildRelationshipViewParams(this.record, item); // new helper method in EntityInfo
174
+ }
175
+ BuildRelationshipViewParamsByEntityName(relatedEntityName) {
176
+ if (this.record) {
177
+ const eri = this.record.EntityInfo.RelatedEntities.find(x => x.RelatedEntity === relatedEntityName);
178
+ if (eri)
179
+ return this.BuildRelationshipViewParams(eri);
180
+ }
181
+ return {};
182
+ }
183
+ GetRelatedEntityTabDisplayName(relatedEntityName) {
184
+ if (this.record) {
185
+ const eri = this.record.EntityInfo.RelatedEntities.find(x => x.RelatedEntity === relatedEntityName);
186
+ if (eri)
187
+ return eri.DisplayName;
188
+ }
189
+ return relatedEntityName;
190
+ }
191
+ ValidatePendingRecords() {
192
+ const results = [];
193
+ for (let i = 0; i < this._pendingRecords.length; i++) {
194
+ const pendingRecord = this._pendingRecords[i];
195
+ results.push(pendingRecord.Validate());
196
+ }
197
+ return results;
198
+ }
199
+ Validate() {
200
+ const valResults = this.record.Validate();
201
+ const pendingValResults = this.ValidatePendingRecords();
202
+ for (let i = 0; i < pendingValResults.length; i++) {
203
+ const pendingValResult = pendingValResults[i];
204
+ if (!pendingValResult.Success) {
205
+ valResults.Success = false;
206
+ valResults.Errors.push(...pendingValResult.Errors);
207
+ }
208
+ }
209
+ return valResults;
210
+ }
211
+ SaveRecord(StopEditModeAfterSave) {
212
+ return __awaiter(this, void 0, void 0, function* () {
213
+ try {
214
+ if (this.record) {
215
+ yield this.PopulatePendingRecords(); // do this before we validate as we must validate pending records too
216
+ const valResults = this.Validate();
217
+ if (valResults.Success) {
218
+ const result = yield this.InternalSaveRecord();
219
+ if (result) {
220
+ // we have saved the record, so clear the pending records
221
+ this._pendingRecords = [];
222
+ if (StopEditModeAfterSave)
223
+ this.EditMode = false;
224
+ this.sharedService.CreateSimpleNotification('Record saved succesfully', 'success', 2500);
225
+ return true;
226
+ }
227
+ else
228
+ this.sharedService.CreateSimpleNotification('Error saving record', 'error', 5000);
229
+ }
230
+ else {
231
+ this.sharedService.CreateSimpleNotification('Validation Errors\n' + valResults.Errors.map(x => x.Message).join('\n'), 'warning', 10000);
232
+ }
233
+ }
234
+ return false; // if we get here, we have failed/validation error/etc
235
+ }
236
+ catch (e) {
237
+ this.sharedService.CreateSimpleNotification('Error saving record: ' + e, 'error', 5000);
238
+ return false;
239
+ }
240
+ });
241
+ }
242
+ Wait(duration) {
243
+ return __awaiter(this, void 0, void 0, function* () {
244
+ return new Promise(resolve => setTimeout(resolve, duration));
245
+ });
246
+ }
247
+ CancelEdit() {
248
+ if (this.record) {
249
+ const r = this.record;
250
+ if (r.Dirty || this.PendingRecordsDirty()) {
251
+ // ask the user to make sure they want to throw out changes
252
+ if (!confirm('Are you sure you want to cancel your changes?')) {
253
+ return;
254
+ }
255
+ r.Revert();
256
+ const pendingRecords = this.PendingRecords;
257
+ if (pendingRecords && pendingRecords.length > 0) {
258
+ // we have pending records, so we need to revert them as well
259
+ pendingRecords.forEach(p => {
260
+ p.Revert();
261
+ });
262
+ }
263
+ if (this.userViewGridComponents && this.userViewGridComponents.length > 0) {
264
+ // we have grids, so we need to revert them as well
265
+ this.userViewGridComponents.forEach(grid => {
266
+ grid.RevertPendingChanges();
267
+ });
268
+ }
269
+ }
270
+ // if we get here we are good to go
271
+ this.EditMode = false;
272
+ }
273
+ }
274
+ PendingRecordsDirty() {
275
+ this.PopulatePendingRecords();
276
+ const pendingRecords = this.PendingRecords;
277
+ if (pendingRecords && pendingRecords.length > 0) {
278
+ for (let i = 0; i < pendingRecords.length; i++) {
279
+ if (pendingRecords[i].Dirty)
280
+ return true;
281
+ }
282
+ }
283
+ return false;
284
+ }
285
+ PopulatePendingRecords() {
286
+ return __awaiter(this, void 0, void 0, function* () {
287
+ // this method is called by the parent class at the right time to populate all of the pending records for a transaction
288
+ // all we do is talk to all of our grids and get their pending records and xfer them over to the PendingRecords array
289
+ // the parent class will then take care of the rest...
290
+ this._pendingRecords = []; // wipe out our array first
291
+ const grids = this.userViewGridComponents;
292
+ if (grids && grids.length > 0) {
293
+ for (let i = 0; i < grids.length; i++) {
294
+ const grid = grids.get(i);
295
+ if (grid) {
296
+ yield grid.EditingComplete(); // need to check this and wait for it to make sure grid editing is done before we try to save
297
+ grid.PendingRecords.forEach(p => {
298
+ // populate our pending grids array from the composite of all the grids
299
+ this.PendingRecords.push(p.record);
300
+ });
301
+ }
302
+ }
303
+ }
304
+ });
305
+ }
306
+ InternalSaveRecord() {
307
+ return __awaiter(this, void 0, void 0, function* () {
308
+ if (this.record) {
309
+ // save the record, but first create a transaction group if we have any other stuf to submit
310
+ yield this.PopulatePendingRecords();
311
+ if (this._pendingRecords.length > 0) {
312
+ // we need to create a transaction group
313
+ const md = new Metadata();
314
+ const tg = yield md.CreateTransactionGroup();
315
+ this.record.TransactionGroup = tg;
316
+ this.record.Save(); // DO NOT USE await - trans group.submit() is where we await
317
+ // now add to the rest of the pending records
318
+ for (let i = 0; i < this._pendingRecords.length; i++) {
319
+ const x = this._pendingRecords[i];
320
+ x.TransactionGroup = tg;
321
+ x.Save(); // DO NOT USE await - trans group.submit() is where we await
322
+ }
323
+ // finally submit the TG
324
+ return yield tg.Submit();
325
+ }
326
+ else
327
+ return yield this.record.Save();
328
+ }
329
+ else
330
+ return false;
331
+ });
332
+ }
333
+ GetTabTopPosition() {
334
+ if (this.elementRef && this.elementRef.nativeElement) {
335
+ const tabs = this.elementRef.nativeElement.getElementsByClassName('k-tabstrip');
336
+ if (tabs && tabs.length > 0) {
337
+ const tabElement = tabs[0];
338
+ const tabRect = tabElement.getBoundingClientRect();
339
+ const bodyRect = document.body.getBoundingClientRect();
340
+ return tabRect.top - bodyRect.top;
341
+ }
342
+ }
343
+ return 0;
344
+ }
345
+ setTabHeight() {
346
+ // Subscribe to the window resize event
347
+ this.resizeSub = fromEvent(window, 'resize').pipe(debounceTime(100) // Debounce the resize event to avoid frequent updates
348
+ ).subscribe(() => {
349
+ // Update the grid height when the window is resized
350
+ this.ResizeTab();
351
+ });
352
+ // Set the initial grid height with a slight delay to allow stuff to get set
353
+ setTimeout(() => {
354
+ this.ResizeTab();
355
+ }, 100);
356
+ }
357
+ get ContainerObjectHeight() {
358
+ return window.innerHeight; // default, can be overriden by subclasses
359
+ }
360
+ ResizeTab() {
361
+ this._tabMargin = this.GetTabTopPosition();
362
+ const height = this.ContainerObjectHeight - this._tabMargin - this.BottomMargin;
363
+ this.TabHeight = height.toString() + 'px';
364
+ this.GridBottomMargin = 40;
365
+ }
366
+ setupSplitterLayoutDebounce() {
367
+ this.splitterLayoutChangeSubject.pipe(debounceTime(300)).subscribe(() => {
368
+ this.setTabHeight(); // set the height of the tab first
369
+ // then wait through the timeout so the tab height change actually takes effect in the DOM - that's why we have the timeout
370
+ setTimeout(() => {
371
+ this.sharedService.InvokeManualResize();
372
+ }, 100);
373
+ });
374
+ }
375
+ splitterLayoutChange() {
376
+ this.splitterLayoutChangeSubject.next();
377
+ }
378
+ ResizeGrids() {
379
+ // do with a little timeout to let the visible grid show up and the DOM settle
380
+ setTimeout(() => {
381
+ if (this.userViewGridComponents && this.userViewGridComponents.length > 0) {
382
+ this.userViewGridComponents.forEach(grid => {
383
+ //grid.ResizeGrid(); // automatic now via mjFillContainer
384
+ });
385
+ }
386
+ }, 10);
387
+ }
388
+ ShowDependencies() {
389
+ return __awaiter(this, void 0, void 0, function* () {
390
+ // for now dump to console
391
+ const md = new Metadata();
392
+ const dep = yield md.GetRecordDependencies(this.record.EntityInfo.Name, this.record.PrimaryKey.Value);
393
+ console.log('Dependencies for: ' + this.record.EntityInfo.Name + ' ' + this.record.PrimaryKey.Value);
394
+ console.log(dep);
395
+ // if (confirm('Do you want to merge records test?') == true) {
396
+ // const mergeResult = await md.MergeRecords({
397
+ // EntityName: this.record.EntityInfo.Name,
398
+ // SurvivingRecordID: this.record.PrimaryKey.Value,
399
+ // RecordsToMerge: [4],
400
+ // })
401
+ // console.log(mergeResult);
402
+ // }
403
+ });
404
+ }
405
+ get EntityInfo() {
406
+ try {
407
+ const r = this.record;
408
+ if (r)
409
+ return r.EntityInfo;
410
+ else
411
+ return undefined;
412
+ }
413
+ catch (e) {
414
+ LogError(e);
415
+ return undefined;
416
+ }
417
+ }
418
+ }
419
+ BaseFormComponent.ɵfac = function BaseFormComponent_Factory(t) { return new (t || BaseFormComponent)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.SharedService), i0.ɵɵdirectiveInject(i2.Router), i0.ɵɵdirectiveInject(i2.ActivatedRoute)); };
420
+ BaseFormComponent.ɵdir = /*@__PURE__*/ i0.ɵɵdefineDirective({ type: BaseFormComponent, viewQuery: function BaseFormComponent_Query(rf, ctx) { if (rf & 1) {
421
+ i0.ɵɵviewQuery(TabStripComponent, 5);
422
+ i0.ɵɵviewQuery(UserViewGridComponent, 5);
423
+ } if (rf & 2) {
424
+ let _t;
425
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.tabComponent = _t.first);
426
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.userViewGridComponents = _t);
427
+ } }, features: [i0.ɵɵInheritDefinitionFeature] });
428
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BaseFormComponent, [{
429
+ type: Directive
430
+ }], () => [{ type: i0.ElementRef }, { type: i1.SharedService }, { type: i2.Router }, { type: i2.ActivatedRoute }], { tabComponent: [{
431
+ type: ViewChild,
432
+ args: [TabStripComponent, { static: false }]
433
+ }], userViewGridComponents: [{
434
+ type: ViewChildren,
435
+ args: [UserViewGridComponent]
436
+ }] }); })();
@@ -0,0 +1,6 @@
1
+ import { BaseEntity } from "@memberjunction/core";
2
+ import { BaseRecordComponent } from "./base-record-component";
3
+ export declare class BaseFormSectionComponent extends BaseRecordComponent {
4
+ record: BaseEntity;
5
+ EditMode: boolean;
6
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseRecordComponent } from "./base-record-component";
2
+ // This is a base class for form sections, it is used to have a clear hiearchy for all sections to subclass
3
+ // which is primarily needed for the Class Factory and registration process to differentiate between sections and other components
4
+ export class BaseFormSectionComponent extends BaseRecordComponent {
5
+ constructor() {
6
+ super(...arguments);
7
+ this.EditMode = false;
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ import { BaseEntity, EntityFieldInfo } from "@memberjunction/core";
2
+ export declare abstract class BaseRecordComponent {
3
+ abstract record: BaseEntity;
4
+ FormatField(entityFieldInfo: EntityFieldInfo, value: any, decimals?: number, currency?: string): string;
5
+ FormatValue(fieldName: string, decimals?: number, currency?: string): string;
6
+ }
@@ -0,0 +1,15 @@
1
+ export class BaseRecordComponent {
2
+ FormatField(entityFieldInfo, value, decimals = 2, currency = 'USD') {
3
+ return entityFieldInfo.FormatValue(value, decimals, currency);
4
+ }
5
+ FormatValue(fieldName, decimals = 2, currency = 'USD') {
6
+ if (!this.record)
7
+ throw new Error('this.record not set');
8
+ const r = this.record;
9
+ const f = r.Fields.find(f => f.Name.trim().toLowerCase() === fieldName.trim().toLowerCase() ||
10
+ f.CodeName.trim().toLowerCase() === fieldName.trim().toLowerCase());
11
+ if (!f)
12
+ throw new Error(`Field ${fieldName} not found in entity ${r.EntityInfo.Name}`);
13
+ return f.FormatValue(decimals, currency);
14
+ }
15
+ }
@@ -0,0 +1,7 @@
1
+ import { BaseFormComponent } from './base-form-component';
2
+ import * as i0 from "@angular/core";
3
+ export declare class FormToolbarComponent {
4
+ form: BaseFormComponent;
5
+ static ɵfac: i0.ɵɵFactoryDeclaration<FormToolbarComponent, never>;
6
+ static ɵcmp: i0.ɵɵComponentDeclaration<FormToolbarComponent, "mj-form-toolbar", never, { "form": { "alias": "form"; "required": false; }; }, {}, never, never, false, never>;
7
+ }
@@ -0,0 +1,93 @@
1
+ import { Component, Input } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ import * as i1 from "@angular/common";
4
+ import * as i2 from "@memberjunction/ng-record-changes";
5
+ function FormToolbarComponent_button_0_Template(rf, ctx) { if (rf & 1) {
6
+ const _r8 = i0.ɵɵgetCurrentView();
7
+ i0.ɵɵelementStart(0, "button", 2);
8
+ i0.ɵɵlistener("click", function FormToolbarComponent_button_0_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r8); const ctx_r7 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r7.form.StartEditMode()); });
9
+ i0.ɵɵtext(1, "Edit Record");
10
+ i0.ɵɵelementEnd();
11
+ } }
12
+ function FormToolbarComponent_button_1_Template(rf, ctx) { if (rf & 1) {
13
+ const _r10 = i0.ɵɵgetCurrentView();
14
+ i0.ɵɵelementStart(0, "button", 2);
15
+ i0.ɵɵlistener("click", function FormToolbarComponent_button_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r9 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r9.form.SaveRecord(true)); });
16
+ i0.ɵɵtext(1, "Save Record");
17
+ i0.ɵɵelementEnd();
18
+ } }
19
+ function FormToolbarComponent_button_2_Template(rf, ctx) { if (rf & 1) {
20
+ const _r12 = i0.ɵɵgetCurrentView();
21
+ i0.ɵɵelementStart(0, "button", 2);
22
+ i0.ɵɵlistener("click", function FormToolbarComponent_button_2_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const ctx_r11 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r11.form.CancelEdit()); });
23
+ i0.ɵɵtext(1, "Cancel");
24
+ i0.ɵɵelementEnd();
25
+ } }
26
+ function FormToolbarComponent_button_3_Template(rf, ctx) { if (rf & 1) {
27
+ const _r14 = i0.ɵɵgetCurrentView();
28
+ i0.ɵɵelementStart(0, "button", 2);
29
+ i0.ɵɵlistener("click", function FormToolbarComponent_button_3_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r13 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r13.form.RemoveFavorite()); });
30
+ i0.ɵɵtext(1, "Remove Favorite");
31
+ i0.ɵɵelementEnd();
32
+ } }
33
+ function FormToolbarComponent_button_4_Template(rf, ctx) { if (rf & 1) {
34
+ const _r16 = i0.ɵɵgetCurrentView();
35
+ i0.ɵɵelementStart(0, "button", 2);
36
+ i0.ɵɵlistener("click", function FormToolbarComponent_button_4_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r16); const ctx_r15 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r15.form.MakeFavorite()); });
37
+ i0.ɵɵtext(1, "Make Favorite");
38
+ i0.ɵɵelementEnd();
39
+ } }
40
+ function FormToolbarComponent_button_5_Template(rf, ctx) { if (rf & 1) {
41
+ const _r18 = i0.ɵɵgetCurrentView();
42
+ i0.ɵɵelementStart(0, "button", 2);
43
+ i0.ɵɵlistener("click", function FormToolbarComponent_button_5_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r18); const ctx_r17 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r17.form.handleHistoryDialog()); });
44
+ i0.ɵɵtext(1, "History");
45
+ i0.ɵɵelementEnd();
46
+ } }
47
+ function FormToolbarComponent_mj_record_changes_7_Template(rf, ctx) { if (rf & 1) {
48
+ const _r20 = i0.ɵɵgetCurrentView();
49
+ i0.ɵɵelementStart(0, "mj-record-changes", 3);
50
+ i0.ɵɵlistener("dialogClosed", function FormToolbarComponent_mj_record_changes_7_Template_mj_record_changes_dialogClosed_0_listener() { i0.ɵɵrestoreView(_r20); const ctx_r19 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r19.form.handleHistoryDialog()); });
51
+ i0.ɵɵelementEnd();
52
+ } if (rf & 2) {
53
+ const ctx_r6 = i0.ɵɵnextContext();
54
+ i0.ɵɵproperty("record", ctx_r6.form.record);
55
+ } }
56
+ export class FormToolbarComponent {
57
+ }
58
+ FormToolbarComponent.ɵfac = function FormToolbarComponent_Factory(t) { return new (t || FormToolbarComponent)(); };
59
+ FormToolbarComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FormToolbarComponent, selectors: [["mj-form-toolbar"]], inputs: { form: "form" }, decls: 8, vars: 7, consts: [["kendoButton", "", 3, "click", 4, "ngIf"], [3, "record", "dialogClosed", 4, "ngIf"], ["kendoButton", "", 3, "click"], [3, "record", "dialogClosed"]], template: function FormToolbarComponent_Template(rf, ctx) { if (rf & 1) {
60
+ i0.ɵɵtemplate(0, FormToolbarComponent_button_0_Template, 2, 0, "button", 0)(1, FormToolbarComponent_button_1_Template, 2, 0, "button", 0)(2, FormToolbarComponent_button_2_Template, 2, 0, "button", 0)(3, FormToolbarComponent_button_3_Template, 2, 0, "button", 0)(4, FormToolbarComponent_button_4_Template, 2, 0, "button", 0)(5, FormToolbarComponent_button_5_Template, 2, 0, "button", 0);
61
+ i0.ɵɵelement(6, "hr");
62
+ i0.ɵɵtemplate(7, FormToolbarComponent_mj_record_changes_7_Template, 1, 1, "mj-record-changes", 1);
63
+ } if (rf & 2) {
64
+ i0.ɵɵproperty("ngIf", !ctx.form.EditMode && ctx.form.UserCanEdit);
65
+ i0.ɵɵadvance();
66
+ i0.ɵɵproperty("ngIf", ctx.form.EditMode);
67
+ i0.ɵɵadvance();
68
+ i0.ɵɵproperty("ngIf", ctx.form.EditMode);
69
+ i0.ɵɵadvance();
70
+ i0.ɵɵproperty("ngIf", ctx.form.FavoriteInitDone && ctx.form.IsFavorite);
71
+ i0.ɵɵadvance();
72
+ i0.ɵɵproperty("ngIf", ctx.form.FavoriteInitDone && !ctx.form.IsFavorite);
73
+ i0.ɵɵadvance();
74
+ i0.ɵɵproperty("ngIf", ctx.form.EntityInfo == null ? null : ctx.form.EntityInfo.TrackRecordChanges);
75
+ i0.ɵɵadvance(2);
76
+ i0.ɵɵproperty("ngIf", ctx.form.isHistoryDialogOpen);
77
+ } }, dependencies: [i1.NgIf, i2.RecordChangesComponent], styles: ["button[_ngcontent-%COMP%] { margin-right: 10px; }"] });
78
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FormToolbarComponent, [{
79
+ type: Component,
80
+ args: [{ selector: 'mj-form-toolbar', template: `
81
+ <button kendoButton *ngIf="!this.form.EditMode && this.form.UserCanEdit" (click)="this.form.StartEditMode()">Edit Record</button>
82
+ <button kendoButton *ngIf="this.form.EditMode" (click)="this.form.SaveRecord(true)">Save Record</button>
83
+ <button kendoButton *ngIf="this.form.EditMode" (click)="this.form.CancelEdit()">Cancel</button>
84
+ <button kendoButton *ngIf="this.form.FavoriteInitDone && this.form.IsFavorite" (click)="this.form.RemoveFavorite()">Remove Favorite</button>
85
+ <button kendoButton *ngIf="this.form.FavoriteInitDone && !this.form.IsFavorite" (click)="this.form.MakeFavorite()">Make Favorite</button>
86
+ <button kendoButton *ngIf="this.form.EntityInfo?.TrackRecordChanges" (click)="this.form.handleHistoryDialog()">History</button>
87
+ <hr />
88
+ <mj-record-changes *ngIf="this.form.isHistoryDialogOpen" [record]="this.form.record" (dialogClosed)="this.form.handleHistoryDialog()"></mj-record-changes>
89
+ `, styles: ["button { margin-right: 10px; }"] }]
90
+ }], null, { form: [{
91
+ type: Input
92
+ }] }); })();
93
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(FormToolbarComponent, { className: "FormToolbarComponent", filePath: "src\\lib\\form-toolbar.ts", lineNumber: 19 }); })();
@@ -0,0 +1,17 @@
1
+ import { OnDestroy, ViewContainerRef, AfterViewInit, OnChanges, SimpleChanges } from '@angular/core';
2
+ import { BaseEntity } from '@memberjunction/core';
3
+ import * as i0 from "@angular/core";
4
+ export declare class SectionLoaderComponent implements AfterViewInit, OnDestroy, OnChanges {
5
+ Entity: string;
6
+ Section: string;
7
+ record: BaseEntity;
8
+ EditMode: boolean;
9
+ container: ViewContainerRef;
10
+ private _sectionObj;
11
+ ngOnChanges(changes: SimpleChanges): void;
12
+ ngAfterViewInit(): void;
13
+ private loadComponent;
14
+ ngOnDestroy(): void;
15
+ static ɵfac: i0.ɵɵFactoryDeclaration<SectionLoaderComponent, never>;
16
+ static ɵcmp: i0.ɵɵComponentDeclaration<SectionLoaderComponent, "mj-form-section", never, { "Entity": { "alias": "Entity"; "required": false; }; "Section": { "alias": "Section"; "required": false; }; "record": { "alias": "record"; "required": false; }; "EditMode": { "alias": "EditMode"; "required": false; }; }, {}, never, never, false, never>;
17
+ }
@@ -0,0 +1,66 @@
1
+ import { Component, Input, ViewChild, ViewContainerRef } from '@angular/core';
2
+ import { MJGlobal } from '@memberjunction/global';
3
+ import { BaseFormSectionComponent } from './base-form-section-component';
4
+ import * as i0 from "@angular/core";
5
+ const _c0 = ["container"];
6
+ function SectionLoaderComponent_ng_template_0_Template(rf, ctx) { }
7
+ export class SectionLoaderComponent {
8
+ constructor() {
9
+ this.EditMode = false;
10
+ this._sectionObj = null;
11
+ }
12
+ ngOnChanges(changes) {
13
+ // react to EditMode change
14
+ if (changes['EditMode'] && !changes['EditMode'].firstChange && this._sectionObj) {
15
+ this._sectionObj.EditMode = this.EditMode;
16
+ }
17
+ }
18
+ ngAfterViewInit() {
19
+ // do this via a promise in order to defer the load by one pass of the event loop
20
+ // because that results in Angular not complaining about change detection due to dynamic
21
+ // nature of the inner component
22
+ Promise.resolve().then(() => this.loadComponent());
23
+ }
24
+ loadComponent() {
25
+ const sectionInfo = MJGlobal.Instance.ClassFactory.GetRegistration(BaseFormSectionComponent, `${this.Entity}.${this.Section}`);
26
+ if (sectionInfo) {
27
+ const componentRef = this.container.createComponent(sectionInfo.SubClass);
28
+ // pass in record and edit mode
29
+ this._sectionObj = componentRef.instance;
30
+ this._sectionObj.record = this.record;
31
+ this._sectionObj.EditMode = this.EditMode;
32
+ }
33
+ }
34
+ ngOnDestroy() {
35
+ // Don't forget to cleanup dynamically created components
36
+ this.container.clear();
37
+ }
38
+ }
39
+ SectionLoaderComponent.ɵfac = function SectionLoaderComponent_Factory(t) { return new (t || SectionLoaderComponent)(); };
40
+ SectionLoaderComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: SectionLoaderComponent, selectors: [["mj-form-section"]], viewQuery: function SectionLoaderComponent_Query(rf, ctx) { if (rf & 1) {
41
+ i0.ɵɵviewQuery(_c0, 5, ViewContainerRef);
42
+ } if (rf & 2) {
43
+ let _t;
44
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.container = _t.first);
45
+ } }, inputs: { Entity: "Entity", Section: "Section", record: "record", EditMode: "EditMode" }, features: [i0.ɵɵNgOnChangesFeature], decls: 2, vars: 0, consts: [["container", ""]], template: function SectionLoaderComponent_Template(rf, ctx) { if (rf & 1) {
46
+ i0.ɵɵtemplate(0, SectionLoaderComponent_ng_template_0_Template, 0, 0, "ng-template", null, 0, i0.ɵɵtemplateRefExtractor);
47
+ } }, encapsulation: 2 });
48
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SectionLoaderComponent, [{
49
+ type: Component,
50
+ args: [{
51
+ selector: 'mj-form-section',
52
+ template: `<ng-template #container></ng-template>`
53
+ }]
54
+ }], null, { Entity: [{
55
+ type: Input
56
+ }], Section: [{
57
+ type: Input
58
+ }], record: [{
59
+ type: Input
60
+ }], EditMode: [{
61
+ type: Input
62
+ }], container: [{
63
+ type: ViewChild,
64
+ args: ['container', { read: ViewContainerRef }]
65
+ }] }); })();
66
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SectionLoaderComponent, { className: "SectionLoaderComponent", filePath: "src\\lib\\section-loader-component.ts", lineNumber: 10 }); })();
@@ -0,0 +1,12 @@
1
+ import * as i0 from "@angular/core";
2
+ import * as i1 from "./lib/section-loader-component";
3
+ import * as i2 from "./lib/form-toolbar";
4
+ import * as i3 from "@angular/common";
5
+ import * as i4 from "@angular/forms";
6
+ import * as i5 from "@progress/kendo-angular-layout";
7
+ import * as i6 from "@memberjunction/ng-record-changes";
8
+ export declare class BaseFormsModule {
9
+ static ɵfac: i0.ɵɵFactoryDeclaration<BaseFormsModule, never>;
10
+ static ɵmod: i0.ɵɵNgModuleDeclaration<BaseFormsModule, [typeof i1.SectionLoaderComponent, typeof i2.FormToolbarComponent], [typeof i3.CommonModule, typeof i4.FormsModule, typeof i5.TabStripModule, typeof i6.RecordChangesModule], [typeof i1.SectionLoaderComponent, typeof i2.FormToolbarComponent]>;
11
+ static ɵinj: i0.ɵɵInjectorDeclaration<BaseFormsModule>;
12
+ }
package/dist/module.js ADDED
@@ -0,0 +1,42 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ // Kendo UI Angular imports
4
+ import { FormsModule } from '@angular/forms';
5
+ import { TabStripModule } from '@progress/kendo-angular-layout';
6
+ import { SectionLoaderComponent } from './lib/section-loader-component';
7
+ import { FormToolbarComponent } from './lib/form-toolbar';
8
+ import { RecordChangesModule } from '@memberjunction/ng-record-changes';
9
+ import * as i0 from "@angular/core";
10
+ export class BaseFormsModule {
11
+ }
12
+ BaseFormsModule.ɵfac = function BaseFormsModule_Factory(t) { return new (t || BaseFormsModule)(); };
13
+ BaseFormsModule.ɵmod = /*@__PURE__*/ i0.ɵɵdefineNgModule({ type: BaseFormsModule });
14
+ BaseFormsModule.ɵinj = /*@__PURE__*/ i0.ɵɵdefineInjector({ imports: [CommonModule,
15
+ FormsModule,
16
+ TabStripModule,
17
+ RecordChangesModule] });
18
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BaseFormsModule, [{
19
+ type: NgModule,
20
+ args: [{
21
+ declarations: [
22
+ SectionLoaderComponent,
23
+ FormToolbarComponent
24
+ ],
25
+ imports: [
26
+ CommonModule,
27
+ FormsModule,
28
+ TabStripModule,
29
+ RecordChangesModule
30
+ ],
31
+ exports: [
32
+ SectionLoaderComponent,
33
+ FormToolbarComponent
34
+ ]
35
+ }]
36
+ }], null, null); })();
37
+ (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0.ɵɵsetNgModuleScope(BaseFormsModule, { declarations: [SectionLoaderComponent,
38
+ FormToolbarComponent], imports: [CommonModule,
39
+ FormsModule,
40
+ TabStripModule,
41
+ RecordChangesModule], exports: [SectionLoaderComponent,
42
+ FormToolbarComponent] }); })();
@@ -0,0 +1,6 @@
1
+ export * from './module';
2
+ export * from './lib/base-record-component';
3
+ export * from './lib/base-form-section-component';
4
+ export * from './lib/base-form-component';
5
+ export * from './lib/section-loader-component';
6
+ export * from './lib/form-toolbar';
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Public API Surface
3
+ */
4
+ export * from './module';
5
+ export * from './lib/base-record-component';
6
+ export * from './lib/base-form-section-component';
7
+ export * from './lib/base-form-component';
8
+ export * from './lib/section-loader-component';
9
+ export * from './lib/form-toolbar';
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@memberjunction/ng-base-forms",
3
+ "version": "0.9.2",
4
+ "description": "MemberJunction: Angular Base Form Components",
5
+ "main": "./dist/public-api.js",
6
+ "typings": "./dist/public-api.d.ts",
7
+ "files": [
8
+ "/dist"
9
+ ],
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1",
12
+ "build": "ngc"
13
+ },
14
+ "keywords": [],
15
+ "author": "",
16
+ "license": "ISC",
17
+ "devDependencies": {
18
+ "@angular/compiler": "~17.2.2",
19
+ "@angular/compiler-cli": "~17.2.2"
20
+ },
21
+ "peerDependencies": {
22
+ "@angular/common": "~17.2.2",
23
+ "@angular/core": "~17.2.2",
24
+ "@angular/forms": "~17.2.2",
25
+ "@angular/router": "~17.2.2"
26
+ },
27
+ "dependencies": {
28
+ "@memberjunction/global": "^0.9.156",
29
+ "@memberjunction/core": "^0.9.177",
30
+ "@memberjunction/ng-shared": "^0.9.34",
31
+ "@memberjunction/ng-record-changes": "^0.9.116",
32
+ "@memberjunction/ng-user-view-grid": "^0.9.216",
33
+ "@progress/kendo-angular-layout": "~15.1.0",
34
+ "tslib": "^2.3.0"
35
+ },
36
+ "sideEffects": false
37
+ }