@memberjunction/codegen-lib 3.4.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +828 -630
- package/dist/Angular/angular-codegen.d.ts +10 -3
- package/dist/Angular/angular-codegen.d.ts.map +1 -1
- package/dist/Angular/angular-codegen.js +164 -199
- package/dist/Angular/angular-codegen.js.map +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.d.ts.map +1 -1
- package/dist/Angular/entity-data-grid-related-entity-component.js +8 -10
- package/dist/Angular/entity-data-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/join-grid-related-entity-component.d.ts +1 -1
- package/dist/Angular/join-grid-related-entity-component.js +8 -36
- package/dist/Angular/join-grid-related-entity-component.js.map +1 -1
- package/dist/Angular/related-entity-components.js +13 -79
- package/dist/Angular/related-entity-components.js.map +1 -1
- package/dist/Angular/timeline-related-entity-component.d.ts +1 -1
- package/dist/Angular/timeline-related-entity-component.js +14 -38
- package/dist/Angular/timeline-related-entity-component.js.map +1 -1
- package/dist/Config/config.d.ts.map +1 -1
- package/dist/Config/config.js +171 -177
- package/dist/Config/config.js.map +1 -1
- package/dist/Config/db-connection.d.ts +1 -1
- package/dist/Config/db-connection.d.ts.map +1 -1
- package/dist/Config/db-connection.js +6 -33
- package/dist/Config/db-connection.js.map +1 -1
- package/dist/Database/dbSchema.js +28 -35
- package/dist/Database/dbSchema.js.map +1 -1
- package/dist/Database/manage-metadata.d.ts +302 -6
- package/dist/Database/manage-metadata.d.ts.map +1 -1
- package/dist/Database/manage-metadata.js +1294 -445
- package/dist/Database/manage-metadata.js.map +1 -1
- package/dist/Database/reorder-columns.d.ts +1 -1
- package/dist/Database/reorder-columns.d.ts.map +1 -1
- package/dist/Database/reorder-columns.js +1 -5
- package/dist/Database/reorder-columns.js.map +1 -1
- package/dist/Database/sql.d.ts +1 -1
- package/dist/Database/sql.d.ts.map +1 -1
- package/dist/Database/sql.js +67 -98
- package/dist/Database/sql.js.map +1 -1
- package/dist/Database/sql_codegen.d.ts +15 -2
- package/dist/Database/sql_codegen.d.ts.map +1 -1
- package/dist/Database/sql_codegen.js +282 -253
- package/dist/Database/sql_codegen.js.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts +11 -0
- package/dist/Manifest/GenerateClassRegistrationsManifest.d.ts.map +1 -1
- package/dist/Manifest/GenerateClassRegistrationsManifest.js +43 -41
- package/dist/Manifest/GenerateClassRegistrationsManifest.js.map +1 -1
- package/dist/Misc/action_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/action_subclasses_codegen.js +15 -26
- package/dist/Misc/action_subclasses_codegen.js.map +1 -1
- package/dist/Misc/advanced_generation.d.ts +69 -7
- package/dist/Misc/advanced_generation.d.ts.map +1 -1
- package/dist/Misc/advanced_generation.js +114 -75
- package/dist/Misc/advanced_generation.js.map +1 -1
- package/dist/Misc/createNewUser.d.ts +1 -1
- package/dist/Misc/createNewUser.js +22 -26
- package/dist/Misc/createNewUser.js.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.d.ts +7 -2
- package/dist/Misc/entity_subclasses_codegen.d.ts.map +1 -1
- package/dist/Misc/entity_subclasses_codegen.js +56 -45
- package/dist/Misc/entity_subclasses_codegen.js.map +1 -1
- package/dist/Misc/graphql_server_codegen.d.ts.map +1 -1
- package/dist/Misc/graphql_server_codegen.js +39 -42
- package/dist/Misc/graphql_server_codegen.js.map +1 -1
- package/dist/Misc/runCommand.d.ts +1 -1
- package/dist/Misc/runCommand.js +13 -20
- package/dist/Misc/runCommand.js.map +1 -1
- package/dist/Misc/sql_logging.d.ts +1 -1
- package/dist/Misc/sql_logging.d.ts.map +1 -1
- package/dist/Misc/sql_logging.js +21 -51
- package/dist/Misc/sql_logging.js.map +1 -1
- package/dist/Misc/status_logging.js +45 -60
- package/dist/Misc/status_logging.js.map +1 -1
- package/dist/Misc/system_integrity.d.ts +1 -1
- package/dist/Misc/system_integrity.d.ts.map +1 -1
- package/dist/Misc/system_integrity.js +12 -16
- package/dist/Misc/system_integrity.js.map +1 -1
- package/dist/Misc/temp_batch_file.js +15 -22
- package/dist/Misc/temp_batch_file.js.map +1 -1
- package/dist/Misc/util.d.ts.map +1 -1
- package/dist/Misc/util.js +17 -28
- package/dist/Misc/util.js.map +1 -1
- package/dist/__tests__/metadataConfig.test.d.ts +12 -0
- package/dist/__tests__/metadataConfig.test.d.ts.map +1 -0
- package/dist/__tests__/metadataConfig.test.js +604 -0
- package/dist/__tests__/metadataConfig.test.js.map +1 -0
- package/dist/index.d.ts +21 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -41
- package/dist/index.js.map +1 -1
- package/dist/runCodeGen.d.ts +1 -0
- package/dist/runCodeGen.d.ts.map +1 -1
- package/dist/runCodeGen.js +150 -178
- package/dist/runCodeGen.js.map +1 -1
- package/package.json +29 -23
|
@@ -1,84 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const status_logging_1 = require("../Misc/status_logging");
|
|
9
|
-
const fs_1 = __importDefault(require("fs"));
|
|
10
|
-
const path_1 = __importDefault(require("path"));
|
|
11
|
-
const config_1 = require("../Config/config");
|
|
12
|
-
const related_entity_components_1 = require("./related-entity-components");
|
|
13
|
-
const util_1 = require("../Misc/util");
|
|
1
|
+
import { GeneratedFormSectionType, EntityFieldTSType, EntityFieldValueListType, Metadata } from '@memberjunction/core';
|
|
2
|
+
import { logError, logStatus } from '../Misc/status_logging.js';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { mjCoreSchema, outputOptionValue, configInfo } from '../Config/config.js';
|
|
6
|
+
import { RelatedEntityDisplayComponentGeneratorBase } from './related-entity-components.js';
|
|
7
|
+
import { sortBySequenceAndCreatedAt } from '../Misc/util.js';
|
|
14
8
|
/**
|
|
15
9
|
* Represents metadata about an Angular form section that is generated for an entity
|
|
16
10
|
*/
|
|
17
|
-
class AngularFormSectionInfo {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*/
|
|
29
|
-
TabCode;
|
|
30
|
-
/**
|
|
31
|
-
* The TypeScript class name for the section component
|
|
32
|
-
*/
|
|
33
|
-
ClassName;
|
|
34
|
-
/**
|
|
35
|
-
* The filename where the section component will be saved
|
|
36
|
-
*/
|
|
37
|
-
FileName;
|
|
38
|
-
/**
|
|
39
|
-
* The complete TypeScript component code for the section
|
|
40
|
-
*/
|
|
41
|
-
ComponentCode;
|
|
42
|
-
/**
|
|
43
|
-
* Array of entity fields that belong to this section
|
|
44
|
-
*/
|
|
45
|
-
Fields;
|
|
46
|
-
/**
|
|
47
|
-
* The filename without the .ts extension
|
|
48
|
-
*/
|
|
49
|
-
FileNameWithoutExtension;
|
|
50
|
-
/**
|
|
51
|
-
* The class name of the entity this section belongs to
|
|
52
|
-
*/
|
|
53
|
-
EntityClassName;
|
|
54
|
-
/**
|
|
55
|
-
* Indicates if this section represents a related entity tab
|
|
56
|
-
*/
|
|
57
|
-
IsRelatedEntity = false;
|
|
58
|
-
/**
|
|
59
|
-
* Specifies where related entity tabs should be displayed relative to field tabs
|
|
60
|
-
*/
|
|
61
|
-
RelatedEntityDisplayLocation = 'After Field Tabs';
|
|
62
|
-
/**
|
|
63
|
-
* The generation result for related entity components
|
|
64
|
-
*/
|
|
65
|
-
GeneratedOutput;
|
|
66
|
-
/**
|
|
67
|
-
* The minimum sequence number from fields in this section (used for sorting)
|
|
68
|
-
*/
|
|
69
|
-
MinSequence;
|
|
70
|
-
/**
|
|
71
|
-
* The unique camelCase key used for this section in the sectionsExpanded object
|
|
72
|
-
*/
|
|
73
|
-
UniqueKey;
|
|
11
|
+
export class AngularFormSectionInfo {
|
|
12
|
+
constructor() {
|
|
13
|
+
/**
|
|
14
|
+
* Indicates if this section represents a related entity tab
|
|
15
|
+
*/
|
|
16
|
+
this.IsRelatedEntity = false;
|
|
17
|
+
/**
|
|
18
|
+
* Specifies where related entity tabs should be displayed relative to field tabs
|
|
19
|
+
*/
|
|
20
|
+
this.RelatedEntityDisplayLocation = 'After Field Tabs';
|
|
21
|
+
}
|
|
74
22
|
}
|
|
75
|
-
exports.AngularFormSectionInfo = AngularFormSectionInfo;
|
|
76
23
|
/**
|
|
77
24
|
* Base class for generating Angular client code for MemberJunction entities.
|
|
78
25
|
* This class handles the generation of Angular components, forms, and modules based on entity metadata.
|
|
79
26
|
* You can sub-class this class to create your own Angular client code generator logic.
|
|
80
27
|
*/
|
|
81
|
-
class AngularClientGeneratorBase {
|
|
28
|
+
export class AngularClientGeneratorBase {
|
|
29
|
+
constructor() {
|
|
30
|
+
/**
|
|
31
|
+
* Base name used for generating sub-module names
|
|
32
|
+
* @protected
|
|
33
|
+
*/
|
|
34
|
+
this.subModule_BaseName = 'GeneratedForms_SubModule_';
|
|
35
|
+
}
|
|
82
36
|
/**
|
|
83
37
|
* Main entry point for generating Angular code for a collection of entities
|
|
84
38
|
* @param entities Array of EntityInfo objects to generate Angular code for
|
|
@@ -89,24 +43,24 @@ class AngularClientGeneratorBase {
|
|
|
89
43
|
*/
|
|
90
44
|
async generateAngularCode(entities, directory, modulePrefix, contextUser) {
|
|
91
45
|
try {
|
|
92
|
-
const entityPath =
|
|
46
|
+
const entityPath = path.join(directory, 'Entities');
|
|
93
47
|
//const classMapEntries: string[] = [];
|
|
94
48
|
const componentImports = [];
|
|
95
49
|
const relatedEntityModuleImports = [];
|
|
96
50
|
const componentNames = [];
|
|
97
51
|
const sections = [];
|
|
98
|
-
if (!
|
|
99
|
-
|
|
52
|
+
if (!fs.existsSync(entityPath))
|
|
53
|
+
fs.mkdirSync(entityPath, { recursive: true }); // create the directory if it doesn't exist
|
|
100
54
|
for (let i = 0; i < entities.length; ++i) {
|
|
101
55
|
const entity = entities[i];
|
|
102
56
|
if (entity.PrimaryKeys && entity.PrimaryKeys.length > 0 && entity.IncludeInAPI) {
|
|
103
|
-
const thisEntityPath =
|
|
104
|
-
if (!
|
|
105
|
-
|
|
57
|
+
const thisEntityPath = path.join(entityPath, entity.ClassName);
|
|
58
|
+
if (!fs.existsSync(thisEntityPath))
|
|
59
|
+
fs.mkdirSync(thisEntityPath, { recursive: true }); // create the directory if it doesn't exist
|
|
106
60
|
const { htmlCode, additionalSections, relatedEntitySections } = await this.generateSingleEntityHTMLForAngular(entity, contextUser);
|
|
107
61
|
const tsCode = this.generateSingleEntityTypeScriptForAngular(entity, additionalSections, relatedEntitySections);
|
|
108
|
-
|
|
109
|
-
|
|
62
|
+
fs.writeFileSync(path.join(thisEntityPath, `${entity.ClassName.toLowerCase()}.form.component.ts`), tsCode);
|
|
63
|
+
fs.writeFileSync(path.join(thisEntityPath, `${entity.ClassName.toLowerCase()}.form.component.html`), htmlCode);
|
|
110
64
|
// Sections are now inline in HTML templates, no separate files needed
|
|
111
65
|
// Just track them for module generation purposes
|
|
112
66
|
if (additionalSections.length > 0) {
|
|
@@ -115,7 +69,7 @@ class AngularClientGeneratorBase {
|
|
|
115
69
|
});
|
|
116
70
|
}
|
|
117
71
|
const componentName = `${entity.ClassName}FormComponent`;
|
|
118
|
-
componentImports.push(`import { ${componentName}
|
|
72
|
+
componentImports.push(`import { ${componentName} } from "./Entities/${entity.ClassName}/${entity.ClassName.toLowerCase()}.form.component";`);
|
|
119
73
|
const currentComponentDistinctRelatedEntityClassNames = [];
|
|
120
74
|
relatedEntitySections.forEach(s => s.GeneratedOutput.Component.ImportItems.forEach(i => {
|
|
121
75
|
if (!currentComponentDistinctRelatedEntityClassNames.find(ii => ii.itemClassName === i.ClassName))
|
|
@@ -140,16 +94,16 @@ class AngularClientGeneratorBase {
|
|
|
140
94
|
// now the imports are good
|
|
141
95
|
}
|
|
142
96
|
else {
|
|
143
|
-
|
|
97
|
+
logStatus(` Entity ${entity.Name} does not have a primary key or is not included in the API, skipping code generation for this entity`);
|
|
144
98
|
}
|
|
145
99
|
}
|
|
146
|
-
const maxComponentsPerModule =
|
|
100
|
+
const maxComponentsPerModule = outputOptionValue('Angular', 'maxComponentsPerModule', 25);
|
|
147
101
|
const moduleCode = this.generateAngularModule(componentImports, componentNames, relatedEntityModuleImports, sections, modulePrefix, maxComponentsPerModule);
|
|
148
|
-
|
|
102
|
+
fs.writeFileSync(path.join(directory, 'generated-forms.module.ts'), moduleCode);
|
|
149
103
|
return true;
|
|
150
104
|
}
|
|
151
105
|
catch (err) {
|
|
152
|
-
|
|
106
|
+
logError(err);
|
|
153
107
|
return false;
|
|
154
108
|
}
|
|
155
109
|
}
|
|
@@ -184,19 +138,11 @@ import { FormsModule } from '@angular/forms';
|
|
|
184
138
|
|
|
185
139
|
// MemberJunction Imports
|
|
186
140
|
import { BaseFormsModule } from '@memberjunction/ng-base-forms';
|
|
187
|
-
import { FormToolbarModule } from '@memberjunction/ng-form-toolbar';
|
|
188
141
|
import { EntityViewerModule } from '@memberjunction/ng-entity-viewer';
|
|
189
142
|
import { LinkDirectivesModule } from '@memberjunction/ng-link-directives';
|
|
190
143
|
import { MJTabStripModule } from "@memberjunction/ng-tabstrip";
|
|
191
144
|
import { ContainerDirectivesModule } from "@memberjunction/ng-container-directives";
|
|
192
|
-
|
|
193
|
-
// Kendo Imports
|
|
194
|
-
import { InputsModule } from '@progress/kendo-angular-inputs';
|
|
195
|
-
import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
|
|
196
|
-
import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
|
197
145
|
import { LayoutModule } from '@progress/kendo-angular-layout';
|
|
198
|
-
import { ComboBoxModule } from '@progress/kendo-angular-dropdowns';
|
|
199
|
-
import { DropDownListModule } from '@progress/kendo-angular-dropdowns';
|
|
200
146
|
|
|
201
147
|
// Import Generated Components
|
|
202
148
|
${componentImports.join('\n')}
|
|
@@ -205,14 +151,9 @@ ${relatedEntityModuleImports.filter(remi => remi.library.trim().toLowerCase() !=
|
|
|
205
151
|
.join('\n')}
|
|
206
152
|
${moduleCode}
|
|
207
153
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// code do NOTHING - the point is to prevent the code from being eliminated during tree shaking
|
|
212
|
-
// since it is dynamically instantiated on demand, and the Angular compiler has no way to know that,
|
|
213
|
-
// in production builds tree shaking will eliminate the code unless we do this
|
|
214
|
-
${componentNames.map(c => `Load${c.componentName}();`).join('\n ')}
|
|
215
|
-
}
|
|
154
|
+
// Note: LoadXXXGeneratedForms() functions have been removed. Tree-shaking prevention
|
|
155
|
+
// is now handled by the pre-built class registration manifest system.
|
|
156
|
+
// See packages/CodeGenLib/CLASS_MANIFEST_GUIDE.md for details.
|
|
216
157
|
`;
|
|
217
158
|
}
|
|
218
159
|
/**
|
|
@@ -274,11 +215,6 @@ export class ${modulePrefix}GeneratedFormsModule { }`;
|
|
|
274
215
|
// so we need to combine the two into a single return value and send back to the caller
|
|
275
216
|
return subModules.join('\n\n') + '\n\n' + masterModuleCode;
|
|
276
217
|
}
|
|
277
|
-
/**
|
|
278
|
-
* Base name used for generating sub-module names
|
|
279
|
-
* @protected
|
|
280
|
-
*/
|
|
281
|
-
subModule_BaseName = 'GeneratedForms_SubModule_';
|
|
282
218
|
/**
|
|
283
219
|
* Get the base name for the sub-modules. Override this method to change the base name.
|
|
284
220
|
* @returns The base name for sub-modules (default: 'GeneratedForms_SubModule_')
|
|
@@ -298,17 +234,11 @@ imports: [
|
|
|
298
234
|
CommonModule,
|
|
299
235
|
FormsModule,
|
|
300
236
|
LayoutModule,
|
|
301
|
-
|
|
302
|
-
ButtonsModule,
|
|
303
|
-
DateInputsModule,
|
|
237
|
+
BaseFormsModule,
|
|
304
238
|
EntityViewerModule,
|
|
305
239
|
LinkDirectivesModule,
|
|
306
|
-
BaseFormsModule,
|
|
307
|
-
FormToolbarModule,
|
|
308
240
|
MJTabStripModule,
|
|
309
|
-
ContainerDirectivesModule
|
|
310
|
-
DropDownListModule,
|
|
311
|
-
ComboBoxModule${additionalModulesToImport.length > 0 ? ',\n ' + additionalModulesToImport.join(',\n ') : ''}
|
|
241
|
+
ContainerDirectivesModule${additionalModulesToImport.length > 0 ? ',\n ' + additionalModulesToImport.join(',\n ') : ''}
|
|
312
242
|
],
|
|
313
243
|
exports: [
|
|
314
244
|
]
|
|
@@ -355,7 +285,7 @@ export class ${this.SubModuleBaseName}${moduleNumber} { }
|
|
|
355
285
|
relatedEntitySections.filter(s => s.GeneratedOutput && s.GeneratedOutput.CodeOutput.length > 0)
|
|
356
286
|
.map(s => s.GeneratedOutput.CodeOutput.split("\n").map(l => ` ${l}`).join("\n")).join('\n') : '';
|
|
357
287
|
// Generate unique keys for all sections FIRST, then use them everywhere
|
|
358
|
-
const sectionsWithoutTop = additionalSections.filter(s => s.Type !==
|
|
288
|
+
const sectionsWithoutTop = additionalSections.filter(s => s.Type !== GeneratedFormSectionType.Top && s.Name);
|
|
359
289
|
const allSections = [...sectionsWithoutTop, ...relatedEntitySections];
|
|
360
290
|
// Assign unique keys to each section
|
|
361
291
|
const usedKeys = new Set();
|
|
@@ -390,14 +320,15 @@ export class ${this.SubModuleBaseName}${moduleNumber} { }
|
|
|
390
320
|
const sectionInitCode = sectionInitEntries.length > 0
|
|
391
321
|
? `\n\n override async ngOnInit() {\n await super.ngOnInit();\n this.initSections([\n${sectionInitEntries.join(',\n')}\n ]);\n }`
|
|
392
322
|
: '';
|
|
393
|
-
const entityPackageName =
|
|
323
|
+
const entityPackageName = configInfo.entityPackageName || 'mj_generatedentities';
|
|
394
324
|
return `import { Component } from '@angular/core';
|
|
395
|
-
import { ${entityObjectClass}Entity } from '${entity.SchemaName ===
|
|
325
|
+
import { ${entityObjectClass}Entity } from '${entity.SchemaName === mjCoreSchema ? '@memberjunction/core-entities' : entityPackageName}';
|
|
396
326
|
import { RegisterClass } from '@memberjunction/global';
|
|
397
327
|
import { BaseFormComponent } from '@memberjunction/ng-base-forms';
|
|
398
328
|
${generationImports.length > 0 ? generationImports + '\n' : ''}
|
|
399
329
|
@RegisterClass(BaseFormComponent, '${entity.Name}') // Tell MemberJunction about this class
|
|
400
330
|
@Component({
|
|
331
|
+
standalone: false,
|
|
401
332
|
selector: 'gen-${entity.ClassName.toLowerCase()}-form',
|
|
402
333
|
templateUrl: './${entity.ClassName.toLowerCase()}.form.component.html'
|
|
403
334
|
})
|
|
@@ -405,9 +336,6 @@ export class ${entity.ClassName}FormComponent extends BaseFormComponent {
|
|
|
405
336
|
public record!: ${entityObjectClass}Entity;${generationInjectedCode.length > 0 ? '\n' + generationInjectedCode : ''}${sectionInitCode}
|
|
406
337
|
}
|
|
407
338
|
|
|
408
|
-
export function Load${entity.ClassName}FormComponent() {
|
|
409
|
-
// does nothing, but called to prevent tree-shaking from eliminating this component from the build
|
|
410
|
-
}
|
|
411
339
|
`;
|
|
412
340
|
}
|
|
413
341
|
/**
|
|
@@ -416,7 +344,7 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
416
344
|
* @returns True if the entity has top area fields, false otherwise
|
|
417
345
|
*/
|
|
418
346
|
entityHasTopArea(entity) {
|
|
419
|
-
return entity.Fields.some(f => f.GeneratedFormSectionType ===
|
|
347
|
+
return entity.Fields.some(f => f.GeneratedFormSectionType === GeneratedFormSectionType.Top);
|
|
420
348
|
}
|
|
421
349
|
/**
|
|
422
350
|
* Generates HTML for the top area section of an entity form
|
|
@@ -466,26 +394,27 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
466
394
|
* @param categoryIcons Optional map of category names to Font Awesome icon classes
|
|
467
395
|
* @returns Array of generated form sections
|
|
468
396
|
*/
|
|
469
|
-
generateAngularAdditionalSections(entity, startIndex,
|
|
397
|
+
generateAngularAdditionalSections(entity, startIndex, fieldCategories) {
|
|
470
398
|
const sections = [];
|
|
471
399
|
let index = startIndex;
|
|
472
|
-
const sortedFields =
|
|
400
|
+
const sortedFields = sortBySequenceAndCreatedAt(entity.Fields);
|
|
473
401
|
for (const field of sortedFields) {
|
|
474
402
|
if (field.IncludeInGeneratedForm) {
|
|
475
|
-
if (field.GeneratedFormSectionType ===
|
|
476
|
-
this.AddSectionIfNeeded(entity, sections,
|
|
477
|
-
else if (field.GeneratedFormSectionType ===
|
|
478
|
-
this.AddSectionIfNeeded(entity, sections,
|
|
479
|
-
else if (field.GeneratedFormSectionType ===
|
|
480
|
-
this.AddSectionIfNeeded(entity, sections,
|
|
403
|
+
if (field.GeneratedFormSectionType === GeneratedFormSectionType.Category && field.Category && field.Category !== '' && field.IncludeInGeneratedForm)
|
|
404
|
+
this.AddSectionIfNeeded(entity, sections, GeneratedFormSectionType.Category, field.Category, field.Sequence);
|
|
405
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Details)
|
|
406
|
+
this.AddSectionIfNeeded(entity, sections, GeneratedFormSectionType.Details, "Details", field.Sequence);
|
|
407
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Top)
|
|
408
|
+
this.AddSectionIfNeeded(entity, sections, GeneratedFormSectionType.Top, "Top", field.Sequence);
|
|
481
409
|
}
|
|
482
410
|
}
|
|
483
|
-
// Sort sections
|
|
411
|
+
// Sort sections: Top first, then own categories, then inherited categories
|
|
412
|
+
// (nearest parent first), then System Metadata last
|
|
484
413
|
sections.sort((a, b) => {
|
|
485
414
|
// Top sections always first
|
|
486
|
-
if (a.Type ===
|
|
415
|
+
if (a.Type === GeneratedFormSectionType.Top)
|
|
487
416
|
return -1;
|
|
488
|
-
if (b.Type ===
|
|
417
|
+
if (b.Type === GeneratedFormSectionType.Top)
|
|
489
418
|
return 1;
|
|
490
419
|
// System sections always last (after related entities)
|
|
491
420
|
const aIsSystem = a.Name.toLowerCase() === 'system' || a.Name.toLowerCase() === 'system metadata';
|
|
@@ -494,6 +423,13 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
494
423
|
return 1;
|
|
495
424
|
if (!aIsSystem && bIsSystem)
|
|
496
425
|
return -1;
|
|
426
|
+
// Inherited sections come after own sections (but before System)
|
|
427
|
+
const aIsInherited = fieldCategories?.[a.Name]?.inheritedFromEntityName != null;
|
|
428
|
+
const bIsInherited = fieldCategories?.[b.Name]?.inheritedFromEntityName != null;
|
|
429
|
+
if (aIsInherited && !bIsInherited)
|
|
430
|
+
return 1;
|
|
431
|
+
if (!aIsInherited && bIsInherited)
|
|
432
|
+
return -1;
|
|
497
433
|
// Otherwise sort by sequence
|
|
498
434
|
const aSeq = a.MinSequence ?? Number.MAX_SAFE_INTEGER;
|
|
499
435
|
const bSeq = b.MinSequence ?? Number.MAX_SAFE_INTEGER;
|
|
@@ -503,19 +439,20 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
503
439
|
let sectionIndex = 0;
|
|
504
440
|
for (const section of sections) {
|
|
505
441
|
let sectionName = '';
|
|
506
|
-
if (section.Type ===
|
|
442
|
+
if (section.Type === GeneratedFormSectionType.Top) {
|
|
507
443
|
section.TabCode = this.generateTopAreaHTMLForAngular(entity);
|
|
508
444
|
sectionName = 'top-area';
|
|
509
445
|
}
|
|
510
446
|
else {
|
|
511
|
-
if (section.Type ===
|
|
447
|
+
if (section.Type === GeneratedFormSectionType.Category)
|
|
512
448
|
sectionName = this.stripWhiteSpace(section.Name.toLowerCase());
|
|
513
|
-
else if (section.Type ===
|
|
449
|
+
else if (section.Type === GeneratedFormSectionType.Details)
|
|
514
450
|
sectionName = 'details';
|
|
515
451
|
// Generate collapsible panel HTML inline instead of using separate components
|
|
516
452
|
const formHTML = this.generateSectionHTMLForAngular(entity, section);
|
|
517
|
-
// Use category-specific icon from
|
|
518
|
-
const
|
|
453
|
+
// Use category-specific icon from metadata if available, otherwise fall back to keyword matching
|
|
454
|
+
const categoryInfo = fieldCategories ? fieldCategories[section.Name] : undefined;
|
|
455
|
+
const icon = categoryInfo?.icon || this.getIconForCategory(section.Name);
|
|
519
456
|
// NOTE: We'll set the UniqueKey later in generateSingleEntityTypeScriptForAngular()
|
|
520
457
|
// For now, just use a placeholder that will be replaced
|
|
521
458
|
const sectionKey = this.camelCase(section.Name);
|
|
@@ -529,18 +466,22 @@ export function Load${entity.ClassName}FormComponent() {
|
|
|
529
466
|
}).join(' ') : '';
|
|
530
467
|
// No additional indentation needed - formHTML is already properly indented
|
|
531
468
|
const indentedFormHTML = formHTML;
|
|
469
|
+
// Build inherited variant attributes if this category comes from a parent entity
|
|
470
|
+
const inheritedAttrs = categoryInfo?.inheritedFromEntityName
|
|
471
|
+
? `\n Variant="inherited"\n InheritedFromEntity="${categoryInfo.inheritedFromEntityName}"`
|
|
472
|
+
: '';
|
|
532
473
|
section.TabCode = `${sectionIndex > 0 ? '\n' : ''} <!-- ${section.Name} Section -->
|
|
533
|
-
<mj-collapsible-panel
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
[
|
|
538
|
-
[
|
|
474
|
+
<mj-collapsible-panel field-panels
|
|
475
|
+
SectionKey="${sectionKey}"
|
|
476
|
+
SectionName="${section.Name}"
|
|
477
|
+
Icon="${icon}"${inheritedAttrs}
|
|
478
|
+
[Form]="this"
|
|
479
|
+
[FormContext]="formContext">
|
|
539
480
|
${indentedFormHTML}
|
|
540
481
|
</mj-collapsible-panel>`;
|
|
541
482
|
sectionIndex++;
|
|
542
483
|
}
|
|
543
|
-
if (section.Type !==
|
|
484
|
+
if (section.Type !== GeneratedFormSectionType.Top)
|
|
544
485
|
index++; // don't increment the tab index for TOP AREA, becuse it won't be rendered as a tab
|
|
545
486
|
}
|
|
546
487
|
return sections;
|
|
@@ -555,23 +496,29 @@ ${indentedFormHTML}
|
|
|
555
496
|
let html = '';
|
|
556
497
|
// figure out which fields will be in this section first
|
|
557
498
|
section.Fields = [];
|
|
558
|
-
const sortedFields =
|
|
499
|
+
const sortedFields = sortBySequenceAndCreatedAt(entity.Fields);
|
|
559
500
|
for (const field of sortedFields) {
|
|
560
501
|
if (field.IncludeInGeneratedForm) {
|
|
561
502
|
let bMatch = false;
|
|
562
|
-
if (field.GeneratedFormSectionType ===
|
|
503
|
+
if (field.GeneratedFormSectionType === GeneratedFormSectionType.Top && section.Type === GeneratedFormSectionType.Top) {
|
|
563
504
|
// match, include the field in the output
|
|
564
505
|
bMatch = true;
|
|
565
506
|
}
|
|
566
|
-
else if (field.GeneratedFormSectionType ===
|
|
507
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Category && field.Category && section.Name && field.Category.trim().toLowerCase() === section.Name.trim().toLowerCase()) {
|
|
567
508
|
// match, include the field in the output
|
|
568
509
|
bMatch = true;
|
|
569
510
|
}
|
|
570
|
-
else if (field.GeneratedFormSectionType ===
|
|
511
|
+
else if (field.GeneratedFormSectionType === GeneratedFormSectionType.Details && section.Type === GeneratedFormSectionType.Details) {
|
|
571
512
|
// match, include the field in the output
|
|
572
513
|
bMatch = true;
|
|
573
514
|
}
|
|
574
515
|
if (bMatch && field.Name.toLowerCase() !== 'id') {
|
|
516
|
+
// Skip virtual fields that are the name-field-map of an FK field.
|
|
517
|
+
// The FK field itself will display the name via RelatedEntityNameFieldMap
|
|
518
|
+
// at runtime, so emitting the virtual field would be redundant.
|
|
519
|
+
if (field.IsVirtual && this.isVirtualNameFieldForFK(entity, field)) {
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
575
522
|
section.Fields.push(field); // add the field to the section fields array
|
|
576
523
|
}
|
|
577
524
|
}
|
|
@@ -582,27 +529,27 @@ ${indentedFormHTML}
|
|
|
582
529
|
if (!field.ReadOnly) {
|
|
583
530
|
// first, check to see if we have a ValueListType != None, if so, generate a dropdown.
|
|
584
531
|
// If value list type is ListOrUserEntry, then generate a combobox, if ValueListType = List, then generate a dropdown
|
|
585
|
-
if (field.ValueListTypeEnum !==
|
|
532
|
+
if (field.ValueListTypeEnum !== EntityFieldValueListType.None) {
|
|
586
533
|
// build the possible values list
|
|
587
|
-
if (field.ValueListTypeEnum ===
|
|
588
|
-
//
|
|
589
|
-
editControl = `
|
|
534
|
+
if (field.ValueListTypeEnum === EntityFieldValueListType.ListOrUserEntry) {
|
|
535
|
+
// autocomplete (allows user-entered values)
|
|
536
|
+
editControl = `autocomplete`;
|
|
590
537
|
}
|
|
591
|
-
else if (field.ValueListTypeEnum ===
|
|
592
|
-
//
|
|
593
|
-
editControl = `
|
|
538
|
+
else if (field.ValueListTypeEnum === EntityFieldValueListType.List) {
|
|
539
|
+
// select (fixed list)
|
|
540
|
+
editControl = `select`;
|
|
594
541
|
}
|
|
595
542
|
}
|
|
596
543
|
else {
|
|
597
544
|
// no value list, generate a text box, checkbox, or date picker
|
|
598
|
-
if (field.TSType ===
|
|
545
|
+
if (field.TSType === EntityFieldTSType.Boolean)
|
|
599
546
|
editControl = `checkbox`;
|
|
600
|
-
else if (field.TSType ===
|
|
547
|
+
else if (field.TSType === EntityFieldTSType.Date)
|
|
601
548
|
editControl = `datepicker`;
|
|
602
|
-
else if (field.TSType ===
|
|
603
|
-
editControl = `
|
|
604
|
-
else if (field.TSType ===
|
|
605
|
-
if (field.Length < 0 || field.MaxLength >
|
|
549
|
+
else if (field.TSType === EntityFieldTSType.Number)
|
|
550
|
+
editControl = `number`;
|
|
551
|
+
else if (field.TSType === EntityFieldTSType.String) {
|
|
552
|
+
if (field.Length < 0 || field.MaxLength > 1000) // length < 0 means nvarchar(max) or similar, so use textarea; nvarchar(1000) and below get single-line textbox
|
|
606
553
|
editControl = `textarea`;
|
|
607
554
|
else
|
|
608
555
|
editControl = `textbox`;
|
|
@@ -613,10 +560,8 @@ ${indentedFormHTML}
|
|
|
613
560
|
editControl = 'code';
|
|
614
561
|
}
|
|
615
562
|
let linkType = null;
|
|
616
|
-
let linkComponentType = null;
|
|
617
563
|
if (field.RelatedEntity && field.RelatedEntity.length > 0) {
|
|
618
564
|
linkType = 'Record';
|
|
619
|
-
linkComponentType = `\n LinkComponentType="${field.RelatedEntityDisplayType}"`;
|
|
620
565
|
}
|
|
621
566
|
else if (field.ExtendedType && field.ExtendedType.length > 0) {
|
|
622
567
|
switch (field.ExtendedType.trim().toLowerCase()) {
|
|
@@ -630,17 +575,38 @@ ${indentedFormHTML}
|
|
|
630
575
|
}
|
|
631
576
|
// next, generate HTML for the field, use fillContainer if we have just one field
|
|
632
577
|
html += ` <mj-form-field ${section.Fields.length === 1 ? '' : ''}
|
|
633
|
-
[
|
|
578
|
+
[Record]="record"
|
|
634
579
|
[ShowLabel]="${section.Fields.length > 1 ? 'true' : 'false'}"
|
|
635
580
|
FieldName="${field.CodeName}"
|
|
636
581
|
Type="${editControl}"
|
|
637
582
|
[EditMode]="EditMode"
|
|
638
|
-
[
|
|
583
|
+
[FormContext]="formContext"${linkType ? `\n LinkType="${linkType}"` : ''}
|
|
639
584
|
></mj-form-field>
|
|
640
585
|
`;
|
|
641
586
|
}
|
|
642
587
|
return html;
|
|
643
588
|
}
|
|
589
|
+
/**
|
|
590
|
+
* Checks whether a virtual field is the name-field-map target of an FK field on the same entity.
|
|
591
|
+
* For example, if entity has `ParentID` (FK) with `RelatedEntityNameFieldMap = 'Parent'`,
|
|
592
|
+
* then the virtual `Parent` field is redundant on the form since the FK field will display
|
|
593
|
+
* the name at runtime.
|
|
594
|
+
*/
|
|
595
|
+
isVirtualNameFieldForFK(entity, virtualField) {
|
|
596
|
+
const vNameLower = virtualField.Name.toLowerCase();
|
|
597
|
+
return entity.Fields.some(f => {
|
|
598
|
+
if (f === virtualField || !f.RelatedEntity)
|
|
599
|
+
return false;
|
|
600
|
+
// 1. Exact match via RelatedEntityNameFieldMap
|
|
601
|
+
if (f.RelatedEntityNameFieldMap === virtualField.Name)
|
|
602
|
+
return true;
|
|
603
|
+
// 2. Case-insensitive convention: virtual "Foo" matches FK "FooID"
|
|
604
|
+
const fkLower = f.Name.toLowerCase();
|
|
605
|
+
if (fkLower === vNameLower + 'id')
|
|
606
|
+
return true;
|
|
607
|
+
return false;
|
|
608
|
+
});
|
|
609
|
+
}
|
|
644
610
|
/**
|
|
645
611
|
* Generates the tab name for a related entity tab. Appends the field's display name to the tab name
|
|
646
612
|
* if there are multiple tabs for the same related entity to differentiate them.
|
|
@@ -664,7 +630,7 @@ ${indentedFormHTML}
|
|
|
664
630
|
// if the fkeyField has wrapping [] then remove them
|
|
665
631
|
fkeyField = fkeyField.trim().replace('[', '').replace(']', '');
|
|
666
632
|
// let's get the actual entityInfo for the related entity so we can get the field and see if it has a display name
|
|
667
|
-
const md = new
|
|
633
|
+
const md = new Metadata();
|
|
668
634
|
const re = md.EntityByID(relatedEntity.RelatedEntityID);
|
|
669
635
|
const f = re.Fields.find(f => f.Name.trim().toLowerCase() === fkeyField.trim().toLowerCase());
|
|
670
636
|
if (f)
|
|
@@ -682,7 +648,7 @@ ${indentedFormHTML}
|
|
|
682
648
|
* @returns Promise resolving to array of related entity tab sections
|
|
683
649
|
*/
|
|
684
650
|
async generateRelatedEntityTabs(entity, startIndex, contextUser) {
|
|
685
|
-
const md = new
|
|
651
|
+
const md = new Metadata();
|
|
686
652
|
const tabs = [];
|
|
687
653
|
// Sort related entities by Sequence (user's explicit ordering), then by RelatedEntity name (stable tiebreaker)
|
|
688
654
|
const sortedRelatedEntities = entity.RelatedEntities
|
|
@@ -717,7 +683,7 @@ ${indentedFormHTML}
|
|
|
717
683
|
}
|
|
718
684
|
// Calculate section key before generation (may be replaced later if duplicate)
|
|
719
685
|
const sectionKey = this.camelCase(tabName);
|
|
720
|
-
const component = await
|
|
686
|
+
const component = await RelatedEntityDisplayComponentGeneratorBase.GetComponent(relatedEntity, contextUser);
|
|
721
687
|
const generateResults = await component.Generate({
|
|
722
688
|
Entity: entity,
|
|
723
689
|
RelationshipInfo: relatedEntity,
|
|
@@ -731,21 +697,23 @@ ${indentedFormHTML}
|
|
|
731
697
|
// Determine slot based on DisplayLocation
|
|
732
698
|
const slot = relatedEntity.DisplayLocation === 'Before Field Tabs' ? 'before-panels' : 'after-panels';
|
|
733
699
|
const tabCode = `${index > 0 ? '\n' : ''} <!-- ${tabName} Section -->
|
|
734
|
-
<mj-collapsible-panel
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
[
|
|
740
|
-
[
|
|
741
|
-
[
|
|
742
|
-
[
|
|
743
|
-
|
|
700
|
+
<mj-collapsible-panel ${slot}
|
|
701
|
+
SectionKey="${sectionKey}"
|
|
702
|
+
SectionName="${tabName}"
|
|
703
|
+
Icon="${iconClass}"
|
|
704
|
+
Variant="related-entity"
|
|
705
|
+
[Form]="this"
|
|
706
|
+
[FormContext]="formContext"
|
|
707
|
+
[BadgeCount]="GetSectionRowCount('${sectionKey}')"
|
|
708
|
+
[DefaultExpanded]="false">
|
|
709
|
+
@if (record.IsSaved) {
|
|
710
|
+
<div>
|
|
744
711
|
${componentCodeWithIndent}
|
|
745
712
|
</div>
|
|
713
|
+
}
|
|
746
714
|
</mj-collapsible-panel>`;
|
|
747
715
|
tabs.push({
|
|
748
|
-
Type:
|
|
716
|
+
Type: GeneratedFormSectionType.Category,
|
|
749
717
|
IsRelatedEntity: true,
|
|
750
718
|
RelatedEntityDisplayLocation: relatedEntity.DisplayLocation,
|
|
751
719
|
Name: tabName,
|
|
@@ -895,22 +863,10 @@ ${componentCodeWithIndent}
|
|
|
895
863
|
* @returns Promise resolving to an object containing the HTML code and section information
|
|
896
864
|
*/
|
|
897
865
|
async generateSingleEntityHTMLForAngular(entity, contextUser) {
|
|
898
|
-
// Load category icons from
|
|
899
|
-
|
|
900
|
-
const entitySettings = entity.Settings;
|
|
901
|
-
if (entitySettings) {
|
|
902
|
-
const iconSetting = entitySettings.find((s) => s.Name === 'FieldCategoryIcons');
|
|
903
|
-
if (iconSetting && iconSetting.Value) {
|
|
904
|
-
try {
|
|
905
|
-
categoryIcons = JSON.parse(iconSetting.Value);
|
|
906
|
-
}
|
|
907
|
-
catch (e) {
|
|
908
|
-
// Invalid JSON, ignore and fall back to keyword matching
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
}
|
|
866
|
+
// Load category metadata (icons + inheritance info) from the typed FieldCategories property
|
|
867
|
+
const fieldCategories = entity.FieldCategories;
|
|
912
868
|
const topArea = this.generateTopAreaHTMLForAngular(entity);
|
|
913
|
-
const additionalSections = this.generateAngularAdditionalSections(entity, 0,
|
|
869
|
+
const additionalSections = this.generateAngularAdditionalSections(entity, 0, fieldCategories);
|
|
914
870
|
// calc ending index for additional sections so we can pass taht into the related entity tabs because they need to start incrementally up from there...
|
|
915
871
|
const endingIndex = additionalSections && additionalSections.length ? (topArea && topArea.length > 0 ? additionalSections.length - 1 : additionalSections.length) : 0;
|
|
916
872
|
const relatedEntitySections = await this.generateRelatedEntityTabs(entity, endingIndex, contextUser);
|
|
@@ -926,7 +882,12 @@ ${componentCodeWithIndent}
|
|
|
926
882
|
* @returns Generated HTML with splitter layout
|
|
927
883
|
*/
|
|
928
884
|
generateSingleEntityHTMLWithSplitterForAngular(topArea, additionalSections, relatedEntitySections) {
|
|
929
|
-
const htmlCode = `<mj-record-form-container [
|
|
885
|
+
const htmlCode = `<mj-record-form-container [Record]="record" [FormComponent]="this"
|
|
886
|
+
(Navigate)="OnFormNavigate($event)"
|
|
887
|
+
(DeleteRequested)="OnDeleteRequested()"
|
|
888
|
+
(FavoriteToggled)="OnFavoriteToggled()"
|
|
889
|
+
(HistoryRequested)="OnHistoryRequested()"
|
|
890
|
+
(ListManagementRequested)="OnListManagementRequested()">
|
|
930
891
|
<kendo-splitter orientation="vertical" (layoutChange)="splitterLayoutChange()">
|
|
931
892
|
<kendo-splitter-pane [collapsible]="true" [size]="TopAreaHeight">
|
|
932
893
|
${this.innerTopAreaHTML(topArea)}
|
|
@@ -960,7 +921,7 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
960
921
|
*/
|
|
961
922
|
innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections) {
|
|
962
923
|
// Filter out Top sections as they're handled separately
|
|
963
|
-
const sectionsToRender = additionalSections.filter(s => s.Type !==
|
|
924
|
+
const sectionsToRender = additionalSections.filter(s => s.Type !== GeneratedFormSectionType.Top);
|
|
964
925
|
// Order: before-panels, field-panels, after-panels
|
|
965
926
|
// The RecordFormContainer handles the related-entity-grid wrapper via named slots
|
|
966
927
|
const beforePanels = relatedEntitySections.filter(s => s.RelatedEntityDisplayLocation === 'Before Field Tabs');
|
|
@@ -1003,7 +964,7 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
1003
964
|
const relatedEntityAfterFieldTabs = relatedEntitySections.filter(s => s.RelatedEntityDisplayLocation === 'After Field Tabs');
|
|
1004
965
|
return ` <mj-tabstrip (TabSelected)="onTabSelect($event)" (ResizeContainer)="InvokeManualResize()">
|
|
1005
966
|
${relatedEntityBeforeFieldTabs ? relatedEntityBeforeFieldTabs.map(s => s.TabCode).join('\n') : ''}
|
|
1006
|
-
${additionalSections ? additionalSections.filter(s => s.Type !==
|
|
967
|
+
${additionalSections ? additionalSections.filter(s => s.Type !== GeneratedFormSectionType.Top).map(s => s.TabCode).join('\n ') : ''}
|
|
1007
968
|
${relatedEntityAfterFieldTabs ? relatedEntityAfterFieldTabs.map(s => s.TabCode).join('\n') : ''}
|
|
1008
969
|
</mj-tabstrip>`;
|
|
1009
970
|
}
|
|
@@ -1015,7 +976,12 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
1015
976
|
* @returns Generated HTML without splitter layout
|
|
1016
977
|
*/
|
|
1017
978
|
generateSingleEntityHTMLWithOUTSplitterForAngular(topArea, additionalSections, relatedEntitySections) {
|
|
1018
|
-
const htmlCode = `<mj-record-form-container [
|
|
979
|
+
const htmlCode = `<mj-record-form-container [Record]="record" [FormComponent]="this"
|
|
980
|
+
(Navigate)="OnFormNavigate($event)"
|
|
981
|
+
(DeleteRequested)="OnDeleteRequested()"
|
|
982
|
+
(FavoriteToggled)="OnFavoriteToggled()"
|
|
983
|
+
(HistoryRequested)="OnHistoryRequested()"
|
|
984
|
+
(ListManagementRequested)="OnListManagementRequested()">
|
|
1019
985
|
${this.innerTopAreaHTML(topArea)}
|
|
1020
986
|
${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
1021
987
|
</mj-record-form-container>
|
|
@@ -1023,5 +989,4 @@ ${this.innerCollapsiblePanelsHTML(additionalSections, relatedEntitySections)}
|
|
|
1023
989
|
return htmlCode;
|
|
1024
990
|
}
|
|
1025
991
|
}
|
|
1026
|
-
exports.AngularClientGeneratorBase = AngularClientGeneratorBase;
|
|
1027
992
|
//# sourceMappingURL=angular-codegen.js.map
|