@memberjunction/ng-dashboard-viewer 5.11.0 → 5.12.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.
@@ -669,7 +669,7 @@ export class DashboardViewerComponent {
669
669
  // Create the panel wrapper with header and content
670
670
  const wrapper = document.createElement('div');
671
671
  wrapper.className = 'dashboard-part-wrapper';
672
- wrapper.style.cssText = 'display: flex; flex-direction: column; height: 100%; background: #fff;';
672
+ wrapper.style.cssText = 'display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);';
673
673
  // Only show header in edit mode - GL tabs already display the title in view mode
674
674
  if (this.isEditing) {
675
675
  const header = this.createPartHeader(panel, panel.id);
@@ -745,16 +745,16 @@ export class DashboardViewerComponent {
745
745
  align-items: center;
746
746
  gap: 8px;
747
747
  padding: 10px 12px;
748
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
749
- border-bottom: 1px solid #e0e0e0;
748
+ background: var(--mj-bg-surface-card);
749
+ border-bottom: 1px solid var(--mj-border-default);
750
750
  min-height: 40px;
751
751
  `;
752
752
  // Icon and title
753
753
  const titleSection = document.createElement('div');
754
754
  titleSection.style.cssText = 'display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;';
755
755
  titleSection.innerHTML = `
756
- <i class="${panel.icon || 'fa-solid fa-puzzle-piece'}" style="color: #5c6bc0; font-size: 14px;"></i>
757
- <span style="font-weight: 500; font-size: 14px; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${panel.title}</span>
756
+ <i class="${panel.icon || 'fa-solid fa-puzzle-piece'}" style="color: var(--mj-brand-primary); font-size: 14px;"></i>
757
+ <span style="font-weight: 500; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${panel.title}</span>
758
758
  `;
759
759
  header.appendChild(titleSection);
760
760
  // Action buttons (only in edit mode)
@@ -774,19 +774,19 @@ export class DashboardViewerComponent {
774
774
  border: none;
775
775
  border-radius: 4px;
776
776
  background: transparent;
777
- color: #666;
777
+ color: var(--mj-text-secondary);
778
778
  cursor: pointer;
779
779
  transition: all 0.15s;
780
780
  `;
781
781
  configBtn.innerHTML = '<i class="fa-solid fa-cog" style="font-size: 12px;"></i>';
782
782
  configBtn.addEventListener('click', () => this.onConfigurePart(panelId));
783
783
  configBtn.addEventListener('mouseenter', () => {
784
- configBtn.style.background = '#e0e0e0';
785
- configBtn.style.color = '#333';
784
+ configBtn.style.background = 'var(--mj-border-default)';
785
+ configBtn.style.color = 'var(--mj-text-primary)';
786
786
  });
787
787
  configBtn.addEventListener('mouseleave', () => {
788
788
  configBtn.style.background = 'transparent';
789
- configBtn.style.color = '#666';
789
+ configBtn.style.color = 'var(--mj-text-secondary)';
790
790
  });
791
791
  // Remove button
792
792
  const removeBtn = document.createElement('button');
@@ -801,19 +801,19 @@ export class DashboardViewerComponent {
801
801
  border: none;
802
802
  border-radius: 4px;
803
803
  background: transparent;
804
- color: #666;
804
+ color: var(--mj-text-secondary);
805
805
  cursor: pointer;
806
806
  transition: all 0.15s;
807
807
  `;
808
808
  removeBtn.innerHTML = '<i class="fa-solid fa-times" style="font-size: 12px;"></i>';
809
809
  removeBtn.addEventListener('click', () => this.onRemovePart(panelId));
810
810
  removeBtn.addEventListener('mouseenter', () => {
811
- removeBtn.style.background = '#ffebee';
812
- removeBtn.style.color = '#d32f2f';
811
+ removeBtn.style.background = 'color-mix(in srgb, var(--mj-status-error) 10%, transparent)';
812
+ removeBtn.style.color = 'var(--mj-status-error)';
813
813
  });
814
814
  removeBtn.addEventListener('mouseleave', () => {
815
815
  removeBtn.style.background = 'transparent';
816
- removeBtn.style.color = '#666';
816
+ removeBtn.style.color = 'var(--mj-text-secondary)';
817
817
  });
818
818
  actions.appendChild(configBtn);
819
819
  actions.appendChild(removeBtn);
@@ -844,9 +844,9 @@ export class DashboardViewerComponent {
844
844
  const url = config['url'];
845
845
  if (!url) {
846
846
  container.innerHTML = `
847
- <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;">
848
- <i class="fa-solid fa-globe" style="font-size: 48px; color: #ccc; margin-bottom: 16px;"></i>
849
- <h4 style="margin: 0 0 8px 0; color: #333;">No URL Configured</h4>
847
+ <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--mj-text-secondary); text-align: center; padding: 24px;">
848
+ <i class="fa-solid fa-globe" style="font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;"></i>
849
+ <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">No URL Configured</h4>
850
850
  <p style="margin: 0; font-size: 13px;">Click the configure button to set a URL for this part.</p>
851
851
  </div>
852
852
  `;
@@ -876,9 +876,9 @@ export class DashboardViewerComponent {
876
876
  const entityName = config['entityName'];
877
877
  if (!viewId && !entityName) {
878
878
  container.innerHTML = `
879
- <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;">
880
- <i class="fa-solid fa-table" style="font-size: 48px; color: #ccc; margin-bottom: 16px;"></i>
881
- <h4 style="margin: 0 0 8px 0; color: #333;">No View Selected</h4>
879
+ <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--mj-text-secondary); text-align: center; padding: 24px;">
880
+ <i class="fa-solid fa-table" style="font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;"></i>
881
+ <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">No View Selected</h4>
882
882
  <p style="margin: 0; font-size: 13px;">Click configure to select a view for this part.</p>
883
883
  </div>
884
884
  `;
@@ -888,21 +888,21 @@ export class DashboardViewerComponent {
888
888
  const displayModeValue = config['displayMode'];
889
889
  const displayMode = displayModeValue === 'grid' ? 'Grid View' : displayModeValue === 'cards' ? 'Card View' : 'Timeline View';
890
890
  container.innerHTML = `
891
- <div style="display: flex; flex-direction: column; height: 100%; background: #fff;">
892
- <div style="padding: 16px 20px; border-bottom: 1px solid #e0e0e0; background: #fafafa;">
891
+ <div style="display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);">
892
+ <div style="padding: 16px 20px; border-bottom: 1px solid var(--mj-border-default); background: var(--mj-bg-surface-card);">
893
893
  <div style="display: flex; align-items: center; gap: 12px;">
894
- <i class="fa-solid fa-table" style="font-size: 20px; color: #5c6bc0;"></i>
894
+ <i class="fa-solid fa-table" style="font-size: 20px; color: var(--mj-brand-primary);"></i>
895
895
  <div>
896
- <div style="font-weight: 500; color: #333; font-size: 14px;">Entity View</div>
897
- <div style="font-size: 12px; color: #666;">${entityName || 'View ' + viewInfo}</div>
896
+ <div style="font-weight: 500; color: var(--mj-text-primary); font-size: 14px;">Entity View</div>
897
+ <div style="font-size: 12px; color: var(--mj-text-secondary);">${entityName || 'View ' + viewInfo}</div>
898
898
  </div>
899
- <span style="margin-left: auto; padding: 4px 10px; background: #e3f2fd; color: #1976d2; border-radius: 12px; font-size: 11px; font-weight: 500;">${displayMode}</span>
899
+ <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent); color: var(--mj-brand-primary); border-radius: 12px; font-size: 11px; font-weight: 500;">${displayMode}</span>
900
900
  </div>
901
901
  </div>
902
- <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; padding: 24px;">
902
+ <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;">
903
903
  <i class="fa-solid fa-spinner fa-spin" style="font-size: 24px; margin-bottom: 12px;"></i>
904
904
  <p style="margin: 0; font-size: 13px;">Entity grid loading...</p>
905
- <p style="margin: 8px 0 0 0; font-size: 11px; color: #bbb;">Full implementation pending Angular integration</p>
905
+ <p style="margin: 8px 0 0 0; font-size: 11px; color: var(--mj-text-muted);">Full implementation pending Angular integration</p>
906
906
  </div>
907
907
  </div>
908
908
  `;
@@ -912,9 +912,9 @@ export class DashboardViewerComponent {
912
912
  const queryName = config['queryName'];
913
913
  if (!queryId && !queryName) {
914
914
  container.innerHTML = `
915
- <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;">
916
- <i class="fa-solid fa-database" style="font-size: 48px; color: #ccc; margin-bottom: 16px;"></i>
917
- <h4 style="margin: 0 0 8px 0; color: #333;">No Query Selected</h4>
915
+ <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--mj-text-secondary); text-align: center; padding: 24px;">
916
+ <i class="fa-solid fa-database" style="font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;"></i>
917
+ <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">No Query Selected</h4>
918
918
  <p style="margin: 0; font-size: 13px;">Click configure to select a query for this part.</p>
919
919
  </div>
920
920
  `;
@@ -923,21 +923,21 @@ export class DashboardViewerComponent {
923
923
  const autoRefreshSeconds = config['autoRefreshSeconds'] || 0;
924
924
  const queryInfo = queryName || (queryId ? queryId.substring(0, 8) + '...' : 'Unknown');
925
925
  container.innerHTML = `
926
- <div style="display: flex; flex-direction: column; height: 100%; background: #fff;">
927
- <div style="padding: 16px 20px; border-bottom: 1px solid #e0e0e0; background: #fafafa;">
926
+ <div style="display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);">
927
+ <div style="padding: 16px 20px; border-bottom: 1px solid var(--mj-border-default); background: var(--mj-bg-surface-card);">
928
928
  <div style="display: flex; align-items: center; gap: 12px;">
929
- <i class="fa-solid fa-database" style="font-size: 20px; color: #5c6bc0;"></i>
929
+ <i class="fa-solid fa-database" style="font-size: 20px; color: var(--mj-brand-primary);"></i>
930
930
  <div>
931
- <div style="font-weight: 500; color: #333; font-size: 14px;">Query Results</div>
932
- <div style="font-size: 12px; color: #666;">${queryInfo}</div>
931
+ <div style="font-weight: 500; color: var(--mj-text-primary); font-size: 14px;">Query Results</div>
932
+ <div style="font-size: 12px; color: var(--mj-text-secondary);">${queryInfo}</div>
933
933
  </div>
934
- <span style="margin-left: auto; padding: 4px 10px; background: #e8f5e9; color: #388e3c; border-radius: 12px; font-size: 11px; font-weight: 500;">${autoRefreshSeconds > 0 ? 'Refresh: ' + autoRefreshSeconds + 's' : 'Manual refresh'}</span>
934
+ <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-status-success) 10%, transparent); color: var(--mj-status-success); border-radius: 12px; font-size: 11px; font-weight: 500;">${autoRefreshSeconds > 0 ? 'Refresh: ' + autoRefreshSeconds + 's' : 'Manual refresh'}</span>
935
935
  </div>
936
936
  </div>
937
- <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; padding: 24px;">
937
+ <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;">
938
938
  <i class="fa-solid fa-spinner fa-spin" style="font-size: 24px; margin-bottom: 12px;"></i>
939
939
  <p style="margin: 0; font-size: 13px;">Query grid loading...</p>
940
- <p style="margin: 8px 0 0 0; font-size: 11px; color: #bbb;">Full implementation pending Angular integration</p>
940
+ <p style="margin: 8px 0 0 0; font-size: 11px; color: var(--mj-text-muted);">Full implementation pending Angular integration</p>
941
941
  </div>
942
942
  </div>
943
943
  `;
@@ -946,9 +946,9 @@ export class DashboardViewerComponent {
946
946
  const artifactId = config['artifactId'];
947
947
  if (!artifactId) {
948
948
  container.innerHTML = `
949
- <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;">
950
- <i class="fa-solid fa-cube" style="font-size: 48px; color: #ccc; margin-bottom: 16px;"></i>
951
- <h4 style="margin: 0 0 8px 0; color: #333;">No Artifact Selected</h4>
949
+ <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--mj-text-secondary); text-align: center; padding: 24px;">
950
+ <i class="fa-solid fa-cube" style="font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;"></i>
951
+ <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">No Artifact Selected</h4>
952
952
  <p style="margin: 0; font-size: 13px;">Click configure to select an artifact for this part.</p>
953
953
  </div>
954
954
  `;
@@ -958,21 +958,21 @@ export class DashboardViewerComponent {
958
958
  const artifactInfo = artifactId.substring(0, 8) + '...';
959
959
  const versionInfo = versionNumber ? `v${versionNumber}` : 'Latest';
960
960
  container.innerHTML = `
961
- <div style="display: flex; flex-direction: column; height: 100%; background: #fff;">
962
- <div style="padding: 16px 20px; border-bottom: 1px solid #e0e0e0; background: #fafafa;">
961
+ <div style="display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);">
962
+ <div style="padding: 16px 20px; border-bottom: 1px solid var(--mj-border-default); background: var(--mj-bg-surface-card);">
963
963
  <div style="display: flex; align-items: center; gap: 12px;">
964
- <i class="fa-solid fa-cube" style="font-size: 20px; color: #5c6bc0;"></i>
964
+ <i class="fa-solid fa-cube" style="font-size: 20px; color: var(--mj-brand-primary);"></i>
965
965
  <div>
966
- <div style="font-weight: 500; color: #333; font-size: 14px;">Artifact</div>
967
- <div style="font-size: 12px; color: #666;">ID: ${artifactInfo}</div>
966
+ <div style="font-weight: 500; color: var(--mj-text-primary); font-size: 14px;">Artifact</div>
967
+ <div style="font-size: 12px; color: var(--mj-text-secondary);">ID: ${artifactInfo}</div>
968
968
  </div>
969
- <span style="margin-left: auto; padding: 4px 10px; background: #fce4ec; color: #c2185b; border-radius: 12px; font-size: 11px; font-weight: 500;">${versionInfo}</span>
969
+ <span style="margin-left: auto; padding: 4px 10px; background: color-mix(in srgb, var(--mj-status-error) 10%, transparent); color: var(--mj-status-error); border-radius: 12px; font-size: 11px; font-weight: 500;">${versionInfo}</span>
970
970
  </div>
971
971
  </div>
972
- <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; padding: 24px;">
972
+ <div style="flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;">
973
973
  <i class="fa-solid fa-spinner fa-spin" style="font-size: 24px; margin-bottom: 12px;"></i>
974
974
  <p style="margin: 0; font-size: 13px;">Artifact viewer loading...</p>
975
- <p style="margin: 8px 0 0 0; font-size: 11px; color: #bbb;">Full implementation pending Angular integration</p>
975
+ <p style="margin: 8px 0 0 0; font-size: 11px; color: var(--mj-text-muted);">Full implementation pending Angular integration</p>
976
976
  </div>
977
977
  </div>
978
978
  `;
@@ -980,9 +980,9 @@ export class DashboardViewerComponent {
980
980
  renderPlaceholderPart(panel, container, partType) {
981
981
  const partTypeName = partType?.Name || 'Custom';
982
982
  container.innerHTML = `
983
- <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;">
984
- <i class="fa-solid fa-puzzle-piece" style="font-size: 48px; color: #ccc; margin-bottom: 16px;"></i>
985
- <h4 style="margin: 0 0 8px 0; color: #333;">${partTypeName} Part</h4>
983
+ <div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--mj-text-secondary); text-align: center; padding: 24px;">
984
+ <i class="fa-solid fa-puzzle-piece" style="font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;"></i>
985
+ <h4 style="margin: 0 0 8px 0; color: var(--mj-text-primary);">${partTypeName} Part</h4>
986
986
  <p style="margin: 0; font-size: 13px;">This part type is not yet fully implemented.</p>
987
987
  </div>
988
988
  `;
@@ -1076,11 +1076,11 @@ export class DashboardViewerComponent {
1076
1076
  i0.ɵɵconditional(ctx.isLoading ? 3 : -1);
1077
1077
  i0.ɵɵadvance(3);
1078
1078
  i0.ɵɵconditional(!ctx.hasPanels && !ctx.isLoading ? 6 : -1);
1079
- } }, dependencies: [i1.LoadingComponent, i2.DashboardBreadcrumbComponent], styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, #f5f5f5);\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, #5c6bc0);\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, #ffffff);\n border-bottom: 1px solid var(--dashboard-border, #e0e0e0);\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, #333);\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, #5c6bc0);\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, #ff9800);\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, #ddd);\n border-radius: 4px;\n background: var(--dashboard-button-bg, #fff);\n color: var(--dashboard-button-color, #333);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, #f5f5f5);\n border-color: var(--dashboard-button-hover-border, #ccc);\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, #e8eaf6);\n border-color: var(--dashboard-button-active-border, #5c6bc0);\n color: var(--dashboard-button-active-color, #5c6bc0);\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, #5c6bc0);\n border-color: var(--dashboard-button-primary-border, #3f51b5);\n color: var(--dashboard-button-primary-color, #fff);\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, #3f51b5);\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #fff;\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\n border-bottom: 1px solid #e0e0e0;\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 255, 255, 0.9);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--dashboard-empty-color, #666);\n max-width: 400px;\n padding: 40px;\n}\n\n.empty-state .empty-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: linear-gradient(135deg, #e8eaf6 0%, #c5cae9 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n}\n\n.empty-state .empty-icon i {\n font-size: 36px;\n color: #5c6bc0;\n}\n\n.empty-state h3 {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 500;\n color: #333;\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: var(--dashboard-empty-text, #666);\n line-height: 1.5;\n}\n\n.add-part-button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: linear-gradient(135deg, #5c6bc0 0%, #3f51b5 100%);\n color: #fff;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px rgba(92, 107, 192, 0.3);\n}\n\n.add-part-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(92, 107, 192, 0.4);\n}\n\n.add-part-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, #d32f2f);\n background: var(--dashboard-error-bg, #ffebee);\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: white !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: white !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: #f5f5f5 !important;\n border-bottom: 1px solid #ebebeb !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid #ebebeb !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: #e8e8e8 !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: white !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid #ebebeb !important;\n border-bottom-color: white !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important; \n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: #5c6bc0 !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: #c62828 !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"], encapsulation: 2 });
1079
+ } }, dependencies: [i1.LoadingComponent, i2.DashboardBreadcrumbComponent], styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, var(--mj-bg-surface-card));\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, var(--mj-bg-surface));\n border-bottom: 1px solid var(--dashboard-border, var(--mj-border-default));\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, var(--mj-text-primary));\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, var(--mj-brand-primary));\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, var(--mj-status-warning));\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, var(--mj-border-strong));\n border-radius: 4px;\n background: var(--dashboard-button-bg, var(--mj-bg-surface));\n color: var(--dashboard-button-color, var(--mj-text-primary));\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, var(--mj-bg-surface-card));\n border-color: var(--dashboard-button-hover-border, var(--mj-border-strong));\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)));\n border-color: var(--dashboard-button-active-border, var(--mj-brand-primary));\n color: var(--dashboard-button-active-color, var(--mj-brand-primary));\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, var(--mj-brand-primary));\n border-color: var(--dashboard-button-primary-border, var(--mj-brand-primary-hover));\n color: var(--dashboard-button-primary-color, var(--mj-text-inverse));\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, var(--mj-brand-primary-hover));\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-surface) 90%, transparent);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--dashboard-empty-color, var(--mj-text-secondary));\n max-width: 400px;\n padding: 40px;\n}\n\n.empty-state .empty-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n}\n\n.empty-state .empty-icon i {\n font-size: 36px;\n color: var(--mj-brand-primary);\n}\n\n.empty-state h3 {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: var(--dashboard-empty-text, var(--mj-text-secondary));\n line-height: 1.5;\n}\n\n.add-part-button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.add-part-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.add-part-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, var(--mj-status-error));\n background: var(--dashboard-error-bg, color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)));\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: var(--mj-bg-surface) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: var(--mj-bg-surface) !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-card) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-sunken) !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important;\n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: var(--mj-brand-primary) !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"], encapsulation: 2 });
1080
1080
  }
1081
1081
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DashboardViewerComponent, [{
1082
1082
  type: Component,
1083
- args: [{ standalone: false, selector: 'mj-dashboard-viewer', encapsulation: ViewEncapsulation.None, template: "<!-- Dashboard Viewer Component -->\n<div class=\"dashboard-viewer\" [class.editing]=\"isEditing\" [class.has-toolbar]=\"shouldShowToolbar\">\n <!-- Breadcrumb Navigation (hidden in edit mode) -->\n @if (showBreadcrumb && !isEditing && dashboard) {\n <mj-dashboard-breadcrumb\n [Categories]=\"Categories\"\n [CurrentCategoryId]=\"dashboard.CategoryID\"\n [CurrentDashboard]=\"dashboard\"\n [ShowDashboardName]=\"true\"\n [AllowDragDrop]=\"false\"\n Size=\"large\"\n RootIcon=\"fa-solid fa-gauge-high\"\n RootLabel=\"Dashboards\"\n (Navigate)=\"onBreadcrumbNavigate($event)\">\n </mj-dashboard-breadcrumb>\n }\n\n <!-- Toolbar (auto-hides when all elements are disabled) -->\n @if (shouldShowToolbar) {\n <div class=\"dashboard-toolbar\">\n <div class=\"toolbar-left\">\n @if (dashboard && (isEditing || !showBreadcrumb)) {\n <span class=\"dashboard-title\">\n <i class=\"fa-solid fa-chart-line\"></i>\n {{ dashboard.Name }}\n </span>\n }\n </div>\n <div class=\"toolbar-center\">\n @if (hasUnsavedChanges && isEditing) {\n <span class=\"unsaved-indicator\">\n <i class=\"fa-solid fa-circle\"></i>\n Unsaved changes\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Open in New Tab button (when embedded) -->\n @if (showOpenInTabButton && !isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onOpenInTabClick()\"\n title=\"Open in its own tab\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in Tab\n </button>\n }\n @if (isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n }\n @if (showEditButton) {\n <button\n class=\"toolbar-button\"\n [class.active]=\"isEditing\"\n (click)=\"toggleEditMode()\">\n <i class=\"fa-solid fa-edit\"></i>\n {{ isEditing ? 'Editing' : 'Edit' }}\n </button>\n }\n @if (isEditing && hasUnsavedChanges) {\n <button\n class=\"toolbar-button primary\"\n (click)=\"save()\">\n <i class=\"fa-solid fa-save\"></i>\n Save\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Loading Overlay -->\n @if (isLoading) {\n <div class=\"loading-overlay\">\n <mj-loading text=\"Loading dashboard...\"></mj-loading>\n </div>\n }\n\n <!-- Layout Container -->\n <div class=\"layout-container\" #layoutContainer>\n <!-- Golden Layout will render panels here -->\n </div>\n\n <!-- Empty State -->\n @if (!hasPanels && !isLoading) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-layer-group\"></i>\n </div>\n <h3>No parts configured</h3>\n <p>This dashboard has no parts yet. Add your first part to start visualizing your data.</p>\n @if (isEditing) {\n <button class=\"add-part-button\" (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Your First Part\n </button>\n }\n </div>\n }\n</div>\n", styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, #f5f5f5);\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, #5c6bc0);\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, #ffffff);\n border-bottom: 1px solid var(--dashboard-border, #e0e0e0);\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, #333);\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, #5c6bc0);\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, #ff9800);\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, #ddd);\n border-radius: 4px;\n background: var(--dashboard-button-bg, #fff);\n color: var(--dashboard-button-color, #333);\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, #f5f5f5);\n border-color: var(--dashboard-button-hover-border, #ccc);\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, #e8eaf6);\n border-color: var(--dashboard-button-active-border, #5c6bc0);\n color: var(--dashboard-button-active-color, #5c6bc0);\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, #5c6bc0);\n border-color: var(--dashboard-button-primary-border, #3f51b5);\n color: var(--dashboard-button-primary-color, #fff);\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, #3f51b5);\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #fff;\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);\n border-bottom: 1px solid #e0e0e0;\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: rgba(255, 255, 255, 0.9);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--dashboard-empty-color, #666);\n max-width: 400px;\n padding: 40px;\n}\n\n.empty-state .empty-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: linear-gradient(135deg, #e8eaf6 0%, #c5cae9 100%);\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n}\n\n.empty-state .empty-icon i {\n font-size: 36px;\n color: #5c6bc0;\n}\n\n.empty-state h3 {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 500;\n color: #333;\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: var(--dashboard-empty-text, #666);\n line-height: 1.5;\n}\n\n.add-part-button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: linear-gradient(135deg, #5c6bc0 0%, #3f51b5 100%);\n color: #fff;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px rgba(92, 107, 192, 0.3);\n}\n\n.add-part-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(92, 107, 192, 0.4);\n}\n\n.add-part-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, #d32f2f);\n background: var(--dashboard-error-bg, #ffebee);\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: white !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: white !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: #f5f5f5 !important;\n border-bottom: 1px solid #ebebeb !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid #ebebeb !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: #e8e8e8 !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: white !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid #ebebeb !important;\n border-bottom-color: white !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important; \n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: #5c6bc0 !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: #c62828 !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"] }]
1083
+ args: [{ standalone: false, selector: 'mj-dashboard-viewer', encapsulation: ViewEncapsulation.None, template: "<!-- Dashboard Viewer Component -->\n<div class=\"dashboard-viewer\" [class.editing]=\"isEditing\" [class.has-toolbar]=\"shouldShowToolbar\">\n <!-- Breadcrumb Navigation (hidden in edit mode) -->\n @if (showBreadcrumb && !isEditing && dashboard) {\n <mj-dashboard-breadcrumb\n [Categories]=\"Categories\"\n [CurrentCategoryId]=\"dashboard.CategoryID\"\n [CurrentDashboard]=\"dashboard\"\n [ShowDashboardName]=\"true\"\n [AllowDragDrop]=\"false\"\n Size=\"large\"\n RootIcon=\"fa-solid fa-gauge-high\"\n RootLabel=\"Dashboards\"\n (Navigate)=\"onBreadcrumbNavigate($event)\">\n </mj-dashboard-breadcrumb>\n }\n\n <!-- Toolbar (auto-hides when all elements are disabled) -->\n @if (shouldShowToolbar) {\n <div class=\"dashboard-toolbar\">\n <div class=\"toolbar-left\">\n @if (dashboard && (isEditing || !showBreadcrumb)) {\n <span class=\"dashboard-title\">\n <i class=\"fa-solid fa-chart-line\"></i>\n {{ dashboard.Name }}\n </span>\n }\n </div>\n <div class=\"toolbar-center\">\n @if (hasUnsavedChanges && isEditing) {\n <span class=\"unsaved-indicator\">\n <i class=\"fa-solid fa-circle\"></i>\n Unsaved changes\n </span>\n }\n </div>\n <div class=\"toolbar-right\">\n <!-- Open in New Tab button (when embedded) -->\n @if (showOpenInTabButton && !isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onOpenInTabClick()\"\n title=\"Open in its own tab\">\n <i class=\"fa-solid fa-up-right-from-square\"></i>\n Open in Tab\n </button>\n }\n @if (isEditing) {\n <button\n class=\"toolbar-button\"\n (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n }\n @if (showEditButton) {\n <button\n class=\"toolbar-button\"\n [class.active]=\"isEditing\"\n (click)=\"toggleEditMode()\">\n <i class=\"fa-solid fa-edit\"></i>\n {{ isEditing ? 'Editing' : 'Edit' }}\n </button>\n }\n @if (isEditing && hasUnsavedChanges) {\n <button\n class=\"toolbar-button primary\"\n (click)=\"save()\">\n <i class=\"fa-solid fa-save\"></i>\n Save\n </button>\n }\n </div>\n </div>\n }\n\n <!-- Loading Overlay -->\n @if (isLoading) {\n <div class=\"loading-overlay\">\n <mj-loading text=\"Loading dashboard...\"></mj-loading>\n </div>\n }\n\n <!-- Layout Container -->\n <div class=\"layout-container\" #layoutContainer>\n <!-- Golden Layout will render panels here -->\n </div>\n\n <!-- Empty State -->\n @if (!hasPanels && !isLoading) {\n <div class=\"empty-state\">\n <div class=\"empty-icon\">\n <i class=\"fa-solid fa-layer-group\"></i>\n </div>\n <h3>No parts configured</h3>\n <p>This dashboard has no parts yet. Add your first part to start visualizing your data.</p>\n @if (isEditing) {\n <button class=\"add-part-button\" (click)=\"onAddPanelClick()\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Your First Part\n </button>\n }\n </div>\n }\n</div>\n", styles: ["/**\n * Dashboard Viewer Component Styles\n *\n * IMPORTANT: For Golden Layout tabs to display correctly, you must import\n * Golden Layout's CSS in your application's global styles:\n *\n * @import 'golden-layout/dist/css/goldenlayout-base.css';\n *\n * Or add to your angular.json styles array:\n * \"node_modules/golden-layout/dist/css/goldenlayout-base.css\"\n */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n}\n\n.dashboard-viewer {\n display: flex;\n flex-direction: column;\n width: 100%;\n height: 100%;\n background: var(--dashboard-bg, var(--mj-bg-surface-card));\n}\n\n.dashboard-viewer.editing .layout-container {\n outline: 2px dashed var(--dashboard-edit-outline, var(--mj-brand-primary));\n outline-offset: -2px;\n}\n\n/* ========================================\n Toolbar\n ======================================== */\n\n.dashboard-toolbar {\n flex: 0 0 48px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background: var(--dashboard-toolbar-bg, var(--mj-bg-surface));\n border-bottom: 1px solid var(--dashboard-border, var(--mj-border-default));\n box-sizing: border-box;\n z-index: 10;\n}\n\n.toolbar-left,\n.toolbar-center,\n.toolbar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.toolbar-left {\n flex: 1;\n}\n\n.toolbar-center {\n flex: 0 0 auto;\n}\n\n.toolbar-right {\n flex: 1;\n justify-content: flex-end;\n}\n\n.dashboard-title {\n font-size: 16px;\n font-weight: 500;\n color: var(--dashboard-title-color, var(--mj-text-primary));\n}\n\n.dashboard-title i {\n margin-right: 8px;\n color: var(--dashboard-title-icon, var(--mj-brand-primary));\n}\n\n.unsaved-indicator {\n font-size: 12px;\n color: var(--dashboard-unsaved-color, var(--mj-status-warning));\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.unsaved-indicator i {\n font-size: 8px;\n}\n\n.toolbar-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--dashboard-button-border, var(--mj-border-strong));\n border-radius: 4px;\n background: var(--dashboard-button-bg, var(--mj-bg-surface));\n color: var(--dashboard-button-color, var(--mj-text-primary));\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.toolbar-button:hover {\n background: var(--dashboard-button-hover-bg, var(--mj-bg-surface-card));\n border-color: var(--dashboard-button-hover-border, var(--mj-border-strong));\n}\n\n.toolbar-button.active {\n background: var(--dashboard-button-active-bg, color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)));\n border-color: var(--dashboard-button-active-border, var(--mj-brand-primary));\n color: var(--dashboard-button-active-color, var(--mj-brand-primary));\n}\n\n.toolbar-button.primary {\n background: var(--dashboard-button-primary-bg, var(--mj-brand-primary));\n border-color: var(--dashboard-button-primary-border, var(--mj-brand-primary-hover));\n color: var(--dashboard-button-primary-color, var(--mj-text-inverse));\n}\n\n.toolbar-button.primary:hover {\n background: var(--dashboard-button-primary-hover-bg, var(--mj-brand-primary-hover));\n}\n\n.toolbar-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Layout Container\n ======================================== */\n\n/* Layout container fills remaining space in the flex column */\n.layout-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n min-height: 0;\n}\n\n/* ========================================\n Dashboard Panel Content (our wrapper)\n ======================================== */\n\n/* Dashboard panel wrapper (header + content) */\n:host ::ng-deep .dashboard-part-wrapper {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n}\n\n/* Dashboard part header */\n:host ::ng-deep .dashboard-part-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 12px;\n background: var(--mj-bg-surface-card);\n border-bottom: 1px solid var(--mj-border-default);\n min-height: 40px;\n flex-shrink: 0;\n}\n\n/* Dashboard part content */\n:host ::ng-deep .dashboard-part-content {\n flex: 1;\n overflow: auto;\n min-height: 0;\n}\n\n/* ========================================\n Loading Overlay\n ======================================== */\n\n.loading-overlay {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-bg-surface) 90%, transparent);\n z-index: 100;\n}\n\n/* ========================================\n Empty State\n ======================================== */\n\n.empty-state {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n text-align: center;\n color: var(--dashboard-empty-color, var(--mj-text-secondary));\n max-width: 400px;\n padding: 40px;\n}\n\n.empty-state .empty-icon {\n width: 80px;\n height: 80px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0 auto 24px;\n}\n\n.empty-state .empty-icon i {\n font-size: 36px;\n color: var(--mj-brand-primary);\n}\n\n.empty-state h3 {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.empty-state p {\n margin: 0 0 24px 0;\n font-size: 14px;\n color: var(--dashboard-empty-text, var(--mj-text-secondary));\n line-height: 1.5;\n}\n\n.add-part-button {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 12px 24px;\n border: none;\n border-radius: 6px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.add-part-button:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.add-part-button i {\n font-size: 14px;\n}\n\n/* ========================================\n Panel Error State\n ======================================== */\n\n:host ::ng-deep .panel-error {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n padding: 24px;\n text-align: center;\n color: var(--dashboard-error-color, var(--mj-status-error));\n background: var(--dashboard-error-bg, color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)));\n}\n\n:host ::ng-deep .panel-error i {\n font-size: 32px;\n margin-bottom: 12px;\n}\n\n:host ::ng-deep .panel-error span {\n font-size: 14px;\n}\n\n/* ========================================\n Golden Layout Overrides\n These require ViewEncapsulation.None to work on dynamic GL elements\n ======================================== */\n\n/* Override shell's hide-tab-bar rule that hides GL headers when dashboard is inside shell */\n/* The shell hides .lm_header when mj-tab-container has hide-tab-bar class */\n/* We need to ensure OUR Golden Layout headers are always visible */\nmj-dashboard-viewer .lm_header {\n display: block !important;\n}\n\n/* Ensure GL root container fills available space */\nmj-dashboard-viewer .lm_goldenlayout {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure the root layout item fills space */\nmj-dashboard-viewer .lm_root {\n width: 100% !important;\n height: 100% !important;\n}\n\n/* Ensure proper box-sizing for all GL layout elements */\nmj-dashboard-viewer .lm_item,\nmj-dashboard-viewer .lm_content,\nmj-dashboard-viewer .lm_stack,\nmj-dashboard-viewer .lm_row,\nmj-dashboard-viewer .lm_column {\n box-sizing: border-box !important;\n}\n\n/* Ensure layout items don't overflow */\nmj-dashboard-viewer .lm_item {\n overflow: hidden !important;\n}\n\n/* Fix for .lm_items - the content container inside stacks */\nmj-dashboard-viewer .lm_items {\n width: 100% !important;\n height: calc(100% - 38px) !important; /* Account for header height (38px) */\n box-sizing: border-box !important;\n position: relative !important;\n}\n\n/* When tabs are maximized, no header visible */\nmj-dashboard-viewer .lm_stack.lm_maximised > .lm_items {\n height: 100% !important;\n}\n\n/* Target the ComponentItem div inside lm_items */\nmj-dashboard-viewer .lm_items > div {\n width: 100% !important;\n height: 100% !important;\n box-sizing: border-box !important;\n flex-direction: column !important;\n}\n\n/* Only apply flex display to the active/visible tab content div */\nmj-dashboard-viewer .lm_items > div:not([style*=\"display: none\"]) {\n display: flex !important;\n}\n\n/* Clearfix for float-based row layout */\nmj-dashboard-viewer .lm_row::after {\n content: \"\" !important;\n display: table !important;\n clear: both !important;\n}\n\n/* Force content children to respect parent bounds */\nmj-dashboard-viewer .lm_content > * {\n max-width: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_content {\n background: var(--mj-bg-surface) !important;\n display: flex !important;\n flex-direction: column !important;\n height: 100% !important;\n width: 100% !important;\n}\n\nmj-dashboard-viewer .lm_item_container {\n background: var(--mj-bg-surface) !important;\n}\n\n/* Tab header styling */\nmj-dashboard-viewer .lm_header {\n height: 38px !important;\n padding-top: 2px !important;\n padding-left: 4px !important;\n background: var(--mj-bg-surface-card) !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n overflow: visible !important;\n box-sizing: border-box !important;\n}\n\nmj-dashboard-viewer .lm_tabs {\n height: 36px !important;\n}\n\n/* Hide Golden Layout window controls (popout, maximize) */\nmj-dashboard-viewer .lm_controls {\n display: none !important;\n}\n\n/* Tab styling */\nmj-dashboard-viewer .lm_header .lm_tab {\n padding: 0 16px 0 28px !important; /* Extra left padding for icon */\n font-size: 13px !important;\n height: 35px !important;\n line-height: 35px !important;\n box-sizing: border-box !important;\n cursor: pointer !important;\n user-select: none !important;\n background: transparent !important;\n border: none !important;\n border-bottom: 1px solid var(--mj-border-default) !important;\n transition: all 0.15s ease !important;\n position: relative !important;\n z-index: 1 !important;\n margin-right: 1px !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover {\n background: var(--mj-bg-surface-sunken) !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab.lm_active {\n background: var(--mj-bg-surface) !important;\n height: 36px !important;\n margin-bottom: -1px !important;\n margin-right: 0 !important;\n border: 1px solid var(--mj-border-default) !important;\n border-bottom-color: var(--mj-bg-surface) !important;\n border-radius: 4px 4px 0 0 !important;\n z-index: 2 !important;\n}\n\nmj-dashboard-viewer .lm_title {\n cursor: pointer !important;\n user-select: none !important;\n}\n\n/* Panel icon styling - icon is direct child of .lm_tab to preserve GL's drag/drop */\n/* Positioned absolutely in the left padding area so it doesn't affect GL's title structure */\nmj-dashboard-viewer .lm_header .lm_tab .panel-icon {\n position: absolute !important;\n left: 10px !important;\n top: 50% !important;\n transform: translateY(-30%) !important;\n font-family: \"Font Awesome 6 Free\", \"Font Awesome 6 Pro\", \"Font Awesome 5 Free\", \"Font Awesome 5 Pro\", FontAwesome !important;\n font-weight: 900 !important; /* Required for solid icons */\n font-size: 12px !important;\n color: var(--mj-brand-primary) !important;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nmj-dashboard-viewer .lm_close_tab {\n position: absolute !important;\n right: 4px !important;\n top: 50% !important;\n transform: translateY(-50%) !important;\n width: 16px !important;\n height: 16px !important;\n cursor: pointer !important;\n opacity: 0 !important;\n transition: all 0.15s ease !important;\n}\n\nmj-dashboard-viewer .lm_header .lm_tab:hover .lm_close_tab {\n opacity: 0.7 !important;\n}\n\nmj-dashboard-viewer .lm_close_tab:hover {\n opacity: 1 !important;\n color: var(--mj-status-error) !important;\n}\n\n/* Add padding for close button when editing */\nmj-dashboard-viewer .dashboard-viewer.editing .lm_header .lm_tab {\n padding-right: 24px !important;\n}\n\n/* ========================================\n View Mode (Not Editing) - Lock Layout\n ======================================== */\n\n/* Hide close buttons on tabs when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_close_tab {\n display: none !important;\n}\n\n/* Remove extra padding for close button when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n padding-right: 16px !important;\n}\n\n/* Disable splitter dragging when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter {\n pointer-events: none !important;\n cursor: default !important;\n}\n\n/* Hide splitter drag handle visual when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_splitter .lm_drag_handle {\n display: none !important;\n}\n\n/* Disable tab dragging cursor when not editing */\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_header .lm_tab {\n cursor: default !important;\n}\n\nmj-dashboard-viewer .dashboard-viewer:not(.editing) .lm_title {\n cursor: default !important;\n}\n"] }]
1084
1084
  }], () => [{ type: i0.ChangeDetectorRef }, { type: i0.ApplicationRef }, { type: i0.Injector }, { type: i0.EnvironmentInjector }], { dashboard: [{
1085
1085
  type: Input
1086
1086
  }], dashboardId: [{