@memberjunction/ng-dashboards 2.42.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.
Files changed (117) hide show
  1. package/dist/AI/ai-dashboard.component.d.ts +54 -0
  2. package/dist/AI/ai-dashboard.component.d.ts.map +1 -0
  3. package/dist/AI/ai-dashboard.component.js +248 -0
  4. package/dist/AI/ai-dashboard.component.js.map +1 -0
  5. package/dist/AI/components/agents/agent-configuration.component.d.ts +41 -0
  6. package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -0
  7. package/dist/AI/components/agents/agent-configuration.component.js +325 -0
  8. package/dist/AI/components/agents/agent-configuration.component.js.map +1 -0
  9. package/dist/AI/components/agents/agent-editor.component.d.ts +77 -0
  10. package/dist/AI/components/agents/agent-editor.component.d.ts.map +1 -0
  11. package/dist/AI/components/agents/agent-editor.component.js +869 -0
  12. package/dist/AI/components/agents/agent-editor.component.js.map +1 -0
  13. package/dist/AI/components/agents/agent-filter-panel.component.d.ts +33 -0
  14. package/dist/AI/components/agents/agent-filter-panel.component.d.ts.map +1 -0
  15. package/dist/AI/components/agents/agent-filter-panel.component.js +144 -0
  16. package/dist/AI/components/agents/agent-filter-panel.component.js.map +1 -0
  17. package/dist/AI/components/execution-monitoring.component.d.ts +13 -0
  18. package/dist/AI/components/execution-monitoring.component.d.ts.map +1 -0
  19. package/dist/AI/components/execution-monitoring.component.js +30 -0
  20. package/dist/AI/components/execution-monitoring.component.js.map +1 -0
  21. package/dist/AI/components/models/model-management.component.d.ts +73 -0
  22. package/dist/AI/components/models/model-management.component.d.ts.map +1 -0
  23. package/dist/AI/components/models/model-management.component.js +669 -0
  24. package/dist/AI/components/models/model-management.component.js.map +1 -0
  25. package/dist/AI/components/prompts/prompt-filter-panel.component.d.ts +49 -0
  26. package/dist/AI/components/prompts/prompt-filter-panel.component.d.ts.map +1 -0
  27. package/dist/AI/components/prompts/prompt-filter-panel.component.js +186 -0
  28. package/dist/AI/components/prompts/prompt-filter-panel.component.js.map +1 -0
  29. package/dist/AI/components/prompts/prompt-management.component.d.ts +113 -0
  30. package/dist/AI/components/prompts/prompt-management.component.d.ts.map +1 -0
  31. package/dist/AI/components/prompts/prompt-management.component.js +1316 -0
  32. package/dist/AI/components/prompts/prompt-management.component.js.map +1 -0
  33. package/dist/AI/components/system/system-config-filter-panel.component.d.ts +33 -0
  34. package/dist/AI/components/system/system-config-filter-panel.component.d.ts.map +1 -0
  35. package/dist/AI/components/system/system-config-filter-panel.component.js +146 -0
  36. package/dist/AI/components/system/system-config-filter-panel.component.js.map +1 -0
  37. package/dist/AI/components/system/system-configuration.component.d.ts +37 -0
  38. package/dist/AI/components/system/system-configuration.component.d.ts.map +1 -0
  39. package/dist/AI/components/system/system-configuration.component.js +311 -0
  40. package/dist/AI/components/system/system-configuration.component.js.map +1 -0
  41. package/dist/Actions/actions-management-dashboard.component.d.ts +50 -0
  42. package/dist/Actions/actions-management-dashboard.component.d.ts.map +1 -0
  43. package/dist/Actions/actions-management-dashboard.component.js +282 -0
  44. package/dist/Actions/actions-management-dashboard.component.js.map +1 -0
  45. package/dist/Actions/components/actions-list-view.component.d.ts +52 -0
  46. package/dist/Actions/components/actions-list-view.component.d.ts.map +1 -0
  47. package/dist/Actions/components/actions-list-view.component.js +366 -0
  48. package/dist/Actions/components/actions-list-view.component.js.map +1 -0
  49. package/dist/Actions/components/actions-overview.component.d.ts +71 -0
  50. package/dist/Actions/components/actions-overview.component.d.ts.map +1 -0
  51. package/dist/Actions/components/actions-overview.component.js +605 -0
  52. package/dist/Actions/components/actions-overview.component.js.map +1 -0
  53. package/dist/Actions/components/categories-list-view.component.d.ts +11 -0
  54. package/dist/Actions/components/categories-list-view.component.d.ts.map +1 -0
  55. package/dist/Actions/components/categories-list-view.component.js +35 -0
  56. package/dist/Actions/components/categories-list-view.component.js.map +1 -0
  57. package/dist/Actions/components/code-management.component.d.ts +11 -0
  58. package/dist/Actions/components/code-management.component.d.ts.map +1 -0
  59. package/dist/Actions/components/code-management.component.js +35 -0
  60. package/dist/Actions/components/code-management.component.js.map +1 -0
  61. package/dist/Actions/components/entity-integration.component.d.ts +11 -0
  62. package/dist/Actions/components/entity-integration.component.d.ts.map +1 -0
  63. package/dist/Actions/components/entity-integration.component.js +35 -0
  64. package/dist/Actions/components/entity-integration.component.js.map +1 -0
  65. package/dist/Actions/components/execution-monitoring.component.d.ts +83 -0
  66. package/dist/Actions/components/execution-monitoring.component.d.ts.map +1 -0
  67. package/dist/Actions/components/execution-monitoring.component.js +629 -0
  68. package/dist/Actions/components/execution-monitoring.component.js.map +1 -0
  69. package/dist/Actions/components/executions-list-view.component.d.ts +11 -0
  70. package/dist/Actions/components/executions-list-view.component.d.ts.map +1 -0
  71. package/dist/Actions/components/executions-list-view.component.js +35 -0
  72. package/dist/Actions/components/executions-list-view.component.js.map +1 -0
  73. package/dist/Actions/components/scheduled-actions.component.d.ts +11 -0
  74. package/dist/Actions/components/scheduled-actions.component.d.ts.map +1 -0
  75. package/dist/Actions/components/scheduled-actions.component.js +35 -0
  76. package/dist/Actions/components/scheduled-actions.component.js.map +1 -0
  77. package/dist/Actions/components/security-permissions.component.d.ts +11 -0
  78. package/dist/Actions/components/security-permissions.component.d.ts.map +1 -0
  79. package/dist/Actions/components/security-permissions.component.js +35 -0
  80. package/dist/Actions/components/security-permissions.component.js.map +1 -0
  81. package/dist/Actions/index.d.ts +11 -0
  82. package/dist/Actions/index.d.ts.map +1 -0
  83. package/dist/Actions/index.js +11 -0
  84. package/dist/Actions/index.js.map +1 -0
  85. package/dist/EntityAdmin/components/entity-details.component.d.ts +50 -0
  86. package/dist/EntityAdmin/components/entity-details.component.d.ts.map +1 -0
  87. package/dist/EntityAdmin/components/entity-details.component.js +684 -0
  88. package/dist/EntityAdmin/components/entity-details.component.js.map +1 -0
  89. package/dist/EntityAdmin/components/entity-filter-panel.component.d.ts +31 -0
  90. package/dist/EntityAdmin/components/entity-filter-panel.component.d.ts.map +1 -0
  91. package/dist/EntityAdmin/components/entity-filter-panel.component.js +162 -0
  92. package/dist/EntityAdmin/components/entity-filter-panel.component.js.map +1 -0
  93. package/dist/EntityAdmin/components/erd-composite.component.d.ts +73 -0
  94. package/dist/EntityAdmin/components/erd-composite.component.d.ts.map +1 -0
  95. package/dist/EntityAdmin/components/erd-composite.component.js +288 -0
  96. package/dist/EntityAdmin/components/erd-composite.component.js.map +1 -0
  97. package/dist/EntityAdmin/components/erd-diagram.component.d.ts +47 -0
  98. package/dist/EntityAdmin/components/erd-diagram.component.d.ts.map +1 -0
  99. package/dist/EntityAdmin/components/erd-diagram.component.js +618 -0
  100. package/dist/EntityAdmin/components/erd-diagram.component.js.map +1 -0
  101. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts +50 -0
  102. package/dist/EntityAdmin/entity-admin-dashboard.component.d.ts.map +1 -0
  103. package/dist/EntityAdmin/entity-admin-dashboard.component.js +190 -0
  104. package/dist/EntityAdmin/entity-admin-dashboard.component.js.map +1 -0
  105. package/dist/generic/base-dashboard.d.ts +65 -0
  106. package/dist/generic/base-dashboard.d.ts.map +1 -0
  107. package/dist/generic/base-dashboard.js +86 -0
  108. package/dist/generic/base-dashboard.js.map +1 -0
  109. package/dist/module.d.ts +43 -0
  110. package/dist/module.d.ts.map +1 -0
  111. package/dist/module.js +141 -0
  112. package/dist/module.js.map +1 -0
  113. package/dist/public-api.d.ts +6 -0
  114. package/dist/public-api.d.ts.map +1 -0
  115. package/dist/public-api.js +18 -0
  116. package/dist/public-api.js.map +1 -0
  117. package/package.json +46 -0
@@ -0,0 +1,618 @@
1
+ import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
2
+ import * as d3 from 'd3';
3
+ import * as i0 from "@angular/core";
4
+ const _c0 = ["erdContainer"];
5
+ export class ERDDiagramComponent {
6
+ constructor() {
7
+ this.filteredEntities = [];
8
+ this.allEntityFields = [];
9
+ this.isRefreshingERD = false;
10
+ this.selectedEntityId = null;
11
+ this.entitySelected = new EventEmitter();
12
+ this.entityDeselected = new EventEmitter();
13
+ this.refreshERD = new EventEmitter();
14
+ this.nodes = [];
15
+ this.links = [];
16
+ this.lastKnownSize = { width: 0, height: 0 };
17
+ }
18
+ ngAfterViewInit() {
19
+ setTimeout(() => {
20
+ this.setupERD();
21
+ }, 100);
22
+ // Add resize observer to handle container size changes
23
+ this.setupResizeObserver();
24
+ }
25
+ ngOnChanges(changes) {
26
+ if (changes['filteredEntities'] && !changes['filteredEntities'].firstChange) {
27
+ this.setupERD();
28
+ }
29
+ if (changes['selectedEntityId'] && !changes['selectedEntityId'].firstChange) {
30
+ this.updateSelectionHighlighting();
31
+ }
32
+ }
33
+ ngOnDestroy() {
34
+ if (this.simulation) {
35
+ this.simulation.stop();
36
+ }
37
+ if (this.resizeObserver) {
38
+ this.resizeObserver.disconnect();
39
+ }
40
+ if (this.resizeTimeout) {
41
+ clearTimeout(this.resizeTimeout);
42
+ }
43
+ }
44
+ zoomIn() {
45
+ if (this.zoom) {
46
+ this.svg.transition().duration(300).call(this.zoom.scaleBy, 1.5);
47
+ }
48
+ }
49
+ zoomOut() {
50
+ if (this.zoom) {
51
+ this.svg.transition().duration(300).call(this.zoom.scaleBy, 0.67);
52
+ }
53
+ }
54
+ resetZoom() {
55
+ if (this.zoom) {
56
+ this.svg.transition().duration(500).call(this.zoom.transform, d3.zoomIdentity);
57
+ }
58
+ }
59
+ setupERD() {
60
+ var _a;
61
+ if (!((_a = this.erdContainer) === null || _a === void 0 ? void 0 : _a.nativeElement)) {
62
+ return;
63
+ }
64
+ this.clearVisualization();
65
+ this.createNodes();
66
+ this.createLinks();
67
+ this.createVisualization();
68
+ }
69
+ resizeSVG() {
70
+ var _a;
71
+ if (!this.svg || !((_a = this.erdContainer) === null || _a === void 0 ? void 0 : _a.nativeElement)) {
72
+ return;
73
+ }
74
+ const container = this.erdContainer.nativeElement;
75
+ const width = container.clientWidth || 800;
76
+ const height = container.clientHeight || 600;
77
+ // Prevent resize loops by checking if size actually changed
78
+ if (Math.abs(this.lastKnownSize.width - width) < 5 &&
79
+ Math.abs(this.lastKnownSize.height - height) < 5) {
80
+ return;
81
+ }
82
+ this.lastKnownSize = { width, height };
83
+ // Use CSS for sizing instead of SVG attributes to prevent feedback loops
84
+ const svgElement = this.svg.node();
85
+ if (svgElement) {
86
+ svgElement.style.width = '100%';
87
+ svgElement.style.height = '100%';
88
+ }
89
+ // Set viewBox instead of width/height attributes
90
+ this.svg.attr('viewBox', `0 0 ${width} ${height}`);
91
+ // Update simulation center force
92
+ if (this.simulation) {
93
+ this.simulation.force('center', d3.forceCenter(width / 2, height / 2));
94
+ this.simulation.alpha(0.3).restart();
95
+ }
96
+ }
97
+ triggerResize() {
98
+ // Manual resize trigger for external components (like splitter layout changes)
99
+ if (this.resizeTimeout) {
100
+ clearTimeout(this.resizeTimeout);
101
+ }
102
+ this.resizeTimeout = window.setTimeout(() => {
103
+ this.resizeSVG();
104
+ }, 100);
105
+ }
106
+ setupResizeObserver() {
107
+ var _a;
108
+ if (!((_a = this.erdContainer) === null || _a === void 0 ? void 0 : _a.nativeElement)) {
109
+ return;
110
+ }
111
+ this.resizeObserver = new ResizeObserver((entries) => {
112
+ // Debounce resize events to prevent feedback loops
113
+ if (this.resizeTimeout) {
114
+ clearTimeout(this.resizeTimeout);
115
+ }
116
+ this.resizeTimeout = window.setTimeout(() => {
117
+ var _a;
118
+ // Double-check that container size actually changed
119
+ const container = (_a = this.erdContainer) === null || _a === void 0 ? void 0 : _a.nativeElement;
120
+ if (!container)
121
+ return;
122
+ const newWidth = container.clientWidth;
123
+ const newHeight = container.clientHeight;
124
+ // Only resize if there's a meaningful size change
125
+ if (Math.abs(this.lastKnownSize.width - newWidth) >= 5 ||
126
+ Math.abs(this.lastKnownSize.height - newHeight) >= 5) {
127
+ requestAnimationFrame(() => {
128
+ this.resizeSVG();
129
+ });
130
+ }
131
+ }, 50);
132
+ });
133
+ this.resizeObserver.observe(this.erdContainer.nativeElement);
134
+ }
135
+ clearVisualization() {
136
+ if (this.simulation) {
137
+ this.simulation.stop();
138
+ }
139
+ d3.select(this.erdContainer.nativeElement).selectAll('*').remove();
140
+ }
141
+ createNodes() {
142
+ this.nodes = this.filteredEntities.map(entity => {
143
+ const entityFields = this.allEntityFields.filter(f => f.EntityID === entity.ID);
144
+ const primaryKeys = entityFields.filter(f => f.IsPrimaryKey);
145
+ const foreignKeys = entityFields.filter(f => f.RelatedEntityID && !f.IsPrimaryKey);
146
+ const fieldCount = Math.max(1, primaryKeys.length + foreignKeys.length);
147
+ const baseHeight = 60;
148
+ const fieldHeight = 20;
149
+ const maxHeight = 300;
150
+ const calculatedHeight = Math.min(baseHeight + (fieldCount * fieldHeight), maxHeight);
151
+ return {
152
+ id: entity.ID,
153
+ name: entity.Name || entity.SchemaName || 'Unknown',
154
+ entity: entity,
155
+ width: 180,
156
+ height: calculatedHeight,
157
+ primaryKeys: primaryKeys,
158
+ foreignKeys: foreignKeys
159
+ };
160
+ });
161
+ }
162
+ createLinks() {
163
+ this.links = [];
164
+ const entityMap = new Map(this.nodes.map(node => [node.id, node]));
165
+ this.allEntityFields.forEach(field => {
166
+ if (field.RelatedEntityID && !field.IsPrimaryKey) {
167
+ const sourceNode = entityMap.get(field.EntityID);
168
+ const targetNode = entityMap.get(field.RelatedEntityID);
169
+ if (sourceNode && targetNode) {
170
+ const isSelfReference = field.EntityID === field.RelatedEntityID;
171
+ // Find target field (primary key in target entity)
172
+ const targetField = targetNode.primaryKeys.find(pk => pk.Name === field.RelatedEntityFieldName);
173
+ this.links.push({
174
+ source: sourceNode,
175
+ target: targetNode,
176
+ field: field,
177
+ sourceField: field,
178
+ targetField: targetField,
179
+ isSelfReference: isSelfReference
180
+ });
181
+ }
182
+ }
183
+ });
184
+ }
185
+ createVisualization() {
186
+ const container = this.erdContainer.nativeElement;
187
+ const width = container.clientWidth || 800;
188
+ const height = container.clientHeight || 600;
189
+ // Set initial known size
190
+ this.lastKnownSize = { width, height };
191
+ // Create zoom behavior
192
+ this.zoom = d3.zoom().on('zoom', (event) => {
193
+ this.svg.select('.chart-area').attr('transform', event.transform);
194
+ });
195
+ // Create SVG with CSS-based sizing
196
+ this.svg = d3
197
+ .select(container)
198
+ .append('svg')
199
+ .attr('viewBox', `0 0 ${width} ${height}`)
200
+ .style('width', '100%')
201
+ .style('height', '100%')
202
+ .style('position', 'absolute')
203
+ .style('top', '0')
204
+ .style('left', '0')
205
+ .call(this.zoom)
206
+ .on('click', (event) => {
207
+ // Deselect entity when clicking on background
208
+ if (event.target === event.currentTarget) {
209
+ this.entityDeselected.emit();
210
+ this.clearAllHighlighting();
211
+ }
212
+ });
213
+ const chartArea = this.svg.append('g').attr('class', 'chart-area');
214
+ // Define arrow marker for directional links
215
+ this.svg
216
+ .append('defs')
217
+ .selectAll('marker')
218
+ .data(['end-arrow'])
219
+ .enter()
220
+ .append('marker')
221
+ .attr('id', 'end-arrow')
222
+ .attr('viewBox', '0 -5 10 10')
223
+ .attr('refX', 8)
224
+ .attr('refY', 0)
225
+ .attr('markerWidth', 6)
226
+ .attr('markerHeight', 6)
227
+ .attr('orient', 'auto')
228
+ .append('path')
229
+ .attr('d', 'M0,-5L10,0L0,5')
230
+ .attr('fill', '#666');
231
+ // Create force simulation with dynamic distance based on rectangle sizes
232
+ this.simulation = d3
233
+ .forceSimulation(this.nodes)
234
+ .force('link', d3
235
+ .forceLink(this.links)
236
+ .id((d) => d.id)
237
+ .distance((d) => {
238
+ const sourceNode = d.source;
239
+ const targetNode = d.target;
240
+ const sourceSize = Math.max(sourceNode.width, sourceNode.height);
241
+ const targetSize = Math.max(targetNode.width, targetNode.height);
242
+ return (sourceSize + targetSize) / 2 + 80;
243
+ }))
244
+ .force('charge', d3.forceManyBody().strength(-800))
245
+ .force('center', d3.forceCenter(width / 2, height / 2))
246
+ .force('collision', d3.forceCollide().radius((d) => {
247
+ const node = d;
248
+ return Math.max(node.width, node.height) / 2 + 20;
249
+ }));
250
+ // Create links with arrows
251
+ const link = chartArea.selectAll('.link').data(this.links).enter().append('g').attr('class', 'link-group');
252
+ // Add the link line
253
+ link
254
+ .append('path')
255
+ .attr('class', 'link')
256
+ .attr('stroke', '#666')
257
+ .attr('stroke-opacity', 0.8)
258
+ .attr('stroke-width', 2)
259
+ .attr('fill', 'none')
260
+ .attr('marker-end', (d) => (d.isSelfReference ? 'none' : 'url(#end-arrow)'));
261
+ // Add labels for foreign key relationships
262
+ link
263
+ .append('text')
264
+ .attr('class', 'link-label')
265
+ .attr('font-size', '10px')
266
+ .attr('fill', '#666')
267
+ .attr('text-anchor', 'middle')
268
+ .text((d) => d.sourceField.Name || '');
269
+ // Create entity rectangle nodes
270
+ const node = chartArea
271
+ .selectAll('.node')
272
+ .data(this.nodes)
273
+ .enter()
274
+ .append('g')
275
+ .attr('class', 'node')
276
+ .style('cursor', 'pointer')
277
+ .call(d3
278
+ .drag()
279
+ .on('start', (event, d) => this.dragstarted(event, d))
280
+ .on('drag', (event, d) => this.dragged(event, d))
281
+ .on('end', (event, d) => this.dragended(event, d)));
282
+ // Add main rectangle for entity
283
+ node
284
+ .append('rect')
285
+ .attr('class', 'entity-rect')
286
+ .attr('width', (d) => d.width)
287
+ .attr('height', (d) => d.height)
288
+ .attr('x', (d) => -d.width / 2)
289
+ .attr('y', (d) => -d.height / 2)
290
+ .attr('fill', '#f8f9fa')
291
+ .attr('stroke', '#333')
292
+ .attr('stroke-width', 2)
293
+ .attr('rx', 4);
294
+ // Add entity header
295
+ node
296
+ .append('rect')
297
+ .attr('class', 'entity-header')
298
+ .attr('width', (d) => d.width)
299
+ .attr('height', 30)
300
+ .attr('x', (d) => -d.width / 2)
301
+ .attr('y', (d) => -d.height / 2)
302
+ .attr('fill', '#007bff')
303
+ .attr('rx', 4);
304
+ // Add entity header bottom border (to make only top rounded)
305
+ node
306
+ .append('rect')
307
+ .attr('class', 'entity-header-bottom')
308
+ .attr('width', (d) => d.width)
309
+ .attr('height', 15)
310
+ .attr('x', (d) => -d.width / 2)
311
+ .attr('y', (d) => -d.height / 2 + 15)
312
+ .attr('fill', '#007bff');
313
+ // Add entity name in header
314
+ node
315
+ .append('text')
316
+ .attr('class', 'entity-name')
317
+ .attr('text-anchor', 'middle')
318
+ .attr('y', (d) => -d.height / 2 + 20)
319
+ .attr('font-size', '12px')
320
+ .attr('font-weight', 'bold')
321
+ .attr('fill', 'white')
322
+ .text((d) => d.name);
323
+ // Add primary key fields and foreign key fields
324
+ node.each(function (d) {
325
+ const group = d3.select(this);
326
+ let currentY = -d.height / 2 + 40;
327
+ // Add primary keys
328
+ d.primaryKeys.forEach((pk) => {
329
+ const fieldGroup = group.append('g').attr('class', 'field-group primary-key');
330
+ fieldGroup
331
+ .append('rect')
332
+ .attr('class', 'field-bg')
333
+ .attr('x', -d.width / 2 + 2)
334
+ .attr('y', currentY - 15)
335
+ .attr('width', d.width - 4)
336
+ .attr('height', 18)
337
+ .attr('fill', '#fff3cd');
338
+ fieldGroup
339
+ .append('text')
340
+ .attr('class', 'field-icon')
341
+ .attr('x', -d.width / 2 + 8)
342
+ .attr('y', currentY - 2)
343
+ .attr('font-size', '10px')
344
+ .attr('fill', '#856404')
345
+ .text('🔑');
346
+ fieldGroup
347
+ .append('text')
348
+ .attr('class', 'field-name')
349
+ .attr('x', -d.width / 2 + 25)
350
+ .attr('y', currentY - 2)
351
+ .attr('font-size', '11px')
352
+ .attr('font-weight', 'bold')
353
+ .attr('fill', '#856404')
354
+ .text(pk.Name || '');
355
+ currentY += 20;
356
+ });
357
+ // Add foreign key fields
358
+ d.foreignKeys.forEach((fk) => {
359
+ const fieldGroup = group.append('g').attr('class', 'field-group foreign-key');
360
+ fieldGroup
361
+ .append('rect')
362
+ .attr('class', 'field-bg')
363
+ .attr('x', -d.width / 2 + 2)
364
+ .attr('y', currentY - 15)
365
+ .attr('width', d.width - 4)
366
+ .attr('height', 18)
367
+ .attr('fill', '#cce5ff');
368
+ fieldGroup
369
+ .append('text')
370
+ .attr('class', 'field-icon')
371
+ .attr('x', -d.width / 2 + 8)
372
+ .attr('y', currentY - 2)
373
+ .attr('font-size', '10px')
374
+ .attr('fill', '#004085')
375
+ .text('🔗');
376
+ fieldGroup
377
+ .append('text')
378
+ .attr('class', 'field-name')
379
+ .attr('x', -d.width / 2 + 25)
380
+ .attr('y', currentY - 2)
381
+ .attr('font-size', '11px')
382
+ .attr('fill', '#004085')
383
+ .text(fk.Name || '');
384
+ currentY += 20;
385
+ });
386
+ });
387
+ // Add click handler for entity selection
388
+ node
389
+ .on('click', (event, d) => {
390
+ event.stopPropagation();
391
+ // Check if this entity is already selected
392
+ if (this.selectedEntityId === d.entity.ID) {
393
+ // If clicking on the selected entity, deselect it
394
+ this.entityDeselected.emit();
395
+ }
396
+ else {
397
+ // Select the new entity
398
+ this.entitySelected.emit(d.entity);
399
+ // Check if entity is too small and needs auto-zoom
400
+ const currentTransform = d3.zoomTransform(this.svg.node());
401
+ const currentRenderedWidth = d.width * currentTransform.k;
402
+ const shouldAutoZoom = currentRenderedWidth < 20;
403
+ if (shouldAutoZoom) {
404
+ this.zoomToEntity(d.entity.ID);
405
+ }
406
+ }
407
+ // Update visual selection state
408
+ this.updateSelectionHighlighting();
409
+ })
410
+ .append('title')
411
+ .text((d) => `${d.name}\nPrimary Keys: ${d.primaryKeys.length}\nForeign Keys: ${d.foreignKeys.length}`);
412
+ // Update positions on simulation tick
413
+ this.simulation.on('tick', () => {
414
+ // Update link positions with proper rectangle edge connections
415
+ link.select('path').attr('d', (d) => {
416
+ const source = d.source;
417
+ const target = d.target;
418
+ if (d.isSelfReference) {
419
+ // Handle self-referencing relationships with a loop
420
+ const loopSize = 30;
421
+ return `M ${source.x + source.width / 2} ${source.y}
422
+ Q ${source.x + source.width / 2 + loopSize} ${source.y - loopSize}
423
+ ${source.x} ${source.y - source.height / 2}`;
424
+ }
425
+ else {
426
+ // Calculate connection points with 90-degree routing
427
+ const sourceConnectPoint = this.getSourceConnectionPoint(source, target, d.sourceField);
428
+ const targetConnectPoint = this.getTargetConnectionPoint(target, d.targetField);
429
+ return this.createOrthogonalPath(sourceConnectPoint, targetConnectPoint);
430
+ }
431
+ });
432
+ // Update link labels
433
+ link.select('text').attr('transform', (d) => {
434
+ if (d.isSelfReference) {
435
+ const source = d.source;
436
+ return `translate(${source.x + source.width / 2 + 15}, ${source.y - source.height / 2 - 10})`;
437
+ }
438
+ else {
439
+ const source = d.source;
440
+ const target = d.target;
441
+ const midX = (source.x + target.x) / 2;
442
+ const midY = (source.y + target.y) / 2;
443
+ return `translate(${midX}, ${midY - 8})`;
444
+ }
445
+ });
446
+ node.attr('transform', (d) => `translate(${d.x},${d.y})`);
447
+ });
448
+ }
449
+ getSourceConnectionPoint(sourceNode, _targetNode, field) {
450
+ // Always connect from the right edge for source connections
451
+ let connectY = sourceNode.y;
452
+ // If we have field information, connect near the foreign key field
453
+ if (field) {
454
+ const fkIndex = sourceNode.foreignKeys.findIndex((fk) => fk.ID === field.ID);
455
+ if (fkIndex >= 0) {
456
+ const fieldY = -sourceNode.height / 2 + 40 + sourceNode.primaryKeys.length * 20 + fkIndex * 20;
457
+ connectY = sourceNode.y + fieldY + 10;
458
+ }
459
+ }
460
+ return {
461
+ x: sourceNode.x + sourceNode.width / 2,
462
+ y: connectY,
463
+ };
464
+ }
465
+ getTargetConnectionPoint(targetNode, targetField) {
466
+ // Always connect to the left edge of target
467
+ let connectY = targetNode.y - targetNode.height / 2 + 40; // Default to primary key area
468
+ // If we have target field information, connect near the specific primary key field
469
+ if (targetField && targetField.IsPrimaryKey) {
470
+ const pkIndex = targetNode.primaryKeys.findIndex((pk) => pk.ID === targetField.ID);
471
+ if (pkIndex >= 0) {
472
+ const fieldY = -targetNode.height / 2 + 40 + pkIndex * 20;
473
+ connectY = targetNode.y + fieldY + 10;
474
+ }
475
+ }
476
+ return {
477
+ x: targetNode.x - targetNode.width / 2,
478
+ y: connectY,
479
+ };
480
+ }
481
+ createOrthogonalPath(source, target) {
482
+ // Ensure right-to-left connection with proper orthogonal routing
483
+ const dx = target.x - source.x;
484
+ // Calculate midpoint for orthogonal routing
485
+ let midX;
486
+ if (dx > 0) {
487
+ // Target is to the right of source - use 70% of distance for turn point
488
+ midX = source.x + dx * 0.7;
489
+ }
490
+ else {
491
+ // Target is to the left of source - create wider arc for better visibility
492
+ midX = source.x + Math.max(dx * 0.3, -50); // Don't go too far left
493
+ }
494
+ // Create clean orthogonal path
495
+ return `M ${source.x} ${source.y}
496
+ L ${midX} ${source.y}
497
+ L ${midX} ${target.y}
498
+ L ${target.x} ${target.y}`;
499
+ }
500
+ clearAllHighlighting() {
501
+ if (!this.svg)
502
+ return;
503
+ this.svg.selectAll('.node')
504
+ .classed('selected', false)
505
+ .classed('relationship-connected', false)
506
+ .classed('entity-connections-highlighted', false);
507
+ this.svg.selectAll('.entity-rect')
508
+ .classed('highlighted', false)
509
+ .classed('relationship-highlighted', false)
510
+ .classed('connection-highlighted', false)
511
+ .style('stroke', '#333')
512
+ .style('stroke-width', '2px')
513
+ .style('filter', null);
514
+ this.svg.selectAll('.link-group')
515
+ .classed('highlighted', false);
516
+ this.svg.selectAll('.link')
517
+ .classed('highlighted', false);
518
+ this.svg.selectAll('.link-label')
519
+ .classed('highlighted', false);
520
+ }
521
+ updateSelectionHighlighting() {
522
+ if (!this.svg)
523
+ return;
524
+ // Clear existing selections
525
+ this.clearAllHighlighting();
526
+ // Highlight selected entity if any
527
+ if (this.selectedEntityId) {
528
+ this.svg.selectAll('.node')
529
+ .filter((d) => d.id === this.selectedEntityId)
530
+ .classed('selected', true)
531
+ .select('.entity-rect')
532
+ .style('stroke', '#4CAF50')
533
+ .style('stroke-width', '4px')
534
+ .style('filter', 'drop-shadow(0 0 8px rgba(76, 175, 80, 0.6))');
535
+ }
536
+ }
537
+ zoomToEntity(entityId) {
538
+ const node = this.nodes.find(n => n.id === entityId);
539
+ if (!node || !this.zoom)
540
+ return;
541
+ const container = this.erdContainer.nativeElement;
542
+ const width = container.clientWidth;
543
+ const height = container.clientHeight;
544
+ const scale = 1.5;
545
+ const x = width / 2 - (node.x || 0) * scale;
546
+ const y = height / 2 - (node.y || 0) * scale;
547
+ this.svg.transition().duration(750).call(this.zoom.transform, d3.zoomIdentity.translate(x, y).scale(scale));
548
+ }
549
+ dragstarted(event, d) {
550
+ if (!event.active)
551
+ this.simulation.alphaTarget(0.3).restart();
552
+ d.fx = d.x;
553
+ d.fy = d.y;
554
+ }
555
+ dragged(event, d) {
556
+ d.fx = event.x;
557
+ d.fy = event.y;
558
+ }
559
+ dragended(event, d) {
560
+ if (!event.active)
561
+ this.simulation.alphaTarget(0);
562
+ d.fx = null;
563
+ d.fy = null;
564
+ }
565
+ }
566
+ ERDDiagramComponent.ɵfac = function ERDDiagramComponent_Factory(t) { return new (t || ERDDiagramComponent)(); };
567
+ ERDDiagramComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ERDDiagramComponent, selectors: [["mj-erd-diagram"]], viewQuery: function ERDDiagramComponent_Query(rf, ctx) { if (rf & 1) {
568
+ i0.ɵɵviewQuery(_c0, 5);
569
+ } if (rf & 2) {
570
+ let _t;
571
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.erdContainer = _t.first);
572
+ } }, inputs: { filteredEntities: "filteredEntities", allEntityFields: "allEntityFields", isRefreshingERD: "isRefreshingERD", selectedEntityId: "selectedEntityId" }, outputs: { entitySelected: "entitySelected", entityDeselected: "entityDeselected", refreshERD: "refreshERD" }, features: [i0.ɵɵNgOnChangesFeature], decls: 15, vars: 0, consts: [["erdContainer", ""], [1, "erd-section"], [1, "section-header"], [1, "erd-controls"], ["title", "Zoom In", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-search-plus"], ["title", "Zoom Out", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-search-minus"], ["title", "Reset Zoom", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-expand"], ["title", "Refresh Diagram", 1, "control-btn", 3, "click"], [1, "fa-solid", "fa-refresh"], [1, "erd-container"]], template: function ERDDiagramComponent_Template(rf, ctx) { if (rf & 1) {
573
+ const _r1 = i0.ɵɵgetCurrentView();
574
+ i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "h3");
575
+ i0.ɵɵtext(3, "Entity Relationship Diagram");
576
+ i0.ɵɵelementEnd();
577
+ i0.ɵɵelementStart(4, "div", 3)(5, "button", 4);
578
+ i0.ɵɵlistener("click", function ERDDiagramComponent_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.zoomIn()); });
579
+ i0.ɵɵelement(6, "span", 5);
580
+ i0.ɵɵelementEnd();
581
+ i0.ɵɵelementStart(7, "button", 6);
582
+ i0.ɵɵlistener("click", function ERDDiagramComponent_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.zoomOut()); });
583
+ i0.ɵɵelement(8, "span", 7);
584
+ i0.ɵɵelementEnd();
585
+ i0.ɵɵelementStart(9, "button", 8);
586
+ i0.ɵɵlistener("click", function ERDDiagramComponent_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.resetZoom()); });
587
+ i0.ɵɵelement(10, "span", 9);
588
+ i0.ɵɵelementEnd();
589
+ i0.ɵɵelementStart(11, "button", 10);
590
+ i0.ɵɵlistener("click", function ERDDiagramComponent_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.setupERD()); });
591
+ i0.ɵɵelement(12, "span", 11);
592
+ i0.ɵɵelementEnd()()();
593
+ i0.ɵɵelement(13, "div", 12, 0);
594
+ i0.ɵɵelementEnd();
595
+ } }, styles: [".erd-section[_ngcontent-%COMP%] {\n background: white;\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.section-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background-color: #f5f5f5;\n border-bottom: 1px solid #e0e0e0;\n \n h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n }\n}\n\n.erd-controls[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.control-btn[_ngcontent-%COMP%] {\n padding: 4px 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n \n &:hover {\n background-color: #f0f0f0;\n }\n}\n\n.erd-container[_ngcontent-%COMP%] {\n flex: 1;\n position: relative;\n background-color: #fafafa;\n overflow: hidden;\n min-height: 400px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n \n svg {\n position: absolute;\n top: 0;\n left: 0;\n width: 100% !important;\n height: 100% !important;\n max-width: 100%;\n max-height: 100%;\n box-sizing: border-box;\n }\n}\n\n.loading-overlay[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(250, 250, 250, 0.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.3s ease-in-out;\n}\n\n.loading-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.loading-spinner[_ngcontent-%COMP%] {\n position: relative;\n width: 60px;\n height: 60px;\n}\n\n.spinner-ring[_ngcontent-%COMP%] {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_spin 1.5s linear infinite;\n \n &:nth-child(1) {\n border-top-color: #2196f3;\n animation-delay: 0s;\n }\n \n &:nth-child(2) {\n border-top-color: #9c27b0;\n animation-delay: 0.3s;\n transform: scale(0.8);\n }\n \n &:nth-child(3) {\n border-top-color: #ff6b35;\n animation-delay: 0.6s;\n transform: scale(0.6);\n }\n}\n\n.loading-text[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #666;\n font-weight: 500;\n text-align: center;\n}\n\n.control-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n// SVG visualization styles\n[_nghost-%COMP%] {\n .erd-container svg {\n .node .entity-rect.highlighted {\n stroke: #ff9800 !important;\n stroke-width: 4px !important;\n filter: drop-shadow(0 0 8px rgba(255, 152, 0, 0.6));\n animation: pulse 1s ease-in-out infinite alternate;\n }\n \n .node.selected {\n .entity-rect {\n stroke: #2196f3 !important;\n stroke-width: 4px !important;\n filter: drop-shadow(0 0 12px rgba(33, 150, 243, 0.8));\n fill: #f3f8ff !important;\n animation: selectedPulse 2s ease-in-out infinite alternate;\n }\n \n .entity-header {\n fill: #2196f3 !important;\n }\n \n .entity-header-bottom {\n fill: #2196f3 !important;\n }\n \n .entity-name {\n fill: white !important;\n font-weight: bold !important;\n }\n }\n \n @keyframes selectedPulse {\n 0% { \n stroke-width: 4px;\n filter: drop-shadow(0 0 12px rgba(33, 150, 243, 0.8));\n }\n 100% { \n stroke-width: 5px;\n filter: drop-shadow(0 0 16px rgba(33, 150, 243, 1));\n }\n }\n \n .node.relationship-connected {\n .entity-rect.relationship-highlighted {\n stroke: #ff6b35 !important;\n stroke-width: 3px !important;\n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.5));\n animation: entityPulse 1.5s ease-in-out infinite alternate;\n }\n \n .entity-header {\n fill: #ff6b35 !important;\n }\n \n .entity-header-bottom {\n fill: #ff6b35 !important;\n }\n }\n \n .node.entity-connections-highlighted {\n .entity-rect.connection-highlighted {\n stroke: #9b59b6 !important;\n stroke-width: 3px !important;\n filter: drop-shadow(0 0 6px rgba(155, 89, 182, 0.5));\n opacity: 0.9;\n }\n \n .entity-header {\n fill: #9b59b6 !important;\n opacity: 0.8;\n }\n \n .entity-header-bottom {\n fill: #9b59b6 !important;\n opacity: 0.8;\n }\n }\n \n .link-group {\n .link {\n stroke-width: 2px;\n transition: all 0.2s ease;\n cursor: pointer;\n \n &:hover {\n stroke-width: 3px;\n stroke: #ff6b35;\n opacity: 1;\n }\n \n &.highlighted {\n stroke: #ff6b35 !important;\n stroke-width: 4px !important;\n opacity: 1 !important;\n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.6));\n animation: linkPulse 1.5s ease-in-out infinite alternate;\n }\n }\n \n .link-label {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n opacity: 0.8;\n pointer-events: none;\n transition: opacity 0.2s ease;\n \n &.highlighted {\n opacity: 1;\n font-weight: bold;\n fill: #ff6b35;\n }\n }\n \n &.highlighted {\n z-index: 10;\n }\n }\n \n .field-group {\n .field-bg {\n transition: fill 0.2s;\n }\n \n &:hover .field-bg {\n fill: #e3f2fd !important;\n }\n }\n \n @keyframes pulse {\n 0% { stroke-width: 4px; }\n 100% { stroke-width: 6px; }\n }\n \n @keyframes linkPulse {\n 0% { \n stroke-width: 4px; \n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.6));\n }\n 100% { \n stroke-width: 5px; \n filter: drop-shadow(0 0 10px rgba(255, 107, 53, 0.8));\n }\n }\n \n @keyframes entityPulse {\n 0% { \n stroke-width: 3px; \n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.5));\n }\n 100% { \n stroke-width: 4px; \n filter: drop-shadow(0 0 8px rgba(255, 107, 53, 0.7));\n }\n }\n }\n}"] });
596
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ERDDiagramComponent, [{
597
+ type: Component,
598
+ args: [{ selector: 'mj-erd-diagram', template: "<div class=\"erd-section\">\n <div class=\"section-header\">\n <h3>Entity Relationship Diagram</h3>\n <div class=\"erd-controls\">\n <button class=\"control-btn\" (click)=\"zoomIn()\" title=\"Zoom In\">\n <span class=\"fa-solid fa-search-plus\"></span>\n </button>\n <button class=\"control-btn\" (click)=\"zoomOut()\" title=\"Zoom Out\">\n <span class=\"fa-solid fa-search-minus\"></span>\n </button>\n <button class=\"control-btn\" (click)=\"resetZoom()\" title=\"Reset Zoom\">\n <span class=\"fa-solid fa-expand\"></span>\n </button>\n <button class=\"control-btn\" (click)=\"setupERD()\" title=\"Refresh Diagram\">\n <span class=\"fa-solid fa-refresh\"></span>\n </button>\n </div>\n </div>\n <div class=\"erd-container\" #erdContainer></div>\n</div>", styles: [".erd-section {\n background: white;\n overflow: hidden;\n height: 100%;\n display: flex;\n flex-direction: column;\n}\n\n.section-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 12px 16px;\n background-color: #f5f5f5;\n border-bottom: 1px solid #e0e0e0;\n \n h3 {\n margin: 0;\n font-size: 14px;\n font-weight: 600;\n color: #333;\n }\n}\n\n.erd-controls {\n display: flex;\n gap: 8px;\n}\n\n.control-btn {\n padding: 4px 8px;\n border: 1px solid #ccc;\n border-radius: 4px;\n background: white;\n cursor: pointer;\n font-size: 12px;\n \n &:hover {\n background-color: #f0f0f0;\n }\n}\n\n.erd-container {\n flex: 1;\n position: relative;\n background-color: #fafafa;\n overflow: hidden;\n min-height: 400px;\n width: 100%;\n max-width: 100%;\n box-sizing: border-box;\n \n svg {\n position: absolute;\n top: 0;\n left: 0;\n width: 100% !important;\n height: 100% !important;\n max-width: 100%;\n max-height: 100%;\n box-sizing: border-box;\n }\n}\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(250, 250, 250, 0.95);\n backdrop-filter: blur(2px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.3s ease-in-out;\n}\n\n.loading-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n}\n\n.loading-spinner {\n position: relative;\n width: 60px;\n height: 60px;\n}\n\n.spinner-ring {\n position: absolute;\n width: 100%;\n height: 100%;\n border: 3px solid transparent;\n border-radius: 50%;\n animation: spin 1.5s linear infinite;\n \n &:nth-child(1) {\n border-top-color: #2196f3;\n animation-delay: 0s;\n }\n \n &:nth-child(2) {\n border-top-color: #9c27b0;\n animation-delay: 0.3s;\n transform: scale(0.8);\n }\n \n &:nth-child(3) {\n border-top-color: #ff6b35;\n animation-delay: 0.6s;\n transform: scale(0.6);\n }\n}\n\n.loading-text {\n font-size: 14px;\n color: #666;\n font-weight: 500;\n text-align: center;\n}\n\n.control-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n// SVG visualization styles\n:host ::ng-deep {\n .erd-container svg {\n .node .entity-rect.highlighted {\n stroke: #ff9800 !important;\n stroke-width: 4px !important;\n filter: drop-shadow(0 0 8px rgba(255, 152, 0, 0.6));\n animation: pulse 1s ease-in-out infinite alternate;\n }\n \n .node.selected {\n .entity-rect {\n stroke: #2196f3 !important;\n stroke-width: 4px !important;\n filter: drop-shadow(0 0 12px rgba(33, 150, 243, 0.8));\n fill: #f3f8ff !important;\n animation: selectedPulse 2s ease-in-out infinite alternate;\n }\n \n .entity-header {\n fill: #2196f3 !important;\n }\n \n .entity-header-bottom {\n fill: #2196f3 !important;\n }\n \n .entity-name {\n fill: white !important;\n font-weight: bold !important;\n }\n }\n \n @keyframes selectedPulse {\n 0% { \n stroke-width: 4px;\n filter: drop-shadow(0 0 12px rgba(33, 150, 243, 0.8));\n }\n 100% { \n stroke-width: 5px;\n filter: drop-shadow(0 0 16px rgba(33, 150, 243, 1));\n }\n }\n \n .node.relationship-connected {\n .entity-rect.relationship-highlighted {\n stroke: #ff6b35 !important;\n stroke-width: 3px !important;\n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.5));\n animation: entityPulse 1.5s ease-in-out infinite alternate;\n }\n \n .entity-header {\n fill: #ff6b35 !important;\n }\n \n .entity-header-bottom {\n fill: #ff6b35 !important;\n }\n }\n \n .node.entity-connections-highlighted {\n .entity-rect.connection-highlighted {\n stroke: #9b59b6 !important;\n stroke-width: 3px !important;\n filter: drop-shadow(0 0 6px rgba(155, 89, 182, 0.5));\n opacity: 0.9;\n }\n \n .entity-header {\n fill: #9b59b6 !important;\n opacity: 0.8;\n }\n \n .entity-header-bottom {\n fill: #9b59b6 !important;\n opacity: 0.8;\n }\n }\n \n .link-group {\n .link {\n stroke-width: 2px;\n transition: all 0.2s ease;\n cursor: pointer;\n \n &:hover {\n stroke-width: 3px;\n stroke: #ff6b35;\n opacity: 1;\n }\n \n &.highlighted {\n stroke: #ff6b35 !important;\n stroke-width: 4px !important;\n opacity: 1 !important;\n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.6));\n animation: linkPulse 1.5s ease-in-out infinite alternate;\n }\n }\n \n .link-label {\n font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n opacity: 0.8;\n pointer-events: none;\n transition: opacity 0.2s ease;\n \n &.highlighted {\n opacity: 1;\n font-weight: bold;\n fill: #ff6b35;\n }\n }\n \n &.highlighted {\n z-index: 10;\n }\n }\n \n .field-group {\n .field-bg {\n transition: fill 0.2s;\n }\n \n &:hover .field-bg {\n fill: #e3f2fd !important;\n }\n }\n \n @keyframes pulse {\n 0% { stroke-width: 4px; }\n 100% { stroke-width: 6px; }\n }\n \n @keyframes linkPulse {\n 0% { \n stroke-width: 4px; \n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.6));\n }\n 100% { \n stroke-width: 5px; \n filter: drop-shadow(0 0 10px rgba(255, 107, 53, 0.8));\n }\n }\n \n @keyframes entityPulse {\n 0% { \n stroke-width: 3px; \n filter: drop-shadow(0 0 6px rgba(255, 107, 53, 0.5));\n }\n 100% { \n stroke-width: 4px; \n filter: drop-shadow(0 0 8px rgba(255, 107, 53, 0.7));\n }\n }\n }\n}"] }]
599
+ }], null, { erdContainer: [{
600
+ type: ViewChild,
601
+ args: ['erdContainer', { static: false }]
602
+ }], filteredEntities: [{
603
+ type: Input
604
+ }], allEntityFields: [{
605
+ type: Input
606
+ }], isRefreshingERD: [{
607
+ type: Input
608
+ }], selectedEntityId: [{
609
+ type: Input
610
+ }], entitySelected: [{
611
+ type: Output
612
+ }], entityDeselected: [{
613
+ type: Output
614
+ }], refreshERD: [{
615
+ type: Output
616
+ }] }); })();
617
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ERDDiagramComponent, { className: "ERDDiagramComponent", filePath: "src/EntityAdmin/components/erd-diagram.component.ts", lineNumber: 33 }); })();
618
+ //# sourceMappingURL=erd-diagram.component.js.map