@memberjunction/ng-dashboard-viewer 5.11.0 → 5.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/breadcrumb/dashboard-breadcrumb.component.js +2 -2
- package/dist/lib/config-dialogs/confirm-dialog.component.js +2 -2
- package/dist/lib/config-panels/artifact-config-panel.component.js +2 -2
- package/dist/lib/config-panels/query-config-panel.component.js +2 -2
- package/dist/lib/config-panels/view-config-panel.component.js +2 -2
- package/dist/lib/config-panels/weburl-config-panel.component.js +2 -2
- package/dist/lib/dashboard-browser/dashboard-browser.component.js +5 -5
- package/dist/lib/dashboard-browser/dashboard-browser.component.js.map +1 -1
- package/dist/lib/dashboard-viewer/dashboard-viewer.component.js +54 -54
- package/dist/lib/dashboard-viewer/dashboard-viewer.component.js.map +1 -1
- package/dist/lib/dialogs/add-panel-dialog/add-panel-dialog.component.js +2 -2
- package/dist/lib/dialogs/edit-part-dialog/edit-part-dialog.component.js +2 -2
- package/dist/lib/parts/artifact-part.component.js +2 -2
- package/dist/lib/parts/artifact-part.component.js.map +1 -1
- package/dist/lib/parts/query-part.component.js +2 -2
- package/dist/lib/parts/query-part.component.js.map +1 -1
- package/dist/lib/parts/view-part.component.js +2 -2
- package/dist/lib/parts/view-part.component.js.map +1 -1
- package/dist/lib/parts/weburl-part.component.js +2 -2
- package/dist/lib/parts/weburl-part.component.js.map +1 -1
- package/dist/lib/services/golden-layout-wrapper.service.js +1 -1
- package/dist/lib/services/golden-layout-wrapper.service.js.map +1 -1
- package/package.json +9 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/dashboard-viewer/dashboard-viewer.component.ts","../../../src/lib/dashboard-viewer/dashboard-viewer.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnDestroy,\n ViewChild,\n ElementRef,\n ChangeDetectorRef,\n ApplicationRef,\n Injector,\n ComponentRef,\n createComponent,\n EnvironmentInjector,\n Type,\n ViewEncapsulation\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { Metadata, RunView } from '@memberjunction/core';\nimport { MJGlobal, UUIDsEqual } from '@memberjunction/global';\nimport { DashboardEngine, MJDashboardEntity, MJDashboardPartTypeEntity, MJDashboardCategoryEntity } from '@memberjunction/core-entities';\nimport { BreadcrumbNavigateEvent } from '../breadcrumb/dashboard-breadcrumb.component';\nimport { ResolvedLayoutConfig } from 'golden-layout';\nimport {\n DashboardConfig,\n DashboardPanel,\n PanelConfig,\n PanelInteractionEvent,\n DashboardConfigChangedEvent,\n LayoutChangedEvent,\n DashboardNavRequestEvent,\n createDefaultDashboardConfig,\n generatePanelId,\n extractPanelsFromLayout,\n findPanelInLayout\n} from '../models/dashboard-types';\nimport { GoldenLayoutWrapperService, LayoutLocation } from '../services/golden-layout-wrapper.service';\nimport { BaseDashboardPart } from '../parts/base-dashboard-part';\n\n/**\n * Main dashboard viewer component.\n * Renders a configurable dashboard with draggable/resizable panels using Golden Layout.\n *\n * This component is GENERIC and has no routing dependencies.\n * Navigation events are bubbled up for the parent component to handle.\n */\n@Component({\n standalone: false,\n selector: 'mj-dashboard-viewer',\n templateUrl: './dashboard-viewer.component.html',\n styleUrls: ['./dashboard-viewer.component.css'],\n encapsulation: ViewEncapsulation.None\n})\nexport class DashboardViewerComponent implements OnDestroy {\n // ========================================\n // Inputs\n // ========================================\n\n private _dashboard: MJDashboardEntity | null = null;\n private _dashboardId: string | null = null;\n\n /** The dashboard entity to display */\n @Input()\n set dashboard(value: MJDashboardEntity | null) {\n const previous = this._dashboard;\n this._dashboard = value;\n if (value && value !== previous) {\n this.onDashboardChanged();\n }\n }\n get dashboard(): MJDashboardEntity | null {\n return this._dashboard;\n }\n\n /** Alternative: Load dashboard by ID */\n @Input()\n set dashboardId(value: string | null) {\n const previous = this._dashboardId;\n this._dashboardId = value;\n if (value && value !== previous) {\n this.loadDashboardById(value);\n }\n }\n get dashboardId(): string | null {\n return this._dashboardId;\n }\n\n /** Whether the dashboard is in edit mode */\n private _isEditing = false;\n\n @Input()\n set isEditing(value: boolean) {\n const previous = this._isEditing;\n this._isEditing = value;\n // When isEditing changes (and layout exists), reinitialize to apply GL settings\n if (value !== previous && this._glService) {\n console.log('[DashboardViewer] isEditing changed from', previous, 'to', value);\n this.updatePanelEditModes();\n }\n }\n get isEditing(): boolean {\n return this._isEditing;\n }\n\n /** Whether to show the toolbar */\n private _showToolbar = true;\n\n @Input()\n set showToolbar(value: boolean) {\n this._showToolbar = value;\n }\n get showToolbar(): boolean {\n return this._showToolbar;\n }\n\n /** Whether to auto-save layout changes */\n private _autoSave = false;\n\n @Input()\n set autoSave(value: boolean) {\n this._autoSave = value;\n }\n get autoSave(): boolean {\n return this._autoSave;\n }\n\n /** Whether to show the breadcrumb navigation */\n @Input() showBreadcrumb = true;\n\n /** Whether to show the \"Open in Tab\" button (for embedded dashboards) */\n @Input() showOpenInTabButton = false;\n\n /** Whether to show the Edit button */\n @Input() showEditButton = true;\n\n /** All categories for breadcrumb path resolution */\n @Input() Categories: MJDashboardCategoryEntity[] = [];\n\n /**\n * Computed: Should the toolbar be visible?\n * Auto-hides when showToolbar=false OR when all toolbar elements are disabled\n */\n public get shouldShowToolbar(): boolean {\n if (!this._showToolbar) {\n return false;\n }\n // If all elements are hidden, hide the toolbar entirely\n return this.showBreadcrumb || this.showOpenInTabButton || this.showEditButton;\n }\n\n // ========================================\n // Outputs\n // ========================================\n\n /** Emitted when dashboard configuration changes */\n @Output() configChanged = new EventEmitter<DashboardConfigChangedEvent>();\n\n /** Emitted when a panel requests navigation to another resource */\n @Output() navigationRequested = new EventEmitter<DashboardNavRequestEvent>();\n\n /** Emitted when a panel interaction occurs */\n @Output() panelInteraction = new EventEmitter<PanelInteractionEvent>();\n\n /** Emitted when the dashboard is saved */\n @Output() dashboardSaved = new EventEmitter<MJDashboardEntity>();\n\n /** Emitted when an error occurs */\n @Output() error = new EventEmitter<{ message: string; error?: Error }>();\n\n /** Emitted when edit mode changes */\n @Output() editModeChanged = new EventEmitter<boolean>();\n\n /** Emitted when user navigates via breadcrumb */\n @Output() breadcrumbNavigate = new EventEmitter<BreadcrumbNavigateEvent>();\n\n /** Emitted when user clicks \"Open in Tab\" button */\n @Output() openInTab = new EventEmitter<{ dashboardId: string; dashboardName: string }>();\n\n // ========================================\n // View Children\n // ========================================\n\n @ViewChild('layoutContainer', { static: true }) layoutContainer!: ElementRef<HTMLElement>;\n\n // ========================================\n // State\n // ========================================\n\n public isLoading = false;\n public config: DashboardConfig | null = null;\n public partTypes: MJDashboardPartTypeEntity[] = [];\n public hasUnsavedChanges = false;\n\n /**\n * Helper to check if layout has any panels (for template use).\n * Panels are stored in componentState within the layout tree.\n */\n public get hasPanels(): boolean {\n return extractPanelsFromLayout(this.config?.layout ?? null).length > 0;\n }\n\n private readonly _destroy$ = new Subject<void>();\n private readonly _panelComponents = new Map<string, { wrapper: HTMLElement; componentRef?: ComponentRef<BaseDashboardPart> }>();\n private _glService: GoldenLayoutWrapperService | null = null;\n\n /** Promise that resolves when part types are loaded - used to ensure layout waits for part types */\n private _partTypesLoaded: Promise<void> | null = null;\n\n // ========================================\n // Constructor\n // ========================================\n\n constructor(\n private readonly cdr: ChangeDetectorRef,\n private readonly appRef: ApplicationRef,\n private readonly injector: Injector,\n private readonly environmentInjector: EnvironmentInjector\n ) {\n // Store the promise so layout initialization can wait for it\n this._partTypesLoaded = this.loadPartTypes();\n }\n\n // ========================================\n // Lifecycle\n // ========================================\n\n ngOnDestroy(): void {\n this._destroy$.next();\n this._destroy$.complete();\n this.destroyLayout();\n }\n\n // ========================================\n // Public Methods\n // ========================================\n\n /**\n * Toggle edit mode\n */\n public toggleEditMode(): void {\n this.isEditing = !this.isEditing;\n this.editModeChanged.emit(this.isEditing);\n this.updatePanelEditModes();\n }\n\n /**\n * Add a new panel to the dashboard.\n * The panel is stored in GL's componentState - no separate panels array.\n */\n public async addPanel(\n partTypeId: string,\n panelConfig: PanelConfig,\n title: string,\n icon?: string,\n location?: LayoutLocation\n ): Promise<void> {\n if (!this.config || !this._glService) {\n return;\n }\n\n const partType = this.partTypes.find(pt => UUIDsEqual(pt.ID, partTypeId));\n if (!partType) {\n this.error.emit({ message: `Unknown panel type: ${partTypeId}` });\n return;\n }\n\n const panel: DashboardPanel = {\n id: generatePanelId(),\n partTypeId,\n title,\n icon: icon || partType.Icon || 'fa-solid fa-window-maximize',\n config: panelConfig\n };\n\n // Add to Golden Layout - panel data stored in componentState\n this._glService.addPanel(panel, location);\n\n // Sync the layout config from Golden Layout to capture the new panel\n // The layout IS the source of truth - it contains the panel in componentState\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n\n this.markDirty();\n }\n\n /**\n * Remove a panel from the dashboard.\n * Panels live in GL's componentState, so removing from GL removes the panel.\n */\n public removePanel(panelId: string): void {\n if (!this.config || !this._glService) return;\n\n // Remove from layout (panel data is in componentState)\n this._glService.removePanel(panelId);\n\n // Sync layout config to persist the removal\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n\n // Destroy component\n this.destroyPanelComponent(panelId);\n\n this.markDirty();\n }\n\n /**\n * Save the current dashboard configuration\n */\n public async save(): Promise<boolean> {\n if (!this._dashboard || !this.config) return false;\n\n try {\n this.isLoading = true;\n this.cdr.detectChanges();\n\n // Update the layout config from Golden Layout\n if (this._glService) {\n const glConfig = this._glService.getLayoutConfig();\n if (glConfig) {\n this.config.layout = glConfig;\n }\n }\n\n // Save to UIConfigDetails\n this._dashboard.UIConfigDetails = JSON.stringify(this.config);\n const saved = await this._dashboard.Save();\n\n if (saved) {\n this.hasUnsavedChanges = false;\n this.dashboardSaved.emit(this._dashboard);\n }\n\n return saved;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.error.emit({ message: 'Failed to save dashboard', error });\n return false;\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Refresh all panels\n */\n public async refreshAllPanels(): Promise<void> {\n // For placeholder implementation, reinitialize the layout\n if (this._glService) {\n await this.initializeLayout();\n }\n }\n\n /**\n * Get the current configuration\n */\n public getConfig(): DashboardConfig | null {\n return this.config;\n }\n\n /**\n * Get available part types\n */\n public getPartTypes(): MJDashboardPartTypeEntity[] {\n return this.partTypes;\n }\n\n /**\n * Get a panel by ID.\n * Extracts panel from the layout's componentState (single source of truth).\n */\n public getPanel(panelId: string): DashboardPanel | null {\n return findPanelInLayout(this.config?.layout ?? null, panelId);\n }\n\n /**\n * Get the part type for a panel\n */\n public getPartTypeForPanel(panelId: string): MJDashboardPartTypeEntity | null {\n const panel = this.getPanel(panelId);\n if (!panel) return null;\n return this.partTypes.find(pt => UUIDsEqual(pt.ID, panel.partTypeId)) ?? null;\n }\n\n /**\n * Update a panel's configuration.\n * Since panels live in componentState within the layout, we need to\n * update the layout tree directly or reinitialize with updated data.\n */\n public updatePanelConfig(panelId: string, newConfig: PanelConfig, title?: string, icon?: string): void {\n if (!this.config || !this._glService) return;\n\n // Get current layout which contains all panel data\n const currentLayout = this._glService.getLayoutConfig();\n if (!currentLayout) return;\n\n // Update panel in the layout tree\n this.updatePanelInLayout(currentLayout, panelId, newConfig, title, icon);\n this.config.layout = currentLayout;\n\n this.markDirty();\n\n // Reinitialize layout to reflect the updated panel\n this.initializeLayout();\n }\n\n /**\n * Recursively update a panel's config within the layout tree\n */\n private updatePanelInLayout(\n layout: ResolvedLayoutConfig,\n panelId: string,\n newConfig: PanelConfig,\n title?: string,\n icon?: string\n ): void {\n if (!layout.root) return;\n\n const updateNode = (node: ResolvedLayoutConfig['root']): void => {\n if (!node) return;\n\n // If this is a component with matching panelId\n if (node.type === 'component') {\n const componentNode = node as unknown as { componentState?: DashboardPanel; title?: string };\n if (componentNode.componentState?.id === panelId) {\n componentNode.componentState.config = newConfig;\n if (title) {\n componentNode.componentState.title = title;\n componentNode.title = title;\n }\n if (icon) componentNode.componentState.icon = icon;\n }\n }\n\n // Recursively process children\n const containerNode = node as unknown as { content?: ResolvedLayoutConfig['root'][] };\n if (containerNode.content && Array.isArray(containerNode.content)) {\n for (const child of containerNode.content) {\n updateNode(child);\n }\n }\n };\n\n updateNode(layout.root);\n }\n\n /**\n * Handle add panel button click - emits event for parent to show dialog\n */\n public onAddPanelClick(): void {\n // Emit interaction event for parent to handle\n // Parent should show AddPanelDialog and call addPanel() with result\n this.panelInteraction.emit({\n panelId: '',\n interactionType: 'custom',\n payload: { action: 'add-panel-requested', partTypes: this.partTypes }\n });\n }\n\n /**\n * Handle \"Open in Tab\" button click - emits event for parent to open dashboard in its own tab\n */\n public onOpenInTabClick(): void {\n if (this._dashboard) {\n this.openInTab.emit({\n dashboardId: this._dashboard.ID,\n dashboardName: this._dashboard.Name\n });\n }\n }\n\n /**\n * Handle breadcrumb navigation\n */\n public onBreadcrumbNavigate(event: BreadcrumbNavigateEvent): void {\n this.breadcrumbNavigate.emit(event);\n }\n\n // ========================================\n // Private Methods - Initialization\n // ========================================\n\n private async loadPartTypes(): Promise<void> {\n try {\n await DashboardEngine.Instance.Config(false);\n this.partTypes = DashboardEngine.Instance.DashboardPartTypes;\n } catch (err) {\n console.error('Failed to load dashboard part types:', err);\n }\n }\n\n private async loadDashboardById(id: string): Promise<void> {\n try {\n this.isLoading = true;\n this.cdr.detectChanges();\n\n const md = new Metadata();\n const dashboard = await md.GetEntityObject<MJDashboardEntity>('MJ: Dashboards');\n const loaded = await dashboard.Load(id);\n\n if (loaded) {\n this._dashboard = dashboard;\n this.onDashboardChanged();\n } else {\n this.error.emit({ message: `Dashboard not found: ${id}` });\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.error.emit({ message: 'Failed to load dashboard', error });\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n private async onDashboardChanged(): Promise<void> {\n if (!this._dashboard) return;\n\n // Parse or create config\n this.config = this.parseOrCreateConfig();\n\n // Wait for part types to be loaded before initializing layout\n // This ensures partTypes array is populated when createPanelComponent is called\n if (this._partTypesLoaded) {\n await this._partTypesLoaded;\n }\n\n // Initialize layout\n this.initializeLayout();\n }\n\n private parseOrCreateConfig(): DashboardConfig {\n if (!this._dashboard?.UIConfigDetails) {\n return createDefaultDashboardConfig();\n }\n\n try {\n const parsed = JSON.parse(this._dashboard.UIConfigDetails);\n\n // Validate it has the expected structure (layout + settings)\n if (parsed.layout !== undefined && parsed.settings) {\n return parsed as DashboardConfig;\n }\n\n // Invalid format, return default\n console.warn('[DashboardViewer] Invalid config format, using default');\n return createDefaultDashboardConfig();\n } catch {\n return createDefaultDashboardConfig();\n }\n }\n\n // ========================================\n // Private Methods - Layout\n // ========================================\n\n private initializeLayout(): void {\n if (!this.config || !this.layoutContainer?.nativeElement) {\n return;\n }\n\n // Destroy existing layout\n this.destroyLayout();\n\n // Create new Golden Layout service\n this._glService = new GoldenLayoutWrapperService();\n\n // Subscribe to layout events\n this.subscribeToLayoutEvents();\n\n // Panel factory - called by GL when it binds a component\n // The panel comes directly from GL's componentState (single source of truth)\n const panelFactory = (panel: DashboardPanel, container: HTMLElement) => {\n this.createPanelComponent(panel, container);\n };\n\n // Initialize with saved layout (or null for empty dashboard)\n // Golden Layout's native ResolvedLayoutConfig is the source of truth\n // Panel data is embedded in each component's componentState\n this._glService.initialize(\n this.layoutContainer.nativeElement,\n this.config.layout,\n panelFactory,\n this.isEditing\n );\n\n // After GL.initialize() completes, all components from the saved layout\n // have been synchronously bound via the panelFactory callback.\n // However, Angular needs time to complete change detection and render\n // the dynamic components. A single delayed updateSize() ensures GL\n // recalculates dimensions after Angular has finished rendering.\n setTimeout(() => {\n this._glService?.updateSize();\n this.cdr.detectChanges();\n }, 100);\n }\n\n /** Flag to prevent panel removal during layout reinit */\n private _isReinitializing = false;\n\n private destroyLayout(): void {\n // Set flag to prevent onPanelClosed from removing panels during reinit\n this._isReinitializing = true;\n\n // Destroy all panel components\n this._panelComponents.forEach((entry, panelId) => {\n this.destroyPanelComponent(panelId);\n });\n this._panelComponents.clear();\n\n // Destroy Golden Layout\n if (this._glService) {\n this._glService.destroy();\n this._glService = null;\n }\n\n this._isReinitializing = false;\n }\n\n private subscribeToLayoutEvents(): void {\n if (!this._glService) return;\n\n this._glService.onLayoutChanged\n .pipe(takeUntil(this._destroy$))\n .subscribe((event: LayoutChangedEvent) => {\n this.onLayoutChanged(event);\n });\n\n this._glService.onPanelClosed\n .pipe(takeUntil(this._destroy$))\n .subscribe((panelId: string) => {\n this.onPanelClosed(panelId);\n });\n\n this._glService.onPanelSelected\n .pipe(takeUntil(this._destroy$))\n .subscribe((panelId: string) => {\n this.onPanelSelected(panelId);\n });\n }\n\n private onLayoutChanged(event: LayoutChangedEvent): void {\n if (this.config) {\n this.config.layout = event.layout;\n this.markDirty();\n\n // Map layout change types to config change types\n const changeType = event.changeType === 'close' ? 'panel-removed' : 'layout';\n this.configChanged.emit({\n config: this.config,\n changeType\n });\n }\n }\n\n private onPanelClosed(panelId: string): void {\n // Skip panel removal during layout reinit (panels are being recreated, not actually closed)\n if (this._isReinitializing) {\n return;\n }\n\n // Sync layout config - panel was removed from GL's tree\n if (this.config && this._glService) {\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n }\n\n // Destroy component\n this.destroyPanelComponent(panelId);\n this.markDirty();\n }\n\n private onPanelSelected(panelId: string): void {\n // Could be used to highlight selected panel in edit mode\n }\n\n // ========================================\n // Private Methods - Panel Components\n // ========================================\n\n /**\n * Create a panel component from the DashboardPanel data.\n * Panel comes directly from GL's componentState - no lookup needed.\n */\n private createPanelComponent(panel: DashboardPanel, container: HTMLElement): void {\n const partType = this.partTypes.find(pt => UUIDsEqual(pt.ID, panel.partTypeId));\n\n // Create the panel wrapper with header and content\n const wrapper = document.createElement('div');\n wrapper.className = 'dashboard-part-wrapper';\n wrapper.style.cssText = 'display: flex; flex-direction: column; height: 100%; background: #fff;';\n\n // Only show header in edit mode - GL tabs already display the title in view mode\n if (this.isEditing) {\n const header = this.createPartHeader(panel, panel.id);\n wrapper.appendChild(header);\n }\n\n // Create content area\n const content = document.createElement('div');\n content.className = 'dashboard-part-content';\n content.style.cssText = 'flex: 1; overflow: auto; min-height: 0;';\n\n // Try to create dynamic component via ClassFactory\n const componentRef = this.createDynamicPartComponent(panel, partType, content);\n\n if (!componentRef) {\n // Fallback to static rendering if no DriverClass or component creation failed\n this.renderPartContent(panel, content, partType);\n }\n\n wrapper.appendChild(content);\n container.appendChild(wrapper);\n\n // Store reference for cleanup\n this._panelComponents.set(panel.id, { wrapper, componentRef: componentRef || undefined });\n }\n\n /**\n * Create a dynamic part component using ClassFactory\n */\n private createDynamicPartComponent(\n panel: DashboardPanel,\n partType: MJDashboardPartTypeEntity | undefined,\n container: HTMLElement\n ): ComponentRef<BaseDashboardPart> | null {\n if (!partType?.DriverClass) {\n return null;\n }\n\n try {\n // Use ClassFactory to create instance and get the component class\n const partInstance = MJGlobal.Instance.ClassFactory.CreateInstance<BaseDashboardPart>(\n BaseDashboardPart,\n partType.DriverClass\n );\n\n if (!partInstance) {\n return null;\n }\n\n // Get the Angular component class from the instance\n // The constructor is a concrete class that extends BaseDashboardPart\n const componentClass = (partInstance as object).constructor as Type<BaseDashboardPart>;\n\n // Create the component dynamically\n const componentRef = createComponent(componentClass, {\n environmentInjector: this.environmentInjector,\n elementInjector: this.injector\n });\n\n // Set inputs on the component\n const instance = componentRef.instance;\n instance.Panel = panel;\n instance.PartType = partType;\n instance.IsEditing = this.isEditing;\n\n // Subscribe to events\n instance.ConfigureRequested.subscribe(() => {\n this.onConfigurePart(panel.id);\n });\n instance.RemoveRequested.subscribe(() => {\n this.onRemovePart(panel.id);\n });\n instance.NavigationRequested.subscribe((event: DashboardNavRequestEvent) => {\n this.navigationRequested.emit(event);\n });\n\n // Attach component to DOM\n container.appendChild(componentRef.location.nativeElement);\n\n // Attach to Angular's change detection\n this.appRef.attachView(componentRef.hostView);\n\n return componentRef;\n } catch (error) {\n console.error('[DashboardViewer] Failed to create dynamic component:', error);\n return null;\n }\n }\n\n private createPartHeader(panel: DashboardPanel, panelId: string): HTMLElement {\n const header = document.createElement('div');\n header.className = 'dashboard-part-header';\n header.style.cssText = `\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 `;\n\n // Icon and title\n const titleSection = document.createElement('div');\n titleSection.style.cssText = 'display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;';\n titleSection.innerHTML = `\n <i class=\"${panel.icon || 'fa-solid fa-puzzle-piece'}\" style=\"color: #5c6bc0; font-size: 14px;\"></i>\n <span style=\"font-weight: 500; font-size: 14px; color: #333; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\">${panel.title}</span>\n `;\n header.appendChild(titleSection);\n\n // Action buttons (only in edit mode)\n if (this.isEditing) {\n const actions = document.createElement('div');\n actions.style.cssText = 'display: flex; gap: 4px;';\n\n // Configure button\n const configBtn = document.createElement('button');\n configBtn.className = 'part-action-btn';\n configBtn.title = 'Configure';\n configBtn.style.cssText = `\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: #666;\n cursor: pointer;\n transition: all 0.15s;\n `;\n configBtn.innerHTML = '<i class=\"fa-solid fa-cog\" style=\"font-size: 12px;\"></i>';\n configBtn.addEventListener('click', () => this.onConfigurePart(panelId));\n configBtn.addEventListener('mouseenter', () => {\n configBtn.style.background = '#e0e0e0';\n configBtn.style.color = '#333';\n });\n configBtn.addEventListener('mouseleave', () => {\n configBtn.style.background = 'transparent';\n configBtn.style.color = '#666';\n });\n\n // Remove button\n const removeBtn = document.createElement('button');\n removeBtn.className = 'part-action-btn';\n removeBtn.title = 'Remove';\n removeBtn.style.cssText = `\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: #666;\n cursor: pointer;\n transition: all 0.15s;\n `;\n removeBtn.innerHTML = '<i class=\"fa-solid fa-times\" style=\"font-size: 12px;\"></i>';\n removeBtn.addEventListener('click', () => this.onRemovePart(panelId));\n removeBtn.addEventListener('mouseenter', () => {\n removeBtn.style.background = '#ffebee';\n removeBtn.style.color = '#d32f2f';\n });\n removeBtn.addEventListener('mouseleave', () => {\n removeBtn.style.background = 'transparent';\n removeBtn.style.color = '#666';\n });\n\n actions.appendChild(configBtn);\n actions.appendChild(removeBtn);\n header.appendChild(actions);\n }\n\n return header;\n }\n\n private renderPartContent(panel: DashboardPanel, container: HTMLElement, partType: MJDashboardPartTypeEntity | undefined): void {\n const config = panel.config;\n\n switch (config?.type) {\n case 'WebURL':\n this.renderWebURLPart(panel, container, config);\n break;\n case 'View':\n this.renderViewPart(panel, container, config);\n break;\n case 'Query':\n this.renderQueryPart(panel, container, config);\n break;\n case 'Artifact':\n this.renderArtifactPart(panel, container, config);\n break;\n default:\n this.renderPlaceholderPart(panel, container, partType);\n }\n }\n\n private renderWebURLPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const url = config['url'] as string | undefined;\n if (!url) {\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;\">\n <i class=\"fa-solid fa-globe\" style=\"font-size: 48px; color: #ccc; margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: #333;\">No URL Configured</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click the configure button to set a URL for this part.</p>\n </div>\n `;\n return;\n }\n\n const sandboxMode = config['sandboxMode'] as string | undefined;\n // Determine sandbox permissions based on mode\n let sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups';\n if (sandboxMode === 'strict') {\n sandbox = 'allow-scripts';\n } else if (sandboxMode === 'permissive') {\n sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation';\n }\n\n const iframe = document.createElement('iframe');\n iframe.src = url;\n iframe.style.cssText = 'width: 100%; height: 100%; border: none;';\n iframe.sandbox.value = sandbox;\n if (config['allowFullscreen'] !== false) {\n iframe.allowFullscreen = true;\n }\n iframe.title = panel.title;\n\n container.appendChild(iframe);\n }\n\n private renderViewPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const viewId = config['viewId'] as string | undefined;\n const entityName = config['entityName'] as string | undefined;\n if (!viewId && !entityName) {\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;\">\n <i class=\"fa-solid fa-table\" style=\"font-size: 48px; color: #ccc; margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: #333;\">No View Selected</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click configure to select a view for this part.</p>\n </div>\n `;\n return;\n }\n\n const viewInfo = viewId ? viewId.substring(0, 8) + '...' : entityName;\n const displayModeValue = config['displayMode'] as string | undefined;\n const displayMode = displayModeValue === 'grid' ? 'Grid View' : displayModeValue === 'cards' ? 'Card View' : 'Timeline View';\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; height: 100%; background: #fff;\">\n <div style=\"padding: 16px 20px; border-bottom: 1px solid #e0e0e0; background: #fafafa;\">\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <i class=\"fa-solid fa-table\" style=\"font-size: 20px; color: #5c6bc0;\"></i>\n <div>\n <div style=\"font-weight: 500; color: #333; font-size: 14px;\">Entity View</div>\n <div style=\"font-size: 12px; color: #666;\">${entityName || 'View ' + viewInfo}</div>\n </div>\n <span style=\"margin-left: auto; padding: 4px 10px; background: #e3f2fd; color: #1976d2; border-radius: 12px; font-size: 11px; font-weight: 500;\">${displayMode}</span>\n </div>\n </div>\n <div style=\"flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; padding: 24px;\">\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 24px; margin-bottom: 12px;\"></i>\n <p style=\"margin: 0; font-size: 13px;\">Entity grid loading...</p>\n <p style=\"margin: 8px 0 0 0; font-size: 11px; color: #bbb;\">Full implementation pending Angular integration</p>\n </div>\n </div>\n `;\n }\n\n private renderQueryPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const queryId = config['queryId'] as string | undefined;\n const queryName = config['queryName'] as string | undefined;\n if (!queryId && !queryName) {\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;\">\n <i class=\"fa-solid fa-database\" style=\"font-size: 48px; color: #ccc; margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: #333;\">No Query Selected</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click configure to select a query for this part.</p>\n </div>\n `;\n return;\n }\n\n const autoRefreshSeconds = (config['autoRefreshSeconds'] as number) || 0;\n const queryInfo = queryName || (queryId ? queryId.substring(0, 8) + '...' : 'Unknown');\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; height: 100%; background: #fff;\">\n <div style=\"padding: 16px 20px; border-bottom: 1px solid #e0e0e0; background: #fafafa;\">\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <i class=\"fa-solid fa-database\" style=\"font-size: 20px; color: #5c6bc0;\"></i>\n <div>\n <div style=\"font-weight: 500; color: #333; font-size: 14px;\">Query Results</div>\n <div style=\"font-size: 12px; color: #666;\">${queryInfo}</div>\n </div>\n <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>\n </div>\n </div>\n <div style=\"flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; padding: 24px;\">\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 24px; margin-bottom: 12px;\"></i>\n <p style=\"margin: 0; font-size: 13px;\">Query grid loading...</p>\n <p style=\"margin: 8px 0 0 0; font-size: 11px; color: #bbb;\">Full implementation pending Angular integration</p>\n </div>\n </div>\n `;\n }\n\n private renderArtifactPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const artifactId = config['artifactId'] as string | undefined;\n if (!artifactId) {\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;\">\n <i class=\"fa-solid fa-cube\" style=\"font-size: 48px; color: #ccc; margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: #333;\">No Artifact Selected</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click configure to select an artifact for this part.</p>\n </div>\n `;\n return;\n }\n\n const versionNumber = config['versionNumber'] as number | undefined;\n const artifactInfo = artifactId.substring(0, 8) + '...';\n const versionInfo = versionNumber ? `v${versionNumber}` : 'Latest';\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; height: 100%; background: #fff;\">\n <div style=\"padding: 16px 20px; border-bottom: 1px solid #e0e0e0; background: #fafafa;\">\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <i class=\"fa-solid fa-cube\" style=\"font-size: 20px; color: #5c6bc0;\"></i>\n <div>\n <div style=\"font-weight: 500; color: #333; font-size: 14px;\">Artifact</div>\n <div style=\"font-size: 12px; color: #666;\">ID: ${artifactInfo}</div>\n </div>\n <span style=\"margin-left: auto; padding: 4px 10px; background: #fce4ec; color: #c2185b; border-radius: 12px; font-size: 11px; font-weight: 500;\">${versionInfo}</span>\n </div>\n </div>\n <div style=\"flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #999; padding: 24px;\">\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 24px; margin-bottom: 12px;\"></i>\n <p style=\"margin: 0; font-size: 13px;\">Artifact viewer loading...</p>\n <p style=\"margin: 8px 0 0 0; font-size: 11px; color: #bbb;\">Full implementation pending Angular integration</p>\n </div>\n </div>\n `;\n }\n\n private renderPlaceholderPart(panel: DashboardPanel, container: HTMLElement, partType: MJDashboardPartTypeEntity | undefined): void {\n const partTypeName = partType?.Name || 'Custom';\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: #666; text-align: center; padding: 24px;\">\n <i class=\"fa-solid fa-puzzle-piece\" style=\"font-size: 48px; color: #ccc; margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: #333;\">${partTypeName} Part</h4>\n <p style=\"margin: 0; font-size: 13px;\">This part type is not yet fully implemented.</p>\n </div>\n `;\n }\n\n private onConfigurePart(panelId: string): void {\n // Emit event for parent to handle configuration\n this.panelInteraction.emit({\n panelId,\n interactionType: 'custom',\n payload: { action: 'configure-part-requested' }\n });\n }\n\n private onRemovePart(panelId: string): void {\n // Emit event for parent to show confirmation dialog\n const panel = findPanelInLayout(this.config?.layout ?? null, panelId);\n this.panelInteraction.emit({\n panelId,\n interactionType: 'custom',\n payload: {\n action: 'remove-part-requested',\n panelTitle: panel?.title || 'this part'\n }\n });\n }\n\n /**\n * Confirm removal of a panel (called by parent after confirmation dialog)\n */\n public confirmRemovePanel(panelId: string): void {\n this.removePanel(panelId);\n }\n\n private destroyPanelComponent(panelId: string): void {\n const entry = this._panelComponents.get(panelId);\n if (entry) {\n // Destroy the Angular component if present\n if (entry.componentRef) {\n this.appRef.detachView(entry.componentRef.hostView);\n entry.componentRef.destroy();\n }\n this._panelComponents.delete(panelId);\n }\n }\n\n private updatePanelEditModes(): void {\n // Update IsEditing on all dynamic components\n this._panelComponents.forEach((entry) => {\n if (entry.componentRef) {\n entry.componentRef.instance.IsEditing = this.isEditing;\n }\n });\n\n // Save current layout before reinitializing (preserves user's arrangement)\n if (this._glService && this.config) {\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n }\n\n // Reinitialize layout to apply new Golden Layout settings (edit mode lock/unlock)\n if (this._glService) {\n this.initializeLayout();\n }\n }\n\n // ========================================\n // Private Methods - State\n // ========================================\n\n private markDirty(): void {\n this.hasUnsavedChanges = true;\n\n if (this.autoSave && this.config) {\n this.save();\n }\n }\n}\n","<!-- 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"]}
|
|
1
|
+
{"version":3,"file":"dashboard-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/dashboard-viewer/dashboard-viewer.component.ts","../../../src/lib/dashboard-viewer/dashboard-viewer.component.html"],"names":[],"mappings":"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","sourcesContent":["import {\n Component,\n Input,\n Output,\n EventEmitter,\n OnDestroy,\n ViewChild,\n ElementRef,\n ChangeDetectorRef,\n ApplicationRef,\n Injector,\n ComponentRef,\n createComponent,\n EnvironmentInjector,\n Type,\n ViewEncapsulation\n} from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\nimport { Metadata, RunView } from '@memberjunction/core';\nimport { MJGlobal, UUIDsEqual } from '@memberjunction/global';\nimport { DashboardEngine, MJDashboardEntity, MJDashboardPartTypeEntity, MJDashboardCategoryEntity } from '@memberjunction/core-entities';\nimport { BreadcrumbNavigateEvent } from '../breadcrumb/dashboard-breadcrumb.component';\nimport { ResolvedLayoutConfig } from 'golden-layout';\nimport {\n DashboardConfig,\n DashboardPanel,\n PanelConfig,\n PanelInteractionEvent,\n DashboardConfigChangedEvent,\n LayoutChangedEvent,\n DashboardNavRequestEvent,\n createDefaultDashboardConfig,\n generatePanelId,\n extractPanelsFromLayout,\n findPanelInLayout\n} from '../models/dashboard-types';\nimport { GoldenLayoutWrapperService, LayoutLocation } from '../services/golden-layout-wrapper.service';\nimport { BaseDashboardPart } from '../parts/base-dashboard-part';\n\n/**\n * Main dashboard viewer component.\n * Renders a configurable dashboard with draggable/resizable panels using Golden Layout.\n *\n * This component is GENERIC and has no routing dependencies.\n * Navigation events are bubbled up for the parent component to handle.\n */\n@Component({\n standalone: false,\n selector: 'mj-dashboard-viewer',\n templateUrl: './dashboard-viewer.component.html',\n styleUrls: ['./dashboard-viewer.component.css'],\n encapsulation: ViewEncapsulation.None\n})\nexport class DashboardViewerComponent implements OnDestroy {\n // ========================================\n // Inputs\n // ========================================\n\n private _dashboard: MJDashboardEntity | null = null;\n private _dashboardId: string | null = null;\n\n /** The dashboard entity to display */\n @Input()\n set dashboard(value: MJDashboardEntity | null) {\n const previous = this._dashboard;\n this._dashboard = value;\n if (value && value !== previous) {\n this.onDashboardChanged();\n }\n }\n get dashboard(): MJDashboardEntity | null {\n return this._dashboard;\n }\n\n /** Alternative: Load dashboard by ID */\n @Input()\n set dashboardId(value: string | null) {\n const previous = this._dashboardId;\n this._dashboardId = value;\n if (value && value !== previous) {\n this.loadDashboardById(value);\n }\n }\n get dashboardId(): string | null {\n return this._dashboardId;\n }\n\n /** Whether the dashboard is in edit mode */\n private _isEditing = false;\n\n @Input()\n set isEditing(value: boolean) {\n const previous = this._isEditing;\n this._isEditing = value;\n // When isEditing changes (and layout exists), reinitialize to apply GL settings\n if (value !== previous && this._glService) {\n console.log('[DashboardViewer] isEditing changed from', previous, 'to', value);\n this.updatePanelEditModes();\n }\n }\n get isEditing(): boolean {\n return this._isEditing;\n }\n\n /** Whether to show the toolbar */\n private _showToolbar = true;\n\n @Input()\n set showToolbar(value: boolean) {\n this._showToolbar = value;\n }\n get showToolbar(): boolean {\n return this._showToolbar;\n }\n\n /** Whether to auto-save layout changes */\n private _autoSave = false;\n\n @Input()\n set autoSave(value: boolean) {\n this._autoSave = value;\n }\n get autoSave(): boolean {\n return this._autoSave;\n }\n\n /** Whether to show the breadcrumb navigation */\n @Input() showBreadcrumb = true;\n\n /** Whether to show the \"Open in Tab\" button (for embedded dashboards) */\n @Input() showOpenInTabButton = false;\n\n /** Whether to show the Edit button */\n @Input() showEditButton = true;\n\n /** All categories for breadcrumb path resolution */\n @Input() Categories: MJDashboardCategoryEntity[] = [];\n\n /**\n * Computed: Should the toolbar be visible?\n * Auto-hides when showToolbar=false OR when all toolbar elements are disabled\n */\n public get shouldShowToolbar(): boolean {\n if (!this._showToolbar) {\n return false;\n }\n // If all elements are hidden, hide the toolbar entirely\n return this.showBreadcrumb || this.showOpenInTabButton || this.showEditButton;\n }\n\n // ========================================\n // Outputs\n // ========================================\n\n /** Emitted when dashboard configuration changes */\n @Output() configChanged = new EventEmitter<DashboardConfigChangedEvent>();\n\n /** Emitted when a panel requests navigation to another resource */\n @Output() navigationRequested = new EventEmitter<DashboardNavRequestEvent>();\n\n /** Emitted when a panel interaction occurs */\n @Output() panelInteraction = new EventEmitter<PanelInteractionEvent>();\n\n /** Emitted when the dashboard is saved */\n @Output() dashboardSaved = new EventEmitter<MJDashboardEntity>();\n\n /** Emitted when an error occurs */\n @Output() error = new EventEmitter<{ message: string; error?: Error }>();\n\n /** Emitted when edit mode changes */\n @Output() editModeChanged = new EventEmitter<boolean>();\n\n /** Emitted when user navigates via breadcrumb */\n @Output() breadcrumbNavigate = new EventEmitter<BreadcrumbNavigateEvent>();\n\n /** Emitted when user clicks \"Open in Tab\" button */\n @Output() openInTab = new EventEmitter<{ dashboardId: string; dashboardName: string }>();\n\n // ========================================\n // View Children\n // ========================================\n\n @ViewChild('layoutContainer', { static: true }) layoutContainer!: ElementRef<HTMLElement>;\n\n // ========================================\n // State\n // ========================================\n\n public isLoading = false;\n public config: DashboardConfig | null = null;\n public partTypes: MJDashboardPartTypeEntity[] = [];\n public hasUnsavedChanges = false;\n\n /**\n * Helper to check if layout has any panels (for template use).\n * Panels are stored in componentState within the layout tree.\n */\n public get hasPanels(): boolean {\n return extractPanelsFromLayout(this.config?.layout ?? null).length > 0;\n }\n\n private readonly _destroy$ = new Subject<void>();\n private readonly _panelComponents = new Map<string, { wrapper: HTMLElement; componentRef?: ComponentRef<BaseDashboardPart> }>();\n private _glService: GoldenLayoutWrapperService | null = null;\n\n /** Promise that resolves when part types are loaded - used to ensure layout waits for part types */\n private _partTypesLoaded: Promise<void> | null = null;\n\n // ========================================\n // Constructor\n // ========================================\n\n constructor(\n private readonly cdr: ChangeDetectorRef,\n private readonly appRef: ApplicationRef,\n private readonly injector: Injector,\n private readonly environmentInjector: EnvironmentInjector\n ) {\n // Store the promise so layout initialization can wait for it\n this._partTypesLoaded = this.loadPartTypes();\n }\n\n // ========================================\n // Lifecycle\n // ========================================\n\n ngOnDestroy(): void {\n this._destroy$.next();\n this._destroy$.complete();\n this.destroyLayout();\n }\n\n // ========================================\n // Public Methods\n // ========================================\n\n /**\n * Toggle edit mode\n */\n public toggleEditMode(): void {\n this.isEditing = !this.isEditing;\n this.editModeChanged.emit(this.isEditing);\n this.updatePanelEditModes();\n }\n\n /**\n * Add a new panel to the dashboard.\n * The panel is stored in GL's componentState - no separate panels array.\n */\n public async addPanel(\n partTypeId: string,\n panelConfig: PanelConfig,\n title: string,\n icon?: string,\n location?: LayoutLocation\n ): Promise<void> {\n if (!this.config || !this._glService) {\n return;\n }\n\n const partType = this.partTypes.find(pt => UUIDsEqual(pt.ID, partTypeId));\n if (!partType) {\n this.error.emit({ message: `Unknown panel type: ${partTypeId}` });\n return;\n }\n\n const panel: DashboardPanel = {\n id: generatePanelId(),\n partTypeId,\n title,\n icon: icon || partType.Icon || 'fa-solid fa-window-maximize',\n config: panelConfig\n };\n\n // Add to Golden Layout - panel data stored in componentState\n this._glService.addPanel(panel, location);\n\n // Sync the layout config from Golden Layout to capture the new panel\n // The layout IS the source of truth - it contains the panel in componentState\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n\n this.markDirty();\n }\n\n /**\n * Remove a panel from the dashboard.\n * Panels live in GL's componentState, so removing from GL removes the panel.\n */\n public removePanel(panelId: string): void {\n if (!this.config || !this._glService) return;\n\n // Remove from layout (panel data is in componentState)\n this._glService.removePanel(panelId);\n\n // Sync layout config to persist the removal\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n\n // Destroy component\n this.destroyPanelComponent(panelId);\n\n this.markDirty();\n }\n\n /**\n * Save the current dashboard configuration\n */\n public async save(): Promise<boolean> {\n if (!this._dashboard || !this.config) return false;\n\n try {\n this.isLoading = true;\n this.cdr.detectChanges();\n\n // Update the layout config from Golden Layout\n if (this._glService) {\n const glConfig = this._glService.getLayoutConfig();\n if (glConfig) {\n this.config.layout = glConfig;\n }\n }\n\n // Save to UIConfigDetails\n this._dashboard.UIConfigDetails = JSON.stringify(this.config);\n const saved = await this._dashboard.Save();\n\n if (saved) {\n this.hasUnsavedChanges = false;\n this.dashboardSaved.emit(this._dashboard);\n }\n\n return saved;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.error.emit({ message: 'Failed to save dashboard', error });\n return false;\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Refresh all panels\n */\n public async refreshAllPanels(): Promise<void> {\n // For placeholder implementation, reinitialize the layout\n if (this._glService) {\n await this.initializeLayout();\n }\n }\n\n /**\n * Get the current configuration\n */\n public getConfig(): DashboardConfig | null {\n return this.config;\n }\n\n /**\n * Get available part types\n */\n public getPartTypes(): MJDashboardPartTypeEntity[] {\n return this.partTypes;\n }\n\n /**\n * Get a panel by ID.\n * Extracts panel from the layout's componentState (single source of truth).\n */\n public getPanel(panelId: string): DashboardPanel | null {\n return findPanelInLayout(this.config?.layout ?? null, panelId);\n }\n\n /**\n * Get the part type for a panel\n */\n public getPartTypeForPanel(panelId: string): MJDashboardPartTypeEntity | null {\n const panel = this.getPanel(panelId);\n if (!panel) return null;\n return this.partTypes.find(pt => UUIDsEqual(pt.ID, panel.partTypeId)) ?? null;\n }\n\n /**\n * Update a panel's configuration.\n * Since panels live in componentState within the layout, we need to\n * update the layout tree directly or reinitialize with updated data.\n */\n public updatePanelConfig(panelId: string, newConfig: PanelConfig, title?: string, icon?: string): void {\n if (!this.config || !this._glService) return;\n\n // Get current layout which contains all panel data\n const currentLayout = this._glService.getLayoutConfig();\n if (!currentLayout) return;\n\n // Update panel in the layout tree\n this.updatePanelInLayout(currentLayout, panelId, newConfig, title, icon);\n this.config.layout = currentLayout;\n\n this.markDirty();\n\n // Reinitialize layout to reflect the updated panel\n this.initializeLayout();\n }\n\n /**\n * Recursively update a panel's config within the layout tree\n */\n private updatePanelInLayout(\n layout: ResolvedLayoutConfig,\n panelId: string,\n newConfig: PanelConfig,\n title?: string,\n icon?: string\n ): void {\n if (!layout.root) return;\n\n const updateNode = (node: ResolvedLayoutConfig['root']): void => {\n if (!node) return;\n\n // If this is a component with matching panelId\n if (node.type === 'component') {\n const componentNode = node as unknown as { componentState?: DashboardPanel; title?: string };\n if (componentNode.componentState?.id === panelId) {\n componentNode.componentState.config = newConfig;\n if (title) {\n componentNode.componentState.title = title;\n componentNode.title = title;\n }\n if (icon) componentNode.componentState.icon = icon;\n }\n }\n\n // Recursively process children\n const containerNode = node as unknown as { content?: ResolvedLayoutConfig['root'][] };\n if (containerNode.content && Array.isArray(containerNode.content)) {\n for (const child of containerNode.content) {\n updateNode(child);\n }\n }\n };\n\n updateNode(layout.root);\n }\n\n /**\n * Handle add panel button click - emits event for parent to show dialog\n */\n public onAddPanelClick(): void {\n // Emit interaction event for parent to handle\n // Parent should show AddPanelDialog and call addPanel() with result\n this.panelInteraction.emit({\n panelId: '',\n interactionType: 'custom',\n payload: { action: 'add-panel-requested', partTypes: this.partTypes }\n });\n }\n\n /**\n * Handle \"Open in Tab\" button click - emits event for parent to open dashboard in its own tab\n */\n public onOpenInTabClick(): void {\n if (this._dashboard) {\n this.openInTab.emit({\n dashboardId: this._dashboard.ID,\n dashboardName: this._dashboard.Name\n });\n }\n }\n\n /**\n * Handle breadcrumb navigation\n */\n public onBreadcrumbNavigate(event: BreadcrumbNavigateEvent): void {\n this.breadcrumbNavigate.emit(event);\n }\n\n // ========================================\n // Private Methods - Initialization\n // ========================================\n\n private async loadPartTypes(): Promise<void> {\n try {\n await DashboardEngine.Instance.Config(false);\n this.partTypes = DashboardEngine.Instance.DashboardPartTypes;\n } catch (err) {\n console.error('Failed to load dashboard part types:', err);\n }\n }\n\n private async loadDashboardById(id: string): Promise<void> {\n try {\n this.isLoading = true;\n this.cdr.detectChanges();\n\n const md = new Metadata();\n const dashboard = await md.GetEntityObject<MJDashboardEntity>('MJ: Dashboards');\n const loaded = await dashboard.Load(id);\n\n if (loaded) {\n this._dashboard = dashboard;\n this.onDashboardChanged();\n } else {\n this.error.emit({ message: `Dashboard not found: ${id}` });\n }\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n this.error.emit({ message: 'Failed to load dashboard', error });\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n }\n }\n\n private async onDashboardChanged(): Promise<void> {\n if (!this._dashboard) return;\n\n // Parse or create config\n this.config = this.parseOrCreateConfig();\n\n // Wait for part types to be loaded before initializing layout\n // This ensures partTypes array is populated when createPanelComponent is called\n if (this._partTypesLoaded) {\n await this._partTypesLoaded;\n }\n\n // Initialize layout\n this.initializeLayout();\n }\n\n private parseOrCreateConfig(): DashboardConfig {\n if (!this._dashboard?.UIConfigDetails) {\n return createDefaultDashboardConfig();\n }\n\n try {\n const parsed = JSON.parse(this._dashboard.UIConfigDetails);\n\n // Validate it has the expected structure (layout + settings)\n if (parsed.layout !== undefined && parsed.settings) {\n return parsed as DashboardConfig;\n }\n\n // Invalid format, return default\n console.warn('[DashboardViewer] Invalid config format, using default');\n return createDefaultDashboardConfig();\n } catch {\n return createDefaultDashboardConfig();\n }\n }\n\n // ========================================\n // Private Methods - Layout\n // ========================================\n\n private initializeLayout(): void {\n if (!this.config || !this.layoutContainer?.nativeElement) {\n return;\n }\n\n // Destroy existing layout\n this.destroyLayout();\n\n // Create new Golden Layout service\n this._glService = new GoldenLayoutWrapperService();\n\n // Subscribe to layout events\n this.subscribeToLayoutEvents();\n\n // Panel factory - called by GL when it binds a component\n // The panel comes directly from GL's componentState (single source of truth)\n const panelFactory = (panel: DashboardPanel, container: HTMLElement) => {\n this.createPanelComponent(panel, container);\n };\n\n // Initialize with saved layout (or null for empty dashboard)\n // Golden Layout's native ResolvedLayoutConfig is the source of truth\n // Panel data is embedded in each component's componentState\n this._glService.initialize(\n this.layoutContainer.nativeElement,\n this.config.layout,\n panelFactory,\n this.isEditing\n );\n\n // After GL.initialize() completes, all components from the saved layout\n // have been synchronously bound via the panelFactory callback.\n // However, Angular needs time to complete change detection and render\n // the dynamic components. A single delayed updateSize() ensures GL\n // recalculates dimensions after Angular has finished rendering.\n setTimeout(() => {\n this._glService?.updateSize();\n this.cdr.detectChanges();\n }, 100);\n }\n\n /** Flag to prevent panel removal during layout reinit */\n private _isReinitializing = false;\n\n private destroyLayout(): void {\n // Set flag to prevent onPanelClosed from removing panels during reinit\n this._isReinitializing = true;\n\n // Destroy all panel components\n this._panelComponents.forEach((entry, panelId) => {\n this.destroyPanelComponent(panelId);\n });\n this._panelComponents.clear();\n\n // Destroy Golden Layout\n if (this._glService) {\n this._glService.destroy();\n this._glService = null;\n }\n\n this._isReinitializing = false;\n }\n\n private subscribeToLayoutEvents(): void {\n if (!this._glService) return;\n\n this._glService.onLayoutChanged\n .pipe(takeUntil(this._destroy$))\n .subscribe((event: LayoutChangedEvent) => {\n this.onLayoutChanged(event);\n });\n\n this._glService.onPanelClosed\n .pipe(takeUntil(this._destroy$))\n .subscribe((panelId: string) => {\n this.onPanelClosed(panelId);\n });\n\n this._glService.onPanelSelected\n .pipe(takeUntil(this._destroy$))\n .subscribe((panelId: string) => {\n this.onPanelSelected(panelId);\n });\n }\n\n private onLayoutChanged(event: LayoutChangedEvent): void {\n if (this.config) {\n this.config.layout = event.layout;\n this.markDirty();\n\n // Map layout change types to config change types\n const changeType = event.changeType === 'close' ? 'panel-removed' : 'layout';\n this.configChanged.emit({\n config: this.config,\n changeType\n });\n }\n }\n\n private onPanelClosed(panelId: string): void {\n // Skip panel removal during layout reinit (panels are being recreated, not actually closed)\n if (this._isReinitializing) {\n return;\n }\n\n // Sync layout config - panel was removed from GL's tree\n if (this.config && this._glService) {\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n }\n\n // Destroy component\n this.destroyPanelComponent(panelId);\n this.markDirty();\n }\n\n private onPanelSelected(panelId: string): void {\n // Could be used to highlight selected panel in edit mode\n }\n\n // ========================================\n // Private Methods - Panel Components\n // ========================================\n\n /**\n * Create a panel component from the DashboardPanel data.\n * Panel comes directly from GL's componentState - no lookup needed.\n */\n private createPanelComponent(panel: DashboardPanel, container: HTMLElement): void {\n const partType = this.partTypes.find(pt => UUIDsEqual(pt.ID, panel.partTypeId));\n\n // Create the panel wrapper with header and content\n const wrapper = document.createElement('div');\n wrapper.className = 'dashboard-part-wrapper';\n wrapper.style.cssText = 'display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);';\n\n // Only show header in edit mode - GL tabs already display the title in view mode\n if (this.isEditing) {\n const header = this.createPartHeader(panel, panel.id);\n wrapper.appendChild(header);\n }\n\n // Create content area\n const content = document.createElement('div');\n content.className = 'dashboard-part-content';\n content.style.cssText = 'flex: 1; overflow: auto; min-height: 0;';\n\n // Try to create dynamic component via ClassFactory\n const componentRef = this.createDynamicPartComponent(panel, partType, content);\n\n if (!componentRef) {\n // Fallback to static rendering if no DriverClass or component creation failed\n this.renderPartContent(panel, content, partType);\n }\n\n wrapper.appendChild(content);\n container.appendChild(wrapper);\n\n // Store reference for cleanup\n this._panelComponents.set(panel.id, { wrapper, componentRef: componentRef || undefined });\n }\n\n /**\n * Create a dynamic part component using ClassFactory\n */\n private createDynamicPartComponent(\n panel: DashboardPanel,\n partType: MJDashboardPartTypeEntity | undefined,\n container: HTMLElement\n ): ComponentRef<BaseDashboardPart> | null {\n if (!partType?.DriverClass) {\n return null;\n }\n\n try {\n // Use ClassFactory to create instance and get the component class\n const partInstance = MJGlobal.Instance.ClassFactory.CreateInstance<BaseDashboardPart>(\n BaseDashboardPart,\n partType.DriverClass\n );\n\n if (!partInstance) {\n return null;\n }\n\n // Get the Angular component class from the instance\n // The constructor is a concrete class that extends BaseDashboardPart\n const componentClass = (partInstance as object).constructor as Type<BaseDashboardPart>;\n\n // Create the component dynamically\n const componentRef = createComponent(componentClass, {\n environmentInjector: this.environmentInjector,\n elementInjector: this.injector\n });\n\n // Set inputs on the component\n const instance = componentRef.instance;\n instance.Panel = panel;\n instance.PartType = partType;\n instance.IsEditing = this.isEditing;\n\n // Subscribe to events\n instance.ConfigureRequested.subscribe(() => {\n this.onConfigurePart(panel.id);\n });\n instance.RemoveRequested.subscribe(() => {\n this.onRemovePart(panel.id);\n });\n instance.NavigationRequested.subscribe((event: DashboardNavRequestEvent) => {\n this.navigationRequested.emit(event);\n });\n\n // Attach component to DOM\n container.appendChild(componentRef.location.nativeElement);\n\n // Attach to Angular's change detection\n this.appRef.attachView(componentRef.hostView);\n\n return componentRef;\n } catch (error) {\n console.error('[DashboardViewer] Failed to create dynamic component:', error);\n return null;\n }\n }\n\n private createPartHeader(panel: DashboardPanel, panelId: string): HTMLElement {\n const header = document.createElement('div');\n header.className = 'dashboard-part-header';\n header.style.cssText = `\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 `;\n\n // Icon and title\n const titleSection = document.createElement('div');\n titleSection.style.cssText = 'display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;';\n titleSection.innerHTML = `\n <i class=\"${panel.icon || 'fa-solid fa-puzzle-piece'}\" style=\"color: var(--mj-brand-primary); font-size: 14px;\"></i>\n <span style=\"font-weight: 500; font-size: 14px; color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\">${panel.title}</span>\n `;\n header.appendChild(titleSection);\n\n // Action buttons (only in edit mode)\n if (this.isEditing) {\n const actions = document.createElement('div');\n actions.style.cssText = 'display: flex; gap: 4px;';\n\n // Configure button\n const configBtn = document.createElement('button');\n configBtn.className = 'part-action-btn';\n configBtn.title = 'Configure';\n configBtn.style.cssText = `\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n `;\n configBtn.innerHTML = '<i class=\"fa-solid fa-cog\" style=\"font-size: 12px;\"></i>';\n configBtn.addEventListener('click', () => this.onConfigurePart(panelId));\n configBtn.addEventListener('mouseenter', () => {\n configBtn.style.background = 'var(--mj-border-default)';\n configBtn.style.color = 'var(--mj-text-primary)';\n });\n configBtn.addEventListener('mouseleave', () => {\n configBtn.style.background = 'transparent';\n configBtn.style.color = 'var(--mj-text-secondary)';\n });\n\n // Remove button\n const removeBtn = document.createElement('button');\n removeBtn.className = 'part-action-btn';\n removeBtn.title = 'Remove';\n removeBtn.style.cssText = `\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 4px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n `;\n removeBtn.innerHTML = '<i class=\"fa-solid fa-times\" style=\"font-size: 12px;\"></i>';\n removeBtn.addEventListener('click', () => this.onRemovePart(panelId));\n removeBtn.addEventListener('mouseenter', () => {\n removeBtn.style.background = 'color-mix(in srgb, var(--mj-status-error) 10%, transparent)';\n removeBtn.style.color = 'var(--mj-status-error)';\n });\n removeBtn.addEventListener('mouseleave', () => {\n removeBtn.style.background = 'transparent';\n removeBtn.style.color = 'var(--mj-text-secondary)';\n });\n\n actions.appendChild(configBtn);\n actions.appendChild(removeBtn);\n header.appendChild(actions);\n }\n\n return header;\n }\n\n private renderPartContent(panel: DashboardPanel, container: HTMLElement, partType: MJDashboardPartTypeEntity | undefined): void {\n const config = panel.config;\n\n switch (config?.type) {\n case 'WebURL':\n this.renderWebURLPart(panel, container, config);\n break;\n case 'View':\n this.renderViewPart(panel, container, config);\n break;\n case 'Query':\n this.renderQueryPart(panel, container, config);\n break;\n case 'Artifact':\n this.renderArtifactPart(panel, container, config);\n break;\n default:\n this.renderPlaceholderPart(panel, container, partType);\n }\n }\n\n private renderWebURLPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const url = config['url'] as string | undefined;\n if (!url) {\n container.innerHTML = `\n <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;\">\n <i class=\"fa-solid fa-globe\" style=\"font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: var(--mj-text-primary);\">No URL Configured</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click the configure button to set a URL for this part.</p>\n </div>\n `;\n return;\n }\n\n const sandboxMode = config['sandboxMode'] as string | undefined;\n // Determine sandbox permissions based on mode\n let sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups';\n if (sandboxMode === 'strict') {\n sandbox = 'allow-scripts';\n } else if (sandboxMode === 'permissive') {\n sandbox = 'allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-top-navigation';\n }\n\n const iframe = document.createElement('iframe');\n iframe.src = url;\n iframe.style.cssText = 'width: 100%; height: 100%; border: none;';\n iframe.sandbox.value = sandbox;\n if (config['allowFullscreen'] !== false) {\n iframe.allowFullscreen = true;\n }\n iframe.title = panel.title;\n\n container.appendChild(iframe);\n }\n\n private renderViewPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const viewId = config['viewId'] as string | undefined;\n const entityName = config['entityName'] as string | undefined;\n if (!viewId && !entityName) {\n container.innerHTML = `\n <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;\">\n <i class=\"fa-solid fa-table\" style=\"font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: var(--mj-text-primary);\">No View Selected</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click configure to select a view for this part.</p>\n </div>\n `;\n return;\n }\n\n const viewInfo = viewId ? viewId.substring(0, 8) + '...' : entityName;\n const displayModeValue = config['displayMode'] as string | undefined;\n const displayMode = displayModeValue === 'grid' ? 'Grid View' : displayModeValue === 'cards' ? 'Card View' : 'Timeline View';\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);\">\n <div style=\"padding: 16px 20px; border-bottom: 1px solid var(--mj-border-default); background: var(--mj-bg-surface-card);\">\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <i class=\"fa-solid fa-table\" style=\"font-size: 20px; color: var(--mj-brand-primary);\"></i>\n <div>\n <div style=\"font-weight: 500; color: var(--mj-text-primary); font-size: 14px;\">Entity View</div>\n <div style=\"font-size: 12px; color: var(--mj-text-secondary);\">${entityName || 'View ' + viewInfo}</div>\n </div>\n <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>\n </div>\n </div>\n <div style=\"flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;\">\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 24px; margin-bottom: 12px;\"></i>\n <p style=\"margin: 0; font-size: 13px;\">Entity grid loading...</p>\n <p style=\"margin: 8px 0 0 0; font-size: 11px; color: var(--mj-text-muted);\">Full implementation pending Angular integration</p>\n </div>\n </div>\n `;\n }\n\n private renderQueryPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const queryId = config['queryId'] as string | undefined;\n const queryName = config['queryName'] as string | undefined;\n if (!queryId && !queryName) {\n container.innerHTML = `\n <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;\">\n <i class=\"fa-solid fa-database\" style=\"font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: var(--mj-text-primary);\">No Query Selected</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click configure to select a query for this part.</p>\n </div>\n `;\n return;\n }\n\n const autoRefreshSeconds = (config['autoRefreshSeconds'] as number) || 0;\n const queryInfo = queryName || (queryId ? queryId.substring(0, 8) + '...' : 'Unknown');\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);\">\n <div style=\"padding: 16px 20px; border-bottom: 1px solid var(--mj-border-default); background: var(--mj-bg-surface-card);\">\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <i class=\"fa-solid fa-database\" style=\"font-size: 20px; color: var(--mj-brand-primary);\"></i>\n <div>\n <div style=\"font-weight: 500; color: var(--mj-text-primary); font-size: 14px;\">Query Results</div>\n <div style=\"font-size: 12px; color: var(--mj-text-secondary);\">${queryInfo}</div>\n </div>\n <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>\n </div>\n </div>\n <div style=\"flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;\">\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 24px; margin-bottom: 12px;\"></i>\n <p style=\"margin: 0; font-size: 13px;\">Query grid loading...</p>\n <p style=\"margin: 8px 0 0 0; font-size: 11px; color: var(--mj-text-muted);\">Full implementation pending Angular integration</p>\n </div>\n </div>\n `;\n }\n\n private renderArtifactPart(panel: DashboardPanel, container: HTMLElement, config: PanelConfig): void {\n const artifactId = config['artifactId'] as string | undefined;\n if (!artifactId) {\n container.innerHTML = `\n <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;\">\n <i class=\"fa-solid fa-cube\" style=\"font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: var(--mj-text-primary);\">No Artifact Selected</h4>\n <p style=\"margin: 0; font-size: 13px;\">Click configure to select an artifact for this part.</p>\n </div>\n `;\n return;\n }\n\n const versionNumber = config['versionNumber'] as number | undefined;\n const artifactInfo = artifactId.substring(0, 8) + '...';\n const versionInfo = versionNumber ? `v${versionNumber}` : 'Latest';\n container.innerHTML = `\n <div style=\"display: flex; flex-direction: column; height: 100%; background: var(--mj-bg-surface);\">\n <div style=\"padding: 16px 20px; border-bottom: 1px solid var(--mj-border-default); background: var(--mj-bg-surface-card);\">\n <div style=\"display: flex; align-items: center; gap: 12px;\">\n <i class=\"fa-solid fa-cube\" style=\"font-size: 20px; color: var(--mj-brand-primary);\"></i>\n <div>\n <div style=\"font-weight: 500; color: var(--mj-text-primary); font-size: 14px;\">Artifact</div>\n <div style=\"font-size: 12px; color: var(--mj-text-secondary);\">ID: ${artifactInfo}</div>\n </div>\n <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>\n </div>\n </div>\n <div style=\"flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--mj-text-muted); padding: 24px;\">\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 24px; margin-bottom: 12px;\"></i>\n <p style=\"margin: 0; font-size: 13px;\">Artifact viewer loading...</p>\n <p style=\"margin: 8px 0 0 0; font-size: 11px; color: var(--mj-text-muted);\">Full implementation pending Angular integration</p>\n </div>\n </div>\n `;\n }\n\n private renderPlaceholderPart(panel: DashboardPanel, container: HTMLElement, partType: MJDashboardPartTypeEntity | undefined): void {\n const partTypeName = partType?.Name || 'Custom';\n container.innerHTML = `\n <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;\">\n <i class=\"fa-solid fa-puzzle-piece\" style=\"font-size: 48px; color: var(--mj-text-muted); margin-bottom: 16px;\"></i>\n <h4 style=\"margin: 0 0 8px 0; color: var(--mj-text-primary);\">${partTypeName} Part</h4>\n <p style=\"margin: 0; font-size: 13px;\">This part type is not yet fully implemented.</p>\n </div>\n `;\n }\n\n private onConfigurePart(panelId: string): void {\n // Emit event for parent to handle configuration\n this.panelInteraction.emit({\n panelId,\n interactionType: 'custom',\n payload: { action: 'configure-part-requested' }\n });\n }\n\n private onRemovePart(panelId: string): void {\n // Emit event for parent to show confirmation dialog\n const panel = findPanelInLayout(this.config?.layout ?? null, panelId);\n this.panelInteraction.emit({\n panelId,\n interactionType: 'custom',\n payload: {\n action: 'remove-part-requested',\n panelTitle: panel?.title || 'this part'\n }\n });\n }\n\n /**\n * Confirm removal of a panel (called by parent after confirmation dialog)\n */\n public confirmRemovePanel(panelId: string): void {\n this.removePanel(panelId);\n }\n\n private destroyPanelComponent(panelId: string): void {\n const entry = this._panelComponents.get(panelId);\n if (entry) {\n // Destroy the Angular component if present\n if (entry.componentRef) {\n this.appRef.detachView(entry.componentRef.hostView);\n entry.componentRef.destroy();\n }\n this._panelComponents.delete(panelId);\n }\n }\n\n private updatePanelEditModes(): void {\n // Update IsEditing on all dynamic components\n this._panelComponents.forEach((entry) => {\n if (entry.componentRef) {\n entry.componentRef.instance.IsEditing = this.isEditing;\n }\n });\n\n // Save current layout before reinitializing (preserves user's arrangement)\n if (this._glService && this.config) {\n const currentLayout = this._glService.getLayoutConfig();\n if (currentLayout) {\n this.config.layout = currentLayout;\n }\n }\n\n // Reinitialize layout to apply new Golden Layout settings (edit mode lock/unlock)\n if (this._glService) {\n this.initializeLayout();\n }\n }\n\n // ========================================\n // Private Methods - State\n // ========================================\n\n private markDirty(): void {\n this.hasUnsavedChanges = true;\n\n if (this.autoSave && this.config) {\n this.save();\n }\n }\n}\n","<!-- 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"]}
|
|
@@ -435,11 +435,11 @@ export class AddPanelDialogComponent {
|
|
|
435
435
|
i0.ɵɵconditionalCreate(0, AddPanelDialogComponent_Conditional_0_Template, 4, 2, "div", 1);
|
|
436
436
|
} if (rf & 2) {
|
|
437
437
|
i0.ɵɵconditional(ctx.visible ? 0 : -1);
|
|
438
|
-
} }, styles: ["\n\n\n\n\n\n\n\n\n\n.add-part-dialog-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: _ngcontent-%COMP%_fadeIn 0.15s ease;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n\n\n\n\n\n.add-part-dialog[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n width: 560px;\n max-width: 90vw;\n max-height: 85vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUp 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n\n\n\n\n\n.dialog-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid #e0e0e0;\n background: linear-gradient(135deg, #f5f7fa 0%, #e8eaf6 100%);\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #5c6bc0;\n font-size: 16px;\n}\n\n.back-button[_ngcontent-%COMP%], \n.close-button[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: #666;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.back-button[_ngcontent-%COMP%]:hover, \n.close-button[_ngcontent-%COMP%]:hover {\n background: rgba(0, 0, 0, 0.08);\n color: #333;\n}\n\n.back-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n.close-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n\n\n\n\n\n.dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 0;\n}\n\n.instruction[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: #666;\n}\n\n\n\n\n\n\n.part-type-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.part-type-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n border: 2px solid #e8eaf6;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n background: #fff;\n}\n\n.part-type-card[_ngcontent-%COMP%]:hover {\n background: #f5f7fa;\n border-color: #5c6bc0;\n transform: translateX(4px);\n box-shadow: 0 4px 12px rgba(92, 107, 192, 0.15);\n}\n\n.part-type-card[_ngcontent-%COMP%]:hover .card-arrow[_ngcontent-%COMP%] {\n opacity: 1;\n transform: translateX(0);\n}\n\n.card-icon[_ngcontent-%COMP%] {\n width: 56px;\n height: 56px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n background: linear-gradient(135deg, #e8eaf6 0%, #c5cae9 100%);\n color: #5c6bc0;\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n}\n\n.card-content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.card-title[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n}\n\n.card-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: #666;\n line-height: 1.5;\n}\n\n.card-arrow[_ngcontent-%COMP%] {\n flex-shrink: 0;\n color: #5c6bc0;\n opacity: 0;\n transform: translateX(-8px);\n transition: all 0.2s ease;\n font-size: 16px;\n}\n\n\n\n\n\n\n.no-types[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 40px;\n text-align: center;\n}\n\n.no-types[_ngcontent-%COMP%] .warning-icon[_ngcontent-%COMP%] {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: #fff3e0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.no-types[_ngcontent-%COMP%] .warning-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 28px;\n color: #ff9800;\n}\n\n.no-types[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 500;\n color: #333;\n}\n\n.no-types[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n\n\n\n\n\n.config-content[_ngcontent-%COMP%] {\n padding: 24px;\n}\n\n.form-group[_ngcontent-%COMP%] {\n margin-bottom: 20px;\n}\n\n.form-group[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: #333;\n}\n\n.form-group[_ngcontent-%COMP%] input[_ngcontent-%COMP%], \n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%] {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 14px;\n color: #333;\n background: #fff;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n}\n\n.form-group[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:focus, \n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #5c6bc0;\n box-shadow: 0 0 0 3px rgba(92, 107, 192, 0.1);\n}\n\n.form-group[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder {\n color: #999;\n}\n\n.form-hint[_ngcontent-%COMP%] {\n display: block;\n margin-top: 6px;\n font-size: 12px;\n color: #999;\n}\n\n.config-placeholder[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: #999;\n background: #f9f9f9;\n border-radius: 8px;\n margin-top: 20px;\n}\n\n.config-placeholder[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n margin-bottom: 12px;\n color: #ccc;\n}\n\n.config-placeholder[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n\n\n\n\n\n.dialog-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n background: #fafafa;\n}\n\n.btn-secondary[_ngcontent-%COMP%] {\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: #fff;\n border: 1px solid #ddd;\n color: #333;\n}\n\n.btn-secondary[_ngcontent-%COMP%]:hover {\n background: #f5f5f5;\n border-color: #ccc;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: linear-gradient(135deg, #5c6bc0 0%, #3f51b5 100%);\n border: none;\n color: #fff;\n box-shadow: 0 2px 8px rgba(92, 107, 192, 0.3);\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(92, 107, 192, 0.4);\n}\n\n.btn-primary[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.btn-primary[_ngcontent-%COMP%]:disabled {\n background: #ccc;\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.btn-primary[_ngcontent-%COMP%]:disabled:hover {\n transform: none;\n box-shadow: none;\n}\n\n\n\n\n\n\n.validation-errors[_ngcontent-%COMP%] {\n margin-top: 16px;\n padding: 12px 16px;\n background: #fff5f5;\n border: 1px solid #ffcdd2;\n border-radius: 8px;\n}\n\n.error-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #d32f2f;\n}\n\n.error-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.error-item[_ngcontent-%COMP%] + .error-item[_ngcontent-%COMP%] {\n margin-top: 8px;\n}\n\n\n\n\n\n\n.loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: #666;\n}\n\n.loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n color: #5c6bc0;\n}\n\n.loading-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n\n\n\n\n\n.error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: #ff9800;\n text-align: center;\n}\n\n.error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 14px;\n max-width: 300px;\n}\n\n.error-state[_ngcontent-%COMP%] .error-hint[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #666;\n margin-top: 4px;\n}"] });
|
|
438
|
+
} }, styles: ["\n\n\n\n\n\n\n\n\n\n.add-part-dialog-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: _ngcontent-%COMP%_fadeIn 0.15s ease;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n\n\n\n\n\n.add-part-dialog[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n width: 560px;\n max-width: 90vw;\n max-height: 85vh;\n overflow: hidden;\n animation: _ngcontent-%COMP%_slideUp 0.2s ease;\n}\n\n@keyframes _ngcontent-%COMP%_slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n\n\n\n\n\n.dialog-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.back-button[_ngcontent-%COMP%], \n.close-button[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.back-button[_ngcontent-%COMP%]:hover, \n.close-button[_ngcontent-%COMP%]:hover {\n background: rgba(0, 0, 0, 0.08);\n color: var(--mj-text-primary);\n}\n\n.back-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n.close-button[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n\n\n\n\n\n.dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 0;\n}\n\n.instruction[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n\n\n\n\n\n.part-type-grid[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.part-type-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n}\n\n.part-type-card[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-page);\n border-color: var(--mj-brand-primary);\n transform: translateX(4px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.part-type-card[_ngcontent-%COMP%]:hover .card-arrow[_ngcontent-%COMP%] {\n opacity: 1;\n transform: translateX(0);\n}\n\n.card-icon[_ngcontent-%COMP%] {\n width: 56px;\n height: 56px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.card-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n}\n\n.card-content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.card-title[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.card-arrow[_ngcontent-%COMP%] {\n flex-shrink: 0;\n color: var(--mj-brand-primary);\n opacity: 0;\n transform: translateX(-8px);\n transition: all 0.2s ease;\n font-size: 16px;\n}\n\n\n\n\n\n\n.no-types[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 40px;\n text-align: center;\n}\n\n.no-types[_ngcontent-%COMP%] .warning-icon[_ngcontent-%COMP%] {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.no-types[_ngcontent-%COMP%] .warning-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 28px;\n color: var(--mj-status-warning);\n}\n\n.no-types[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.no-types[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n\n\n\n\n\n.config-content[_ngcontent-%COMP%] {\n padding: 24px;\n}\n\n.form-group[_ngcontent-%COMP%] {\n margin-bottom: 20px;\n}\n\n.form-group[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.form-group[_ngcontent-%COMP%] input[_ngcontent-%COMP%], \n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%] {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n}\n\n.form-group[_ngcontent-%COMP%] input[_ngcontent-%COMP%]:focus, \n.form-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.form-group[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.form-hint[_ngcontent-%COMP%] {\n display: block;\n margin-top: 6px;\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.config-placeholder[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-disabled);\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n margin-top: 20px;\n}\n\n.config-placeholder[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n margin-bottom: 12px;\n color: var(--mj-border-strong);\n}\n\n.config-placeholder[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n\n\n\n\n\n.dialog-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.btn-secondary[_ngcontent-%COMP%] {\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.btn-secondary[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-brand-primary);\n border: none;\n color: var(--mj-text-inverse);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.btn-primary[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.btn-primary[_ngcontent-%COMP%]:disabled {\n background: var(--mj-border-strong);\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.btn-primary[_ngcontent-%COMP%]:disabled:hover {\n transform: none;\n box-shadow: none;\n}\n\n\n\n\n\n\n.validation-errors[_ngcontent-%COMP%] {\n margin-top: 16px;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n border-radius: 8px;\n}\n\n.error-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.error-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.error-item[_ngcontent-%COMP%] + .error-item[_ngcontent-%COMP%] {\n margin-top: 8px;\n}\n\n\n\n\n\n\n.loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-text-secondary);\n}\n\n.loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.loading-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n\n\n\n\n\n.error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-status-warning);\n text-align: center;\n}\n\n.error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 14px;\n max-width: 300px;\n}\n\n.error-state[_ngcontent-%COMP%] .error-hint[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n}"] });
|
|
439
439
|
}
|
|
440
440
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AddPanelDialogComponent, [{
|
|
441
441
|
type: Component,
|
|
442
|
-
args: [{ standalone: false, selector: 'mj-add-panel-dialog', template: "<!-- Add Part Dialog -->\n@if (visible) {\n <div class=\"add-part-dialog-overlay\" (click)=\"onCancel()\">\n <div class=\"add-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Step 1: Select Part Type -->\n @if (step === 'select-type') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <h3>\n <i class=\"fa-solid fa-plus-circle\"></i>\n Add Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <p class=\"instruction\">Select a part type to add to your dashboard:</p>\n <div class=\"part-type-grid\">\n @for (partType of partTypes; track partType) {\n <div\n class=\"part-type-card\"\n (click)=\"onPartTypeSelect(partType)\">\n <div class=\"card-icon\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n </div>\n <div class=\"card-content\">\n <span class=\"card-title\">{{ partType.Name }}</span>\n @if (partType.Description) {\n <span class=\"card-description\">\n {{ partType.Description }}\n </span>\n }\n </div>\n <i class=\"fa-solid fa-chevron-right card-arrow\"></i>\n </div>\n }\n </div>\n @if (partTypes.length === 0) {\n <div class=\"no-types\">\n <div class=\"warning-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <h4>No part types available</h4>\n <p>Dashboard part types have not been configured in the system.</p>\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n <!-- Step 2: Configure Part -->\n @if (step === 'configure') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <button class=\"back-button\" (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <h3>\n <i [class]=\"selectedPartType?.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n Configure {{ getConfigTypeName() }} Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content - Dynamic Config Panel -->\n <div class=\"dialog-content config-content\">\n <!-- Loading state -->\n @if (isLoadingPanel) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (loadError && !isLoadingPanel) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ loadError }}</span>\n <p class=\"error-hint\">You can still add this part with default settings.</p>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n <!-- No config panel available (custom type) -->\n @if (!hasConfigPanel() && !isLoadingPanel && !loadError) {\n <div class=\"config-placeholder\">\n <i class=\"fa-solid fa-cog\"></i>\n <p>This part type uses default configuration.</p>\n </div>\n }\n <!-- Validation Errors -->\n @if (currentResult && currentResult.errors.length > 0) {\n <div class=\"validation-errors\">\n @for (error of currentResult.errors; track error) {\n <div class=\"error-item\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n {{ error }}\n </div>\n }\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-primary\" (click)=\"addPart()\" [disabled]=\"!canAddPart && !loadError\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/**\n * Add Part Dialog Styles\n */\n\n/* ========================================\n Overlay\n ======================================== */\n\n.add-part-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: fadeIn 0.15s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* ========================================\n Dialog Container\n ======================================== */\n\n.add-part-dialog {\n display: flex;\n flex-direction: column;\n background: #fff;\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n width: 560px;\n max-width: 90vw;\n max-height: 85vh;\n overflow: hidden;\n animation: slideUp 0.2s ease;\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* ========================================\n Header\n ======================================== */\n\n.dialog-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid #e0e0e0;\n background: linear-gradient(135deg, #f5f7fa 0%, #e8eaf6 100%);\n}\n\n.dialog-header h3 {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.dialog-header h3 i {\n color: #5c6bc0;\n font-size: 16px;\n}\n\n.back-button,\n.close-button {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: #666;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.back-button:hover,\n.close-button:hover {\n background: rgba(0, 0, 0, 0.08);\n color: #333;\n}\n\n.back-button i,\n.close-button i {\n font-size: 16px;\n}\n\n/* ========================================\n Content\n ======================================== */\n\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 0;\n}\n\n.instruction {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: #666;\n}\n\n/* ========================================\n Part Type Grid\n ======================================== */\n\n.part-type-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.part-type-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n border: 2px solid #e8eaf6;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n background: #fff;\n}\n\n.part-type-card:hover {\n background: #f5f7fa;\n border-color: #5c6bc0;\n transform: translateX(4px);\n box-shadow: 0 4px 12px rgba(92, 107, 192, 0.15);\n}\n\n.part-type-card:hover .card-arrow {\n opacity: 1;\n transform: translateX(0);\n}\n\n.card-icon {\n width: 56px;\n height: 56px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n background: linear-gradient(135deg, #e8eaf6 0%, #c5cae9 100%);\n color: #5c6bc0;\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 24px;\n}\n\n.card-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.card-title {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n}\n\n.card-description {\n font-size: 13px;\n color: #666;\n line-height: 1.5;\n}\n\n.card-arrow {\n flex-shrink: 0;\n color: #5c6bc0;\n opacity: 0;\n transform: translateX(-8px);\n transition: all 0.2s ease;\n font-size: 16px;\n}\n\n/* ========================================\n No Types\n ======================================== */\n\n.no-types {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 40px;\n text-align: center;\n}\n\n.no-types .warning-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: #fff3e0;\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.no-types .warning-icon i {\n font-size: 28px;\n color: #ff9800;\n}\n\n.no-types h4 {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 500;\n color: #333;\n}\n\n.no-types p {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n/* ========================================\n Configuration Form\n ======================================== */\n\n.config-content {\n padding: 24px;\n}\n\n.form-group {\n margin-bottom: 20px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: #333;\n}\n\n.form-group input,\n.form-group select {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid #ddd;\n border-radius: 8px;\n font-size: 14px;\n color: #333;\n background: #fff;\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n}\n\n.form-group input:focus,\n.form-group select:focus {\n outline: none;\n border-color: #5c6bc0;\n box-shadow: 0 0 0 3px rgba(92, 107, 192, 0.1);\n}\n\n.form-group input::placeholder {\n color: #999;\n}\n\n.form-hint {\n display: block;\n margin-top: 6px;\n font-size: 12px;\n color: #999;\n}\n\n.config-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: #999;\n background: #f9f9f9;\n border-radius: 8px;\n margin-top: 20px;\n}\n\n.config-placeholder i {\n font-size: 32px;\n margin-bottom: 12px;\n color: #ccc;\n}\n\n.config-placeholder p {\n margin: 0;\n font-size: 14px;\n}\n\n/* ========================================\n Footer\n ======================================== */\n\n.dialog-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n background: #fafafa;\n}\n\n.btn-secondary {\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: #fff;\n border: 1px solid #ddd;\n color: #333;\n}\n\n.btn-secondary:hover {\n background: #f5f5f5;\n border-color: #ccc;\n}\n\n.btn-primary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: linear-gradient(135deg, #5c6bc0 0%, #3f51b5 100%);\n border: none;\n color: #fff;\n box-shadow: 0 2px 8px rgba(92, 107, 192, 0.3);\n}\n\n.btn-primary:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px rgba(92, 107, 192, 0.4);\n}\n\n.btn-primary i {\n font-size: 12px;\n}\n\n.btn-primary:disabled {\n background: #ccc;\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.btn-primary:disabled:hover {\n transform: none;\n box-shadow: none;\n}\n\n/* ========================================\n Validation Errors\n ======================================== */\n\n.validation-errors {\n margin-top: 16px;\n padding: 12px 16px;\n background: #fff5f5;\n border: 1px solid #ffcdd2;\n border-radius: 8px;\n}\n\n.error-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #d32f2f;\n}\n\n.error-item i {\n font-size: 14px;\n}\n\n.error-item + .error-item {\n margin-top: 8px;\n}\n\n/* ========================================\n Loading State\n ======================================== */\n\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: #666;\n}\n\n.loading-state i {\n font-size: 24px;\n color: #5c6bc0;\n}\n\n.loading-state span {\n font-size: 14px;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: #ff9800;\n text-align: center;\n}\n\n.error-state i {\n font-size: 32px;\n}\n\n.error-state span {\n font-size: 14px;\n max-width: 300px;\n}\n\n.error-state .error-hint {\n font-size: 12px;\n color: #666;\n margin-top: 4px;\n}\n"] }]
|
|
442
|
+
args: [{ standalone: false, selector: 'mj-add-panel-dialog', template: "<!-- Add Part Dialog -->\n@if (visible) {\n <div class=\"add-part-dialog-overlay\" (click)=\"onCancel()\">\n <div class=\"add-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Step 1: Select Part Type -->\n @if (step === 'select-type') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <h3>\n <i class=\"fa-solid fa-plus-circle\"></i>\n Add Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <p class=\"instruction\">Select a part type to add to your dashboard:</p>\n <div class=\"part-type-grid\">\n @for (partType of partTypes; track partType) {\n <div\n class=\"part-type-card\"\n (click)=\"onPartTypeSelect(partType)\">\n <div class=\"card-icon\">\n <i [class]=\"partType.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n </div>\n <div class=\"card-content\">\n <span class=\"card-title\">{{ partType.Name }}</span>\n @if (partType.Description) {\n <span class=\"card-description\">\n {{ partType.Description }}\n </span>\n }\n </div>\n <i class=\"fa-solid fa-chevron-right card-arrow\"></i>\n </div>\n }\n </div>\n @if (partTypes.length === 0) {\n <div class=\"no-types\">\n <div class=\"warning-icon\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n </div>\n <h4>No part types available</h4>\n <p>Dashboard part types have not been configured in the system.</p>\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n <!-- Step 2: Configure Part -->\n @if (step === 'configure') {\n <!-- Header -->\n <div class=\"dialog-header\">\n <button class=\"back-button\" (click)=\"goBack()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <h3>\n <i [class]=\"selectedPartType?.Icon || 'fa-solid fa-puzzle-piece'\"></i>\n Configure {{ getConfigTypeName() }} Part\n </h3>\n <button class=\"close-button\" (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content - Dynamic Config Panel -->\n <div class=\"dialog-content config-content\">\n <!-- Loading state -->\n @if (isLoadingPanel) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (loadError && !isLoadingPanel) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ loadError }}</span>\n <p class=\"error-hint\">You can still add this part with default settings.</p>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n <!-- No config panel available (custom type) -->\n @if (!hasConfigPanel() && !isLoadingPanel && !loadError) {\n <div class=\"config-placeholder\">\n <i class=\"fa-solid fa-cog\"></i>\n <p>This part type uses default configuration.</p>\n </div>\n }\n <!-- Validation Errors -->\n @if (currentResult && currentResult.errors.length > 0) {\n <div class=\"validation-errors\">\n @for (error of currentResult.errors; track error) {\n <div class=\"error-item\">\n <i class=\"fa-solid fa-exclamation-circle\"></i>\n {{ error }}\n </div>\n }\n </div>\n }\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button class=\"btn-primary\" (click)=\"addPart()\" [disabled]=\"!canAddPart && !loadError\">\n <i class=\"fa-solid fa-plus\"></i>\n Add Part\n </button>\n <button class=\"btn-secondary\" (click)=\"onCancel()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n}\n", styles: ["/**\n * Add Part Dialog Styles\n */\n\n/* ========================================\n Overlay\n ======================================== */\n\n.add-part-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10000;\n animation: fadeIn 0.15s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n/* ========================================\n Dialog Container\n ======================================== */\n\n.add-part-dialog {\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n width: 560px;\n max-width: 90vw;\n max-height: 85vh;\n overflow: hidden;\n animation: slideUp 0.2s ease;\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* ========================================\n Header\n ======================================== */\n\n.dialog-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.dialog-header h3 {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.dialog-header h3 i {\n color: var(--mj-brand-primary);\n font-size: 16px;\n}\n\n.back-button,\n.close-button {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.back-button:hover,\n.close-button:hover {\n background: rgba(0, 0, 0, 0.08);\n color: var(--mj-text-primary);\n}\n\n.back-button i,\n.close-button i {\n font-size: 16px;\n}\n\n/* ========================================\n Content\n ======================================== */\n\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 0;\n}\n\n.instruction {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n/* ========================================\n Part Type Grid\n ======================================== */\n\n.part-type-grid {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.part-type-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n}\n\n.part-type-card:hover {\n background: var(--mj-bg-page);\n border-color: var(--mj-brand-primary);\n transform: translateX(4px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.part-type-card:hover .card-arrow {\n opacity: 1;\n transform: translateX(0);\n}\n\n.card-icon {\n width: 56px;\n height: 56px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 12px;\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.card-icon i {\n font-size: 24px;\n}\n\n.card-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.card-title {\n font-size: 16px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.card-description {\n font-size: 13px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n}\n\n.card-arrow {\n flex-shrink: 0;\n color: var(--mj-brand-primary);\n opacity: 0;\n transform: translateX(-8px);\n transition: all 0.2s ease;\n font-size: 16px;\n}\n\n/* ========================================\n No Types\n ======================================== */\n\n.no-types {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 40px;\n text-align: center;\n}\n\n.no-types .warning-icon {\n width: 64px;\n height: 64px;\n border-radius: 50%;\n background: color-mix(in srgb, var(--mj-status-warning) 10%, var(--mj-bg-surface));\n display: flex;\n align-items: center;\n justify-content: center;\n margin-bottom: 16px;\n}\n\n.no-types .warning-icon i {\n font-size: 28px;\n color: var(--mj-status-warning);\n}\n\n.no-types h4 {\n margin: 0 0 8px 0;\n font-size: 16px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.no-types p {\n margin: 0;\n font-size: 14px;\n color: var(--mj-text-secondary);\n}\n\n/* ========================================\n Configuration Form\n ======================================== */\n\n.config-content {\n padding: 24px;\n}\n\n.form-group {\n margin-bottom: 20px;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 8px;\n font-size: 14px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.form-group input,\n.form-group select {\n width: 100%;\n padding: 12px 14px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 8px;\n font-size: 14px;\n color: var(--mj-text-primary);\n background: var(--mj-bg-surface);\n transition: border-color 0.2s, box-shadow 0.2s;\n box-sizing: border-box;\n}\n\n.form-group input:focus,\n.form-group select:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.form-group input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.form-hint {\n display: block;\n margin-top: 6px;\n font-size: 12px;\n color: var(--mj-text-disabled);\n}\n\n.config-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 40px;\n text-align: center;\n color: var(--mj-text-disabled);\n background: var(--mj-bg-surface-card);\n border-radius: 8px;\n margin-top: 20px;\n}\n\n.config-placeholder i {\n font-size: 32px;\n margin-bottom: 12px;\n color: var(--mj-border-strong);\n}\n\n.config-placeholder p {\n margin: 0;\n font-size: 14px;\n}\n\n/* ========================================\n Footer\n ======================================== */\n\n.dialog-footer {\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n}\n\n.btn-secondary {\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.btn-secondary:hover {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n}\n\n.btn-primary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 24px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n background: var(--mj-brand-primary);\n border: none;\n color: var(--mj-text-inverse);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 30%, transparent);\n}\n\n.btn-primary:hover {\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 40%, transparent);\n}\n\n.btn-primary i {\n font-size: 12px;\n}\n\n.btn-primary:disabled {\n background: var(--mj-border-strong);\n cursor: not-allowed;\n box-shadow: none;\n}\n\n.btn-primary:disabled:hover {\n transform: none;\n box-shadow: none;\n}\n\n/* ========================================\n Validation Errors\n ======================================== */\n\n.validation-errors {\n margin-top: 16px;\n padding: 12px 16px;\n background: color-mix(in srgb, var(--mj-status-error) 5%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-bg-surface));\n border-radius: 8px;\n}\n\n.error-item {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: var(--mj-status-error);\n}\n\n.error-item i {\n font-size: 14px;\n}\n\n.error-item + .error-item {\n margin-top: 8px;\n}\n\n/* ========================================\n Loading State\n ======================================== */\n\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-text-secondary);\n}\n\n.loading-state i {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.loading-state span {\n font-size: 14px;\n}\n\n/* ========================================\n Error State\n ======================================== */\n\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-status-warning);\n text-align: center;\n}\n\n.error-state i {\n font-size: 32px;\n}\n\n.error-state span {\n font-size: 14px;\n max-width: 300px;\n}\n\n.error-state .error-hint {\n font-size: 12px;\n color: var(--mj-text-secondary);\n margin-top: 4px;\n}\n"] }]
|
|
443
443
|
}], () => [{ type: i0.ChangeDetectorRef }], { partTypes: [{
|
|
444
444
|
type: Input
|
|
445
445
|
}], visible: [{
|
|
@@ -304,11 +304,11 @@ export class EditPartDialogComponent {
|
|
|
304
304
|
i0.ɵɵconditionalCreate(0, EditPartDialogComponent_Conditional_0_Template, 20, 7, "div", 1);
|
|
305
305
|
} if (rf & 2) {
|
|
306
306
|
i0.ɵɵconditional(ctx.Visible ? 0 : -1);
|
|
307
|
-
} }, styles: ["\n\n\n.edit-part-dialog-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: _ngcontent-%COMP%_fadeIn 0.15s ease-out;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.edit-part-dialog[_ngcontent-%COMP%] {\n background:
|
|
307
|
+
} }, styles: ["\n\n\n.edit-part-dialog-overlay[_ngcontent-%COMP%] {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: _ngcontent-%COMP%_fadeIn 0.15s ease-out;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.edit-part-dialog[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 90%;\n max-width: 540px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n animation: _ngcontent-%COMP%_slideUp 0.2s ease-out;\n}\n\n@keyframes _ngcontent-%COMP%_slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n\n\n.dialog-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 12px 12px 0 0;\n}\n\n.header-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: 10px;\n color: var(--mj-text-inverse);\n font-size: 18px;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.close-button[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.close-button[_ngcontent-%COMP%]:hover {\n background: rgba(0, 0, 0, 0.08);\n color: var(--mj-text-primary);\n}\n\n\n\n.dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 200px;\n}\n\n\n\n.loading-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-text-secondary);\n}\n\n.loading-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.loading-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n\n\n.error-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-status-error);\n text-align: center;\n}\n\n.error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 32px;\n}\n\n.error-state[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 14px;\n max-width: 300px;\n}\n\n\n\n.dialog-footer[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-start;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 12px 12px;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\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: background 0.2s;\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.btn-primary[_ngcontent-%COMP%]:disabled {\n background: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n.btn-secondary[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.btn-secondary[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}"] });
|
|
308
308
|
}
|
|
309
309
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(EditPartDialogComponent, [{
|
|
310
310
|
type: Component,
|
|
311
|
-
args: [{ standalone: false, selector: 'mj-edit-part-dialog', template: "<!-- Generic Edit Part Dialog -->\n@if (Visible) {\n <div class=\"edit-part-dialog-overlay\" (click)=\"cancel()\">\n <div class=\"edit-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Header -->\n <div class=\"dialog-header\">\n <div class=\"header-icon\">\n <i [class]=\"getIcon()\"></i>\n </div>\n <h3>{{ getDialogTitle() }}</h3>\n <button class=\"close-button\" (click)=\"cancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (LoadError && !IsLoading) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ LoadError }}</span>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button\n class=\"btn-primary\"\n (click)=\"save()\"\n [disabled]=\"!IsValid || IsLoading || LoadError\">\n <i class=\"fa-solid fa-check\"></i>\n {{ getSaveButtonText() }}\n </button>\n <button class=\"btn-secondary\" (click)=\"cancel()\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* Edit Part Dialog Styles */\n\n.edit-part-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.15s ease-out;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.edit-part-dialog {\n background:
|
|
311
|
+
args: [{ standalone: false, selector: 'mj-edit-part-dialog', template: "<!-- Generic Edit Part Dialog -->\n@if (Visible) {\n <div class=\"edit-part-dialog-overlay\" (click)=\"cancel()\">\n <div class=\"edit-part-dialog\" (click)=\"$event.stopPropagation()\">\n <!-- Header -->\n <div class=\"dialog-header\">\n <div class=\"header-icon\">\n <i [class]=\"getIcon()\"></i>\n </div>\n <h3>{{ getDialogTitle() }}</h3>\n <button class=\"close-button\" (click)=\"cancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <!-- Content -->\n <div class=\"dialog-content\">\n <!-- Loading state -->\n @if (IsLoading) {\n <div class=\"loading-state\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Loading configuration panel...</span>\n </div>\n }\n <!-- Error state -->\n @if (LoadError && !IsLoading) {\n <div class=\"error-state\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ LoadError }}</span>\n </div>\n }\n <!-- Dynamic config panel container -->\n <ng-container #configPanelContainer></ng-container>\n </div>\n <!-- Footer -->\n <div class=\"dialog-footer\">\n <button\n class=\"btn-primary\"\n (click)=\"save()\"\n [disabled]=\"!IsValid || IsLoading || LoadError\">\n <i class=\"fa-solid fa-check\"></i>\n {{ getSaveButtonText() }}\n </button>\n <button class=\"btn-secondary\" (click)=\"cancel()\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* Edit Part Dialog Styles */\n\n.edit-part-dialog-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n animation: fadeIn 0.15s ease-out;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.edit-part-dialog {\n background: var(--mj-bg-surface);\n border-radius: 12px;\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);\n width: 90%;\n max-width: 540px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n animation: slideUp 0.2s ease-out;\n}\n\n@keyframes slideUp {\n from {\n opacity: 0;\n transform: translateY(20px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* Header */\n.dialog-header {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 20px 24px;\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 12px 12px 0 0;\n}\n\n.header-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-brand-primary);\n border-radius: 10px;\n color: var(--mj-text-inverse);\n font-size: 18px;\n}\n\n.dialog-header h3 {\n flex: 1;\n margin: 0;\n font-size: 18px;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.close-button {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 6px;\n background: transparent;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.close-button:hover {\n background: rgba(0, 0, 0, 0.08);\n color: var(--mj-text-primary);\n}\n\n/* Content */\n.dialog-content {\n flex: 1;\n overflow-y: auto;\n padding: 24px;\n min-height: 200px;\n}\n\n/* Loading state */\n.loading-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-text-secondary);\n}\n\n.loading-state i {\n font-size: 24px;\n color: var(--mj-brand-primary);\n}\n\n.loading-state span {\n font-size: 14px;\n}\n\n/* Error state */\n.error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 48px 24px;\n color: var(--mj-status-error);\n text-align: center;\n}\n\n.error-state i {\n font-size: 32px;\n}\n\n.error-state span {\n font-size: 14px;\n max-width: 300px;\n}\n\n/* Footer */\n.dialog-footer {\n display: flex;\n justify-content: flex-start;\n gap: 12px;\n padding: 16px 24px;\n border-top: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-card);\n border-radius: 0 0 12px 12px;\n}\n\n.btn-primary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\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: background 0.2s;\n}\n\n.btn-primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.btn-primary:disabled {\n background: var(--mj-border-strong);\n cursor: not-allowed;\n}\n\n.btn-secondary {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 10px 20px;\n border: 1px solid var(--mj-border-strong);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 14px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.btn-secondary:hover {\n background: var(--mj-bg-surface-card);\n border-color: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n"] }]
|
|
312
312
|
}], () => [{ type: i0.ChangeDetectorRef }], { Visible: [{
|
|
313
313
|
type: Input
|
|
314
314
|
}], PartType: [{
|
|
@@ -185,7 +185,7 @@ let ArtifactPartComponent = class ArtifactPartComponent extends BaseDashboardPar
|
|
|
185
185
|
i0.ɵɵconditional(!ctx.IsLoading && !ctx.ErrorMessage && !ctx.hasArtifact ? 3 : -1);
|
|
186
186
|
i0.ɵɵadvance();
|
|
187
187
|
i0.ɵɵconditional(!ctx.IsLoading && !ctx.ErrorMessage && ctx.hasArtifact && ctx.artifactId ? 4 : -1);
|
|
188
|
-
} }, dependencies: [i1.LoadingComponent, i2.ArtifactViewerPanelComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background:
|
|
188
|
+
} }, dependencies: [i1.LoadingComponent, i2.ArtifactViewerPanelComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n }\n\n .loading-state[_ngcontent-%COMP%], \n .error-state[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-status-error);\n }\n\n .empty-state[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 13px;\n }\n\n mj-artifact-viewer-panel[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n }"] });
|
|
189
189
|
};
|
|
190
190
|
ArtifactPartComponent = __decorate([
|
|
191
191
|
RegisterClass(BaseDashboardPart, 'ArtifactPanelRenderer')
|
|
@@ -242,7 +242,7 @@ export { ArtifactPartComponent };
|
|
|
242
242
|
</mj-artifact-viewer-panel>
|
|
243
243
|
}
|
|
244
244
|
</div>
|
|
245
|
-
`, styles: ["\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background:
|
|
245
|
+
`, styles: ["\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .artifact-part {\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n background: var(--mj-bg-surface);\n }\n\n .loading-state,\n .error-state,\n .empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n color: var(--mj-text-secondary);\n text-align: center;\n padding: 24px;\n }\n\n .error-state i,\n .empty-state i {\n font-size: 48px;\n color: var(--mj-text-muted);\n margin-bottom: 16px;\n }\n\n .error-state i {\n color: var(--mj-status-error);\n }\n\n .empty-state h4 {\n margin: 0 0 8px 0;\n color: var(--mj-text-primary);\n }\n\n .empty-state p {\n margin: 0;\n font-size: 13px;\n }\n\n mj-artifact-viewer-panel {\n flex: 1;\n min-height: 0;\n }\n "] }]
|
|
246
246
|
}], () => [{ type: i0.ChangeDetectorRef }], { CurrentUser: [{
|
|
247
247
|
type: Input
|
|
248
248
|
}], EnvironmentId: [{
|