@memberjunction/ng-explorer-core 6.1.0-edge.4 → 6.1.0-edge.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +19 -0
  2. package/dist/generated/lazy-feature-config.d.ts.map +1 -1
  3. package/dist/generated/lazy-feature-config.js +12 -6
  4. package/dist/generated/lazy-feature-config.js.map +1 -1
  5. package/dist/lib/about/about-dialog.component.js +4 -4
  6. package/dist/lib/about/about-dialog.component.js.map +1 -1
  7. package/dist/lib/command-palette/command-palette.component.d.ts +13 -0
  8. package/dist/lib/command-palette/command-palette.component.d.ts.map +1 -1
  9. package/dist/lib/command-palette/command-palette.component.js +106 -42
  10. package/dist/lib/command-palette/command-palette.component.js.map +1 -1
  11. package/dist/lib/conversation-feedback/conversation-feedback.js +1 -1
  12. package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js +1 -1
  13. package/dist/lib/oauth/oauth-callback.component.js +1 -1
  14. package/dist/lib/omnibar/omnibar-palette.component.d.ts.map +1 -1
  15. package/dist/lib/omnibar/omnibar-palette.component.js +9 -3
  16. package/dist/lib/omnibar/omnibar-palette.component.js.map +1 -1
  17. package/dist/lib/omnibar/providers/omnibar-record.provider.d.ts.map +1 -1
  18. package/dist/lib/omnibar/providers/omnibar-record.provider.js +9 -3
  19. package/dist/lib/omnibar/providers/omnibar-record.provider.js.map +1 -1
  20. package/dist/lib/profile/profile-dialog.component.js +1 -1
  21. package/dist/lib/resource-wrappers/chat-conversations-resource.component.d.ts.map +1 -1
  22. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js +13 -12
  23. package/dist/lib/resource-wrappers/chat-conversations-resource.component.js.map +1 -1
  24. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts +24 -0
  25. package/dist/lib/resource-wrappers/chat-deeplink-params.d.ts.map +1 -0
  26. package/dist/lib/resource-wrappers/chat-deeplink-params.js +27 -0
  27. package/dist/lib/resource-wrappers/chat-deeplink-params.js.map +1 -0
  28. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts +7 -0
  29. package/dist/lib/resource-wrappers/dashboard-resource.component.d.ts.map +1 -1
  30. package/dist/lib/resource-wrappers/dashboard-resource.component.js +29 -14
  31. package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
  32. package/dist/lib/resource-wrappers/list-detail-resource.component.js +1 -1
  33. package/dist/lib/resource-wrappers/list-detail-resource.component.js.map +1 -1
  34. package/dist/lib/resource-wrappers/query-resource.component.js +1 -1
  35. package/dist/lib/resource-wrappers/query-resource.component.js.map +1 -1
  36. package/dist/lib/resource-wrappers/record-resource.component.d.ts +3 -0
  37. package/dist/lib/resource-wrappers/record-resource.component.d.ts.map +1 -1
  38. package/dist/lib/resource-wrappers/record-resource.component.js +18 -4
  39. package/dist/lib/resource-wrappers/record-resource.component.js.map +1 -1
  40. package/dist/lib/resource-wrappers/search-results-resource.component.d.ts.map +1 -1
  41. package/dist/lib/resource-wrappers/search-results-resource.component.js +6 -2
  42. package/dist/lib/resource-wrappers/search-results-resource.component.js.map +1 -1
  43. package/dist/lib/resource-wrappers/view-resource.component.d.ts.map +1 -1
  44. package/dist/lib/resource-wrappers/view-resource.component.js +6 -3
  45. package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
  46. package/dist/lib/server-connectivity/server-connectivity-banner.component.d.ts +20 -8
  47. package/dist/lib/server-connectivity/server-connectivity-banner.component.d.ts.map +1 -1
  48. package/dist/lib/server-connectivity/server-connectivity-banner.component.js +25 -20
  49. package/dist/lib/server-connectivity/server-connectivity-banner.component.js.map +1 -1
  50. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js +2 -2
  51. package/dist/lib/shell/components/dialogs/app-access-dialog.component.js.map +1 -1
  52. package/dist/lib/shell/components/header/app-nav.component.js +1 -1
  53. package/dist/lib/shell/components/header/app-switcher.component.js +82 -83
  54. package/dist/lib/shell/components/header/app-switcher.component.js.map +1 -1
  55. package/dist/lib/shell/components/record-open/record-bar.component.js +1 -1
  56. package/dist/lib/shell/components/record-open/record-origin-crumb.component.js +1 -1
  57. package/dist/lib/shell/components/record-open/record-switcher-sheet.component.js +1 -1
  58. package/dist/lib/shell/components/record-open/records-hub-pill.component.js +1 -1
  59. package/dist/lib/shell/components/tabs/tab-container.component.d.ts +25 -0
  60. package/dist/lib/shell/components/tabs/tab-container.component.d.ts.map +1 -1
  61. package/dist/lib/shell/components/tabs/tab-container.component.js +88 -4
  62. package/dist/lib/shell/components/tabs/tab-container.component.js.map +1 -1
  63. package/dist/lib/shell/services/sharing-center-dialog-host.component.js +2 -2
  64. package/dist/lib/shell/services/sharing-center-dialog-host.component.js.map +1 -1
  65. package/dist/lib/shell/shell-chrome-policy.d.ts +54 -0
  66. package/dist/lib/shell/shell-chrome-policy.d.ts.map +1 -0
  67. package/dist/lib/shell/shell-chrome-policy.js +62 -0
  68. package/dist/lib/shell/shell-chrome-policy.js.map +1 -0
  69. package/dist/lib/shell/shell.component.d.ts +42 -0
  70. package/dist/lib/shell/shell.component.d.ts.map +1 -1
  71. package/dist/lib/shell/shell.component.js +394 -255
  72. package/dist/lib/shell/shell.component.js.map +1 -1
  73. package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js +1 -1
  74. package/dist/lib/single-dashboard/single-dashboard.component.js +1 -1
  75. package/dist/lib/single-list-detail/single-list-detail.component.d.ts +9 -0
  76. package/dist/lib/single-list-detail/single-list-detail.component.d.ts.map +1 -1
  77. package/dist/lib/single-list-detail/single-list-detail.component.js +34 -15
  78. package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
  79. package/dist/lib/single-record/single-record.component.d.ts +10 -0
  80. package/dist/lib/single-record/single-record.component.d.ts.map +1 -1
  81. package/dist/lib/single-record/single-record.component.js +23 -3
  82. package/dist/lib/single-record/single-record.component.js.map +1 -1
  83. package/dist/lib/single-search-result/single-search-result.component.d.ts.map +1 -1
  84. package/dist/lib/single-search-result/single-search-result.component.js +5 -2
  85. package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
  86. package/dist/lib/user-notifications/user-notifications.component.js +11 -12
  87. package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
  88. package/dist/lib/user-profile/user-profile.component.js +1 -1
  89. package/dist/public-api.d.ts +1 -0
  90. package/dist/public-api.d.ts.map +1 -1
  91. package/dist/public-api.js +1 -0
  92. package/dist/public-api.js.map +1 -1
  93. package/package.json +62 -62
@@ -1 +1 @@
1
- {"version":3,"file":"view-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/view-resource.component.ts","../../../src/lib/resource-wrappers/view-resource.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';\nimport { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';\nimport { ResourceData, MJUserViewEntityExtended, ViewInfo } from '@memberjunction/core-entities';\nimport { RegisterClass, MJGlobal, MJEventType , UUIDsEqual } from '@memberjunction/global';\nimport { CompositeKey, Metadata, EntityInfo } from '@memberjunction/core';\nimport { RecordOpenedEvent, ViewGridState, EntityViewerComponent, ViewRelatedRecordNavigation } from '@memberjunction/ng-entity-viewer';\nimport { ExportService } from '@memberjunction/ng-export-service';\nimport { ExportColumn } from '@memberjunction/export-engine';\nimport { GraphQLDataProvider, GraphQLListsClient } from '@memberjunction/graphql-dataprovider';\nimport type { SaveViewAsListResult } from '@memberjunction/ng-list-management';\n/**\n * UserViewResource - Resource wrapper for displaying User Views in tabs\n *\n * This component wraps the EntityViewerComponent to display view data.\n * It loads the view configuration and entity, then renders the data grid/cards.\n *\n * Key features:\n * - Loads view by ID from ResourceRecordID\n * - Supports dynamic views by entity name + extra filter\n * - Applies view's WhereClause, GridState, and SortState\n * - Opens records in new tabs via NavigationService\n */\n@RegisterClass(BaseResourceComponent, 'ViewResource')\n@Component({\n standalone: false,\n selector: 'mj-userview-resource',\n templateUrl: './view-resource.component.html',\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n }\n .view-resource-container {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .view-header {\n padding: 16px 20px 8px 20px;\n flex-shrink: 0;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n }\n .header-left {\n flex: 1;\n min-width: 0;\n }\n .header-right {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n }\n .view-title {\n margin: 0 0 4px 0;\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--text-primary, #1a1a1a);\n }\n .view-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--text-secondary, #666);\n }\n .action-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n }\n .action-button:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-default);\n }\n .action-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n .action-button i {\n font-size: 0.875rem;\n }\n .create-button {\n background: var(--mj-brand-primary);\n color: white;\n border-color: var(--mj-brand-primary);\n }\n .create-button:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n }\n .export-button:hover:not(:disabled) {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n }\n .view-loading-state,\n .view-error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 16px;\n }\n .view-error-state {\n color: var(--danger-color, #dc3545);\n }\n .view-error-state i {\n font-size: 2rem;\n }\n .view-error-state p {\n margin: 0;\n font-size: 1rem;\n }\n mj-entity-viewer {\n flex: 1;\n min-height: 0;\n }\n `]\n})\nexport class UserViewResource extends BaseResourceComponent {\n @ViewChild('container', { static: true }) containerElement!: ElementRef<HTMLDivElement>;\n @ViewChild('entityViewer') entityViewerRef?: EntityViewerComponent;\n\n public isLoading: boolean = false;\n public errorMessage: string | null = null;\n public entityInfo: EntityInfo | null = null;\n public viewEntity: MJUserViewEntityExtended | null = null;\n public gridState: ViewGridState | null = null;\n\n // Export state. The header Export button is a FALLBACK shown only for view types that don't\n // provide their own export UI (Cards / Map / Timeline). The Grid renderer has its own in-toolbar\n // Export (full result set + format/sampling dialog), so we suppress this one there to avoid a\n // duplicate/confusing second button — while keeping export available for every other view type.\n public isExporting: boolean = false;\n public showFallbackExportButton: boolean = false;\n\n // Save-as-list dialog state\n public saveAsListDialogVisible = false;\n public saveAsListRecordCount: number | null = null;\n public isSavingAsList = false;\n\n private dataLoaded = false;\n private get metadata() { return this.ProviderToUse; }\n constructor(\n private cdr: ChangeDetectorRef,\n private exportService: ExportService\n ) {\n super();\n }\n\n override set Data(value: ResourceData) {\n const previousRecordId = super.Data?.ResourceRecordID;\n const previousEntity = super.Data?.Configuration?.Entity;\n super.Data = value;\n\n const newRecordId = value?.ResourceRecordID;\n const newEntity = value?.Configuration?.Entity;\n\n // View-type (grid/cards/timeline/map) and per-view-type config are now resolved\n // internally by mj-entity-viewer from the saved view's ViewTypeID — nothing to read here.\n\n // Load on first set, or when the view/entity has changed\n if (!this.dataLoaded || newRecordId !== previousRecordId || newEntity !== previousEntity) {\n this.dataLoaded = true;\n // Reset state before loading new view\n this.entityInfo = null;\n this.viewEntity = null;\n this.gridState = null;\n this.errorMessage = null;\n this.loadView();\n }\n }\n\n override get Data(): ResourceData {\n return super.Data;\n }\n\n /**\n * Load the view and entity based on ResourceData\n */\n private async loadView(): Promise<void> {\n const data = this.Data;\n\n if (!data) {\n this.NotifyLoadComplete();\n return;\n }\n\n this.isLoading = true;\n this.errorMessage = null;\n this.NotifyLoadStarted();\n this.cdr.detectChanges();\n\n try {\n // NavigationService.OpenDynamicView stamps the special marker 'dynamic' as the\n // record ID (tabs require ResourceRecordId) — that is NOT a saved-view ID.\n const recordId = data.ResourceRecordID;\n const isDynamicMarker = typeof recordId === 'string' && recordId.trim().toLowerCase() === 'dynamic';\n // Case 1: Load view by ID\n if (recordId && !isDynamicMarker) {\n await this.loadViewById(recordId);\n }\n // Case 2: Load dynamic view by entity name\n else if (data.Configuration?.Entity) {\n await this.loadDynamicView(\n data.Configuration.Entity as string,\n data.Configuration.ExtraFilter as string | undefined\n );\n }\n else {\n this.errorMessage = 'No view ID or entity specified';\n }\n } catch (error) {\n console.error('Error loading view:', error);\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load view';\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n\n // If there was an error, notify load complete now\n if (this.errorMessage) {\n this.NotifyLoadComplete();\n }\n // Otherwise, wait for dataLoaded event from entity-viewer\n }\n }\n\n /**\n * Load a saved view by its ID\n */\n private async loadViewById(viewId: string): Promise<void> {\n // Load the view entity\n const view = await ViewInfo.GetViewEntity(viewId);\n\n if (!view) {\n throw new Error(`View with ID ${viewId} not found`);\n }\n\n this.viewEntity = view as MJUserViewEntityExtended;\n\n // Check permissions\n if (!this.viewEntity.UserCanView) {\n throw new Error('You do not have permission to view this view');\n }\n\n // Load the entity info\n const entity = this.metadata.Entities.find(e => UUIDsEqual(e.ID, this.viewEntity!.EntityID));\n\n if (!entity) {\n throw new Error(`Entity for view not found`);\n }\n\n this.entityInfo = entity;\n\n // Parse grid state if available\n if (this.viewEntity.GridState) {\n try {\n this.gridState = JSON.parse(this.viewEntity.GridState) as ViewGridState;\n } catch (e) {\n console.warn('Failed to parse GridState:', e);\n this.gridState = null;\n }\n }\n\n // View-type + per-view-type config persistence is fully owned by mj-entity-viewer\n // (it reads ViewTypeID + DisplayState.viewTypeConfigs off the [viewEntity] and, with\n // [AutoSaveView]=\"true\", saves changes back) — nothing to wire here.\n }\n\n /**\n * Load a dynamic view (no saved view, just entity + filter)\n */\n private async loadDynamicView(entityName: string, _extraFilter?: string): Promise<void> {\n const entity = this.metadata.Entities.find(\n e => e.Name.trim().toLowerCase() === entityName.trim().toLowerCase()\n );\n\n if (!entity) {\n throw new Error(`Entity '${entityName}' not found`);\n }\n\n this.entityInfo = entity;\n this.viewEntity = null;\n this.gridState = null;\n\n // For dynamic views, we could create a synthetic viewEntity with just the WhereClause\n // but for now, we'll rely on the entity-viewer's default behavior\n }\n\n /**\n * Handle record opened event - open in new tab\n */\n public onRecordOpened(event: RecordOpenedEvent): void {\n if (event && event.entity && event.compositeKey) {\n this.navigationService.OpenEntityRecord(event.entity.Name, event.compositeKey);\n }\n }\n\n /**\n * Handle a related-record navigation requested from within a view-type renderer\n * (e.g. a foreign-key cell) - open the target record in a new tab.\n */\n public onOpenRelatedRecord(nav: ViewRelatedRecordNavigation): void {\n if (nav?.entityName && nav.recordKey != null) {\n this.navigationService.OpenEntityRecord(nav.entityName, CompositeKey.FromID(String(nav.recordKey)));\n }\n }\n\n /**\n * Handle data loaded event from entity-viewer\n */\n public onDataLoaded(): void {\n this.NotifyLoadComplete();\n this.refreshFallbackExportVisibility();\n }\n\n /** The active view type changed — recompute whether the fallback Export button is needed. */\n public onViewTypeChanged(): void {\n this.refreshFallbackExportVisibility();\n }\n\n /**\n * Show the wrapper's Export button only for view types that don't ship their own export UI. The\n * entity-viewer reports this via {@link EntityViewerComponent.ActiveViewTypeHasOwnExport} (true for\n * the Grid renderer, false for Cards / Map / Timeline). Uses a cached flag + explicit change\n * detection to avoid an ExpressionChanged error from reading child state during a CD pass.\n */\n private refreshFallbackExportVisibility(): void {\n const next = !!this.entityViewerRef && !this.entityViewerRef.ActiveViewTypeHasOwnExport;\n if (next !== this.showFallbackExportButton) {\n this.showFallbackExportButton = next;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Fallback export for view types without their own export UI (Cards / Map / Timeline). Pulls the\n * FULL result set via the entity-viewer's capped, filter/sort-aware fetch (same path the grid uses,\n * so it's bounded and warns past the cap) and writes an Excel file.\n */\n public async onExport(): Promise<void> {\n // Never fail silently — a \"nothing happens\" click is impossible to diagnose. Surface the\n // reason both in the UI and the console.\n if (!this.entityInfo || !this.entityViewerRef) {\n console.error('[ViewResource] Export: viewer not ready', {\n hasEntity: !!this.entityInfo, hasViewer: !!this.entityViewerRef\n });\n this.showNotification('Export is not ready yet — try again in a moment.', 'error', 5000);\n return;\n }\n this.isExporting = true;\n this.cdr.detectChanges();\n this.showNotification('Preparing your Excel export…', 'info', 2000);\n try {\n const rows = await this.entityViewerRef.FetchAllRowsForExport();\n if (!rows || rows.length === 0) {\n this.showNotification('Nothing to export — the view returned no records.', 'warning', 5000);\n return;\n }\n const result = await this.exportService.toExcel(rows, {\n fileName: this.buildExportFileName(),\n columns: this.buildExportColumns(),\n includeHeaders: true\n });\n if (result.success) {\n this.exportService.downloadResult(result);\n this.showNotification(`Exported ${rows.length.toLocaleString()} record(s) to Excel.`, 'success', 3000);\n } else {\n this.showNotification('Export failed while building the file.', 'error', 5000);\n console.error('[ViewResource] Export: toExcel returned failure', result);\n }\n } catch (e) {\n this.showNotification('Error exporting data — see console for details.', 'error', 5000);\n console.error('[ViewResource] Export error:', e);\n } finally {\n this.isExporting = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Columns to export — from grid state, else the view's columns, else the entity's real fields. */\n private buildExportColumns(): ExportColumn[] {\n if (!this.entityInfo) return [];\n if (this.gridState?.columnSettings && this.gridState.columnSettings.length > 0) {\n return this.gridState.columnSettings\n .filter(col => col.hidden !== true)\n .map(col => ({ name: col.Name, displayName: col.DisplayName || col.Name }));\n }\n if (this.viewEntity?.Columns) {\n return this.viewEntity.Columns\n .filter(col => !col.hidden)\n .map(col => ({ name: col.Name, displayName: col.DisplayName || col.Name }));\n }\n return this.entityInfo.Fields\n .filter(f => !f.IsVirtual)\n .map(f => ({ name: f.Name, displayName: f.DisplayNameOrName }));\n }\n\n private buildExportFileName(): string {\n const viewName = this.viewEntity?.Name || 'Data';\n return `${this.entityInfo!.Name}_${viewName}_${new Date().toISOString().split('T')[0]}`;\n }\n\n /**\n * Get display name for the resource tab\n */\n override async GetResourceDisplayName(data: ResourceData): Promise<string> {\n if (data.ResourceRecordID) {\n const compositeKey = new CompositeKey([{ FieldName: 'ID', Value: data.ResourceRecordID }]);\n const name = await this.metadata.GetEntityRecordName('MJ: User Views', compositeKey);\n return name ? name : `View: ${data.ResourceRecordID}`;\n }\n else if (data.Configuration?.Entity) {\n const entityName = data.Configuration.Entity as string;\n const hasFilter = data.Configuration.ExtraFilter;\n return `${entityName} [Dynamic${hasFilter ? ' - Filtered' : ' - All'}]`;\n }\n return 'User Views [Error]';\n }\n\n /**\n * Get icon class for the resource tab\n */\n override async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-table-list';\n }\n\n /**\n * Handle creating a new record for the current entity\n */\n public onCreateNewRecord(): void {\n if (!this.entityInfo) return;\n\n // Use NavigationService to open a new record form\n this.navigationService.OpenNewEntityRecord(this.entityInfo.Name);\n }\n\n /**\n * Open the Save-as-List dialog. Only meaningful for saved views (a\n * ViewID is required to materialize). Dynamic views fall back to a\n * user-visible notification rather than silently doing nothing.\n */\n public onSaveAsList(): void {\n if (!this.viewEntity?.ID) {\n this.showNotification('Save as List requires a saved View. Save this view first.', 'info', 4000);\n return;\n }\n // Best-effort record-count hint — the entity-viewer exposes the\n // grid's row count on its gridState; we surface it so the dialog's\n // confirm button can say \"Save List (476 records)\".\n this.saveAsListRecordCount = this.entityViewerRef?.TotalRecordCount ?? null;\n this.saveAsListDialogVisible = true;\n this.cdr.detectChanges();\n }\n\n public onSaveAsListCancelled(): void {\n this.saveAsListDialogVisible = false;\n this.cdr.detectChanges();\n }\n\n public async onSaveAsListSubmit(payload: SaveViewAsListResult): Promise<void> {\n const viewId = this.viewEntity?.ID;\n if (!viewId) return;\n this.isSavingAsList = true;\n this.cdr.detectChanges();\n try {\n const provider = this.ProviderToUse as unknown as GraphQLDataProvider;\n const client = new GraphQLListsClient(provider);\n const result = await client.MaterializeFromView(viewId, {\n ListName: payload.ListName,\n Description: payload.Description,\n CategoryId: payload.CategoryId,\n RememberLineage: payload.RememberLineage,\n UseSnapshot: payload.UseSnapshot,\n RefreshMode: payload.RefreshMode,\n });\n if (result.Success && result.CreatedListId) {\n this.saveAsListDialogVisible = false;\n this.showNotification(\n `List created with ${result.Counts?.Added ?? 0} record(s).`,\n 'success',\n 3000,\n );\n this.navigationService.OpenEntityRecord('MJ: Lists', new CompositeKey([{ FieldName: 'ID', Value: result.CreatedListId }]));\n } else {\n this.showNotification(`Save failed: ${result.Message}`, 'error', 5000);\n }\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n this.showNotification(`Save failed: ${message}`, 'error', 5000);\n } finally {\n this.isSavingAsList = false;\n this.cdr.detectChanges();\n }\n }\n\n\n /**\n * Show a notification to the user\n */\n private showNotification(message: string, style: 'info' | 'success' | 'error' | 'warning', duration: number): void {\n MJGlobal.Instance.RaiseEvent({\n component: this,\n event: MJEventType.DisplaySimpleNotificationRequest,\n eventCode: '',\n args: {\n message,\n style,\n DisplayDuration: duration\n }\n });\n }\n}\n","<div #container class=\"view-resource-container\">\n @if (isLoading) {\n <div class=\"view-loading-state\">\n <mj-loading text=\"Loading view...\" size=\"large\"></mj-loading>\n </div>\n } @else if (errorMessage) {\n <div class=\"view-error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ errorMessage }}</p>\n </div>\n } @else if (entityInfo) {\n <!-- Header with title and action buttons -->\n <div class=\"view-header\">\n <div class=\"header-left\">\n <h2 class=\"view-title\">{{ viewEntity?.Name || entityInfo.Name }}</h2>\n @if (viewEntity?.Description) {\n <p class=\"view-description\">{{ viewEntity!.Description }}</p>\n }\n </div>\n <div class=\"header-right\">\n <!-- Create New Record Button -->\n <button\n class=\"action-button create-button\"\n (click)=\"onCreateNewRecord()\"\n title=\"Create new {{ entityInfo.Name }} record\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>New Record</span>\n </button>\n\n <!-- Save as List Button — only meaningful for saved (non-dynamic) views. -->\n @if (viewEntity?.ID) {\n <button\n class=\"action-button save-as-list-button\"\n (click)=\"onSaveAsList()\"\n [disabled]=\"isSavingAsList\"\n title=\"Materialize these results as a static List\">\n @if (isSavingAsList) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-floppy-disk\"></i>\n }\n <span>{{ isSavingAsList ? 'Saving...' : 'Save as List' }}</span>\n </button>\n }\n <!-- Fallback Export button. Shown ONLY for view types that lack their own export UI\n (Cards / Map / Timeline). The Grid renderer has its own in-toolbar Export (full\n result set + format/sampling dialog), so this is suppressed there to avoid a\n duplicate/confusing second button. -->\n @if (showFallbackExportButton) {\n <button\n class=\"action-button export-button\"\n (click)=\"onExport()\"\n [disabled]=\"isExporting\"\n title=\"Export\">\n @if (isExporting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-file-excel\"></i>\n }\n <span>{{ isExporting ? 'Exporting...' : 'Export' }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Entity Viewer -->\n <mj-entity-viewer\n #entityViewer\n [Entity]=\"entityInfo\"\n [Provider]=\"Provider\"\n [ViewEntity]=\"viewEntity\"\n [GridState]=\"gridState\"\n [AutoSaveView]=\"true\"\n (RecordOpened)=\"onRecordOpened($any($event))\"\n (OpenRelatedRecordRequested)=\"onOpenRelatedRecord($event)\"\n (AfterViewTypeChange)=\"onViewTypeChanged()\"\n (DataLoaded)=\"onDataLoaded()\">\n </mj-entity-viewer>\n\n <!-- Save-as-List dialog -->\n <mj-save-view-as-list-dialog\n [Provider]=\"ProviderToUse\"\n [Visible]=\"saveAsListDialogVisible\"\n [ViewId]=\"viewEntity?.ID ?? null\"\n [ViewName]=\"viewEntity?.Name ?? null\"\n [RecordCount]=\"saveAsListRecordCount\"\n (Save)=\"onSaveAsListSubmit($event)\"\n (Cancel)=\"onSaveAsListCancelled()\">\n </mj-save-view-as-list-dialog>\n }\n</div>\n"]}
1
+ {"version":3,"file":"view-resource.component.js","sourceRoot":"","sources":["../../../src/lib/resource-wrappers/view-resource.component.ts","../../../src/lib/resource-wrappers/view-resource.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core';\nimport { BaseResourceComponent, NavigationService } from '@memberjunction/ng-shared';\nimport { ResourceData, MJUserViewEntityExtended, ViewInfo } from '@memberjunction/core-entities';\nimport { RegisterClass, MJGlobal, MJEventType , UUIDsEqual } from '@memberjunction/global';\nimport { CompositeKey, Metadata, EntityInfo } from '@memberjunction/core';\nimport { RecordOpenedEvent, ViewGridState, EntityViewerComponent, ViewRelatedRecordNavigation } from '@memberjunction/ng-entity-viewer';\nimport { ExportService } from '@memberjunction/ng-export-service';\nimport { ExportColumn } from '@memberjunction/export-engine';\nimport { GraphQLDataProvider, GraphQLListsClient } from '@memberjunction/graphql-dataprovider';\nimport type { SaveViewAsListResult } from '@memberjunction/ng-list-management';\n/**\n * UserViewResource - Resource wrapper for displaying User Views in tabs\n *\n * This component wraps the EntityViewerComponent to display view data.\n * It loads the view configuration and entity, then renders the data grid/cards.\n *\n * Key features:\n * - Loads view by ID from ResourceRecordID\n * - Supports dynamic views by entity name + extra filter\n * - Applies view's WhereClause, GridState, and SortState\n * - Opens records in new tabs via NavigationService\n */\n@RegisterClass(BaseResourceComponent, 'ViewResource')\n@Component({\n standalone: false,\n selector: 'mj-userview-resource',\n templateUrl: './view-resource.component.html',\n styles: [`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n position: relative;\n overflow: hidden;\n }\n .view-resource-container {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n .view-header {\n padding: 16px 20px 8px 20px;\n flex-shrink: 0;\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 16px;\n }\n .header-left {\n flex: 1;\n min-width: 0;\n }\n .header-right {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n }\n .view-title {\n margin: 0 0 4px 0;\n font-size: 1.25rem;\n font-weight: 600;\n color: var(--text-primary, #1a1a1a);\n }\n .view-description {\n margin: 0;\n font-size: 0.875rem;\n color: var(--text-secondary, #666);\n }\n .action-button {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n font-size: 0.875rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n }\n .action-button:hover:not(:disabled) {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-default);\n }\n .action-button:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n }\n .action-button i {\n font-size: 0.875rem;\n }\n .create-button {\n background: var(--mj-brand-primary);\n color: white;\n border-color: var(--mj-brand-primary);\n }\n .create-button:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n border-color: var(--mj-brand-primary-hover);\n }\n .export-button:hover:not(:disabled) {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n }\n .view-loading-state,\n .view-error-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n height: 100%;\n gap: 16px;\n }\n .view-error-state {\n color: var(--danger-color, #dc3545);\n }\n .view-error-state i {\n font-size: 2rem;\n }\n .view-error-state p {\n margin: 0;\n font-size: 1rem;\n }\n mj-entity-viewer {\n flex: 1;\n min-height: 0;\n }\n `]\n})\nexport class UserViewResource extends BaseResourceComponent {\n @ViewChild('container', { static: true }) containerElement!: ElementRef<HTMLDivElement>;\n @ViewChild('entityViewer') entityViewerRef?: EntityViewerComponent;\n\n public isLoading: boolean = false;\n public errorMessage: string | null = null;\n public entityInfo: EntityInfo | null = null;\n public viewEntity: MJUserViewEntityExtended | null = null;\n public gridState: ViewGridState | null = null;\n\n // Export state. The header Export button is a FALLBACK shown only for view types that don't\n // provide their own export UI (Cards / Map / Timeline). The Grid renderer has its own in-toolbar\n // Export (full result set + format/sampling dialog), so we suppress this one there to avoid a\n // duplicate/confusing second button — while keeping export available for every other view type.\n public isExporting: boolean = false;\n public showFallbackExportButton: boolean = false;\n\n // Save-as-list dialog state\n public saveAsListDialogVisible = false;\n public saveAsListRecordCount: number | null = null;\n public isSavingAsList = false;\n\n private dataLoaded = false;\n private get metadata() { return this.ProviderToUse; }\n constructor(\n private cdr: ChangeDetectorRef,\n private exportService: ExportService\n ) {\n super();\n }\n\n override set Data(value: ResourceData) {\n const previousRecordId = super.Data?.ResourceRecordID;\n const previousEntity = super.Data?.Configuration?.Entity;\n super.Data = value;\n\n const newRecordId = value?.ResourceRecordID;\n const newEntity = value?.Configuration?.Entity;\n\n // View-type (grid/cards/timeline/map) and per-view-type config are now resolved\n // internally by mj-entity-viewer from the saved view's ViewTypeID — nothing to read here.\n\n // Load on first set, or when the view/entity has changed\n if (!this.dataLoaded || newRecordId !== previousRecordId || newEntity !== previousEntity) {\n this.dataLoaded = true;\n // Reset state before loading new view\n this.entityInfo = null;\n this.viewEntity = null;\n this.gridState = null;\n this.errorMessage = null;\n this.loadView();\n }\n }\n\n override get Data(): ResourceData {\n return super.Data;\n }\n\n /**\n * Load the view and entity based on ResourceData\n */\n private async loadView(): Promise<void> {\n const data = this.Data;\n\n if (!data) {\n this.NotifyLoadComplete();\n return;\n }\n\n this.isLoading = true;\n this.errorMessage = null;\n this.NotifyLoadStarted();\n this.cdr.detectChanges();\n\n try {\n // NavigationService.OpenDynamicView stamps the special marker 'dynamic' as the\n // record ID (tabs require ResourceRecordId) — that is NOT a saved-view ID.\n const recordId = data.ResourceRecordID;\n const isDynamicMarker = typeof recordId === 'string' && recordId.trim().toLowerCase() === 'dynamic';\n // Case 1: Load view by ID\n if (recordId && !isDynamicMarker) {\n await this.loadViewById(recordId);\n }\n // Case 2: Load dynamic view by entity name\n else if (data.Configuration?.Entity) {\n await this.loadDynamicView(\n data.Configuration.Entity as string,\n data.Configuration.ExtraFilter as string | undefined\n );\n }\n else {\n this.errorMessage = 'No view ID or entity specified';\n }\n } catch (error) {\n console.error('Error loading view:', error);\n this.errorMessage = error instanceof Error ? error.message : 'Failed to load view';\n } finally {\n this.isLoading = false;\n this.cdr.detectChanges();\n\n // If there was an error, notify load complete now\n if (this.errorMessage) {\n this.NotifyLoadComplete();\n }\n // Otherwise, wait for dataLoaded event from entity-viewer\n }\n }\n\n /**\n * Load a saved view by its ID\n */\n private async loadViewById(viewId: string): Promise<void> {\n // Load the view entity\n const view = await ViewInfo.GetViewEntity(viewId);\n\n if (!view) {\n throw new Error(`View with ID ${viewId} not found`);\n }\n\n this.viewEntity = view as MJUserViewEntityExtended;\n\n // Check permissions\n if (!this.viewEntity.UserCanView) {\n throw new Error('You do not have permission to view this view');\n }\n\n // Load the entity info\n const entity = this.metadata.Entities.find(e => UUIDsEqual(e.ID, this.viewEntity!.EntityID));\n\n if (!entity) {\n throw new Error(`Entity for view not found`);\n }\n\n this.entityInfo = entity;\n\n // Parse grid state if available\n if (this.viewEntity.GridState) {\n try {\n this.gridState = JSON.parse(this.viewEntity.GridState) as ViewGridState;\n } catch (e) {\n console.warn('Failed to parse GridState:', e);\n this.gridState = null;\n }\n }\n\n // View-type + per-view-type config persistence is fully owned by mj-entity-viewer\n // (it reads ViewTypeID + DisplayState.viewTypeConfigs off the [viewEntity] and, with\n // [AutoSaveView]=\"true\", saves changes back) — nothing to wire here.\n }\n\n /**\n * Load a dynamic view (no saved view, just entity + filter)\n */\n private async loadDynamicView(entityName: string, _extraFilter?: string): Promise<void> {\n const entity = this.metadata.Entities.find(\n e => e.Name.trim().toLowerCase() === entityName.trim().toLowerCase()\n );\n\n if (!entity) {\n throw new Error(`Entity '${entityName}' not found`);\n }\n\n this.entityInfo = entity;\n this.viewEntity = null;\n this.gridState = null;\n\n // For dynamic views, we could create a synthetic viewEntity with just the WhereClause\n // but for now, we'll rely on the entity-viewer's default behavior\n }\n\n /**\n * Handle record opened event - open in new tab\n */\n public onRecordOpened(event: RecordOpenedEvent): void {\n if (event && event.entity && event.compositeKey) {\n this.navigationService.OpenEntityRecord(event.entity.Name, event.compositeKey);\n }\n }\n\n /**\n * Handle a related-record navigation requested from within a view-type renderer\n * (e.g. a foreign-key cell) - open the target record in a new tab.\n */\n public onOpenRelatedRecord(nav: ViewRelatedRecordNavigation): void {\n if (nav?.entityName && nav.recordKey != null) {\n // The related entity is arbitrary, so its key column can have any name — resolve the key\n // against its metadata rather than hardcoding `ID` via FromID.\n const entityInfo = this.metadata.EntityByName(nav.entityName);\n this.navigationService.OpenEntityRecord(nav.entityName, CompositeKey.FromURLSegment(entityInfo, String(nav.recordKey)));\n }\n }\n\n /**\n * Handle data loaded event from entity-viewer\n */\n public onDataLoaded(): void {\n this.NotifyLoadComplete();\n this.refreshFallbackExportVisibility();\n }\n\n /** The active view type changed — recompute whether the fallback Export button is needed. */\n public onViewTypeChanged(): void {\n this.refreshFallbackExportVisibility();\n }\n\n /**\n * Show the wrapper's Export button only for view types that don't ship their own export UI. The\n * entity-viewer reports this via {@link EntityViewerComponent.ActiveViewTypeHasOwnExport} (true for\n * the Grid renderer, false for Cards / Map / Timeline). Uses a cached flag + explicit change\n * detection to avoid an ExpressionChanged error from reading child state during a CD pass.\n */\n private refreshFallbackExportVisibility(): void {\n const next = !!this.entityViewerRef && !this.entityViewerRef.ActiveViewTypeHasOwnExport;\n if (next !== this.showFallbackExportButton) {\n this.showFallbackExportButton = next;\n this.cdr.detectChanges();\n }\n }\n\n /**\n * Fallback export for view types without their own export UI (Cards / Map / Timeline). Pulls the\n * FULL result set via the entity-viewer's capped, filter/sort-aware fetch (same path the grid uses,\n * so it's bounded and warns past the cap) and writes an Excel file.\n */\n public async onExport(): Promise<void> {\n // Never fail silently — a \"nothing happens\" click is impossible to diagnose. Surface the\n // reason both in the UI and the console.\n if (!this.entityInfo || !this.entityViewerRef) {\n console.error('[ViewResource] Export: viewer not ready', {\n hasEntity: !!this.entityInfo, hasViewer: !!this.entityViewerRef\n });\n this.showNotification('Export is not ready yet — try again in a moment.', 'error', 5000);\n return;\n }\n this.isExporting = true;\n this.cdr.detectChanges();\n this.showNotification('Preparing your Excel export…', 'info', 2000);\n try {\n const rows = await this.entityViewerRef.FetchAllRowsForExport();\n if (!rows || rows.length === 0) {\n this.showNotification('Nothing to export — the view returned no records.', 'warning', 5000);\n return;\n }\n const result = await this.exportService.toExcel(rows, {\n fileName: this.buildExportFileName(),\n columns: this.buildExportColumns(),\n includeHeaders: true\n });\n if (result.success) {\n this.exportService.downloadResult(result);\n this.showNotification(`Exported ${rows.length.toLocaleString()} record(s) to Excel.`, 'success', 3000);\n } else {\n this.showNotification('Export failed while building the file.', 'error', 5000);\n console.error('[ViewResource] Export: toExcel returned failure', result);\n }\n } catch (e) {\n this.showNotification('Error exporting data — see console for details.', 'error', 5000);\n console.error('[ViewResource] Export error:', e);\n } finally {\n this.isExporting = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Columns to export — from grid state, else the view's columns, else the entity's real fields. */\n private buildExportColumns(): ExportColumn[] {\n if (!this.entityInfo) return [];\n if (this.gridState?.columnSettings && this.gridState.columnSettings.length > 0) {\n return this.gridState.columnSettings\n .filter(col => col.hidden !== true)\n .map(col => ({ name: col.Name, displayName: col.DisplayName || col.Name }));\n }\n if (this.viewEntity?.Columns) {\n return this.viewEntity.Columns\n .filter(col => !col.hidden)\n .map(col => ({ name: col.Name, displayName: col.DisplayName || col.Name }));\n }\n return this.entityInfo.Fields\n .filter(f => !f.IsVirtual)\n .map(f => ({ name: f.Name, displayName: f.DisplayNameOrName }));\n }\n\n private buildExportFileName(): string {\n const viewName = this.viewEntity?.Name || 'Data';\n return `${this.entityInfo!.Name}_${viewName}_${new Date().toISOString().split('T')[0]}`;\n }\n\n /**\n * Get display name for the resource tab\n */\n override async GetResourceDisplayName(data: ResourceData): Promise<string> {\n if (data.ResourceRecordID) {\n const compositeKey = CompositeKey.FromID(data.ResourceRecordID); // first-pk-ok: ResourceRecordID of a User Views resource — core entity keyed by ID\n const name = await this.metadata.GetEntityRecordName('MJ: User Views', compositeKey);\n return name ? name : `View: ${data.ResourceRecordID}`;\n }\n else if (data.Configuration?.Entity) {\n const entityName = data.Configuration.Entity as string;\n const hasFilter = data.Configuration.ExtraFilter;\n return `${entityName} [Dynamic${hasFilter ? ' - Filtered' : ' - All'}]`;\n }\n return 'User Views [Error]';\n }\n\n /**\n * Get icon class for the resource tab\n */\n override async GetResourceIconClass(_data: ResourceData): Promise<string> {\n return 'fa-solid fa-table-list';\n }\n\n /**\n * Handle creating a new record for the current entity\n */\n public onCreateNewRecord(): void {\n if (!this.entityInfo) return;\n\n // Use NavigationService to open a new record form\n this.navigationService.OpenNewEntityRecord(this.entityInfo.Name);\n }\n\n /**\n * Open the Save-as-List dialog. Only meaningful for saved views (a\n * ViewID is required to materialize). Dynamic views fall back to a\n * user-visible notification rather than silently doing nothing.\n */\n public onSaveAsList(): void {\n if (!this.viewEntity?.ID) {\n this.showNotification('Save as List requires a saved View. Save this view first.', 'info', 4000);\n return;\n }\n // Best-effort record-count hint — the entity-viewer exposes the\n // grid's row count on its gridState; we surface it so the dialog's\n // confirm button can say \"Save List (476 records)\".\n this.saveAsListRecordCount = this.entityViewerRef?.TotalRecordCount ?? null;\n this.saveAsListDialogVisible = true;\n this.cdr.detectChanges();\n }\n\n public onSaveAsListCancelled(): void {\n this.saveAsListDialogVisible = false;\n this.cdr.detectChanges();\n }\n\n public async onSaveAsListSubmit(payload: SaveViewAsListResult): Promise<void> {\n const viewId = this.viewEntity?.ID;\n if (!viewId) return;\n this.isSavingAsList = true;\n this.cdr.detectChanges();\n try {\n const provider = this.ProviderToUse as unknown as GraphQLDataProvider;\n const client = new GraphQLListsClient(provider);\n const result = await client.MaterializeFromView(viewId, {\n ListName: payload.ListName,\n Description: payload.Description,\n CategoryId: payload.CategoryId,\n RememberLineage: payload.RememberLineage,\n UseSnapshot: payload.UseSnapshot,\n RefreshMode: payload.RefreshMode,\n });\n if (result.Success && result.CreatedListId) {\n this.saveAsListDialogVisible = false;\n this.showNotification(\n `List created with ${result.Counts?.Added ?? 0} record(s).`,\n 'success',\n 3000,\n );\n this.navigationService.OpenEntityRecord('MJ: Lists', CompositeKey.FromID(result.CreatedListId));\n } else {\n this.showNotification(`Save failed: ${result.Message}`, 'error', 5000);\n }\n } catch (e) {\n const message = e instanceof Error ? e.message : String(e);\n this.showNotification(`Save failed: ${message}`, 'error', 5000);\n } finally {\n this.isSavingAsList = false;\n this.cdr.detectChanges();\n }\n }\n\n\n /**\n * Show a notification to the user\n */\n private showNotification(message: string, style: 'info' | 'success' | 'error' | 'warning', duration: number): void {\n MJGlobal.Instance.RaiseEvent({\n component: this,\n event: MJEventType.DisplaySimpleNotificationRequest,\n eventCode: '',\n args: {\n message,\n style,\n DisplayDuration: duration\n }\n });\n }\n}\n","<div #container class=\"view-resource-container\">\n @if (isLoading) {\n <div class=\"view-loading-state\">\n <mj-loading text=\"Loading view...\" size=\"large\"></mj-loading>\n </div>\n } @else if (errorMessage) {\n <div class=\"view-error-state\">\n <i class=\"fas fa-exclamation-triangle\"></i>\n <p>{{ errorMessage }}</p>\n </div>\n } @else if (entityInfo) {\n <!-- Header with title and action buttons -->\n <div class=\"view-header\">\n <div class=\"header-left\">\n <h2 class=\"view-title\">{{ viewEntity?.Name || entityInfo.Name }}</h2>\n @if (viewEntity?.Description) {\n <p class=\"view-description\">{{ viewEntity!.Description }}</p>\n }\n </div>\n <div class=\"header-right\">\n <!-- Create New Record Button -->\n <button\n class=\"action-button create-button\"\n (click)=\"onCreateNewRecord()\"\n title=\"Create new {{ entityInfo.Name }} record\">\n <i class=\"fa-solid fa-plus\"></i>\n <span>New Record</span>\n </button>\n\n <!-- Save as List Button — only meaningful for saved (non-dynamic) views. -->\n @if (viewEntity?.ID) {\n <button\n class=\"action-button save-as-list-button\"\n (click)=\"onSaveAsList()\"\n [disabled]=\"isSavingAsList\"\n title=\"Materialize these results as a static List\">\n @if (isSavingAsList) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-floppy-disk\"></i>\n }\n <span>{{ isSavingAsList ? 'Saving...' : 'Save as List' }}</span>\n </button>\n }\n <!-- Fallback Export button. Shown ONLY for view types that lack their own export UI\n (Cards / Map / Timeline). The Grid renderer has its own in-toolbar Export (full\n result set + format/sampling dialog), so this is suppressed there to avoid a\n duplicate/confusing second button. -->\n @if (showFallbackExportButton) {\n <button\n class=\"action-button export-button\"\n (click)=\"onExport()\"\n [disabled]=\"isExporting\"\n title=\"Export\">\n @if (isExporting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-file-excel\"></i>\n }\n <span>{{ isExporting ? 'Exporting...' : 'Export' }}</span>\n </button>\n }\n </div>\n </div>\n\n <!-- Entity Viewer -->\n <mj-entity-viewer\n #entityViewer\n [Entity]=\"entityInfo\"\n [Provider]=\"Provider\"\n [ViewEntity]=\"viewEntity\"\n [GridState]=\"gridState\"\n [AutoSaveView]=\"true\"\n (RecordOpened)=\"onRecordOpened($any($event))\"\n (OpenRelatedRecordRequested)=\"onOpenRelatedRecord($event)\"\n (AfterViewTypeChange)=\"onViewTypeChanged()\"\n (DataLoaded)=\"onDataLoaded()\">\n </mj-entity-viewer>\n\n <!-- Save-as-List dialog -->\n <mj-save-view-as-list-dialog\n [Provider]=\"ProviderToUse\"\n [Visible]=\"saveAsListDialogVisible\"\n [ViewId]=\"viewEntity?.ID ?? null\"\n [ViewName]=\"viewEntity?.Name ?? null\"\n [RecordCount]=\"saveAsListRecordCount\"\n (Save)=\"onSaveAsListSubmit($event)\"\n (Cancel)=\"onSaveAsListCancelled()\">\n </mj-save-view-as-list-dialog>\n }\n</div>\n"]}
@@ -1,19 +1,31 @@
1
- import { OnInit, OnDestroy } from '@angular/core';
1
+ import { OnDestroy } from '@angular/core';
2
2
  import { AnimationEvent } from '@angular/animations';
3
- import { ServerConnectivityService } from '../services/server-connectivity.service';
4
3
  import * as i0 from "@angular/core";
5
4
  /**
6
5
  * When visible, this banner sets --mj-connectivity-banner-height on <html>
7
6
  * so that any position:fixed element referencing the shell header offset
8
7
  * (e.g. app-switcher mobile dropdown) can account for the banner.
9
8
  */
10
- export declare class ServerConnectivityBannerComponent implements OnInit, OnDestroy {
11
- private connectivityService;
9
+ export declare class ServerConnectivityBannerComponent implements OnDestroy {
12
10
  private static readonly CSS_VAR;
13
- IsConnected: boolean;
14
- private subscription;
15
- constructor(connectivityService: ServerConnectivityService);
16
- ngOnInit(): void;
11
+ private readonly connectivityService;
12
+ /**
13
+ * Connectivity state as a **signal**, not a plain property — and that distinction is the
14
+ * whole point of this shape.
15
+ *
16
+ * The socket state that feeds `IsConnected$` is reported by graphql-ws, which can announce
17
+ * a drop from a callback that lands *inside* an in-flight change-detection pass (during app
18
+ * startup this reliably happens after the banner's own view has been checked). Assigning a
19
+ * plain property there mutates state the `@if` was already evaluated against, so dev-mode
20
+ * check-no-changes reports `NG0100 ... Previous value: '-1'` — `-1` being the `@if` branch
21
+ * index for "no branch rendered". Reading a signal from the template instead registers the
22
+ * view as its consumer, so a write marks the view dirty and Angular re-refreshes it within
23
+ * the same pass; the DOM and the expression can no longer disagree.
24
+ *
25
+ * `toSignal` also owns the subscription lifetime via `DestroyRef`, so there is no manual
26
+ * `Subscription` to unsubscribe.
27
+ */
28
+ readonly IsConnected: import("@angular/core").Signal<boolean>;
17
29
  ngOnDestroy(): void;
18
30
  /** After the enter/leave animation finishes, publish the banner height as a CSS custom property */
19
31
  OnAnimationDone(event: AnimationEvent): void;
@@ -1 +1 @@
1
- {"version":3,"file":"server-connectivity-banner.component.d.ts","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,MAAM,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAuC,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;;AAEpF;;;;GAIG;AACH,qBA0Da,iCAAkC,YAAW,MAAM,EAAE,SAAS;IAM7D,OAAO,CAAC,mBAAmB;IALvC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAE7D,WAAW,UAAQ;IAC1B,OAAO,CAAC,YAAY,CAA2B;gBAE3B,mBAAmB,EAAE,yBAAyB;IAElE,QAAQ,IAAI,IAAI;IAMhB,WAAW,IAAI,IAAI;IAOnB,mGAAmG;IACnG,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAS5C,OAAO,CAAC,eAAe;yCA/BZ,iCAAiC;2CAAjC,iCAAiC;CAqC7C"}
1
+ {"version":3,"file":"server-connectivity-banner.component.d.ts","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,SAAS,EAAU,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAuC,cAAc,EAAE,MAAM,qBAAqB,CAAC;;AAG1F;;;;GAIG;AACH,qBA0Da,iCAAkC,YAAW,SAAS;IACjE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAqC;IAEpE,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqC;IAEzE;;;;;;;;;;;;;;;OAeG;IACH,SAAgB,WAAW,0CAA2E;IAEtG,WAAW,IAAI,IAAI;IAInB,mGAAmG;IACnG,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAS5C,OAAO,CAAC,eAAe;yCArCZ,iCAAiC;2CAAjC,iCAAiC;CA2C7C"}
@@ -1,7 +1,8 @@
1
- import { Component } from '@angular/core';
1
+ import { Component, inject } from '@angular/core';
2
+ import { toSignal } from '@angular/core/rxjs-interop';
2
3
  import { trigger, transition, style, animate } from '@angular/animations';
4
+ import { ServerConnectivityService } from '../services/server-connectivity.service';
3
5
  import * as i0 from "@angular/core";
4
- import * as i1 from "../services/server-connectivity.service";
5
6
  function ServerConnectivityBannerComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
6
7
  const _r1 = i0.ɵɵgetCurrentView();
7
8
  i0.ɵɵdomElementStart(0, "div", 0);
@@ -19,22 +20,26 @@ function ServerConnectivityBannerComponent_Conditional_0_Template(rf, ctx) { if
19
20
  * (e.g. app-switcher mobile dropdown) can account for the banner.
20
21
  */
21
22
  export class ServerConnectivityBannerComponent {
22
- connectivityService;
23
23
  static CSS_VAR = '--mj-connectivity-banner-height';
24
- IsConnected = true;
25
- subscription;
26
- constructor(connectivityService) {
27
- this.connectivityService = connectivityService;
28
- }
29
- ngOnInit() {
30
- this.subscription = this.connectivityService.IsConnected$.subscribe(connected => {
31
- this.IsConnected = connected;
32
- });
33
- }
24
+ connectivityService = inject(ServerConnectivityService);
25
+ /**
26
+ * Connectivity state as a **signal**, not a plain property — and that distinction is the
27
+ * whole point of this shape.
28
+ *
29
+ * The socket state that feeds `IsConnected$` is reported by graphql-ws, which can announce
30
+ * a drop from a callback that lands *inside* an in-flight change-detection pass (during app
31
+ * startup this reliably happens after the banner's own view has been checked). Assigning a
32
+ * plain property there mutates state the `@if` was already evaluated against, so dev-mode
33
+ * check-no-changes reports `NG0100 ... Previous value: '-1'` — `-1` being the `@if` branch
34
+ * index for "no branch rendered". Reading a signal from the template instead registers the
35
+ * view as its consumer, so a write marks the view dirty and Angular re-refreshes it within
36
+ * the same pass; the DOM and the expression can no longer disagree.
37
+ *
38
+ * `toSignal` also owns the subscription lifetime via `DestroyRef`, so there is no manual
39
+ * `Subscription` to unsubscribe.
40
+ */
41
+ IsConnected = toSignal(this.connectivityService.IsConnected$, { initialValue: true });
34
42
  ngOnDestroy() {
35
- if (this.subscription) {
36
- this.subscription.unsubscribe();
37
- }
38
43
  this.setBannerHeight(0);
39
44
  }
40
45
  /** After the enter/leave animation finishes, publish the banner height as a CSS custom property */
@@ -50,11 +55,11 @@ export class ServerConnectivityBannerComponent {
50
55
  setBannerHeight(px) {
51
56
  document.documentElement.style.setProperty(ServerConnectivityBannerComponent.CSS_VAR, `${px}px`);
52
57
  }
53
- static ɵfac = function ServerConnectivityBannerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ServerConnectivityBannerComponent)(i0.ɵɵdirectiveInject(i1.ServerConnectivityService)); };
58
+ static ɵfac = function ServerConnectivityBannerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ServerConnectivityBannerComponent)(); };
54
59
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ServerConnectivityBannerComponent, selectors: [["mj-server-connectivity-banner"]], decls: 1, vars: 1, consts: [[1, "connectivity-banner"], [1, "fa-solid", "fa-triangle-exclamation"]], template: function ServerConnectivityBannerComponent_Template(rf, ctx) { if (rf & 1) {
55
60
  i0.ɵɵconditionalCreate(0, ServerConnectivityBannerComponent_Conditional_0_Template, 4, 1, "div", 0);
56
61
  } if (rf & 2) {
57
- i0.ɵɵconditional(!ctx.IsConnected ? 0 : -1);
62
+ i0.ɵɵconditional(!ctx.IsConnected() ? 0 : -1);
58
63
  } }, styles: ["[_nghost-%COMP%] {\n flex-shrink: 0;\n width: 100%;\n }\n\n .connectivity-banner[_ngcontent-%COMP%] {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border-bottom: 2px solid var(--mj-status-warning-border);\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n }\n\n .connectivity-banner[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n flex-shrink: 0;\n }\n\n .connectivity-banner[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n text-align: center;\n }\n\n @media (max-width: 480px) {\n .connectivity-banner[_ngcontent-%COMP%] {\n padding: 6px 12px;\n font-size: 13px;\n }\n }"], data: { animation: [
59
64
  trigger('slideDown', [
60
65
  transition(':enter', [
@@ -80,13 +85,13 @@ export class ServerConnectivityBannerComponent {
80
85
  ])
81
86
  ])
82
87
  ], template: `
83
- @if (!IsConnected) {
88
+ @if (!IsConnected()) {
84
89
  <div class="connectivity-banner" @slideDown (@slideDown.done)="OnAnimationDone($event)">
85
90
  <i class="fa-solid fa-triangle-exclamation"></i>
86
91
  <span>Server unavailable &mdash; viewing cached data. Some features may not work.</span>
87
92
  </div>
88
93
  }
89
94
  `, styles: ["\n :host {\n flex-shrink: 0;\n width: 100%;\n }\n\n .connectivity-banner {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border-bottom: 2px solid var(--mj-status-warning-border);\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n }\n\n .connectivity-banner i {\n font-size: 16px;\n flex-shrink: 0;\n }\n\n .connectivity-banner span {\n text-align: center;\n }\n\n @media (max-width: 480px) {\n .connectivity-banner {\n padding: 6px 12px;\n font-size: 13px;\n }\n }\n "] }]
90
- }], () => [{ type: i1.ServerConnectivityService }], null); })();
95
+ }], null, null); })();
91
96
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ServerConnectivityBannerComponent, { className: "ServerConnectivityBannerComponent", filePath: "src/lib/server-connectivity/server-connectivity-banner.component.ts", lineNumber: 69 }); })();
92
97
  //# sourceMappingURL=server-connectivity-banner.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-connectivity-banner.component.js","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAkB,MAAM,qBAAqB,CAAC;;;;;IAyBpF,iCAAwF;IAA5C,qOAAmB,8BAAuB,KAAC;IACrF,0BAAgD;IAChD,+BAAM;IAAA,0FAA2E;IACnF,AADmF,oBAAO,EACpF;;IAH2B,sCAAU;;AArBjD;;;;GAIG;AA2DH,MAAM,OAAO,iCAAiC;IAMxB;IALZ,MAAM,CAAU,OAAO,GAAG,iCAAiC,CAAC;IAE7D,WAAW,GAAG,IAAI,CAAC;IAClB,YAAY,CAA2B;IAE/C,YAAoB,mBAA8C;QAA9C,wBAAmB,GAAnB,mBAAmB,CAA2B;IAAG,CAAC;IAEtE,QAAQ;QACN,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE;YAC9E,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QAC/B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,WAAW;QACT,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC;QAClC,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,mGAAmG;IACnG,eAAe,CAAC,KAAqB;QACnC,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC7B,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAI,KAAK,CAAC,OAAuB,CAAC,YAAY,CAAC;YAC3D,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,EAAU;QAChC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CACxC,iCAAiC,CAAC,OAAO,EACzC,GAAG,EAAE,IAAI,CACV,CAAC;IACJ,CAAC;2HApCU,iCAAiC;6DAAjC,iCAAiC;YA3C1C,mGAAoB;;YAApB,2CAKC;23BAjBS;gBACV,OAAO,CAAC,WAAW,EAAE;oBACnB,UAAU,CAAC,QAAQ,EAAE;wBACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;wBACpD,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;qBAC9D,CAAC;oBACF,UAAU,CAAC,QAAQ,EAAE;wBACnB,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;qBAC/E,CAAC;iBACH,CAAC;aACH;;iFA6CU,iCAAiC;cA1D7C,SAAS;2BACE,+BAA+B,cAC7B,IAAI,cACJ;oBACV,OAAO,CAAC,WAAW,EAAE;wBACnB,UAAU,CAAC,QAAQ,EAAE;4BACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;4BACpD,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;yBAC9D,CAAC;wBACF,UAAU,CAAC,QAAQ,EAAE;4BACnB,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;yBAC/E,CAAC;qBACH,CAAC;iBACH,YACS;;;;;;;GAOT;;kFAqCU,iCAAiC","sourcesContent":["import { Component, OnInit, OnDestroy } from '@angular/core';\nimport { trigger, transition, style, animate, AnimationEvent } from '@angular/animations';\nimport { Subscription } from 'rxjs';\nimport { ServerConnectivityService } from '../services/server-connectivity.service';\n\n/**\n * When visible, this banner sets --mj-connectivity-banner-height on <html>\n * so that any position:fixed element referencing the shell header offset\n * (e.g. app-switcher mobile dropdown) can account for the banner.\n */\n@Component({\n selector: 'mj-server-connectivity-banner',\n standalone: true,\n animations: [\n trigger('slideDown', [\n transition(':enter', [\n style({ height: 0, opacity: 0, overflow: 'hidden' }),\n animate('300ms ease-out', style({ height: '*', opacity: 1 }))\n ]),\n transition(':leave', [\n animate('200ms ease-in', style({ height: 0, opacity: 0, overflow: 'hidden' }))\n ])\n ])\n ],\n template: `\n @if (!IsConnected) {\n <div class=\"connectivity-banner\" @slideDown (@slideDown.done)=\"OnAnimationDone($event)\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>Server unavailable &mdash; viewing cached data. Some features may not work.</span>\n </div>\n }\n `,\n styles: [`\n :host {\n flex-shrink: 0;\n width: 100%;\n }\n\n .connectivity-banner {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border-bottom: 2px solid var(--mj-status-warning-border);\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n }\n\n .connectivity-banner i {\n font-size: 16px;\n flex-shrink: 0;\n }\n\n .connectivity-banner span {\n text-align: center;\n }\n\n @media (max-width: 480px) {\n .connectivity-banner {\n padding: 6px 12px;\n font-size: 13px;\n }\n }\n `]\n})\nexport class ServerConnectivityBannerComponent implements OnInit, OnDestroy {\n private static readonly CSS_VAR = '--mj-connectivity-banner-height';\n\n public IsConnected = true;\n private subscription: Subscription | undefined;\n\n constructor(private connectivityService: ServerConnectivityService) {}\n\n ngOnInit(): void {\n this.subscription = this.connectivityService.IsConnected$.subscribe(connected => {\n this.IsConnected = connected;\n });\n }\n\n ngOnDestroy(): void {\n if (this.subscription) {\n this.subscription.unsubscribe();\n }\n this.setBannerHeight(0);\n }\n\n /** After the enter/leave animation finishes, publish the banner height as a CSS custom property */\n OnAnimationDone(event: AnimationEvent): void {\n if (event.toState === 'void') {\n this.setBannerHeight(0);\n } else {\n const height = (event.element as HTMLElement).offsetHeight;\n this.setBannerHeight(height);\n }\n }\n\n private setBannerHeight(px: number): void {\n document.documentElement.style.setProperty(\n ServerConnectivityBannerComponent.CSS_VAR,\n `${px}px`\n );\n }\n}\n"]}
1
+ {"version":3,"file":"server-connectivity-banner.component.js","sourceRoot":"","sources":["../../../src/lib/server-connectivity/server-connectivity-banner.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAkB,MAAM,qBAAqB,CAAC;AAC1F,OAAO,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;;;;IAuB9E,iCAAwF;IAA5C,qOAAmB,8BAAuB,KAAC;IACrF,0BAAgD;IAChD,+BAAM;IAAA,0FAA2E;IACnF,AADmF,oBAAO,EACpF;;IAH2B,sCAAU;;AArBjD;;;;GAIG;AA2DH,MAAM,OAAO,iCAAiC;IACpC,MAAM,CAAU,OAAO,GAAG,iCAAiC,CAAC;IAEnD,mBAAmB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAC;IAEzE;;;;;;;;;;;;;;;OAeG;IACa,WAAW,GAAG,QAAQ,CAAC,IAAI,CAAC,mBAAmB,CAAC,YAAY,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAEtG,WAAW;QACT,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IAED,mGAAmG;IACnG,eAAe,CAAC,KAAqB;QACnC,IAAI,KAAK,CAAC,OAAO,KAAK,MAAM,EAAE,CAAC;YAC7B,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAI,KAAK,CAAC,OAAuB,CAAC,YAAY,CAAC;YAC3D,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,EAAU;QAChC,QAAQ,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,CACxC,iCAAiC,CAAC,OAAO,EACzC,GAAG,EAAE,IAAI,CACV,CAAC;IACJ,CAAC;2HA1CU,iCAAiC;6DAAjC,iCAAiC;YA3C1C,mGAAsB;;YAAtB,6CAKC;23BAjBS;gBACV,OAAO,CAAC,WAAW,EAAE;oBACnB,UAAU,CAAC,QAAQ,EAAE;wBACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;wBACpD,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;qBAC9D,CAAC;oBACF,UAAU,CAAC,QAAQ,EAAE;wBACnB,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;qBAC/E,CAAC;iBACH,CAAC;aACH;;iFA6CU,iCAAiC;cA1D7C,SAAS;2BACE,+BAA+B,cAC7B,IAAI,cACJ;oBACV,OAAO,CAAC,WAAW,EAAE;wBACnB,UAAU,CAAC,QAAQ,EAAE;4BACnB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;4BACpD,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;yBAC9D,CAAC;wBACF,UAAU,CAAC,QAAQ,EAAE;4BACnB,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC;yBAC/E,CAAC;qBACH,CAAC;iBACH,YACS;;;;;;;GAOT;;kFAqCU,iCAAiC","sourcesContent":["import { Component, OnDestroy, inject } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { trigger, transition, style, animate, AnimationEvent } from '@angular/animations';\nimport { ServerConnectivityService } from '../services/server-connectivity.service';\n\n/**\n * When visible, this banner sets --mj-connectivity-banner-height on <html>\n * so that any position:fixed element referencing the shell header offset\n * (e.g. app-switcher mobile dropdown) can account for the banner.\n */\n@Component({\n selector: 'mj-server-connectivity-banner',\n standalone: true,\n animations: [\n trigger('slideDown', [\n transition(':enter', [\n style({ height: 0, opacity: 0, overflow: 'hidden' }),\n animate('300ms ease-out', style({ height: '*', opacity: 1 }))\n ]),\n transition(':leave', [\n animate('200ms ease-in', style({ height: 0, opacity: 0, overflow: 'hidden' }))\n ])\n ])\n ],\n template: `\n @if (!IsConnected()) {\n <div class=\"connectivity-banner\" @slideDown (@slideDown.done)=\"OnAnimationDone($event)\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>Server unavailable &mdash; viewing cached data. Some features may not work.</span>\n </div>\n }\n `,\n styles: [`\n :host {\n flex-shrink: 0;\n width: 100%;\n }\n\n .connectivity-banner {\n background: var(--mj-status-warning-bg);\n color: var(--mj-status-warning-text);\n border-bottom: 2px solid var(--mj-status-warning-border);\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 500;\n }\n\n .connectivity-banner i {\n font-size: 16px;\n flex-shrink: 0;\n }\n\n .connectivity-banner span {\n text-align: center;\n }\n\n @media (max-width: 480px) {\n .connectivity-banner {\n padding: 6px 12px;\n font-size: 13px;\n }\n }\n `]\n})\nexport class ServerConnectivityBannerComponent implements OnDestroy {\n private static readonly CSS_VAR = '--mj-connectivity-banner-height';\n\n private readonly connectivityService = inject(ServerConnectivityService);\n\n /**\n * Connectivity state as a **signal**, not a plain property — and that distinction is the\n * whole point of this shape.\n *\n * The socket state that feeds `IsConnected$` is reported by graphql-ws, which can announce\n * a drop from a callback that lands *inside* an in-flight change-detection pass (during app\n * startup this reliably happens after the banner's own view has been checked). Assigning a\n * plain property there mutates state the `@if` was already evaluated against, so dev-mode\n * check-no-changes reports `NG0100 ... Previous value: '-1'` — `-1` being the `@if` branch\n * index for \"no branch rendered\". Reading a signal from the template instead registers the\n * view as its consumer, so a write marks the view dirty and Angular re-refreshes it within\n * the same pass; the DOM and the expression can no longer disagree.\n *\n * `toSignal` also owns the subscription lifetime via `DestroyRef`, so there is no manual\n * `Subscription` to unsubscribe.\n */\n public readonly IsConnected = toSignal(this.connectivityService.IsConnected$, { initialValue: true });\n\n ngOnDestroy(): void {\n this.setBannerHeight(0);\n }\n\n /** After the enter/leave animation finishes, publish the banner height as a CSS custom property */\n OnAnimationDone(event: AnimationEvent): void {\n if (event.toState === 'void') {\n this.setBannerHeight(0);\n } else {\n const height = (event.element as HTMLElement).offsetHeight;\n this.setBannerHeight(height);\n }\n }\n\n private setBannerHeight(px: number): void {\n document.documentElement.style.setProperty(\n ServerConnectivityBannerComponent.CSS_VAR,\n `${px}px`\n );\n }\n}\n"]}
@@ -50,7 +50,7 @@ function AppAccessDialogComponent_Conditional_0_Template(rf, ctx) { if (rf & 1)
50
50
  i0.ɵɵelementStart(0, "div", 1);
51
51
  i0.ɵɵlistener("click", function AppAccessDialogComponent_Conditional_0_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onDismiss()); });
52
52
  i0.ɵɵelementStart(1, "div", 2);
53
- i0.ɵɵlistener("click", function AppAccessDialogComponent_Conditional_0_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView($event.stopPropagation()); });
53
+ i0.ɵɵlistener("click", function AppAccessDialogComponent_Conditional_0_Template_div_click_1_listener($event) { return $event.stopPropagation(); });
54
54
  i0.ɵɵelementStart(2, "div", 3);
55
55
  i0.ɵɵelement(3, "i", 4);
56
56
  i0.ɵɵelementEnd();
@@ -380,7 +380,7 @@ export class AppAccessDialogComponent {
380
380
  i0.ɵɵconditionalCreate(0, AppAccessDialogComponent_Conditional_0_Template, 13, 11, "div", 0);
381
381
  } if (rf & 2) {
382
382
  i0.ɵɵconditional(ctx.visible ? 0 : -1);
383
- } }, dependencies: [i1.NgClass], styles: [".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.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001; \n\n animation: _ngcontent-%COMP%_fadeIn 0.2s ease-out;\n padding: 16px; \n\n box-sizing: border-box;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.dialog-container[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n padding: 32px;\n max-width: 420px;\n width: 90%;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n text-align: center;\n animation: _ngcontent-%COMP%_slideUp 0.3s 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.dialog-icon[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n}\n\n.dialog-title[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.dialog-message[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 14px;\n line-height: 1.5;\n color: var(--mj-text-secondary);\n}\n\n.dialog-help[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.dialog-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n justify-content: center;\n margin-top: 24px;\n}\n\n.dialog-btn[_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 border: none;\n min-width: 100px;\n}\n\n.dialog-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.dialog-btn.primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: white;\n}\n\n.dialog-btn.primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.dialog-btn.primary[_ngcontent-%COMP%]:active:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.dialog-btn.secondary[_ngcontent-%COMP%], \n.dialog-btn[_ngcontent-%COMP%]:not(.primary):not(.secondary) {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n border: 1px solid var(--mj-border-default);\n}\n\n.dialog-btn.secondary[_ngcontent-%COMP%]:hover:not(:disabled), \n.dialog-btn[_ngcontent-%COMP%]:not(.primary):not(.secondary):hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n}\n\n.dialog-btn.secondary[_ngcontent-%COMP%]:active:not(:disabled), \n.dialog-btn[_ngcontent-%COMP%]:not(.primary):not(.secondary):active:not(:disabled) {\n background: var(--mj-bg-surface-active);\n}\n\n\n\n.fa-spin[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_spin 1s linear infinite;\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n\n\n@media (max-width: 768px) {\n .dialog-container[_ngcontent-%COMP%] {\n max-width: 380px;\n }\n\n .dialog-message[_ngcontent-%COMP%] {\n font-size: 13px;\n }\n}\n\n\n\n@media (max-width: 480px) {\n .dialog-container[_ngcontent-%COMP%] {\n padding: 20px;\n margin: 0;\n max-width: none;\n width: 100%;\n border-radius: 8px;\n }\n\n .dialog-icon[_ngcontent-%COMP%] {\n font-size: 36px;\n margin-bottom: 12px;\n }\n\n .dialog-title[_ngcontent-%COMP%] {\n font-size: 17px;\n margin-bottom: 10px;\n }\n\n .dialog-message[_ngcontent-%COMP%] {\n font-size: 13px;\n margin-bottom: 6px;\n }\n\n .dialog-help[_ngcontent-%COMP%] {\n font-size: 11px;\n margin-bottom: 16px;\n }\n\n .dialog-actions[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 10px;\n margin-top: 20px;\n }\n\n .dialog-btn[_ngcontent-%COMP%] {\n width: 100%;\n padding: 12px 20px;\n font-size: 15px;\n }\n}\n\n\n\n@media (max-width: 360px) {\n .dialog-container[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .dialog-icon[_ngcontent-%COMP%] {\n font-size: 32px;\n }\n\n .dialog-title[_ngcontent-%COMP%] {\n font-size: 16px;\n }\n}"] });
383
+ } }, dependencies: [i1.NgClass], styles: [".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.6);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10001; \n animation: _ngcontent-%COMP%_fadeIn 0.2s ease-out;\n padding: 16px; \n box-sizing: border-box;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.dialog-container[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-card);\n border-radius: 12px;\n padding: 32px;\n max-width: 420px;\n width: 90%;\n box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);\n text-align: center;\n animation: _ngcontent-%COMP%_slideUp 0.3s 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.dialog-icon[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n}\n\n.dialog-title[_ngcontent-%COMP%] {\n margin: 0 0 12px 0;\n font-size: 20px;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.dialog-message[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 14px;\n line-height: 1.5;\n color: var(--mj-text-secondary);\n}\n\n.dialog-help[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 12px;\n color: var(--mj-text-muted);\n font-style: italic;\n}\n\n.dialog-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n justify-content: center;\n margin-top: 24px;\n}\n\n.dialog-btn[_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 border: none;\n min-width: 100px;\n}\n\n.dialog-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.dialog-btn.primary[_ngcontent-%COMP%] {\n background: var(--mj-brand-primary);\n color: white;\n}\n\n.dialog-btn.primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.dialog-btn.primary[_ngcontent-%COMP%]:active:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.dialog-btn.secondary[_ngcontent-%COMP%], \n.dialog-btn[_ngcontent-%COMP%]:not(.primary):not(.secondary) {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n border: 1px solid var(--mj-border-default);\n}\n\n.dialog-btn.secondary[_ngcontent-%COMP%]:hover:not(:disabled), \n.dialog-btn[_ngcontent-%COMP%]:not(.primary):not(.secondary):hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n}\n\n.dialog-btn.secondary[_ngcontent-%COMP%]:active:not(:disabled), \n.dialog-btn[_ngcontent-%COMP%]:not(.primary):not(.secondary):active:not(:disabled) {\n background: var(--mj-bg-surface-active);\n}\n\n\n.fa-spin[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_spin 1s linear infinite;\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n\n@media (max-width: 768px) {\n .dialog-container[_ngcontent-%COMP%] {\n max-width: 380px;\n }\n\n .dialog-message[_ngcontent-%COMP%] {\n font-size: 13px;\n }\n}\n\n\n@media (max-width: 480px) {\n .dialog-container[_ngcontent-%COMP%] {\n padding: 20px;\n margin: 0;\n max-width: none;\n width: 100%;\n border-radius: 8px;\n }\n\n .dialog-icon[_ngcontent-%COMP%] {\n font-size: 36px;\n margin-bottom: 12px;\n }\n\n .dialog-title[_ngcontent-%COMP%] {\n font-size: 17px;\n margin-bottom: 10px;\n }\n\n .dialog-message[_ngcontent-%COMP%] {\n font-size: 13px;\n margin-bottom: 6px;\n }\n\n .dialog-help[_ngcontent-%COMP%] {\n font-size: 11px;\n margin-bottom: 16px;\n }\n\n .dialog-actions[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 10px;\n margin-top: 20px;\n }\n\n .dialog-btn[_ngcontent-%COMP%] {\n width: 100%;\n padding: 12px 20px;\n font-size: 15px;\n }\n}\n\n\n@media (max-width: 360px) {\n .dialog-container[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .dialog-icon[_ngcontent-%COMP%] {\n font-size: 32px;\n }\n\n .dialog-title[_ngcontent-%COMP%] {\n font-size: 16px;\n }\n}"] });
384
384
  }
385
385
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppAccessDialogComponent, [{
386
386
  type: Component,
@@ -1 +1 @@
1
- {"version":3,"file":"app-access-dialog.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/dialogs/app-access-dialog.component.ts","../../../../../src/lib/shell/components/dialogs/app-access-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnDestroy, ChangeDetectorRef, HostListener } from '@angular/core';\n\n/**\n * Type of app access issue\n */\nexport type AppAccessDialogType =\n | 'not_installed' // User can install the app\n | 'disabled' // User has disabled the app, can re-enable\n | 'no_access' // User doesn't have permission\n | 'not_found' // App doesn't exist\n | 'inactive' // App is inactive/disabled by admin\n | 'no_apps' // User has no apps configured at all\n | 'layout_error'; // Golden Layout failed to initialize\n\n/**\n * Configuration for the app access dialog\n */\nexport interface AppAccessDialogConfig {\n type: AppAccessDialogType;\n appName?: string;\n appId?: string;\n}\n\n/**\n * Result from the dialog\n */\nexport interface AppAccessDialogResult {\n action: 'install' | 'enable' | 'redirect' | 'dismissed';\n appId?: string;\n}\n\n/**\n * Dialog component for handling app access errors.\n * Shows appropriate messages and actions based on the type of access issue.\n * Features auto-dismiss with countdown timer for certain dialog types.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-access-dialog',\n templateUrl: './app-access-dialog.component.html',\n styleUrls: ['./app-access-dialog.component.css']\n})\nexport class AppAccessDialogComponent implements OnDestroy {\n @Input() visible = false;\n @Output() visibleChange = new EventEmitter<boolean>();\n @Output() result = new EventEmitter<AppAccessDialogResult>();\n\n config: AppAccessDialogConfig | null = null;\n isProcessing = false;\n\n // Auto-dismiss countdown\n private countdownInterval: ReturnType<typeof setInterval> | null = null;\n countdownSeconds = 0;\n private readonly AUTO_DISMISS_SECONDS = 5;\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n /**\n * Show the dialog with the specified configuration\n */\n show(config: AppAccessDialogConfig): void {\n this.config = config;\n this.visible = true;\n this.isProcessing = false;\n this.visibleChange.emit(true);\n\n // Start countdown for types that auto-dismiss\n if (this.shouldAutoDismiss()) {\n this.startCountdown();\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Hide the dialog\n */\n hide(): void {\n this.stopCountdown();\n this.visible = false;\n this.visibleChange.emit(false);\n this.cdr.detectChanges();\n }\n\n ngOnDestroy(): void {\n this.stopCountdown();\n }\n\n /**\n * Get the dialog title based on type\n */\n get title(): string {\n if (!this.config) return '';\n\n switch (this.config.type) {\n case 'not_installed':\n return 'Add Application?';\n case 'disabled':\n return 'Add Application?';\n case 'no_access':\n return 'Access Denied';\n case 'not_found':\n return 'Application Not Found';\n case 'inactive':\n return 'Application Unavailable';\n case 'no_apps':\n return 'No Applications Available';\n case 'layout_error':\n return 'Display Error';\n default:\n return 'Application Error';\n }\n }\n\n /**\n * Get the dialog icon based on type\n */\n get icon(): string {\n if (!this.config) return 'fa-circle-info';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return 'fa-circle-question';\n case 'no_access':\n return 'fa-lock';\n case 'not_found':\n return 'fa-circle-xmark';\n case 'inactive':\n return 'fa-circle-pause';\n case 'no_apps':\n return 'fa-folder-open';\n case 'layout_error':\n return 'fa-triangle-exclamation';\n default:\n return 'fa-circle-info';\n }\n }\n\n /**\n * Get the dialog icon color based on type\n */\n get iconColor(): string {\n if (!this.config) return 'var(--mj-text-secondary)';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return '#2196F3'; // Blue for actionable\n case 'no_access':\n case 'inactive':\n return '#FF9800'; // Orange for warning\n case 'not_found':\n case 'layout_error':\n return '#F44336'; // Red for error\n case 'no_apps':\n return '#9E9E9E'; // Gray for info\n default:\n return 'var(--mj-text-secondary)';\n }\n }\n\n /**\n * Get the main message based on type\n */\n get message(): string {\n if (!this.config) return '';\n\n const appName = this.config.appName || 'this application';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return `Would you like to add \"${appName}\" to your applications?`;\n case 'no_access':\n return `You don't have permission to access \"${appName}\".`;\n case 'not_found':\n return `The application \"${appName}\" doesn't exist in this system.`;\n case 'inactive':\n return `The application \"${appName}\" is currently inactive and unavailable.`;\n case 'no_apps':\n return `You don't have any applications configured. Your system administrator needs to set up your application access.`;\n case 'layout_error':\n return `There was an error displaying the application interface. The system will redirect you to an available application.`;\n default:\n return 'An error occurred while loading the application.';\n }\n }\n\n /**\n * Get the secondary/help message based on type\n */\n get helpMessage(): string {\n if (!this.config) return '';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return '';\n case 'no_access':\n case 'not_found':\n case 'inactive':\n return 'If you believe this is an error, please contact your system administrator.';\n case 'no_apps':\n return 'Please contact your system administrator to configure your application access.';\n case 'layout_error':\n return 'If this error persists, try clearing your browser cache or contact your system administrator.';\n default:\n return '';\n }\n }\n\n /**\n * Check if the primary action button should be shown\n */\n get showPrimaryAction(): boolean {\n if (!this.config) return false;\n return this.config.type === 'not_installed' || this.config.type === 'disabled';\n }\n\n /**\n * Get the primary action button text\n */\n get primaryActionText(): string {\n if (!this.config) return '';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return 'Add';\n default:\n return 'OK';\n }\n }\n\n /**\n * Get the secondary/dismiss button text with countdown if applicable\n * For actionable dialogs (install/enable), show \"Cancel\"\n * For non-actionable dialogs (errors), show \"OK\" with countdown\n */\n get dismissButtonText(): string {\n // For actionable dialogs, use \"Cancel\"\n if (this.showPrimaryAction) {\n return 'Cancel';\n }\n\n // For non-actionable dialogs, show countdown if active\n if (this.countdownSeconds > 0) {\n return `OK (${this.countdownSeconds})`;\n }\n return 'OK';\n }\n\n /**\n * Check if this dialog type should auto-dismiss\n */\n private shouldAutoDismiss(): boolean {\n if (!this.config) return false;\n return ['no_access', 'not_found', 'inactive', 'layout_error'].includes(this.config.type);\n }\n\n /**\n * Start the countdown timer for auto-dismiss\n */\n private startCountdown(): void {\n this.stopCountdown();\n this.countdownSeconds = this.AUTO_DISMISS_SECONDS;\n\n this.countdownInterval = setInterval(() => {\n this.countdownSeconds--;\n this.cdr.detectChanges();\n\n if (this.countdownSeconds <= 0) {\n this.onDismiss();\n }\n }, 1000);\n }\n\n /**\n * Stop the countdown timer\n */\n private stopCountdown(): void {\n if (this.countdownInterval) {\n clearInterval(this.countdownInterval);\n this.countdownInterval = null;\n }\n this.countdownSeconds = 0;\n }\n\n /**\n * Handle primary action (install/enable)\n */\n async onPrimaryAction(): Promise<void> {\n if (!this.config) return;\n\n this.isProcessing = true;\n this.cdr.detectChanges();\n\n const action = this.config.type === 'not_installed' ? 'install' : 'enable';\n\n this.result.emit({\n action,\n appId: this.config.appId\n });\n\n // Don't hide yet - let the parent component handle the result and close when ready\n }\n\n /**\n * Handle dismiss/redirect action\n */\n onDismiss(): void {\n this.stopCountdown();\n this.result.emit({ action: 'redirect' });\n this.hide();\n }\n\n /**\n * Mark processing as complete (called by parent after install/enable)\n */\n completeProcessing(): void {\n this.isProcessing = false;\n this.hide();\n }\n\n /**\n * Handle keyboard events for the dialog\n * Enter key triggers primary action (Install/Enable)\n * Escape key dismisses the dialog\n */\n @HostListener('document:keydown', ['$event'])\n handleKeyDown(event: KeyboardEvent): void {\n if (!this.visible || this.isProcessing) return;\n\n if (event.key === 'Enter') {\n // Enter triggers primary action if available\n if (this.showPrimaryAction) {\n event.preventDefault();\n event.stopPropagation();\n this.onPrimaryAction();\n }\n } else if (event.key === 'Escape') {\n // Escape dismisses the dialog\n event.preventDefault();\n event.stopPropagation();\n this.onDismiss();\n }\n }\n}\n","@if (visible) {\n <div class=\"dialog-overlay\" (click)=\"onDismiss()\">\n <div class=\"dialog-container\" (click)=\"$event.stopPropagation()\">\n <!-- Icon -->\n <div class=\"dialog-icon\" [style.color]=\"iconColor\">\n <i class=\"fa-solid\" [ngClass]=\"icon\"></i>\n </div>\n <!-- Title -->\n <h2 class=\"dialog-title\">{{ title }}</h2>\n <!-- Message -->\n <p class=\"dialog-message\">{{ message }}</p>\n <!-- Help message -->\n @if (helpMessage) {\n <p class=\"dialog-help\">{{ helpMessage }}</p>\n }\n <!-- Actions -->\n <div class=\"dialog-actions\">\n @if (showPrimaryAction) {\n <button\n class=\"dialog-btn primary\"\n [disabled]=\"isProcessing\"\n (click)=\"onPrimaryAction()\">\n @if (isProcessing) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!isProcessing) {\n <span>{{ primaryActionText }}</span>\n }\n @if (isProcessing) {\n <span>Processing...</span>\n }\n </button>\n }\n <button\n class=\"dialog-btn\"\n [class.secondary]=\"showPrimaryAction\"\n [disabled]=\"isProcessing\"\n (click)=\"onDismiss()\">\n {{ dismissButtonText }}\n </button>\n </div>\n </div>\n </div>\n}\n"]}
1
+ {"version":3,"file":"app-access-dialog.component.js","sourceRoot":"","sources":["../../../../../src/lib/shell/components/dialogs/app-access-dialog.component.ts","../../../../../src/lib/shell/components/dialogs/app-access-dialog.component.html"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnDestroy, ChangeDetectorRef, HostListener } from '@angular/core';\n\n/**\n * Type of app access issue\n */\nexport type AppAccessDialogType =\n | 'not_installed' // User can install the app\n | 'disabled' // User has disabled the app, can re-enable\n | 'no_access' // User doesn't have permission\n | 'not_found' // App doesn't exist\n | 'inactive' // App is inactive/disabled by admin\n | 'no_apps' // User has no apps configured at all\n | 'layout_error'; // Golden Layout failed to initialize\n\n/**\n * Configuration for the app access dialog\n */\nexport interface AppAccessDialogConfig {\n type: AppAccessDialogType;\n appName?: string;\n appId?: string;\n}\n\n/**\n * Result from the dialog\n */\nexport interface AppAccessDialogResult {\n action: 'install' | 'enable' | 'redirect' | 'dismissed';\n appId?: string;\n}\n\n/**\n * Dialog component for handling app access errors.\n * Shows appropriate messages and actions based on the type of access issue.\n * Features auto-dismiss with countdown timer for certain dialog types.\n */\n@Component({\n standalone: false,\n selector: 'mj-app-access-dialog',\n templateUrl: './app-access-dialog.component.html',\n styleUrls: ['./app-access-dialog.component.css']\n})\nexport class AppAccessDialogComponent implements OnDestroy {\n @Input() visible = false;\n @Output() visibleChange = new EventEmitter<boolean>();\n @Output() result = new EventEmitter<AppAccessDialogResult>();\n\n config: AppAccessDialogConfig | null = null;\n isProcessing = false;\n\n // Auto-dismiss countdown\n private countdownInterval: ReturnType<typeof setInterval> | null = null;\n countdownSeconds = 0;\n private readonly AUTO_DISMISS_SECONDS = 5;\n\n constructor(private cdr: ChangeDetectorRef) {}\n\n /**\n * Show the dialog with the specified configuration\n */\n show(config: AppAccessDialogConfig): void {\n this.config = config;\n this.visible = true;\n this.isProcessing = false;\n this.visibleChange.emit(true);\n\n // Start countdown for types that auto-dismiss\n if (this.shouldAutoDismiss()) {\n this.startCountdown();\n }\n\n this.cdr.detectChanges();\n }\n\n /**\n * Hide the dialog\n */\n hide(): void {\n this.stopCountdown();\n this.visible = false;\n this.visibleChange.emit(false);\n this.cdr.detectChanges();\n }\n\n ngOnDestroy(): void {\n this.stopCountdown();\n }\n\n /**\n * Get the dialog title based on type\n */\n get title(): string {\n if (!this.config) return '';\n\n switch (this.config.type) {\n case 'not_installed':\n return 'Add Application?';\n case 'disabled':\n return 'Add Application?';\n case 'no_access':\n return 'Access Denied';\n case 'not_found':\n return 'Application Not Found';\n case 'inactive':\n return 'Application Unavailable';\n case 'no_apps':\n return 'No Applications Available';\n case 'layout_error':\n return 'Display Error';\n default:\n return 'Application Error';\n }\n }\n\n /**\n * Get the dialog icon based on type\n */\n get icon(): string {\n if (!this.config) return 'fa-circle-info';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return 'fa-circle-question';\n case 'no_access':\n return 'fa-lock';\n case 'not_found':\n return 'fa-circle-xmark';\n case 'inactive':\n return 'fa-circle-pause';\n case 'no_apps':\n return 'fa-folder-open';\n case 'layout_error':\n return 'fa-triangle-exclamation';\n default:\n return 'fa-circle-info';\n }\n }\n\n /**\n * Get the dialog icon color based on type\n */\n get iconColor(): string {\n if (!this.config) return 'var(--mj-text-secondary)';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return '#2196F3'; // Blue for actionable\n case 'no_access':\n case 'inactive':\n return '#FF9800'; // Orange for warning\n case 'not_found':\n case 'layout_error':\n return '#F44336'; // Red for error\n case 'no_apps':\n return '#9E9E9E'; // Gray for info\n default:\n return 'var(--mj-text-secondary)';\n }\n }\n\n /**\n * Get the main message based on type\n */\n get message(): string {\n if (!this.config) return '';\n\n const appName = this.config.appName || 'this application';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return `Would you like to add \"${appName}\" to your applications?`;\n case 'no_access':\n return `You don't have permission to access \"${appName}\".`;\n case 'not_found':\n return `The application \"${appName}\" doesn't exist in this system.`;\n case 'inactive':\n return `The application \"${appName}\" is currently inactive and unavailable.`;\n case 'no_apps':\n return `You don't have any applications configured. Your system administrator needs to set up your application access.`;\n case 'layout_error':\n return `There was an error displaying the application interface. The system will redirect you to an available application.`;\n default:\n return 'An error occurred while loading the application.';\n }\n }\n\n /**\n * Get the secondary/help message based on type\n */\n get helpMessage(): string {\n if (!this.config) return '';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return '';\n case 'no_access':\n case 'not_found':\n case 'inactive':\n return 'If you believe this is an error, please contact your system administrator.';\n case 'no_apps':\n return 'Please contact your system administrator to configure your application access.';\n case 'layout_error':\n return 'If this error persists, try clearing your browser cache or contact your system administrator.';\n default:\n return '';\n }\n }\n\n /**\n * Check if the primary action button should be shown\n */\n get showPrimaryAction(): boolean {\n if (!this.config) return false;\n return this.config.type === 'not_installed' || this.config.type === 'disabled';\n }\n\n /**\n * Get the primary action button text\n */\n get primaryActionText(): string {\n if (!this.config) return '';\n\n switch (this.config.type) {\n case 'not_installed':\n case 'disabled':\n return 'Add';\n default:\n return 'OK';\n }\n }\n\n /**\n * Get the secondary/dismiss button text with countdown if applicable\n * For actionable dialogs (install/enable), show \"Cancel\"\n * For non-actionable dialogs (errors), show \"OK\" with countdown\n */\n get dismissButtonText(): string {\n // For actionable dialogs, use \"Cancel\"\n if (this.showPrimaryAction) {\n return 'Cancel';\n }\n\n // For non-actionable dialogs, show countdown if active\n if (this.countdownSeconds > 0) {\n return `OK (${this.countdownSeconds})`;\n }\n return 'OK';\n }\n\n /**\n * Check if this dialog type should auto-dismiss\n */\n private shouldAutoDismiss(): boolean {\n if (!this.config) return false;\n return ['no_access', 'not_found', 'inactive', 'layout_error'].includes(this.config.type);\n }\n\n /**\n * Start the countdown timer for auto-dismiss\n */\n private startCountdown(): void {\n this.stopCountdown();\n this.countdownSeconds = this.AUTO_DISMISS_SECONDS;\n\n this.countdownInterval = setInterval(() => {\n this.countdownSeconds--;\n this.cdr.detectChanges();\n\n if (this.countdownSeconds <= 0) {\n this.onDismiss();\n }\n }, 1000);\n }\n\n /**\n * Stop the countdown timer\n */\n private stopCountdown(): void {\n if (this.countdownInterval) {\n clearInterval(this.countdownInterval);\n this.countdownInterval = null;\n }\n this.countdownSeconds = 0;\n }\n\n /**\n * Handle primary action (install/enable)\n */\n async onPrimaryAction(): Promise<void> {\n if (!this.config) return;\n\n this.isProcessing = true;\n this.cdr.detectChanges();\n\n const action = this.config.type === 'not_installed' ? 'install' : 'enable';\n\n this.result.emit({\n action,\n appId: this.config.appId\n });\n\n // Don't hide yet - let the parent component handle the result and close when ready\n }\n\n /**\n * Handle dismiss/redirect action\n */\n onDismiss(): void {\n this.stopCountdown();\n this.result.emit({ action: 'redirect' });\n this.hide();\n }\n\n /**\n * Mark processing as complete (called by parent after install/enable)\n */\n completeProcessing(): void {\n this.isProcessing = false;\n this.hide();\n }\n\n /**\n * Handle keyboard events for the dialog\n * Enter key triggers primary action (Install/Enable)\n * Escape key dismisses the dialog\n */\n @HostListener('document:keydown', ['$event'])\n handleKeyDown(event: KeyboardEvent): void {\n if (!this.visible || this.isProcessing) return;\n\n if (event.key === 'Enter') {\n // Enter triggers primary action if available\n if (this.showPrimaryAction) {\n event.preventDefault();\n event.stopPropagation();\n this.onPrimaryAction();\n }\n } else if (event.key === 'Escape') {\n // Escape dismisses the dialog\n event.preventDefault();\n event.stopPropagation();\n this.onDismiss();\n }\n }\n}\n","@if (visible) {\n <div class=\"dialog-overlay\" (click)=\"onDismiss()\">\n <div class=\"dialog-container\" (click)=\"$event.stopPropagation()\">\n <!-- Icon -->\n <div class=\"dialog-icon\" [style.color]=\"iconColor\">\n <i class=\"fa-solid\" [ngClass]=\"icon\"></i>\n </div>\n <!-- Title -->\n <h2 class=\"dialog-title\">{{ title }}</h2>\n <!-- Message -->\n <p class=\"dialog-message\">{{ message }}</p>\n <!-- Help message -->\n @if (helpMessage) {\n <p class=\"dialog-help\">{{ helpMessage }}</p>\n }\n <!-- Actions -->\n <div class=\"dialog-actions\">\n @if (showPrimaryAction) {\n <button\n class=\"dialog-btn primary\"\n [disabled]=\"isProcessing\"\n (click)=\"onPrimaryAction()\">\n @if (isProcessing) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n @if (!isProcessing) {\n <span>{{ primaryActionText }}</span>\n }\n @if (isProcessing) {\n <span>Processing...</span>\n }\n </button>\n }\n <button\n class=\"dialog-btn\"\n [class.secondary]=\"showPrimaryAction\"\n [disabled]=\"isProcessing\"\n (click)=\"onDismiss()\">\n {{ dismissButtonText }}\n </button>\n </div>\n </div>\n </div>\n}\n"]}
@@ -629,7 +629,7 @@ export class AppNavComponent {
629
629
  i0.ɵɵconditional(ctx.OverflowItems.length > 0 ? 5 : -1);
630
630
  i0.ɵɵadvance(4);
631
631
  i0.ɵɵconditional(ctx.MoreOpen && ctx.OverflowItems.length > 0 ? 9 : -1);
632
- } }, dependencies: [i3.MJClickableDirective], styles: ["[_nghost-%COMP%] {\n display: block;\n min-width: 0;\n position: relative;\n}\n\n\n\n\n.nav-list[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: nowrap;\n gap: var(--mj-space-1);\n overflow: hidden;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-trailing[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n\n\n.nav-list.tight[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.nav-more-btn) {\n flex-shrink: 1;\n min-width: 0;\n}\n.nav-list.tight[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.nav-more-btn) > span[_ngcontent-%COMP%]:first-of-type {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-4);\n border-radius: var(--mj-radius-md);\n text-decoration: none;\n color: var(--mj-text-secondary);\n font-weight: var(--mj-font-medium);\n font-size: var(--mj-text-sm);\n white-space: nowrap;\n transition: all var(--mj-transition-fast);\n position: relative;\n cursor: pointer;\n user-select: none;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n\n\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: var(--mj-text-base);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.no-icon[_ngcontent-%COMP%] {\n padding-left: var(--mj-space-3);\n padding-right: var(--mj-space-3);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border-radius: var(--mj-radius-full);\n padding: var(--mj-space-0-5) var(--mj-space-1-5);\n font-size: 11px;\n font-weight: var(--mj-font-semibold);\n min-width: 18px;\n text-align: center;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.dynamic) + .nav-item.dynamic[_ngcontent-%COMP%] {\n margin-left: var(--mj-space-1);\n padding-left: var(--mj-space-4);\n border-left: 1px solid var(--mj-border-default);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active) {\n opacity: 0.7;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active):hover {\n opacity: 1;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n transition: all var(--mj-transition-fast);\n margin-left: calc(-1 * var(--mj-space-0-5));\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:hover .dismiss-btn[_ngcontent-%COMP%] {\n display: flex;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n}\n\n\n\n.nav-more-btn[_ngcontent-%COMP%] .more-count[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n border-radius: var(--mj-radius-full);\n padding: var(--mj-space-0-5) var(--mj-space-1-5);\n font-size: 11px;\n font-weight: var(--mj-font-semibold);\n min-width: 18px;\n text-align: center;\n line-height: var(--mj-leading-none);\n}\n.nav-more-btn.active[_ngcontent-%COMP%] .more-count[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n color: var(--mj-brand-primary);\n}\n.nav-more-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: calc(100% + var(--mj-space-2));\n left: 0; \n\n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-lg);\n min-width: 200px;\n width: max-content;\n max-height: calc(100vh - 80px);\n overflow-y: auto;\n padding: var(--mj-space-1) 0;\n z-index: 10000;\n}\n.nav-more-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2-5);\n padding: var(--mj-space-2-5) var(--mj-space-4);\n cursor: pointer;\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-secondary);\n white-space: nowrap;\n transition: all var(--mj-transition-fast);\n}\n.nav-more-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 18px;\n text-align: center;\n font-size: var(--mj-text-base);\n}\n.nav-more-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-more-item[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n}\n.nav-more-item.active[_ngcontent-%COMP%] {\n \n\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n.nav-more-item[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border-radius: var(--mj-radius-full);\n padding: var(--mj-space-0-5) var(--mj-space-1-5);\n font-size: 11px;\n font-weight: var(--mj-font-semibold);\n min-width: 18px;\n text-align: center;\n}\n.nav-more-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n}\n.nav-more-item.dynamic[_ngcontent-%COMP%]:hover .dismiss-btn[_ngcontent-%COMP%] {\n display: flex;\n}\n\n\n\n.nav-skeleton[_ngcontent-%COMP%] {\n display: flex;\n gap: var(--mj-space-1);\n}\n.nav-skeleton-pill[_ngcontent-%COMP%] {\n height: 34px;\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface-sunken);\n animation: _ngcontent-%COMP%_nav-skeleton-pulse 1.2s ease-in-out infinite;\n}\n@keyframes _ngcontent-%COMP%_nav-skeleton-pulse {\n 0%, 100% { opacity: 0.55; }\n 50% { opacity: 1; }\n}\n@media (prefers-reduced-motion: reduce) {\n .nav-skeleton-pill[_ngcontent-%COMP%] {\n animation: none;\n opacity: 0.7;\n }\n}\n\n\n\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: var(--mj-space-0-5);\n padding: 0 var(--mj-space-3);\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n padding: var(--mj-space-3) var(--mj-space-4);\n border-radius: var(--mj-radius-md);\n min-height: 48px; \n\n box-sizing: border-box;\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n}"], changeDetection: 0 });
632
+ } }, dependencies: [i3.MJClickableDirective], styles: ["[_nghost-%COMP%] {\n display: block;\n min-width: 0;\n position: relative;\n}\n\n\n\n.nav-list[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: nowrap;\n gap: var(--mj-space-1);\n overflow: hidden;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n\n\n.nav-list[_ngcontent-%COMP%] .nav-trailing[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n flex: 0 0 auto;\n}\n\n\n.nav-list.tight[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.nav-more-btn) {\n flex-shrink: 1;\n min-width: 0;\n}\n.nav-list.tight[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.nav-more-btn) > span[_ngcontent-%COMP%]:first-of-type {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2);\n padding: var(--mj-space-2) var(--mj-space-4);\n border-radius: var(--mj-radius-md);\n text-decoration: none;\n color: var(--mj-text-secondary);\n font-weight: var(--mj-font-medium);\n font-size: var(--mj-text-sm);\n white-space: nowrap;\n transition: all var(--mj-transition-fast);\n position: relative;\n cursor: pointer;\n user-select: none;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: 2px;\n}\n\n\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.active[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: var(--mj-text-base);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.no-icon[_ngcontent-%COMP%] {\n padding-left: var(--mj-space-3);\n padding-right: var(--mj-space-3);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border-radius: var(--mj-radius-full);\n padding: var(--mj-space-0-5) var(--mj-space-1-5);\n font-size: 11px;\n font-weight: var(--mj-font-semibold);\n min-width: 18px;\n text-align: center;\n}\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%]:not(.dynamic) + .nav-item.dynamic[_ngcontent-%COMP%] {\n margin-left: var(--mj-space-1);\n padding-left: var(--mj-space-4);\n border-left: 1px solid var(--mj-border-default);\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active) {\n opacity: 0.7;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:not(.active):hover {\n opacity: 1;\n}\n\n\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n transition: all var(--mj-transition-fast);\n margin-left: calc(-1 * var(--mj-space-0-5));\n}\n.nav-list[_ngcontent-%COMP%] .nav-item.dynamic[_ngcontent-%COMP%]:hover .dismiss-btn[_ngcontent-%COMP%] {\n display: flex;\n}\n.nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n}\n\n\n.nav-more-btn[_ngcontent-%COMP%] .more-count[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-active);\n color: var(--mj-text-secondary);\n border-radius: var(--mj-radius-full);\n padding: var(--mj-space-0-5) var(--mj-space-1-5);\n font-size: 11px;\n font-weight: var(--mj-font-semibold);\n min-width: 18px;\n text-align: center;\n line-height: var(--mj-leading-none);\n}\n.nav-more-btn.active[_ngcontent-%COMP%] .more-count[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, transparent);\n color: var(--mj-brand-primary);\n}\n.nav-more-dropdown[_ngcontent-%COMP%] {\n position: absolute;\n top: calc(100% + var(--mj-space-2));\n left: 0; \n background: var(--mj-bg-surface-elevated);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n box-shadow: var(--mj-shadow-lg);\n min-width: 200px;\n width: max-content;\n max-height: calc(100vh - 80px);\n overflow-y: auto;\n padding: var(--mj-space-1) 0;\n z-index: 10000;\n}\n.nav-more-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-2-5);\n padding: var(--mj-space-2-5) var(--mj-space-4);\n cursor: pointer;\n font-size: var(--mj-text-sm);\n font-weight: var(--mj-font-medium);\n color: var(--mj-text-secondary);\n white-space: nowrap;\n transition: all var(--mj-transition-fast);\n}\n.nav-more-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 18px;\n text-align: center;\n font-size: var(--mj-text-base);\n}\n.nav-more-item[_ngcontent-%COMP%]:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n.nav-more-item[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-border-focus);\n outline-offset: -2px;\n}\n.nav-more-item.active[_ngcontent-%COMP%] {\n \n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n color: var(--mj-brand-primary);\n}\n.nav-more-item[_ngcontent-%COMP%] .badge[_ngcontent-%COMP%] {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border-radius: var(--mj-radius-full);\n padding: var(--mj-space-0-5) var(--mj-space-1-5);\n font-size: 11px;\n font-weight: var(--mj-font-semibold);\n min-width: 18px;\n text-align: center;\n}\n.nav-more-item[_ngcontent-%COMP%] .dismiss-btn[_ngcontent-%COMP%] {\n display: none;\n align-items: center;\n justify-content: center;\n width: 16px;\n height: 16px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--mj-text-disabled);\n font-size: 10px;\n cursor: pointer;\n}\n.nav-more-item.dynamic[_ngcontent-%COMP%]:hover .dismiss-btn[_ngcontent-%COMP%] {\n display: flex;\n}\n\n\n.nav-skeleton[_ngcontent-%COMP%] {\n display: flex;\n gap: var(--mj-space-1);\n}\n.nav-skeleton-pill[_ngcontent-%COMP%] {\n height: 34px;\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface-sunken);\n animation: _ngcontent-%COMP%_nav-skeleton-pulse 1.2s ease-in-out infinite;\n}\n@keyframes _ngcontent-%COMP%_nav-skeleton-pulse {\n 0%, 100% { opacity: 0.55; }\n 50% { opacity: 1; }\n}\n@media (prefers-reduced-motion: reduce) {\n .nav-skeleton-pill[_ngcontent-%COMP%] {\n animation: none;\n opacity: 0.7;\n }\n}\n\n\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: var(--mj-space-0-5);\n padding: 0 var(--mj-space-3);\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] {\n padding: var(--mj-space-3) var(--mj-space-4);\n border-radius: var(--mj-radius-md);\n min-height: 48px; \n box-sizing: border-box;\n}\n.mobile-nav-content[_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%], .mobile-nav-content [_nghost-%COMP%] .nav-list[_ngcontent-%COMP%] .nav-item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n width: 20px;\n text-align: center;\n}"], changeDetection: 0 });
633
633
  }
634
634
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AppNavComponent, [{
635
635
  type: Component,