@memberjunction/ng-entity-relationship-diagram 5.4.0 → 5.5.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/dist/lib/components/entity-details/entity-details.component.d.ts.map +1 -1
- package/dist/lib/components/entity-details/entity-details.component.js +7 -6
- package/dist/lib/components/entity-details/entity-details.component.js.map +1 -1
- package/dist/lib/components/erd-composite/erd-composite.component.d.ts.map +1 -1
- package/dist/lib/components/erd-composite/erd-composite.component.js +3 -2
- package/dist/lib/components/erd-composite/erd-composite.component.js.map +1 -1
- package/dist/lib/utils/entity-to-erd-adapter.d.ts.map +1 -1
- package/dist/lib/utils/entity-to-erd-adapter.js +8 -7
- package/dist/lib/utils/entity-to-erd-adapter.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-details.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/entity-details/entity-details.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAa,UAAU,EAAE,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,oBAAoB,EAAY,MAAM,sBAAsB,CAAC;;
|
|
1
|
+
{"version":3,"file":"entity-details.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/entity-details/entity-details.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,SAAS,EAAE,aAAa,EAAa,UAAU,EAAE,MAAM,eAAe,CAAC;AACxH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,oBAAoB,EAAY,MAAM,sBAAsB,CAAC;;AAGnG;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBAMa,sBAAuB,YAAW,SAAS;IACD,mBAAmB,EAAG,UAAU,CAAC;IAC1B,0BAA0B,EAAG,UAAU,CAAC;IAEpG,2DAA2D;IAClD,cAAc,EAAE,UAAU,GAAG,IAAI,CAAQ;IAElD,yDAAyD;IAChD,eAAe,EAAE,eAAe,EAAE,CAAM;IAEjD,6CAA6C;IACpC,qBAAqB,UAAQ;IAEtC,oDAAoD;IAC3C,4BAA4B,UAAQ;IAE7C,yDAAyD;IAC/C,UAAU,2BAAkC;IAEtD,gDAAgD;IACtC,UAAU,qBAA4B;IAEhD,6CAA6C;IACnC,mBAAmB,qBAA4B;IAEzD,oDAAoD;IAC1C,0BAA0B,qBAA4B;IAEhE,gFAAgF;IACtE,cAAc,2BAAkC;IAE1D,uDAAuD;IAC7C,UAAU,6CAAoD;IAEjE,WAAW,EAAE,KAAK,GAAG,MAAM,GAAG,cAAc,GAAG,SAAS,CAAS;IACjE,yBAAyB,cAAqB;IAC9C,mBAAmB,cAAqB;IACxC,oBAAoB,cAAqB;IAChD,OAAO,CAAC,wBAAwB,CAAuB;IAEvD,WAAW,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAczC,OAAO,CAAC,oBAAoB;IAYrB,YAAY,IAAI,IAAI;IASpB,YAAY,IAAI,IAAI;IAIpB,mBAAmB,IAAI,IAAI;IAI3B,0BAA0B,IAAI,IAAI;IAIlC,cAAc,CAAC,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,cAAc,GAAG,SAAS,GAAG,IAAI;IAIzE,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE;IAiBpD,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IA+BlD,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAI1C,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQ7C,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQxC,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQzC,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIpD,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI/C,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIhD,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO;IAIvD,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM,EAAE;IAKxD,0BAA0B,CAAC,KAAK,EAAE,eAAe,GAAG,oBAAoB,EAAE;IAU1E,oBAAoB,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI;IAYhE,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,OAAe,GAAG,IAAI;yCAlN5D,sBAAsB;2CAAtB,sBAAsB;CAqNlC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
|
|
2
2
|
import { Metadata } from '@memberjunction/core';
|
|
3
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
3
4
|
import * as i0 from "@angular/core";
|
|
4
5
|
const _c0 = ["fieldsListContainer"];
|
|
5
6
|
const _c1 = ["relationshipsListContainer"];
|
|
@@ -477,7 +478,7 @@ export class EntityDetailsComponent {
|
|
|
477
478
|
getEntityFields(entityId) {
|
|
478
479
|
if (!entityId)
|
|
479
480
|
return [];
|
|
480
|
-
let fields = this.allEntityFields.filter(f => f.EntityID
|
|
481
|
+
let fields = this.allEntityFields.filter(f => UUIDsEqual(f.EntityID, entityId));
|
|
481
482
|
switch (this.fieldFilter) {
|
|
482
483
|
case 'keys':
|
|
483
484
|
return fields.filter(f => f.IsPrimaryKey || f.Name.toLowerCase().includes('id'));
|
|
@@ -495,11 +496,11 @@ export class EntityDetailsComponent {
|
|
|
495
496
|
const relatedEntityIds = new Set();
|
|
496
497
|
// Get entities that this entity references (foreign keys)
|
|
497
498
|
this.allEntityFields
|
|
498
|
-
.filter(f => f.EntityID
|
|
499
|
+
.filter(f => UUIDsEqual(f.EntityID, entityId) && f.RelatedEntityID)
|
|
499
500
|
.forEach(f => relatedEntityIds.add(f.RelatedEntityID));
|
|
500
501
|
// Get entities that reference this entity
|
|
501
502
|
this.allEntityFields
|
|
502
|
-
.filter(f => f.RelatedEntityID
|
|
503
|
+
.filter(f => UUIDsEqual(f.RelatedEntityID, entityId))
|
|
503
504
|
.forEach(f => relatedEntityIds.add(f.EntityID));
|
|
504
505
|
// Remove the current entity from the set (don't return self-references)
|
|
505
506
|
relatedEntityIds.delete(entityId);
|
|
@@ -508,7 +509,7 @@ export class EntityDetailsComponent {
|
|
|
508
509
|
const allEntities = md.Entities;
|
|
509
510
|
const retVals = [];
|
|
510
511
|
relatedEntityIds.forEach(id => {
|
|
511
|
-
const entity = allEntities.find(e => e.ID
|
|
512
|
+
const entity = allEntities.find(e => UUIDsEqual(e.ID, id));
|
|
512
513
|
if (entity) {
|
|
513
514
|
retVals.push(entity);
|
|
514
515
|
}
|
|
@@ -574,7 +575,7 @@ export class EntityDetailsComponent {
|
|
|
574
575
|
if (field.RelatedEntityID) {
|
|
575
576
|
// Find the related entity and select it in the ERD
|
|
576
577
|
const md = new Metadata();
|
|
577
|
-
const relatedEntity = md.Entities.find(e => e.ID
|
|
578
|
+
const relatedEntity = md.Entities.find(e => UUIDsEqual(e.ID, field.RelatedEntityID));
|
|
578
579
|
if (relatedEntity) {
|
|
579
580
|
this.entitySelected.emit(relatedEntity);
|
|
580
581
|
}
|
|
@@ -698,5 +699,5 @@ export class EntityDetailsComponent {
|
|
|
698
699
|
}], openRecord: [{
|
|
699
700
|
type: Output
|
|
700
701
|
}] }); })();
|
|
701
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityDetailsComponent, { className: "EntityDetailsComponent", filePath: "src/lib/components/entity-details/entity-details.component.ts", lineNumber:
|
|
702
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(EntityDetailsComponent, { className: "EntityDetailsComponent", filePath: "src/lib/components/entity-details/entity-details.component.ts", lineNumber: 26 }); })();
|
|
702
703
|
//# sourceMappingURL=entity-details.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-details.component.js","sourceRoot":"","sources":["../../../../src/lib/components/entity-details/entity-details.component.ts","../../../../src/lib/components/entity-details/entity-details.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, ViewChild, ElementRef } from '@angular/core';\nimport { EntityInfo, EntityFieldInfo, EntityFieldValueInfo, Metadata } from '@memberjunction/core';\n\n/**\n * Event emitted when requesting to open an entity record.\n */\nexport interface EntityDetailsOpenRecordEvent {\n EntityName: string;\n RecordID: string;\n}\n\n/**\n * Entity details panel component that displays detailed information about a selected entity.\n * Shows entity metadata, fields with filtering capabilities, and related entities.\n *\n * This component is designed to be used alongside the ERD diagram to provide\n * a detailed view of the currently selected entity.\n */\n@Component({\n standalone: false,\n selector: 'mj-entity-details',\n templateUrl: './entity-details.component.html',\n styleUrls: ['./entity-details.component.css']\n})\nexport class EntityDetailsComponent implements OnChanges {\n @ViewChild('fieldsListContainer', { static: false }) fieldsListContainer!: ElementRef;\n @ViewChild('relationshipsListContainer', { static: false }) relationshipsListContainer!: ElementRef;\n\n /** The currently selected entity to display details for */\n @Input() selectedEntity: EntityInfo | null = null;\n\n /** All entity fields for looking up field information */\n @Input() allEntityFields: EntityFieldInfo[] = [];\n\n /** Whether the fields section is expanded */\n @Input() fieldsSectionExpanded = true;\n\n /** Whether the relationships section is expanded */\n @Input() relationshipsSectionExpanded = true;\n\n /** Emitted when user clicks to open the entity record */\n @Output() openEntity = new EventEmitter<EntityInfo>();\n\n /** Emitted when user clicks the close button */\n @Output() closePanel = new EventEmitter<void>();\n\n /** Emitted when fields section is toggled */\n @Output() fieldsSectionToggle = new EventEmitter<void>();\n\n /** Emitted when relationships section is toggled */\n @Output() relationshipsSectionToggle = new EventEmitter<void>();\n\n /** Emitted when a related entity is selected (clicked in relationships list) */\n @Output() entitySelected = new EventEmitter<EntityInfo>();\n\n /** Emitted when requesting to open an entity record */\n @Output() openRecord = new EventEmitter<EntityDetailsOpenRecordEvent>();\n\n public fieldFilter: 'all' | 'keys' | 'foreign_keys' | 'regular' = 'all';\n public expandedFieldDescriptions = new Set<string>();\n public expandedFieldValues = new Set<string>();\n public expandedFieldDetails = new Set<string>();\n private previousSelectedEntityId: string | null = null;\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['selectedEntity'] && !changes['selectedEntity'].firstChange) {\n const currentEntityId = this.selectedEntity?.ID || null;\n\n // Check if entity actually changed\n if (currentEntityId !== this.previousSelectedEntityId) {\n // Reset scroll positions when entity changes\n this.resetScrollPositions();\n\n this.previousSelectedEntityId = currentEntityId;\n }\n }\n }\n\n private resetScrollPositions(): void {\n // Use setTimeout to ensure the DOM is updated\n setTimeout(() => {\n if (this.fieldsListContainer?.nativeElement) {\n this.fieldsListContainer.nativeElement.scrollTop = 0;\n }\n if (this.relationshipsListContainer?.nativeElement) {\n this.relationshipsListContainer.nativeElement.scrollTop = 0;\n }\n }, 0);\n }\n\n public onOpenEntity(): void {\n if (this.selectedEntity) {\n this.openRecord.emit({\n EntityName: 'MJ: Entities',\n RecordID: this.selectedEntity.ID\n });\n }\n }\n\n public onClosePanel(): void {\n this.closePanel.emit();\n }\n\n public toggleFieldsSection(): void {\n this.fieldsSectionToggle.emit();\n }\n\n public toggleRelationshipsSection(): void {\n this.relationshipsSectionToggle.emit();\n }\n\n public setFieldFilter(filter: 'all' | 'keys' | 'foreign_keys' | 'regular'): void {\n this.fieldFilter = filter;\n }\n\n public getEntityFields(entityId: string): EntityFieldInfo[] {\n if (!entityId) return [];\n\n let fields = this.allEntityFields.filter(f => f.EntityID === entityId);\n\n switch (this.fieldFilter) {\n case 'keys':\n return fields.filter(f => f.IsPrimaryKey || f.Name.toLowerCase().includes('id'));\n case 'foreign_keys':\n return fields.filter(f => f.RelatedEntityID && !f.IsPrimaryKey);\n case 'regular':\n return fields.filter(f => !f.IsPrimaryKey && !f.RelatedEntityID);\n default:\n return fields;\n }\n }\n\n public getRelatedEntities(entityId: string): EntityInfo[] {\n if (!entityId) return [];\n\n const relatedEntityIds = new Set<string>();\n\n // Get entities that this entity references (foreign keys)\n this.allEntityFields\n .filter(f => f.EntityID === entityId && f.RelatedEntityID)\n .forEach(f => relatedEntityIds.add(f.RelatedEntityID!));\n\n // Get entities that reference this entity\n this.allEntityFields\n .filter(f => f.RelatedEntityID === entityId)\n .forEach(f => relatedEntityIds.add(f.EntityID));\n\n // Remove the current entity from the set (don't return self-references)\n relatedEntityIds.delete(entityId);\n\n // Convert to actual EntityInfo objects\n const md = new Metadata();\n const allEntities = md.Entities;\n const retVals: EntityInfo[] = [];\n relatedEntityIds.forEach(id => {\n const entity = allEntities.find(e => e.ID === id);\n if (entity) {\n retVals.push(entity);\n }\n });\n return retVals;\n }\n\n public onFieldClick(field: EntityFieldInfo): void {\n this.toggleFieldDetails(field.ID);\n }\n\n public toggleFieldDescription(fieldId: string): void {\n if (this.expandedFieldDescriptions.has(fieldId)) {\n this.expandedFieldDescriptions.delete(fieldId);\n } else {\n this.expandedFieldDescriptions.add(fieldId);\n }\n }\n\n public toggleFieldValues(fieldId: string): void {\n if (this.expandedFieldValues.has(fieldId)) {\n this.expandedFieldValues.delete(fieldId);\n } else {\n this.expandedFieldValues.add(fieldId);\n }\n }\n\n public toggleFieldDetails(fieldId: string): void {\n if (this.expandedFieldDetails.has(fieldId)) {\n this.expandedFieldDetails.delete(fieldId);\n } else {\n this.expandedFieldDetails.add(fieldId);\n }\n }\n\n public isFieldDescriptionExpanded(fieldId: string): boolean {\n return this.expandedFieldDescriptions.has(fieldId);\n }\n\n public isFieldValuesExpanded(fieldId: string): boolean {\n return this.expandedFieldValues.has(fieldId);\n }\n\n public isFieldDetailsExpanded(fieldId: string): boolean {\n return this.expandedFieldDetails.has(fieldId);\n }\n\n public hasFieldPossibleValues(field: EntityFieldInfo): boolean {\n return field.EntityFieldValues && field.EntityFieldValues.length > 0;\n }\n\n public getFieldPossibleValues(field: EntityFieldInfo): string[] {\n if (!field.EntityFieldValues) return [];\n return field.EntityFieldValues.map(v => v.Value).slice(0, 10);\n }\n\n public getSortedEntityFieldValues(field: EntityFieldInfo): EntityFieldValueInfo[] {\n if (!field.EntityFieldValues) return [];\n return field.EntityFieldValues.sort((a, b) => {\n if (a.Sequence !== undefined && b.Sequence !== undefined) {\n return a.Sequence - b.Sequence;\n }\n return a.Value.localeCompare(b.Value);\n });\n }\n\n public onRelatedEntityClick(event: Event, field: EntityFieldInfo): void {\n event.stopPropagation();\n if (field.RelatedEntityID) {\n // Find the related entity and select it in the ERD\n const md = new Metadata();\n const relatedEntity = md.Entities.find(e => e.ID === field.RelatedEntityID);\n if (relatedEntity) {\n this.entitySelected.emit(relatedEntity);\n }\n }\n }\n\n public selectEntity(entity: EntityInfo, _zoomTo: boolean = false): void {\n this.entitySelected.emit(entity);\n }\n}\n","<div class=\"details-panel\">\n <div class=\"panel-header\">\n <h3>{{ selectedEntity?.Name || selectedEntity?.SchemaName }}</h3>\n <div class=\"panel-actions\">\n <button class=\"open-btn\" (click)=\"onOpenEntity()\" title=\"Open Entity Record\">\n <span class=\"fa-solid fa-external-link-alt\"></span>\n Open\n </button>\n <button class=\"close-btn\" (click)=\"onClosePanel()\">\n <span class=\"fa-solid fa-times\"></span>\n </button>\n </div>\n </div>\n\n <div class=\"entity-info\">\n <div class=\"info-row\">\n <label>Schema:</label>\n <span>{{ selectedEntity?.SchemaName }}</span>\n </div>\n <div class=\"info-row\">\n <label>Table:</label>\n <span>{{ selectedEntity?.BaseTable }}</span>\n </div>\n @if (selectedEntity?.Description) {\n <div class=\"info-row\">\n <label>Description:</label>\n <span>{{ selectedEntity?.Description }}</span>\n </div>\n }\n </div>\n\n <div class=\"fields-section\" [class.section-expanded]=\"fieldsSectionExpanded\" [class.section-collapsed]=\"!fieldsSectionExpanded\">\n <div class=\"section-header extra-padding\">\n <div class=\"section-title-group\" (click)=\"toggleFieldsSection()\">\n <button\n class=\"expand-btn\"\n [class.expanded]=\"fieldsSectionExpanded\"\n title=\"Toggle fields section\">\n <span class=\"fa-solid fa-chevron-right\"></span>\n </button>\n <h4>Fields ({{ selectedEntity ? getEntityFields(selectedEntity.ID).length : 0 }})</h4>\n </div>\n @if (fieldsSectionExpanded) {\n <div class=\"field-filters\">\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'all'\"\n (click)=\"setFieldFilter('all')\"\n title=\"Show all fields\">\n All\n </button>\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'keys'\"\n (click)=\"setFieldFilter('keys')\"\n title=\"Show primary keys and ID fields\">\n Keys\n </button>\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'foreign_keys'\"\n (click)=\"setFieldFilter('foreign_keys')\"\n title=\"Show foreign key fields only\">\n FKs\n </button>\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'regular'\"\n (click)=\"setFieldFilter('regular')\"\n title=\"Show regular data fields\">\n Regular\n </button>\n </div>\n }\n </div>\n @if (fieldsSectionExpanded) {\n <div class=\"fields-list\" #fieldsListContainer>\n @for (field of getEntityFields(selectedEntity?.ID || ''); track field.ID) {\n <div class=\"field-item\"\n [class]=\"{\n 'primary-key-field': field.IsPrimaryKey,\n 'foreign-key-field': field.RelatedEntityID && !field.IsPrimaryKey,\n 'regular-field': !field.IsPrimaryKey && !field.RelatedEntityID,\n 'field-expanded': isFieldDetailsExpanded(field.ID)\n }\"\n (click)=\"onFieldClick(field)\"\n [title]=\"'Click to see more details about ' + field.Name\">\n <div class=\"field-header\">\n <div class=\"field-name\">\n @if (field.IsPrimaryKey) {\n <span class=\"field-icon fa-solid fa-key\" title=\"Primary Key\"></span>\n } @else if (field.RelatedEntityID) {\n <span class=\"field-icon fa-solid fa-link\" title=\"Foreign Key\"></span>\n }\n {{ field.Name }}\n </div>\n <div class=\"field-actions\">\n @if (hasFieldPossibleValues(field)) {\n <button\n class=\"field-values-btn\"\n (click)=\"toggleFieldValues(field.ID)\"\n [class.expanded]=\"isFieldValuesExpanded(field.ID)\"\n title=\"Toggle possible values\">\n <span class=\"fa-solid fa-list\"></span>\n </button>\n }\n @if (field.Description) {\n <button\n class=\"field-info-btn\"\n (click)=\"toggleFieldDescription(field.ID)\"\n [class.expanded]=\"isFieldDescriptionExpanded(field.ID)\"\n title=\"Toggle field description\">\n <span class=\"fa-solid fa-info-circle\"></span>\n </button>\n }\n <div class=\"field-badges\">\n @if (field.IsPrimaryKey) {\n <span class=\"field-badge primary-key\">PK</span>\n }\n @if (field.RelatedEntityID && !field.IsPrimaryKey) {\n <span class=\"field-badge foreign-key\">FK</span>\n }\n </div>\n </div>\n </div>\n <div class=\"field-details\">\n <div class=\"field-type\">{{ field.Type }}</div>\n @if (field.RelatedEntityID) {\n <div class=\"field-relation\">\n <span class=\"fa-solid fa-arrow-right\"></span>\n Related Entity\n </div>\n }\n </div>\n @if (field.Description && isFieldDescriptionExpanded(field.ID)) {\n <div class=\"field-description\">\n <div class=\"description-text\">{{ field.Description }}</div>\n </div>\n }\n @if (hasFieldPossibleValues(field) && isFieldValuesExpanded(field.ID)) {\n <div class=\"field-values\">\n <div class=\"values-header\">Possible Values:</div>\n <div class=\"values-list\">\n @for (value of getFieldPossibleValues(field); track value; let i = $index) {\n <span class=\"value-tag\">{{ value }}</span>\n }\n </div>\n </div>\n }\n @if (isFieldDetailsExpanded(field.ID)) {\n <div class=\"field-comprehensive-details\">\n @if (field.Description) {\n <div class=\"comprehensive-description-top\">\n <div class=\"description-content\">{{ field.Description }}</div>\n </div>\n }\n @if (field.EntityFieldValues && field.EntityFieldValues.length > 0) {\n <div class=\"field-values-comprehensive\">\n <div class=\"values-header\">\n <span class=\"fa-solid fa-list\"></span>\n Possible Values ({{ field.EntityFieldValues.length }})\n </div>\n <div class=\"values-grid\">\n @for (fieldValue of getSortedEntityFieldValues(field); track fieldValue.ID) {\n <div class=\"value-item\">\n <div class=\"value-main\">\n <span class=\"value-text\">{{ fieldValue.Value }}</span>\n @if (fieldValue.Code && fieldValue.Code !== fieldValue.Value) {\n <span class=\"value-code\">[{{ fieldValue.Code }}]</span>\n }\n </div>\n @if (fieldValue.Description && fieldValue.Description !== fieldValue.Value) {\n <div class=\"value-description\">{{ fieldValue.Description }}</div>\n }\n </div>\n }\n </div>\n </div>\n }\n <div class=\"details-grid\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Data Type:</span>\n <span class=\"detail-value\">{{ field.Type }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Nullable:</span>\n <span class=\"detail-value\" [class]=\"field.AllowsNull ? 'nullable-yes' : 'nullable-no'\">\n {{ field.AllowsNull ? 'Yes' : 'No' }}\n </span>\n </div>\n @if (field.Length && field.Length > 0) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Max Length:</span>\n <span class=\"detail-value\">{{ field.Length }}</span>\n </div>\n }\n @if (field.Precision && field.Precision > 0) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Precision:</span>\n <span class=\"detail-value\">{{ field.Precision }}</span>\n </div>\n }\n @if (field.Scale && field.Scale > 0) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Scale:</span>\n <span class=\"detail-value\">{{ field.Scale }}</span>\n </div>\n }\n <div class=\"detail-row\">\n <span class=\"detail-label\">Virtual Field:</span>\n <span class=\"detail-value\" [class]=\"field.IsVirtual ? 'virtual-yes' : 'virtual-no'\">\n {{ field.IsVirtual ? 'Yes' : 'No' }}\n </span>\n </div>\n @if (field.DefaultValue) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Default Value:</span>\n <span class=\"detail-value default-value\">{{ field.DefaultValue }}</span>\n </div>\n }\n @if (field.RelatedEntityID) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Related Entity:</span>\n <span class=\"detail-value related-entity\"\n (click)=\"onRelatedEntityClick($event, field)\"\n title=\"Click to select {{ field.RelatedEntity }} in ERD\">\n {{ field.RelatedEntity }}\n <span class=\"fa-solid fa-arrow-right\"></span>\n </span>\n </div>\n }\n @if (field.AutoIncrement) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Auto Increment:</span>\n <span class=\"detail-value auto-increment\">Yes</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"relationships-section\" [class.section-expanded]=\"relationshipsSectionExpanded\" [class.section-collapsed]=\"!relationshipsSectionExpanded\">\n <div class=\"section-header extra-padding\">\n <div class=\"section-title-group\" (click)=\"toggleRelationshipsSection()\">\n <button\n class=\"expand-btn\"\n [class.expanded]=\"relationshipsSectionExpanded\"\n title=\"Toggle relationships section\">\n <span class=\"fa-solid fa-chevron-right\"></span>\n </button>\n <h4>Related Entities ({{ selectedEntity ? getRelatedEntities(selectedEntity.ID).length : 0 }})</h4>\n </div>\n </div>\n @if (relationshipsSectionExpanded) {\n <div class=\"related-entities-list\" #relationshipsListContainer>\n @for (relatedEntity of getRelatedEntities(selectedEntity?.ID || ''); track relatedEntity.ID) {\n <div class=\"related-entity-item\" (click)=\"selectEntity(relatedEntity, true)\">\n <span class=\"fa-solid fa-database\"></span>\n <span class=\"entity-name\">{{ relatedEntity.Name || relatedEntity.SchemaName }}</span>\n <span class=\"zoom-indicator fa-solid fa-search-plus\" title=\"Click to zoom to entity\"></span>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"entity-details.component.js","sourceRoot":"","sources":["../../../../src/lib/components/entity-details/entity-details.component.ts","../../../../src/lib/components/entity-details/entity-details.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges, ViewChild, ElementRef } from '@angular/core';\nimport { EntityInfo, EntityFieldInfo, EntityFieldValueInfo, Metadata } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Event emitted when requesting to open an entity record.\n */\nexport interface EntityDetailsOpenRecordEvent {\n EntityName: string;\n RecordID: string;\n}\n\n/**\n * Entity details panel component that displays detailed information about a selected entity.\n * Shows entity metadata, fields with filtering capabilities, and related entities.\n *\n * This component is designed to be used alongside the ERD diagram to provide\n * a detailed view of the currently selected entity.\n */\n@Component({\n standalone: false,\n selector: 'mj-entity-details',\n templateUrl: './entity-details.component.html',\n styleUrls: ['./entity-details.component.css']\n})\nexport class EntityDetailsComponent implements OnChanges {\n @ViewChild('fieldsListContainer', { static: false }) fieldsListContainer!: ElementRef;\n @ViewChild('relationshipsListContainer', { static: false }) relationshipsListContainer!: ElementRef;\n\n /** The currently selected entity to display details for */\n @Input() selectedEntity: EntityInfo | null = null;\n\n /** All entity fields for looking up field information */\n @Input() allEntityFields: EntityFieldInfo[] = [];\n\n /** Whether the fields section is expanded */\n @Input() fieldsSectionExpanded = true;\n\n /** Whether the relationships section is expanded */\n @Input() relationshipsSectionExpanded = true;\n\n /** Emitted when user clicks to open the entity record */\n @Output() openEntity = new EventEmitter<EntityInfo>();\n\n /** Emitted when user clicks the close button */\n @Output() closePanel = new EventEmitter<void>();\n\n /** Emitted when fields section is toggled */\n @Output() fieldsSectionToggle = new EventEmitter<void>();\n\n /** Emitted when relationships section is toggled */\n @Output() relationshipsSectionToggle = new EventEmitter<void>();\n\n /** Emitted when a related entity is selected (clicked in relationships list) */\n @Output() entitySelected = new EventEmitter<EntityInfo>();\n\n /** Emitted when requesting to open an entity record */\n @Output() openRecord = new EventEmitter<EntityDetailsOpenRecordEvent>();\n\n public fieldFilter: 'all' | 'keys' | 'foreign_keys' | 'regular' = 'all';\n public expandedFieldDescriptions = new Set<string>();\n public expandedFieldValues = new Set<string>();\n public expandedFieldDetails = new Set<string>();\n private previousSelectedEntityId: string | null = null;\n\n ngOnChanges(changes: SimpleChanges): void {\n if (changes['selectedEntity'] && !changes['selectedEntity'].firstChange) {\n const currentEntityId = this.selectedEntity?.ID || null;\n\n // Check if entity actually changed\n if (currentEntityId !== this.previousSelectedEntityId) {\n // Reset scroll positions when entity changes\n this.resetScrollPositions();\n\n this.previousSelectedEntityId = currentEntityId;\n }\n }\n }\n\n private resetScrollPositions(): void {\n // Use setTimeout to ensure the DOM is updated\n setTimeout(() => {\n if (this.fieldsListContainer?.nativeElement) {\n this.fieldsListContainer.nativeElement.scrollTop = 0;\n }\n if (this.relationshipsListContainer?.nativeElement) {\n this.relationshipsListContainer.nativeElement.scrollTop = 0;\n }\n }, 0);\n }\n\n public onOpenEntity(): void {\n if (this.selectedEntity) {\n this.openRecord.emit({\n EntityName: 'MJ: Entities',\n RecordID: this.selectedEntity.ID\n });\n }\n }\n\n public onClosePanel(): void {\n this.closePanel.emit();\n }\n\n public toggleFieldsSection(): void {\n this.fieldsSectionToggle.emit();\n }\n\n public toggleRelationshipsSection(): void {\n this.relationshipsSectionToggle.emit();\n }\n\n public setFieldFilter(filter: 'all' | 'keys' | 'foreign_keys' | 'regular'): void {\n this.fieldFilter = filter;\n }\n\n public getEntityFields(entityId: string): EntityFieldInfo[] {\n if (!entityId) return [];\n\n let fields = this.allEntityFields.filter(f => UUIDsEqual(f.EntityID, entityId));\n\n switch (this.fieldFilter) {\n case 'keys':\n return fields.filter(f => f.IsPrimaryKey || f.Name.toLowerCase().includes('id'));\n case 'foreign_keys':\n return fields.filter(f => f.RelatedEntityID && !f.IsPrimaryKey);\n case 'regular':\n return fields.filter(f => !f.IsPrimaryKey && !f.RelatedEntityID);\n default:\n return fields;\n }\n }\n\n public getRelatedEntities(entityId: string): EntityInfo[] {\n if (!entityId) return [];\n\n const relatedEntityIds = new Set<string>();\n\n // Get entities that this entity references (foreign keys)\n this.allEntityFields\n .filter(f => UUIDsEqual(f.EntityID, entityId) && f.RelatedEntityID)\n .forEach(f => relatedEntityIds.add(f.RelatedEntityID!));\n\n // Get entities that reference this entity\n this.allEntityFields\n .filter(f => UUIDsEqual(f.RelatedEntityID, entityId))\n .forEach(f => relatedEntityIds.add(f.EntityID));\n\n // Remove the current entity from the set (don't return self-references)\n relatedEntityIds.delete(entityId);\n\n // Convert to actual EntityInfo objects\n const md = new Metadata();\n const allEntities = md.Entities;\n const retVals: EntityInfo[] = [];\n relatedEntityIds.forEach(id => {\n const entity = allEntities.find(e => UUIDsEqual(e.ID, id));\n if (entity) {\n retVals.push(entity);\n }\n });\n return retVals;\n }\n\n public onFieldClick(field: EntityFieldInfo): void {\n this.toggleFieldDetails(field.ID);\n }\n\n public toggleFieldDescription(fieldId: string): void {\n if (this.expandedFieldDescriptions.has(fieldId)) {\n this.expandedFieldDescriptions.delete(fieldId);\n } else {\n this.expandedFieldDescriptions.add(fieldId);\n }\n }\n\n public toggleFieldValues(fieldId: string): void {\n if (this.expandedFieldValues.has(fieldId)) {\n this.expandedFieldValues.delete(fieldId);\n } else {\n this.expandedFieldValues.add(fieldId);\n }\n }\n\n public toggleFieldDetails(fieldId: string): void {\n if (this.expandedFieldDetails.has(fieldId)) {\n this.expandedFieldDetails.delete(fieldId);\n } else {\n this.expandedFieldDetails.add(fieldId);\n }\n }\n\n public isFieldDescriptionExpanded(fieldId: string): boolean {\n return this.expandedFieldDescriptions.has(fieldId);\n }\n\n public isFieldValuesExpanded(fieldId: string): boolean {\n return this.expandedFieldValues.has(fieldId);\n }\n\n public isFieldDetailsExpanded(fieldId: string): boolean {\n return this.expandedFieldDetails.has(fieldId);\n }\n\n public hasFieldPossibleValues(field: EntityFieldInfo): boolean {\n return field.EntityFieldValues && field.EntityFieldValues.length > 0;\n }\n\n public getFieldPossibleValues(field: EntityFieldInfo): string[] {\n if (!field.EntityFieldValues) return [];\n return field.EntityFieldValues.map(v => v.Value).slice(0, 10);\n }\n\n public getSortedEntityFieldValues(field: EntityFieldInfo): EntityFieldValueInfo[] {\n if (!field.EntityFieldValues) return [];\n return field.EntityFieldValues.sort((a, b) => {\n if (a.Sequence !== undefined && b.Sequence !== undefined) {\n return a.Sequence - b.Sequence;\n }\n return a.Value.localeCompare(b.Value);\n });\n }\n\n public onRelatedEntityClick(event: Event, field: EntityFieldInfo): void {\n event.stopPropagation();\n if (field.RelatedEntityID) {\n // Find the related entity and select it in the ERD\n const md = new Metadata();\n const relatedEntity = md.Entities.find(e => UUIDsEqual(e.ID, field.RelatedEntityID));\n if (relatedEntity) {\n this.entitySelected.emit(relatedEntity);\n }\n }\n }\n\n public selectEntity(entity: EntityInfo, _zoomTo: boolean = false): void {\n this.entitySelected.emit(entity);\n }\n}\n","<div class=\"details-panel\">\n <div class=\"panel-header\">\n <h3>{{ selectedEntity?.Name || selectedEntity?.SchemaName }}</h3>\n <div class=\"panel-actions\">\n <button class=\"open-btn\" (click)=\"onOpenEntity()\" title=\"Open Entity Record\">\n <span class=\"fa-solid fa-external-link-alt\"></span>\n Open\n </button>\n <button class=\"close-btn\" (click)=\"onClosePanel()\">\n <span class=\"fa-solid fa-times\"></span>\n </button>\n </div>\n </div>\n\n <div class=\"entity-info\">\n <div class=\"info-row\">\n <label>Schema:</label>\n <span>{{ selectedEntity?.SchemaName }}</span>\n </div>\n <div class=\"info-row\">\n <label>Table:</label>\n <span>{{ selectedEntity?.BaseTable }}</span>\n </div>\n @if (selectedEntity?.Description) {\n <div class=\"info-row\">\n <label>Description:</label>\n <span>{{ selectedEntity?.Description }}</span>\n </div>\n }\n </div>\n\n <div class=\"fields-section\" [class.section-expanded]=\"fieldsSectionExpanded\" [class.section-collapsed]=\"!fieldsSectionExpanded\">\n <div class=\"section-header extra-padding\">\n <div class=\"section-title-group\" (click)=\"toggleFieldsSection()\">\n <button\n class=\"expand-btn\"\n [class.expanded]=\"fieldsSectionExpanded\"\n title=\"Toggle fields section\">\n <span class=\"fa-solid fa-chevron-right\"></span>\n </button>\n <h4>Fields ({{ selectedEntity ? getEntityFields(selectedEntity.ID).length : 0 }})</h4>\n </div>\n @if (fieldsSectionExpanded) {\n <div class=\"field-filters\">\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'all'\"\n (click)=\"setFieldFilter('all')\"\n title=\"Show all fields\">\n All\n </button>\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'keys'\"\n (click)=\"setFieldFilter('keys')\"\n title=\"Show primary keys and ID fields\">\n Keys\n </button>\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'foreign_keys'\"\n (click)=\"setFieldFilter('foreign_keys')\"\n title=\"Show foreign key fields only\">\n FKs\n </button>\n <button\n class=\"filter-btn\"\n [class.active]=\"fieldFilter === 'regular'\"\n (click)=\"setFieldFilter('regular')\"\n title=\"Show regular data fields\">\n Regular\n </button>\n </div>\n }\n </div>\n @if (fieldsSectionExpanded) {\n <div class=\"fields-list\" #fieldsListContainer>\n @for (field of getEntityFields(selectedEntity?.ID || ''); track field.ID) {\n <div class=\"field-item\"\n [class]=\"{\n 'primary-key-field': field.IsPrimaryKey,\n 'foreign-key-field': field.RelatedEntityID && !field.IsPrimaryKey,\n 'regular-field': !field.IsPrimaryKey && !field.RelatedEntityID,\n 'field-expanded': isFieldDetailsExpanded(field.ID)\n }\"\n (click)=\"onFieldClick(field)\"\n [title]=\"'Click to see more details about ' + field.Name\">\n <div class=\"field-header\">\n <div class=\"field-name\">\n @if (field.IsPrimaryKey) {\n <span class=\"field-icon fa-solid fa-key\" title=\"Primary Key\"></span>\n } @else if (field.RelatedEntityID) {\n <span class=\"field-icon fa-solid fa-link\" title=\"Foreign Key\"></span>\n }\n {{ field.Name }}\n </div>\n <div class=\"field-actions\">\n @if (hasFieldPossibleValues(field)) {\n <button\n class=\"field-values-btn\"\n (click)=\"toggleFieldValues(field.ID)\"\n [class.expanded]=\"isFieldValuesExpanded(field.ID)\"\n title=\"Toggle possible values\">\n <span class=\"fa-solid fa-list\"></span>\n </button>\n }\n @if (field.Description) {\n <button\n class=\"field-info-btn\"\n (click)=\"toggleFieldDescription(field.ID)\"\n [class.expanded]=\"isFieldDescriptionExpanded(field.ID)\"\n title=\"Toggle field description\">\n <span class=\"fa-solid fa-info-circle\"></span>\n </button>\n }\n <div class=\"field-badges\">\n @if (field.IsPrimaryKey) {\n <span class=\"field-badge primary-key\">PK</span>\n }\n @if (field.RelatedEntityID && !field.IsPrimaryKey) {\n <span class=\"field-badge foreign-key\">FK</span>\n }\n </div>\n </div>\n </div>\n <div class=\"field-details\">\n <div class=\"field-type\">{{ field.Type }}</div>\n @if (field.RelatedEntityID) {\n <div class=\"field-relation\">\n <span class=\"fa-solid fa-arrow-right\"></span>\n Related Entity\n </div>\n }\n </div>\n @if (field.Description && isFieldDescriptionExpanded(field.ID)) {\n <div class=\"field-description\">\n <div class=\"description-text\">{{ field.Description }}</div>\n </div>\n }\n @if (hasFieldPossibleValues(field) && isFieldValuesExpanded(field.ID)) {\n <div class=\"field-values\">\n <div class=\"values-header\">Possible Values:</div>\n <div class=\"values-list\">\n @for (value of getFieldPossibleValues(field); track value; let i = $index) {\n <span class=\"value-tag\">{{ value }}</span>\n }\n </div>\n </div>\n }\n @if (isFieldDetailsExpanded(field.ID)) {\n <div class=\"field-comprehensive-details\">\n @if (field.Description) {\n <div class=\"comprehensive-description-top\">\n <div class=\"description-content\">{{ field.Description }}</div>\n </div>\n }\n @if (field.EntityFieldValues && field.EntityFieldValues.length > 0) {\n <div class=\"field-values-comprehensive\">\n <div class=\"values-header\">\n <span class=\"fa-solid fa-list\"></span>\n Possible Values ({{ field.EntityFieldValues.length }})\n </div>\n <div class=\"values-grid\">\n @for (fieldValue of getSortedEntityFieldValues(field); track fieldValue.ID) {\n <div class=\"value-item\">\n <div class=\"value-main\">\n <span class=\"value-text\">{{ fieldValue.Value }}</span>\n @if (fieldValue.Code && fieldValue.Code !== fieldValue.Value) {\n <span class=\"value-code\">[{{ fieldValue.Code }}]</span>\n }\n </div>\n @if (fieldValue.Description && fieldValue.Description !== fieldValue.Value) {\n <div class=\"value-description\">{{ fieldValue.Description }}</div>\n }\n </div>\n }\n </div>\n </div>\n }\n <div class=\"details-grid\">\n <div class=\"detail-row\">\n <span class=\"detail-label\">Data Type:</span>\n <span class=\"detail-value\">{{ field.Type }}</span>\n </div>\n <div class=\"detail-row\">\n <span class=\"detail-label\">Nullable:</span>\n <span class=\"detail-value\" [class]=\"field.AllowsNull ? 'nullable-yes' : 'nullable-no'\">\n {{ field.AllowsNull ? 'Yes' : 'No' }}\n </span>\n </div>\n @if (field.Length && field.Length > 0) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Max Length:</span>\n <span class=\"detail-value\">{{ field.Length }}</span>\n </div>\n }\n @if (field.Precision && field.Precision > 0) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Precision:</span>\n <span class=\"detail-value\">{{ field.Precision }}</span>\n </div>\n }\n @if (field.Scale && field.Scale > 0) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Scale:</span>\n <span class=\"detail-value\">{{ field.Scale }}</span>\n </div>\n }\n <div class=\"detail-row\">\n <span class=\"detail-label\">Virtual Field:</span>\n <span class=\"detail-value\" [class]=\"field.IsVirtual ? 'virtual-yes' : 'virtual-no'\">\n {{ field.IsVirtual ? 'Yes' : 'No' }}\n </span>\n </div>\n @if (field.DefaultValue) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Default Value:</span>\n <span class=\"detail-value default-value\">{{ field.DefaultValue }}</span>\n </div>\n }\n @if (field.RelatedEntityID) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Related Entity:</span>\n <span class=\"detail-value related-entity\"\n (click)=\"onRelatedEntityClick($event, field)\"\n title=\"Click to select {{ field.RelatedEntity }} in ERD\">\n {{ field.RelatedEntity }}\n <span class=\"fa-solid fa-arrow-right\"></span>\n </span>\n </div>\n }\n @if (field.AutoIncrement) {\n <div class=\"detail-row\">\n <span class=\"detail-label\">Auto Increment:</span>\n <span class=\"detail-value auto-increment\">Yes</span>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"relationships-section\" [class.section-expanded]=\"relationshipsSectionExpanded\" [class.section-collapsed]=\"!relationshipsSectionExpanded\">\n <div class=\"section-header extra-padding\">\n <div class=\"section-title-group\" (click)=\"toggleRelationshipsSection()\">\n <button\n class=\"expand-btn\"\n [class.expanded]=\"relationshipsSectionExpanded\"\n title=\"Toggle relationships section\">\n <span class=\"fa-solid fa-chevron-right\"></span>\n </button>\n <h4>Related Entities ({{ selectedEntity ? getRelatedEntities(selectedEntity.ID).length : 0 }})</h4>\n </div>\n </div>\n @if (relationshipsSectionExpanded) {\n <div class=\"related-entities-list\" #relationshipsListContainer>\n @for (relatedEntity of getRelatedEntities(selectedEntity?.ID || ''); track relatedEntity.ID) {\n <div class=\"related-entity-item\" (click)=\"selectEntity(relatedEntity, true)\">\n <span class=\"fa-solid fa-database\"></span>\n <span class=\"entity-name\">{{ relatedEntity.Name || relatedEntity.SchemaName }}</span>\n <span class=\"zoom-indicator fa-solid fa-search-plus\" title=\"Click to zoom to entity\"></span>\n </div>\n }\n </div>\n }\n </div>\n</div>\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"erd-composite.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/erd-composite/erd-composite.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAa,MAAM,eAAe,CAAC;AAGrG,OAAO,EAAE,UAAU,EAAE,eAAe,EAAY,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"erd-composite.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/erd-composite/erd-composite.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAa,MAAM,eAAe,CAAC;AAGrG,OAAO,EAAE,UAAU,EAAE,eAAe,EAAY,MAAM,sBAAsB,CAAC;AAE7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,sDAAsD,CAAC;;AAEpF;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACtC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4BAA4B,EAAE,OAAO,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,qBAMa,qBAAsB,YAAW,MAAM,EAAE,SAAS;IAC5B,WAAW,EAAG,oBAAoB,CAAC;IAEpE,+CAA+C;IACtC,eAAe,UAAS;IAEjC;;;;OAIG;IACM,aAAa,EAAE,UAAU,EAAE,GAAG,IAAI,CAAQ;IAEnD;;;OAGG;IACM,eAAe,UAAQ;IAEhC;;;OAGG;IACM,KAAK,SAAK;IAEnB;;OAEG;IACM,UAAU,UAAQ;IAE3B,wCAAwC;IACjC,QAAQ,EAAE,UAAU,EAAE,CAAM;IAEnC,sDAAsD;IAC/C,eAAe,EAAE,eAAe,EAAE,CAAM;IAE/C,mDAAmD;IACzC,WAAW,kCAAyC;IAE9D,6EAA6E;IACnE,eAAe,kCAAyC;IAElE,+EAA+E;IACrE,YAAY,2BAAkC;IAExD,wEAAwE;IAC9D,UAAU;oBAAiC,MAAM;kBAAY,MAAM;OAAK;IAG3E,kBAAkB,UAAQ;IAC1B,qBAAqB,UAAQ;IAC7B,4BAA4B,UAAQ;IAE3C,8DAA8D;IACvD,SAAS,EAAE,SAAS,CAA2B;IAG/C,cAAc,EAAE,UAAU,GAAG,IAAI,CAAQ;IACzC,gBAAgB,EAAE,UAAU,EAAE,CAAM;IACpC,YAAY,UAAS;IAGrB,OAAO,EAAE,YAAY,CAK1B;IAGF,OAAO,CAAC,kBAAkB,CAAoC;IAC9D,OAAO,CAAC,sBAAsB,CAAoC;IAClE,OAAO,CAAC,mBAAmB,CAAuB;IAE5C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB/B,WAAW,IAAI,IAAI;YAML,QAAQ;IAatB,OAAO,CAAC,oBAAoB;IAwBrB,cAAc,IAAI,IAAI;IAItB,eAAe,CAAC,UAAU,EAAE,YAAY,GAAG,IAAI;IAK/C,cAAc,IAAI,IAAI;IAUtB,mBAAmB,IAAI,IAAI;IAY3B,kBAAkB,IAAI,IAAI;IAO1B,gBAAgB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAOjD;;;OAGG;IACI,mBAAmB,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAM5D;;;OAGG;IACI,eAAe,CAAC,KAAK,EAAE,qBAAqB,GAAG,IAAI;IAI1D;;OAEG;IACI,gBAAgB,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;IAKxC,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAIxC,qBAAqB,IAAI,IAAI;IAK7B,4BAA4B,IAAI,IAAI;IAKpC,YAAY,CAAC,KAAK,EAAE;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,GAAG,IAAI;IAIjE,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAUpD,OAAO,CAAC,YAAY;IAkCpB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,UAAU;IAiBlB;;;OAGG;IACI,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAyB7D;;OAEG;IACI,UAAU,IAAI,IAAI;IAMzB;;OAEG;IACI,QAAQ,IAAI,iBAAiB;yCAnVzB,qBAAqB;2CAArB,qBAAqB;CAsVjC"}
|
|
@@ -2,6 +2,7 @@ import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core
|
|
|
2
2
|
import { Subject } from 'rxjs';
|
|
3
3
|
import { debounceTime } from 'rxjs/operators';
|
|
4
4
|
import { Metadata } from '@memberjunction/core';
|
|
5
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
5
6
|
import { MJEntityERDComponent } from '../mj-entity-erd.component';
|
|
6
7
|
import * as i0 from "@angular/core";
|
|
7
8
|
import * as i1 from "@progress/kendo-angular-layout";
|
|
@@ -305,7 +306,7 @@ export class ERDCompositeComponent {
|
|
|
305
306
|
this.relationshipsSectionExpanded = state.relationshipsSectionExpanded;
|
|
306
307
|
}
|
|
307
308
|
if (state.selectedEntityId && this.entities.length > 0) {
|
|
308
|
-
const entity = this.entities.find(e => e.ID
|
|
309
|
+
const entity = this.entities.find(e => UUIDsEqual(e.ID, state.selectedEntityId));
|
|
309
310
|
if (entity) {
|
|
310
311
|
this.selectedEntity = entity;
|
|
311
312
|
}
|
|
@@ -387,5 +388,5 @@ export class ERDCompositeComponent {
|
|
|
387
388
|
}], openRecord: [{
|
|
388
389
|
type: Output
|
|
389
390
|
}] }); })();
|
|
390
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ERDCompositeComponent, { className: "ERDCompositeComponent", filePath: "src/lib/components/erd-composite/erd-composite.component.ts", lineNumber:
|
|
391
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ERDCompositeComponent, { className: "ERDCompositeComponent", filePath: "src/lib/components/erd-composite/erd-composite.component.ts", lineNumber: 74 }); })();
|
|
391
392
|
//# sourceMappingURL=erd-composite.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"erd-composite.component.js","sourceRoot":"","sources":["../../../../src/lib/components/erd-composite/erd-composite.component.ts","../../../../src/lib/components/erd-composite/erd-composite.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ViewChild } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { debounceTime } from 'rxjs/operators';\nimport { EntityInfo, EntityFieldInfo, Metadata } from '@memberjunction/core';\nimport { MJEntityERDComponent } from '../mj-entity-erd.component';\nimport { EntitySelectedEvent, OpenEntityRecordEvent } from '../mj-entity-erd.component';\nimport { ERDConfig, ERDState } from '../../interfaces/erd-types';\nimport { EntityFilter } from '../entity-filter-panel/entity-filter-panel.component';\n\n/**\n * State object for the ERD composite component.\n * Used for state persistence and restoration.\n */\nexport interface ERDCompositeState {\n filterPanelVisible: boolean;\n filterPanelWidth: number;\n filters: EntityFilter;\n selectedEntityId: string | null;\n zoomLevel: number;\n panPosition: { x: number; y: number };\n fieldsSectionExpanded: boolean;\n relationshipsSectionExpanded: boolean;\n}\n\n/**\n * ERD Composite component that combines the ERD diagram with filter and details panels.\n *\n * This is a complete, ready-to-use ERD exploration interface that includes:\n * - Left panel: Entity filter controls (schema, name, status, etc.)\n * - Center: Interactive ERD diagram\n * - Right panel: Entity details (shown when an entity is selected)\n *\n * The component handles all internal state but emits events for:\n * - State changes (for parent to persist user preferences)\n * - Open record requests (for parent to handle navigation)\n *\n * ## Usage\n *\n * ```html\n * <mj-erd-composite\n * (stateChange)=\"onStateChange($event)\"\n * (userStateChange)=\"saveUserPreferences($event)\"\n * (openRecord)=\"navigateToRecord($event)\">\n * </mj-erd-composite>\n * ```\n *\n * ## State Persistence\n *\n * The component emits `userStateChange` events (debounced 1s) when user changes\n * any settings. The parent should save this state and restore it using `loadUserState()`:\n *\n * ```typescript\n * @ViewChild(ERDCompositeComponent) erdComposite!: ERDCompositeComponent;\n *\n * ngAfterViewInit() {\n * const savedState = await this.loadSavedState();\n * if (savedState) {\n * this.erdComposite.loadUserState(savedState);\n * }\n * }\n *\n * onUserStateChange(state: ERDCompositeState) {\n * await this.saveState(state);\n * }\n * ```\n */\n@Component({\n standalone: false,\n selector: 'mj-erd-composite',\n templateUrl: './erd-composite.component.html',\n styleUrls: ['./erd-composite.component.css']\n})\nexport class ERDCompositeComponent implements OnInit, OnDestroy {\n @ViewChild(MJEntityERDComponent) mjEntityErd!: MJEntityERDComponent;\n\n /** Whether the ERD is in a refreshing state */\n @Input() isRefreshingERD = false;\n\n /**\n * Optional: Focus entities to display in the ERD.\n * When provided, only these entities will be shown (useful for single-entity views).\n * When not provided, all entities from metadata are loaded.\n */\n @Input() focusEntities: EntityInfo[] | null = null;\n\n /**\n * Whether to show the filter panel on the left.\n * Set to false for focused/single-entity views.\n */\n @Input() showFilterPanel = true;\n\n /**\n * Depth of relationships to display.\n * 1 = only direct relationships, 2 = relationships of relationships, etc.\n */\n @Input() depth = 1;\n\n /**\n * Whether to show the ERD header bar.\n */\n @Input() showHeader = true;\n\n /** All entities loaded from metadata */\n public entities: EntityInfo[] = [];\n\n /** All entity fields (flattened from all entities) */\n public allEntityFields: EntityFieldInfo[] = [];\n\n /** Emitted on any state change (debounced 50ms) */\n @Output() stateChange = new EventEmitter<ERDCompositeState>();\n\n /** Emitted on user-initiated state changes (debounced 1s) for persistence */\n @Output() userStateChange = new EventEmitter<ERDCompositeState>();\n\n /** Emitted when an entity is opened (e.g., double-clicked in details panel) */\n @Output() entityOpened = new EventEmitter<EntityInfo>();\n\n /** Emitted when requesting to open an entity record (for navigation) */\n @Output() openRecord = new EventEmitter<{EntityName: string, RecordID: string}>();\n\n // Panel visibility and configuration\n public filterPanelVisible = true;\n public fieldsSectionExpanded = true;\n public relationshipsSectionExpanded = true;\n\n /** ERD configuration - skip animation for faster rendering */\n public erdConfig: ERDConfig = { skipAnimation: true };\n\n // Entity state\n public selectedEntity: EntityInfo | null = null;\n public filteredEntities: EntityInfo[] = [];\n public isDataLoaded = false;\n\n // Filters\n public filters: EntityFilter = {\n schemaName: null,\n entityName: '',\n entityStatus: null,\n baseTable: '',\n };\n\n // State management\n private stateChangeSubject = new Subject<ERDCompositeState>();\n private userStateChangeSubject = new Subject<ERDCompositeState>();\n private filterChangeSubject = new Subject<void>();\n\n async ngOnInit(): Promise<void> {\n // Initialize filter panel visibility based on input\n this.filterPanelVisible = this.showFilterPanel;\n\n this.setupStateManagement();\n await this.loadData();\n\n // Use focusEntities if provided, otherwise use all entities\n if (this.focusEntities && this.focusEntities.length > 0) {\n this.filteredEntities = [...this.focusEntities];\n } else {\n this.filteredEntities = [...this.entities];\n this.applyFilters();\n }\n\n this.isDataLoaded = true;\n\n // Notify parent that data is loaded and ready for state loading\n this.emitStateChange();\n }\n\n ngOnDestroy(): void {\n this.stateChangeSubject.complete();\n this.userStateChangeSubject.complete();\n this.filterChangeSubject.complete();\n }\n\n private async loadData(): Promise<void> {\n // Load entities from metadata (always needed for allEntityFields and relationship lookups)\n const md = new Metadata();\n this.entities = md.Entities;\n\n // Load all entity fields from entities\n this.allEntityFields = this.entities\n .map((entity) => {\n return entity.Fields;\n })\n .flat();\n }\n\n private setupStateManagement(): void {\n // State change emissions with debouncing\n this.stateChangeSubject.pipe(\n debounceTime(50)\n ).subscribe(state => {\n this.stateChange.emit(state);\n });\n\n this.userStateChangeSubject.pipe(\n debounceTime(1000)\n ).subscribe(state => {\n this.userStateChange.emit(state);\n });\n\n // Filter changes with debouncing\n this.filterChangeSubject.pipe(\n debounceTime(300)\n ).subscribe(() => {\n this.applyFilters();\n this.emitStateChange();\n this.emitUserStateChange();\n });\n }\n\n public onFilterChange(): void {\n this.filterChangeSubject.next();\n }\n\n public onFiltersChange(newFilters: EntityFilter): void {\n this.filters = { ...newFilters };\n this.onFilterChange();\n }\n\n public onResetFilters(): void {\n this.filters = {\n schemaName: null,\n entityName: '',\n entityStatus: null,\n baseTable: '',\n };\n this.onFilterChange();\n }\n\n public onToggleFilterPanel(): void {\n this.filterPanelVisible = !this.filterPanelVisible;\n\n // Trigger ERD resize when filter panel is toggled\n if (this.mjEntityErd) {\n this.mjEntityErd.triggerResize();\n }\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n public onEntityDeselected(): void {\n this.selectedEntity = null;\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n public onEntitySelected(entity: EntityInfo): void {\n this.selectedEntity = entity;\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n /**\n * Handle entity selection from the mj-entity-erd wrapper.\n * Updates internal state.\n */\n public onERDEntitySelected(event: EntitySelectedEvent): void {\n this.selectedEntity = event.entity;\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n /**\n * Handle open record from the mj-entity-erd wrapper (double-click).\n * Opens the entity record using the entity form.\n */\n public onERDOpenRecord(event: OpenEntityRecordEvent): void {\n this.openRecord.emit({ EntityName: event.EntityName, RecordID: event.RecordID });\n }\n\n /**\n * Handle state changes from the ERD component.\n */\n public onERDStateChange(_state: ERDState): void {\n // State changes are tracked for user preference persistence\n this.emitUserStateChange();\n }\n\n public onEntityOpened(entity: EntityInfo): void {\n this.entityOpened.emit(entity);\n }\n\n public onFieldsSectionToggle(): void {\n this.fieldsSectionExpanded = !this.fieldsSectionExpanded;\n this.emitUserStateChange();\n }\n\n public onRelationshipsSectionToggle(): void {\n this.relationshipsSectionExpanded = !this.relationshipsSectionExpanded;\n this.emitUserStateChange();\n }\n\n public onOpenRecord(event: {EntityName: string, RecordID: string}): void {\n this.openRecord.emit(event);\n }\n\n public onSplitterLayoutChange(_event: unknown): void {\n // Trigger ERD diagram resize when splitter layout changes\n if (this.mjEntityErd) {\n this.mjEntityErd.triggerResize();\n }\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n private applyFilters(): void {\n this.filteredEntities = this.entities.filter(entity => {\n // Schema filter\n if (this.filters.schemaName && entity.SchemaName !== this.filters.schemaName) {\n return false;\n }\n\n // Entity name filter\n if (this.filters.entityName) {\n const searchTerm = this.filters.entityName.toLowerCase();\n const entityName = (entity.Name || entity.SchemaName || '').toLowerCase();\n if (!entityName.includes(searchTerm)) {\n return false;\n }\n }\n\n // Base table filter\n if (this.filters.baseTable) {\n const searchTerm = this.filters.baseTable.toLowerCase();\n const baseTable = (entity.BaseTable || '').toLowerCase();\n if (!baseTable.includes(searchTerm)) {\n return false;\n }\n }\n\n // Status filter\n if (this.filters.entityStatus && entity.Status !== this.filters.entityStatus) {\n return false;\n }\n\n return true;\n });\n }\n\n private emitStateChange(): void {\n const state = this.buildState();\n this.stateChangeSubject.next(state);\n }\n\n private emitUserStateChange(): void {\n const state = this.buildState();\n this.userStateChangeSubject.next(state);\n }\n\n private buildState(): ERDCompositeState {\n return {\n filterPanelVisible: this.filterPanelVisible,\n filterPanelWidth: 320,\n filters: { ...this.filters },\n selectedEntityId: this.selectedEntity?.ID || null,\n zoomLevel: 1,\n panPosition: { x: 0, y: 0 },\n fieldsSectionExpanded: this.fieldsSectionExpanded,\n relationshipsSectionExpanded: this.relationshipsSectionExpanded,\n };\n }\n\n // ============================================================================\n // PUBLIC API\n // ============================================================================\n\n /**\n * Load user state from a previously saved state object.\n * Call this after the component is initialized to restore user preferences.\n */\n public loadUserState(state: Partial<ERDCompositeState>): void {\n if (state.filterPanelVisible !== undefined) {\n this.filterPanelVisible = state.filterPanelVisible;\n }\n if (state.filters) {\n this.filters = { ...state.filters };\n }\n if (state.fieldsSectionExpanded !== undefined) {\n this.fieldsSectionExpanded = state.fieldsSectionExpanded;\n }\n if (state.relationshipsSectionExpanded !== undefined) {\n this.relationshipsSectionExpanded = state.relationshipsSectionExpanded;\n }\n if (state.selectedEntityId && this.entities.length > 0) {\n const entity = this.entities.find(e => e.ID === state.selectedEntityId);\n if (entity) {\n this.selectedEntity = entity;\n }\n } else {\n this.selectedEntity = null;\n }\n\n this.applyFilters();\n }\n\n /**\n * Refresh the ERD diagram.\n */\n public refreshERD(): void {\n if (this.mjEntityErd) {\n this.mjEntityErd.refresh();\n }\n }\n\n /**\n * Get the current state for external use.\n */\n public getState(): ERDCompositeState {\n return this.buildState();\n }\n}\n","<div class=\"erd-composite\">\n <kendo-splitter\n class=\"main-splitter\"\n orientation=\"horizontal\"\n (layoutChange)=\"onSplitterLayoutChange($event)\">\n\n <!-- Filter Panel (Left) -->\n <kendo-splitter-pane\n [size]=\"filterPanelVisible ? '320px' : '0px'\"\n [collapsible]=\"false\"\n [resizable]=\"filterPanelVisible\"\n [scrollable]=\"false\"\n [hidden]=\"!filterPanelVisible\">\n <mj-entity-filter-panel\n [entities]=\"entities\"\n [filteredEntities]=\"filteredEntities\"\n [filters]=\"filters\"\n (filtersChange)=\"onFiltersChange($event)\"\n (filterChange)=\"onFilterChange()\"\n (resetFilters)=\"onResetFilters()\"\n (closePanel)=\"onToggleFilterPanel()\">\n </mj-entity-filter-panel>\n </kendo-splitter-pane>\n\n <!-- ERD Diagram (Center) -->\n <kendo-splitter-pane\n [resizable]=\"true\"\n [scrollable]=\"false\">\n <div class=\"erd-pane-container\">\n <mj-entity-erd\n [entities]=\"filteredEntities\"\n [allEntities]=\"entities\"\n [selectedEntityId]=\"selectedEntity?.ID || null\"\n [isRefreshing]=\"isRefreshingERD\"\n [depth]=\"depth\"\n [includeIncoming]=\"true\"\n [includeOutgoing]=\"true\"\n [showHeader]=\"showHeader\"\n [config]=\"erdConfig\"\n headerTitle=\"Entity Relationship Diagram\"\n (entitySelected)=\"onERDEntitySelected($event)\"\n (entityDeselected)=\"onEntityDeselected()\"\n (openRecord)=\"onERDOpenRecord($event)\"\n (stateChange)=\"onERDStateChange($event)\">\n </mj-entity-erd>\n </div>\n </kendo-splitter-pane>\n\n <!-- Entity Details Panel (Right) -->\n <kendo-splitter-pane\n [size]=\"selectedEntity ? '320px' : '0px'\"\n [collapsible]=\"false\"\n [resizable]=\"!!selectedEntity\"\n [scrollable]=\"false\"\n [hidden]=\"!selectedEntity\">\n <mj-entity-details\n [selectedEntity]=\"selectedEntity\"\n [allEntityFields]=\"allEntityFields\"\n [fieldsSectionExpanded]=\"fieldsSectionExpanded\"\n [relationshipsSectionExpanded]=\"relationshipsSectionExpanded\"\n (openEntity)=\"onEntityOpened($event)\"\n (closePanel)=\"onEntityDeselected()\"\n (fieldsSectionToggle)=\"onFieldsSectionToggle()\"\n (relationshipsSectionToggle)=\"onRelationshipsSectionToggle()\"\n (entitySelected)=\"onEntitySelected($event)\"\n (openRecord)=\"onOpenRecord($event)\">\n </mj-entity-details>\n </kendo-splitter-pane>\n </kendo-splitter>\n</div>\n"]}
|
|
1
|
+
{"version":3,"file":"erd-composite.component.js","sourceRoot":"","sources":["../../../../src/lib/components/erd-composite/erd-composite.component.ts","../../../../src/lib/components/erd-composite/erd-composite.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnDestroy, ViewChild } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { debounceTime } from 'rxjs/operators';\nimport { EntityInfo, EntityFieldInfo, Metadata } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { MJEntityERDComponent } from '../mj-entity-erd.component';\nimport { EntitySelectedEvent, OpenEntityRecordEvent } from '../mj-entity-erd.component';\nimport { ERDConfig, ERDState } from '../../interfaces/erd-types';\nimport { EntityFilter } from '../entity-filter-panel/entity-filter-panel.component';\n\n/**\n * State object for the ERD composite component.\n * Used for state persistence and restoration.\n */\nexport interface ERDCompositeState {\n filterPanelVisible: boolean;\n filterPanelWidth: number;\n filters: EntityFilter;\n selectedEntityId: string | null;\n zoomLevel: number;\n panPosition: { x: number; y: number };\n fieldsSectionExpanded: boolean;\n relationshipsSectionExpanded: boolean;\n}\n\n/**\n * ERD Composite component that combines the ERD diagram with filter and details panels.\n *\n * This is a complete, ready-to-use ERD exploration interface that includes:\n * - Left panel: Entity filter controls (schema, name, status, etc.)\n * - Center: Interactive ERD diagram\n * - Right panel: Entity details (shown when an entity is selected)\n *\n * The component handles all internal state but emits events for:\n * - State changes (for parent to persist user preferences)\n * - Open record requests (for parent to handle navigation)\n *\n * ## Usage\n *\n * ```html\n * <mj-erd-composite\n * (stateChange)=\"onStateChange($event)\"\n * (userStateChange)=\"saveUserPreferences($event)\"\n * (openRecord)=\"navigateToRecord($event)\">\n * </mj-erd-composite>\n * ```\n *\n * ## State Persistence\n *\n * The component emits `userStateChange` events (debounced 1s) when user changes\n * any settings. The parent should save this state and restore it using `loadUserState()`:\n *\n * ```typescript\n * @ViewChild(ERDCompositeComponent) erdComposite!: ERDCompositeComponent;\n *\n * ngAfterViewInit() {\n * const savedState = await this.loadSavedState();\n * if (savedState) {\n * this.erdComposite.loadUserState(savedState);\n * }\n * }\n *\n * onUserStateChange(state: ERDCompositeState) {\n * await this.saveState(state);\n * }\n * ```\n */\n@Component({\n standalone: false,\n selector: 'mj-erd-composite',\n templateUrl: './erd-composite.component.html',\n styleUrls: ['./erd-composite.component.css']\n})\nexport class ERDCompositeComponent implements OnInit, OnDestroy {\n @ViewChild(MJEntityERDComponent) mjEntityErd!: MJEntityERDComponent;\n\n /** Whether the ERD is in a refreshing state */\n @Input() isRefreshingERD = false;\n\n /**\n * Optional: Focus entities to display in the ERD.\n * When provided, only these entities will be shown (useful for single-entity views).\n * When not provided, all entities from metadata are loaded.\n */\n @Input() focusEntities: EntityInfo[] | null = null;\n\n /**\n * Whether to show the filter panel on the left.\n * Set to false for focused/single-entity views.\n */\n @Input() showFilterPanel = true;\n\n /**\n * Depth of relationships to display.\n * 1 = only direct relationships, 2 = relationships of relationships, etc.\n */\n @Input() depth = 1;\n\n /**\n * Whether to show the ERD header bar.\n */\n @Input() showHeader = true;\n\n /** All entities loaded from metadata */\n public entities: EntityInfo[] = [];\n\n /** All entity fields (flattened from all entities) */\n public allEntityFields: EntityFieldInfo[] = [];\n\n /** Emitted on any state change (debounced 50ms) */\n @Output() stateChange = new EventEmitter<ERDCompositeState>();\n\n /** Emitted on user-initiated state changes (debounced 1s) for persistence */\n @Output() userStateChange = new EventEmitter<ERDCompositeState>();\n\n /** Emitted when an entity is opened (e.g., double-clicked in details panel) */\n @Output() entityOpened = new EventEmitter<EntityInfo>();\n\n /** Emitted when requesting to open an entity record (for navigation) */\n @Output() openRecord = new EventEmitter<{EntityName: string, RecordID: string}>();\n\n // Panel visibility and configuration\n public filterPanelVisible = true;\n public fieldsSectionExpanded = true;\n public relationshipsSectionExpanded = true;\n\n /** ERD configuration - skip animation for faster rendering */\n public erdConfig: ERDConfig = { skipAnimation: true };\n\n // Entity state\n public selectedEntity: EntityInfo | null = null;\n public filteredEntities: EntityInfo[] = [];\n public isDataLoaded = false;\n\n // Filters\n public filters: EntityFilter = {\n schemaName: null,\n entityName: '',\n entityStatus: null,\n baseTable: '',\n };\n\n // State management\n private stateChangeSubject = new Subject<ERDCompositeState>();\n private userStateChangeSubject = new Subject<ERDCompositeState>();\n private filterChangeSubject = new Subject<void>();\n\n async ngOnInit(): Promise<void> {\n // Initialize filter panel visibility based on input\n this.filterPanelVisible = this.showFilterPanel;\n\n this.setupStateManagement();\n await this.loadData();\n\n // Use focusEntities if provided, otherwise use all entities\n if (this.focusEntities && this.focusEntities.length > 0) {\n this.filteredEntities = [...this.focusEntities];\n } else {\n this.filteredEntities = [...this.entities];\n this.applyFilters();\n }\n\n this.isDataLoaded = true;\n\n // Notify parent that data is loaded and ready for state loading\n this.emitStateChange();\n }\n\n ngOnDestroy(): void {\n this.stateChangeSubject.complete();\n this.userStateChangeSubject.complete();\n this.filterChangeSubject.complete();\n }\n\n private async loadData(): Promise<void> {\n // Load entities from metadata (always needed for allEntityFields and relationship lookups)\n const md = new Metadata();\n this.entities = md.Entities;\n\n // Load all entity fields from entities\n this.allEntityFields = this.entities\n .map((entity) => {\n return entity.Fields;\n })\n .flat();\n }\n\n private setupStateManagement(): void {\n // State change emissions with debouncing\n this.stateChangeSubject.pipe(\n debounceTime(50)\n ).subscribe(state => {\n this.stateChange.emit(state);\n });\n\n this.userStateChangeSubject.pipe(\n debounceTime(1000)\n ).subscribe(state => {\n this.userStateChange.emit(state);\n });\n\n // Filter changes with debouncing\n this.filterChangeSubject.pipe(\n debounceTime(300)\n ).subscribe(() => {\n this.applyFilters();\n this.emitStateChange();\n this.emitUserStateChange();\n });\n }\n\n public onFilterChange(): void {\n this.filterChangeSubject.next();\n }\n\n public onFiltersChange(newFilters: EntityFilter): void {\n this.filters = { ...newFilters };\n this.onFilterChange();\n }\n\n public onResetFilters(): void {\n this.filters = {\n schemaName: null,\n entityName: '',\n entityStatus: null,\n baseTable: '',\n };\n this.onFilterChange();\n }\n\n public onToggleFilterPanel(): void {\n this.filterPanelVisible = !this.filterPanelVisible;\n\n // Trigger ERD resize when filter panel is toggled\n if (this.mjEntityErd) {\n this.mjEntityErd.triggerResize();\n }\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n public onEntityDeselected(): void {\n this.selectedEntity = null;\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n public onEntitySelected(entity: EntityInfo): void {\n this.selectedEntity = entity;\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n /**\n * Handle entity selection from the mj-entity-erd wrapper.\n * Updates internal state.\n */\n public onERDEntitySelected(event: EntitySelectedEvent): void {\n this.selectedEntity = event.entity;\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n /**\n * Handle open record from the mj-entity-erd wrapper (double-click).\n * Opens the entity record using the entity form.\n */\n public onERDOpenRecord(event: OpenEntityRecordEvent): void {\n this.openRecord.emit({ EntityName: event.EntityName, RecordID: event.RecordID });\n }\n\n /**\n * Handle state changes from the ERD component.\n */\n public onERDStateChange(_state: ERDState): void {\n // State changes are tracked for user preference persistence\n this.emitUserStateChange();\n }\n\n public onEntityOpened(entity: EntityInfo): void {\n this.entityOpened.emit(entity);\n }\n\n public onFieldsSectionToggle(): void {\n this.fieldsSectionExpanded = !this.fieldsSectionExpanded;\n this.emitUserStateChange();\n }\n\n public onRelationshipsSectionToggle(): void {\n this.relationshipsSectionExpanded = !this.relationshipsSectionExpanded;\n this.emitUserStateChange();\n }\n\n public onOpenRecord(event: {EntityName: string, RecordID: string}): void {\n this.openRecord.emit(event);\n }\n\n public onSplitterLayoutChange(_event: unknown): void {\n // Trigger ERD diagram resize when splitter layout changes\n if (this.mjEntityErd) {\n this.mjEntityErd.triggerResize();\n }\n\n this.emitStateChange();\n this.emitUserStateChange();\n }\n\n private applyFilters(): void {\n this.filteredEntities = this.entities.filter(entity => {\n // Schema filter\n if (this.filters.schemaName && entity.SchemaName !== this.filters.schemaName) {\n return false;\n }\n\n // Entity name filter\n if (this.filters.entityName) {\n const searchTerm = this.filters.entityName.toLowerCase();\n const entityName = (entity.Name || entity.SchemaName || '').toLowerCase();\n if (!entityName.includes(searchTerm)) {\n return false;\n }\n }\n\n // Base table filter\n if (this.filters.baseTable) {\n const searchTerm = this.filters.baseTable.toLowerCase();\n const baseTable = (entity.BaseTable || '').toLowerCase();\n if (!baseTable.includes(searchTerm)) {\n return false;\n }\n }\n\n // Status filter\n if (this.filters.entityStatus && entity.Status !== this.filters.entityStatus) {\n return false;\n }\n\n return true;\n });\n }\n\n private emitStateChange(): void {\n const state = this.buildState();\n this.stateChangeSubject.next(state);\n }\n\n private emitUserStateChange(): void {\n const state = this.buildState();\n this.userStateChangeSubject.next(state);\n }\n\n private buildState(): ERDCompositeState {\n return {\n filterPanelVisible: this.filterPanelVisible,\n filterPanelWidth: 320,\n filters: { ...this.filters },\n selectedEntityId: this.selectedEntity?.ID || null,\n zoomLevel: 1,\n panPosition: { x: 0, y: 0 },\n fieldsSectionExpanded: this.fieldsSectionExpanded,\n relationshipsSectionExpanded: this.relationshipsSectionExpanded,\n };\n }\n\n // ============================================================================\n // PUBLIC API\n // ============================================================================\n\n /**\n * Load user state from a previously saved state object.\n * Call this after the component is initialized to restore user preferences.\n */\n public loadUserState(state: Partial<ERDCompositeState>): void {\n if (state.filterPanelVisible !== undefined) {\n this.filterPanelVisible = state.filterPanelVisible;\n }\n if (state.filters) {\n this.filters = { ...state.filters };\n }\n if (state.fieldsSectionExpanded !== undefined) {\n this.fieldsSectionExpanded = state.fieldsSectionExpanded;\n }\n if (state.relationshipsSectionExpanded !== undefined) {\n this.relationshipsSectionExpanded = state.relationshipsSectionExpanded;\n }\n if (state.selectedEntityId && this.entities.length > 0) {\n const entity = this.entities.find(e => UUIDsEqual(e.ID, state.selectedEntityId));\n if (entity) {\n this.selectedEntity = entity;\n }\n } else {\n this.selectedEntity = null;\n }\n\n this.applyFilters();\n }\n\n /**\n * Refresh the ERD diagram.\n */\n public refreshERD(): void {\n if (this.mjEntityErd) {\n this.mjEntityErd.refresh();\n }\n }\n\n /**\n * Get the current state for external use.\n */\n public getState(): ERDCompositeState {\n return this.buildState();\n }\n}\n","<div class=\"erd-composite\">\n <kendo-splitter\n class=\"main-splitter\"\n orientation=\"horizontal\"\n (layoutChange)=\"onSplitterLayoutChange($event)\">\n\n <!-- Filter Panel (Left) -->\n <kendo-splitter-pane\n [size]=\"filterPanelVisible ? '320px' : '0px'\"\n [collapsible]=\"false\"\n [resizable]=\"filterPanelVisible\"\n [scrollable]=\"false\"\n [hidden]=\"!filterPanelVisible\">\n <mj-entity-filter-panel\n [entities]=\"entities\"\n [filteredEntities]=\"filteredEntities\"\n [filters]=\"filters\"\n (filtersChange)=\"onFiltersChange($event)\"\n (filterChange)=\"onFilterChange()\"\n (resetFilters)=\"onResetFilters()\"\n (closePanel)=\"onToggleFilterPanel()\">\n </mj-entity-filter-panel>\n </kendo-splitter-pane>\n\n <!-- ERD Diagram (Center) -->\n <kendo-splitter-pane\n [resizable]=\"true\"\n [scrollable]=\"false\">\n <div class=\"erd-pane-container\">\n <mj-entity-erd\n [entities]=\"filteredEntities\"\n [allEntities]=\"entities\"\n [selectedEntityId]=\"selectedEntity?.ID || null\"\n [isRefreshing]=\"isRefreshingERD\"\n [depth]=\"depth\"\n [includeIncoming]=\"true\"\n [includeOutgoing]=\"true\"\n [showHeader]=\"showHeader\"\n [config]=\"erdConfig\"\n headerTitle=\"Entity Relationship Diagram\"\n (entitySelected)=\"onERDEntitySelected($event)\"\n (entityDeselected)=\"onEntityDeselected()\"\n (openRecord)=\"onERDOpenRecord($event)\"\n (stateChange)=\"onERDStateChange($event)\">\n </mj-entity-erd>\n </div>\n </kendo-splitter-pane>\n\n <!-- Entity Details Panel (Right) -->\n <kendo-splitter-pane\n [size]=\"selectedEntity ? '320px' : '0px'\"\n [collapsible]=\"false\"\n [resizable]=\"!!selectedEntity\"\n [scrollable]=\"false\"\n [hidden]=\"!selectedEntity\">\n <mj-entity-details\n [selectedEntity]=\"selectedEntity\"\n [allEntityFields]=\"allEntityFields\"\n [fieldsSectionExpanded]=\"fieldsSectionExpanded\"\n [relationshipsSectionExpanded]=\"relationshipsSectionExpanded\"\n (openEntity)=\"onEntityOpened($event)\"\n (closePanel)=\"onEntityDeselected()\"\n (fieldsSectionToggle)=\"onFieldsSectionToggle()\"\n (relationshipsSectionToggle)=\"onRelationshipsSectionToggle()\"\n (entitySelected)=\"onEntitySelected($event)\"\n (openRecord)=\"onOpenRecord($event)\">\n </mj-entity-details>\n </kendo-splitter-pane>\n </kendo-splitter>\n</div>\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-to-erd-adapter.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/entity-to-erd-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"entity-to-erd-adapter.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/entity-to-erd-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEnE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAiB,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAEpF;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,eAAe,GAAG,QAAQ,CAmCtE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAc/D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,OAAO,EAAE,CAEpE;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAK7E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,UAAU,GAAG,SAAS,CAEjG;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6FAA6F;IAC7F,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sFAAsF;IACtF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,2GAA2G;IAC3G,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,EAAE,CAAC;IACjB,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,UAAU,EAAE,EACtB,OAAO,GAAE,mBAAwB,GAChC,kBAAkB,CA0JpB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
1
2
|
/**
|
|
2
3
|
* Converts MemberJunction EntityFieldInfo to generic ERDField format.
|
|
3
4
|
*/
|
|
@@ -72,7 +73,7 @@ export function getOriginalEntityFromERDNode(node) {
|
|
|
72
73
|
* Finds an EntityInfo by node ID from an array.
|
|
73
74
|
*/
|
|
74
75
|
export function findEntityByNodeId(nodeId, entities) {
|
|
75
|
-
return entities.find(e => e.ID
|
|
76
|
+
return entities.find(e => UUIDsEqual(e.ID, nodeId));
|
|
76
77
|
}
|
|
77
78
|
/**
|
|
78
79
|
* Builds ERD nodes and links from an array of EntityInfo objects.
|
|
@@ -124,7 +125,7 @@ export function buildERDDataFromEntities(entities, options = {}) {
|
|
|
124
125
|
if (includeOutgoing) {
|
|
125
126
|
for (const field of entity.Fields) {
|
|
126
127
|
if (field.RelatedEntityID) {
|
|
127
|
-
const relatedEntity = allEntities.find(e => e.ID
|
|
128
|
+
const relatedEntity = allEntities.find(e => UUIDsEqual(e.ID, field.RelatedEntityID));
|
|
128
129
|
if (relatedEntity) {
|
|
129
130
|
addNode(relatedEntity);
|
|
130
131
|
const pkField = relatedEntity.Fields.find(f => f.IsPrimaryKey);
|
|
@@ -133,7 +134,7 @@ export function buildERDDataFromEntities(entities, options = {}) {
|
|
|
133
134
|
targetNodeId: field.RelatedEntityID,
|
|
134
135
|
sourceField: entityFieldToERDField(field),
|
|
135
136
|
targetField: pkField ? entityFieldToERDField(pkField) : undefined,
|
|
136
|
-
isSelfReference: entity.ID
|
|
137
|
+
isSelfReference: UUIDsEqual(entity.ID, field.RelatedEntityID),
|
|
137
138
|
relationshipType: 'many-to-one'
|
|
138
139
|
});
|
|
139
140
|
}
|
|
@@ -155,7 +156,7 @@ export function buildERDDataFromEntities(entities, options = {}) {
|
|
|
155
156
|
targetNodeId: entity.ID,
|
|
156
157
|
sourceField: entityFieldToERDField(fkField),
|
|
157
158
|
targetField: pkField ? entityFieldToERDField(pkField) : undefined,
|
|
158
|
-
isSelfReference: relEntity.ID
|
|
159
|
+
isSelfReference: UUIDsEqual(relEntity.ID, entity.ID),
|
|
159
160
|
relationshipType: 'many-to-one'
|
|
160
161
|
});
|
|
161
162
|
}
|
|
@@ -179,7 +180,7 @@ export function buildERDDataFromEntities(entities, options = {}) {
|
|
|
179
180
|
if (includeOutgoing) {
|
|
180
181
|
for (const field of entity.Fields) {
|
|
181
182
|
if (field.RelatedEntityID && !addedNodeIds.has(field.RelatedEntityID)) {
|
|
182
|
-
const relatedEntity = allEntities.find(e => e.ID
|
|
183
|
+
const relatedEntity = allEntities.find(e => UUIDsEqual(e.ID, field.RelatedEntityID));
|
|
183
184
|
if (relatedEntity) {
|
|
184
185
|
addNode(relatedEntity);
|
|
185
186
|
nextDepthEntities.push(relatedEntity);
|
|
@@ -189,7 +190,7 @@ export function buildERDDataFromEntities(entities, options = {}) {
|
|
|
189
190
|
targetNodeId: field.RelatedEntityID,
|
|
190
191
|
sourceField: entityFieldToERDField(field),
|
|
191
192
|
targetField: pkField ? entityFieldToERDField(pkField) : undefined,
|
|
192
|
-
isSelfReference: entity.ID
|
|
193
|
+
isSelfReference: UUIDsEqual(entity.ID, field.RelatedEntityID),
|
|
193
194
|
relationshipType: 'many-to-one'
|
|
194
195
|
});
|
|
195
196
|
}
|
|
@@ -212,7 +213,7 @@ export function buildERDDataFromEntities(entities, options = {}) {
|
|
|
212
213
|
targetNodeId: entity.ID,
|
|
213
214
|
sourceField: entityFieldToERDField(fkField),
|
|
214
215
|
targetField: pkField ? entityFieldToERDField(pkField) : undefined,
|
|
215
|
-
isSelfReference: relEntity.ID
|
|
216
|
+
isSelfReference: UUIDsEqual(relEntity.ID, entity.ID),
|
|
216
217
|
relationshipType: 'many-to-one'
|
|
217
218
|
});
|
|
218
219
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-to-erd-adapter.js","sourceRoot":"","sources":["../../../src/lib/utils/entity-to-erd-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityInfo, EntityFieldInfo } from '@memberjunction/core';\nimport { ERDNode, ERDField, ERDFieldValue, ERDLink } from '../interfaces/erd-types';\n\n/**\n * Converts MemberJunction EntityFieldInfo to generic ERDField format.\n */\nexport function entityFieldToERDField(field: EntityFieldInfo): ERDField {\n const erdField: ERDField = {\n id: field.ID,\n name: field.Name,\n type: field.Type,\n isPrimaryKey: field.IsPrimaryKey,\n description: field.Description,\n allowsNull: field.AllowsNull,\n defaultValue: field.DefaultValue,\n length: field.Length,\n precision: field.Precision,\n scale: field.Scale,\n isVirtual: field.IsVirtual,\n autoIncrement: field.AutoIncrement\n };\n\n // Add related entity info for foreign keys\n if (field.RelatedEntityID) {\n erdField.relatedNodeId = field.RelatedEntityID;\n erdField.relatedNodeName = field.RelatedEntity;\n erdField.relatedFieldName = field.RelatedEntityFieldName;\n }\n\n // Add possible values if they exist\n if (field.EntityFieldValues && field.EntityFieldValues.length > 0) {\n erdField.possibleValues = field.EntityFieldValues.map(v => ({\n id: v.ID,\n value: v.Value,\n code: v.Code,\n description: v.Description,\n sequence: v.Sequence\n } as ERDFieldValue));\n }\n\n return erdField;\n}\n\n/**\n * Converts MemberJunction EntityInfo to generic ERDNode format.\n */\nexport function entityInfoToERDNode(entity: EntityInfo): ERDNode {\n return {\n id: entity.ID,\n name: entity.Name,\n schemaName: entity.SchemaName,\n description: entity.Description,\n status: entity.Status,\n baseTable: entity.BaseTable,\n fields: entity.Fields.map(f => entityFieldToERDField(f)),\n customData: {\n // Store original entity for reverse lookup if needed\n originalEntity: entity\n }\n };\n}\n\n/**\n * Converts an array of MemberJunction EntityInfo objects to ERDNode format.\n */\nexport function entitiesToERDNodes(entities: EntityInfo[]): ERDNode[] {\n return entities.map(e => entityInfoToERDNode(e));\n}\n\n/**\n * Extracts the original EntityInfo from an ERDNode's customData.\n * Returns null if not found.\n */\nexport function getOriginalEntityFromERDNode(node: ERDNode): EntityInfo | null {\n if (node.customData && node.customData['originalEntity']) {\n return node.customData['originalEntity'] as EntityInfo;\n }\n return null;\n}\n\n/**\n * Finds an EntityInfo by node ID from an array.\n */\nexport function findEntityByNodeId(nodeId: string, entities: EntityInfo[]): EntityInfo | undefined {\n return entities.find(e => e.ID === nodeId);\n}\n\n/**\n * Options for building ERD data from entities.\n */\nexport interface BuildERDDataOptions {\n /** Include incoming relationships (entities that reference these entities). Default: true */\n includeIncoming?: boolean;\n /** Include outgoing relationships (entities these reference via FK). Default: true */\n includeOutgoing?: boolean;\n /** All entities in the system (for relationship lookups). Required for includeIncoming/includeOutgoing. */\n allEntities?: EntityInfo[];\n /** Relationship depth - how many hops to include. Default: 1 */\n depth?: number;\n}\n\n/**\n * Result of building ERD data from entities.\n */\nexport interface BuildERDDataResult {\n nodes: ERDNode[];\n links: ERDLink[];\n}\n\n/**\n * Builds ERD nodes and links from an array of EntityInfo objects.\n * This handles the transformation and automatic relationship discovery.\n *\n * @param entities - The primary entities to display\n * @param options - Options for relationship discovery\n * @returns Object containing nodes and links arrays\n *\n * @example\n * ```typescript\n * // Single entity with all related entities\n * const result = buildERDDataFromEntities([myEntity], {\n * allEntities: metadata.Entities,\n * includeIncoming: true,\n * includeOutgoing: true,\n * depth: 1\n * });\n * ```\n */\nexport function buildERDDataFromEntities(\n entities: EntityInfo[],\n options: BuildERDDataOptions = {}\n): BuildERDDataResult {\n const {\n includeIncoming = true,\n includeOutgoing = true,\n allEntities = [],\n depth = 1\n } = options;\n\n const nodes: ERDNode[] = [];\n const links: ERDLink[] = [];\n const addedNodeIds = new Set<string>();\n const addedLinkKeys = new Set<string>();\n\n // Helper to add a node if not already added\n const addNode = (entity: EntityInfo): void => {\n if (!addedNodeIds.has(entity.ID)) {\n nodes.push(entityInfoToERDNode(entity));\n addedNodeIds.add(entity.ID);\n }\n };\n\n // Helper to add a link if not already added\n const addLink = (link: ERDLink): void => {\n const key = `${link.sourceNodeId}->${link.targetNodeId}:${link.sourceField.id}`;\n if (!addedLinkKeys.has(key)) {\n links.push(link);\n addedLinkKeys.add(key);\n }\n };\n\n // First, add all primary entities\n for (const entity of entities) {\n addNode(entity);\n }\n\n // Process relationships for each primary entity\n for (const entity of entities) {\n // Outgoing relationships (this entity references others via FK)\n if (includeOutgoing) {\n for (const field of entity.Fields) {\n if (field.RelatedEntityID) {\n const relatedEntity = allEntities.find(e => e.ID === field.RelatedEntityID);\n if (relatedEntity) {\n addNode(relatedEntity);\n\n const pkField = relatedEntity.Fields.find(f => f.IsPrimaryKey);\n addLink({\n sourceNodeId: entity.ID,\n targetNodeId: field.RelatedEntityID,\n sourceField: entityFieldToERDField(field),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: entity.ID === field.RelatedEntityID,\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n\n // Incoming relationships (other entities reference this one)\n if (includeIncoming) {\n for (const rel of entity.RelatedEntities) {\n // rel.RelatedEntity is the entity that has the FK pointing to this entity\n const relEntity = allEntities.find(e => e.Name === rel.RelatedEntity);\n if (relEntity) {\n addNode(relEntity);\n\n const fkField = relEntity.Fields.find(f => f.Name === rel.RelatedEntityJoinField);\n const pkField = entity.Fields.find(f => f.IsPrimaryKey);\n\n if (fkField) {\n addLink({\n sourceNodeId: relEntity.ID,\n targetNodeId: entity.ID,\n sourceField: entityFieldToERDField(fkField),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: relEntity.ID === entity.ID,\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n }\n\n // If depth > 1, recursively expand relationships\n if (depth > 1) {\n const processedIds = new Set(entities.map(e => e.ID));\n let currentDepthEntities = [...nodes]\n .map(n => getOriginalEntityFromERDNode(n))\n .filter((e): e is EntityInfo => e !== null && !processedIds.has(e.ID));\n\n for (let d = 1; d < depth; d++) {\n const nextDepthEntities: EntityInfo[] = [];\n\n for (const entity of currentDepthEntities) {\n if (processedIds.has(entity.ID)) continue;\n processedIds.add(entity.ID);\n\n // Outgoing\n if (includeOutgoing) {\n for (const field of entity.Fields) {\n if (field.RelatedEntityID && !addedNodeIds.has(field.RelatedEntityID)) {\n const relatedEntity = allEntities.find(e => e.ID === field.RelatedEntityID);\n if (relatedEntity) {\n addNode(relatedEntity);\n nextDepthEntities.push(relatedEntity);\n\n const pkField = relatedEntity.Fields.find(f => f.IsPrimaryKey);\n addLink({\n sourceNodeId: entity.ID,\n targetNodeId: field.RelatedEntityID,\n sourceField: entityFieldToERDField(field),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: entity.ID === field.RelatedEntityID,\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n\n // Incoming\n if (includeIncoming) {\n for (const rel of entity.RelatedEntities) {\n // rel.RelatedEntity is the entity that has the FK pointing to this entity\n const relEntity = allEntities.find(e => e.Name === rel.RelatedEntity);\n if (relEntity && !addedNodeIds.has(relEntity.ID)) {\n addNode(relEntity);\n nextDepthEntities.push(relEntity);\n\n const fkField = relEntity.Fields.find(f => f.Name === rel.RelatedEntityJoinField);\n const pkField = entity.Fields.find(f => f.IsPrimaryKey);\n\n if (fkField) {\n addLink({\n sourceNodeId: relEntity.ID,\n targetNodeId: entity.ID,\n sourceField: entityFieldToERDField(fkField),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: relEntity.ID === entity.ID,\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n }\n\n currentDepthEntities = nextDepthEntities;\n }\n }\n\n return { nodes, links };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"entity-to-erd-adapter.js","sourceRoot":"","sources":["../../../src/lib/utils/entity-to-erd-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityInfo, EntityFieldInfo } from '@memberjunction/core';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { ERDNode, ERDField, ERDFieldValue, ERDLink } from '../interfaces/erd-types';\n\n/**\n * Converts MemberJunction EntityFieldInfo to generic ERDField format.\n */\nexport function entityFieldToERDField(field: EntityFieldInfo): ERDField {\n const erdField: ERDField = {\n id: field.ID,\n name: field.Name,\n type: field.Type,\n isPrimaryKey: field.IsPrimaryKey,\n description: field.Description,\n allowsNull: field.AllowsNull,\n defaultValue: field.DefaultValue,\n length: field.Length,\n precision: field.Precision,\n scale: field.Scale,\n isVirtual: field.IsVirtual,\n autoIncrement: field.AutoIncrement\n };\n\n // Add related entity info for foreign keys\n if (field.RelatedEntityID) {\n erdField.relatedNodeId = field.RelatedEntityID;\n erdField.relatedNodeName = field.RelatedEntity;\n erdField.relatedFieldName = field.RelatedEntityFieldName;\n }\n\n // Add possible values if they exist\n if (field.EntityFieldValues && field.EntityFieldValues.length > 0) {\n erdField.possibleValues = field.EntityFieldValues.map(v => ({\n id: v.ID,\n value: v.Value,\n code: v.Code,\n description: v.Description,\n sequence: v.Sequence\n } as ERDFieldValue));\n }\n\n return erdField;\n}\n\n/**\n * Converts MemberJunction EntityInfo to generic ERDNode format.\n */\nexport function entityInfoToERDNode(entity: EntityInfo): ERDNode {\n return {\n id: entity.ID,\n name: entity.Name,\n schemaName: entity.SchemaName,\n description: entity.Description,\n status: entity.Status,\n baseTable: entity.BaseTable,\n fields: entity.Fields.map(f => entityFieldToERDField(f)),\n customData: {\n // Store original entity for reverse lookup if needed\n originalEntity: entity\n }\n };\n}\n\n/**\n * Converts an array of MemberJunction EntityInfo objects to ERDNode format.\n */\nexport function entitiesToERDNodes(entities: EntityInfo[]): ERDNode[] {\n return entities.map(e => entityInfoToERDNode(e));\n}\n\n/**\n * Extracts the original EntityInfo from an ERDNode's customData.\n * Returns null if not found.\n */\nexport function getOriginalEntityFromERDNode(node: ERDNode): EntityInfo | null {\n if (node.customData && node.customData['originalEntity']) {\n return node.customData['originalEntity'] as EntityInfo;\n }\n return null;\n}\n\n/**\n * Finds an EntityInfo by node ID from an array.\n */\nexport function findEntityByNodeId(nodeId: string, entities: EntityInfo[]): EntityInfo | undefined {\n return entities.find(e => UUIDsEqual(e.ID, nodeId));\n}\n\n/**\n * Options for building ERD data from entities.\n */\nexport interface BuildERDDataOptions {\n /** Include incoming relationships (entities that reference these entities). Default: true */\n includeIncoming?: boolean;\n /** Include outgoing relationships (entities these reference via FK). Default: true */\n includeOutgoing?: boolean;\n /** All entities in the system (for relationship lookups). Required for includeIncoming/includeOutgoing. */\n allEntities?: EntityInfo[];\n /** Relationship depth - how many hops to include. Default: 1 */\n depth?: number;\n}\n\n/**\n * Result of building ERD data from entities.\n */\nexport interface BuildERDDataResult {\n nodes: ERDNode[];\n links: ERDLink[];\n}\n\n/**\n * Builds ERD nodes and links from an array of EntityInfo objects.\n * This handles the transformation and automatic relationship discovery.\n *\n * @param entities - The primary entities to display\n * @param options - Options for relationship discovery\n * @returns Object containing nodes and links arrays\n *\n * @example\n * ```typescript\n * // Single entity with all related entities\n * const result = buildERDDataFromEntities([myEntity], {\n * allEntities: metadata.Entities,\n * includeIncoming: true,\n * includeOutgoing: true,\n * depth: 1\n * });\n * ```\n */\nexport function buildERDDataFromEntities(\n entities: EntityInfo[],\n options: BuildERDDataOptions = {}\n): BuildERDDataResult {\n const {\n includeIncoming = true,\n includeOutgoing = true,\n allEntities = [],\n depth = 1\n } = options;\n\n const nodes: ERDNode[] = [];\n const links: ERDLink[] = [];\n const addedNodeIds = new Set<string>();\n const addedLinkKeys = new Set<string>();\n\n // Helper to add a node if not already added\n const addNode = (entity: EntityInfo): void => {\n if (!addedNodeIds.has(entity.ID)) {\n nodes.push(entityInfoToERDNode(entity));\n addedNodeIds.add(entity.ID);\n }\n };\n\n // Helper to add a link if not already added\n const addLink = (link: ERDLink): void => {\n const key = `${link.sourceNodeId}->${link.targetNodeId}:${link.sourceField.id}`;\n if (!addedLinkKeys.has(key)) {\n links.push(link);\n addedLinkKeys.add(key);\n }\n };\n\n // First, add all primary entities\n for (const entity of entities) {\n addNode(entity);\n }\n\n // Process relationships for each primary entity\n for (const entity of entities) {\n // Outgoing relationships (this entity references others via FK)\n if (includeOutgoing) {\n for (const field of entity.Fields) {\n if (field.RelatedEntityID) {\n const relatedEntity = allEntities.find(e => UUIDsEqual(e.ID, field.RelatedEntityID));\n if (relatedEntity) {\n addNode(relatedEntity);\n\n const pkField = relatedEntity.Fields.find(f => f.IsPrimaryKey);\n addLink({\n sourceNodeId: entity.ID,\n targetNodeId: field.RelatedEntityID,\n sourceField: entityFieldToERDField(field),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: UUIDsEqual(entity.ID, field.RelatedEntityID),\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n\n // Incoming relationships (other entities reference this one)\n if (includeIncoming) {\n for (const rel of entity.RelatedEntities) {\n // rel.RelatedEntity is the entity that has the FK pointing to this entity\n const relEntity = allEntities.find(e => e.Name === rel.RelatedEntity);\n if (relEntity) {\n addNode(relEntity);\n\n const fkField = relEntity.Fields.find(f => f.Name === rel.RelatedEntityJoinField);\n const pkField = entity.Fields.find(f => f.IsPrimaryKey);\n\n if (fkField) {\n addLink({\n sourceNodeId: relEntity.ID,\n targetNodeId: entity.ID,\n sourceField: entityFieldToERDField(fkField),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: UUIDsEqual(relEntity.ID, entity.ID),\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n }\n\n // If depth > 1, recursively expand relationships\n if (depth > 1) {\n const processedIds = new Set(entities.map(e => e.ID));\n let currentDepthEntities = [...nodes]\n .map(n => getOriginalEntityFromERDNode(n))\n .filter((e): e is EntityInfo => e !== null && !processedIds.has(e.ID));\n\n for (let d = 1; d < depth; d++) {\n const nextDepthEntities: EntityInfo[] = [];\n\n for (const entity of currentDepthEntities) {\n if (processedIds.has(entity.ID)) continue;\n processedIds.add(entity.ID);\n\n // Outgoing\n if (includeOutgoing) {\n for (const field of entity.Fields) {\n if (field.RelatedEntityID && !addedNodeIds.has(field.RelatedEntityID)) {\n const relatedEntity = allEntities.find(e => UUIDsEqual(e.ID, field.RelatedEntityID));\n if (relatedEntity) {\n addNode(relatedEntity);\n nextDepthEntities.push(relatedEntity);\n\n const pkField = relatedEntity.Fields.find(f => f.IsPrimaryKey);\n addLink({\n sourceNodeId: entity.ID,\n targetNodeId: field.RelatedEntityID,\n sourceField: entityFieldToERDField(field),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: UUIDsEqual(entity.ID, field.RelatedEntityID),\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n\n // Incoming\n if (includeIncoming) {\n for (const rel of entity.RelatedEntities) {\n // rel.RelatedEntity is the entity that has the FK pointing to this entity\n const relEntity = allEntities.find(e => e.Name === rel.RelatedEntity);\n if (relEntity && !addedNodeIds.has(relEntity.ID)) {\n addNode(relEntity);\n nextDepthEntities.push(relEntity);\n\n const fkField = relEntity.Fields.find(f => f.Name === rel.RelatedEntityJoinField);\n const pkField = entity.Fields.find(f => f.IsPrimaryKey);\n\n if (fkField) {\n addLink({\n sourceNodeId: relEntity.ID,\n targetNodeId: entity.ID,\n sourceField: entityFieldToERDField(fkField),\n targetField: pkField ? entityFieldToERDField(pkField) : undefined,\n isSelfReference: UUIDsEqual(relEntity.ID, entity.ID),\n relationshipType: 'many-to-one'\n });\n }\n }\n }\n }\n }\n\n currentDepthEntities = nextDepthEntities;\n }\n }\n\n return { nodes, links };\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-entity-relationship-diagram",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.5.0",
|
|
4
4
|
"description": "MemberJunction: Entity Relationship Diagram (ERD) component for visualizing entity relationships using D3.js force-directed graphs",
|
|
5
5
|
"main": "./dist/public-api.js",
|
|
6
6
|
"typings": "./dist/public-api.d.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@angular/forms": "21.1.3",
|
|
38
38
|
"@dagrejs/dagre": "^1.1.8",
|
|
39
|
-
"@memberjunction/core": "5.
|
|
39
|
+
"@memberjunction/core": "5.5.0",
|
|
40
40
|
"@progress/kendo-angular-layout": "22.0.1",
|
|
41
41
|
"@types/d3": "^7.4.3",
|
|
42
42
|
"d3": "^7.9.0",
|