cloud-ide-layout 1.0.124 → 1.0.125

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 (16) hide show
  1. package/fesm2022/{cloud-ide-layout-cloud-ide-layout-DJxJ2FIt.mjs → cloud-ide-layout-cloud-ide-layout-DdMf1j7_.mjs} +980 -939
  2. package/fesm2022/cloud-ide-layout-cloud-ide-layout-DdMf1j7_.mjs.map +1 -0
  3. package/fesm2022/{cloud-ide-layout-dashboard-manager.component-C-kubr7Z.mjs → cloud-ide-layout-dashboard-manager.component-C8rnVZpP.mjs} +2 -2
  4. package/fesm2022/{cloud-ide-layout-dashboard-manager.component-C-kubr7Z.mjs.map → cloud-ide-layout-dashboard-manager.component-C8rnVZpP.mjs.map} +1 -1
  5. package/fesm2022/{cloud-ide-layout-drawer-theme.component-J2GTGGFO.mjs → cloud-ide-layout-drawer-theme.component-DV0QsDsO.mjs} +2 -2
  6. package/fesm2022/{cloud-ide-layout-drawer-theme.component-J2GTGGFO.mjs.map → cloud-ide-layout-drawer-theme.component-DV0QsDsO.mjs.map} +1 -1
  7. package/fesm2022/{cloud-ide-layout-floating-entity-selection.component-CGoIPT5y.mjs → cloud-ide-layout-floating-entity-selection.component-DteJtUqk.mjs} +2 -2
  8. package/fesm2022/{cloud-ide-layout-floating-entity-selection.component-CGoIPT5y.mjs.map → cloud-ide-layout-floating-entity-selection.component-DteJtUqk.mjs.map} +1 -1
  9. package/fesm2022/{cloud-ide-layout-home-wrapper.component-DaonafYv.mjs → cloud-ide-layout-home-wrapper.component-DjRHVU59.mjs} +2 -2
  10. package/fesm2022/{cloud-ide-layout-home-wrapper.component-DaonafYv.mjs.map → cloud-ide-layout-home-wrapper.component-DjRHVU59.mjs.map} +1 -1
  11. package/fesm2022/{cloud-ide-layout-sidedrawer-notes.component-DMzp8tJc.mjs → cloud-ide-layout-sidedrawer-notes.component-C2tOsEC4.mjs} +2 -2
  12. package/fesm2022/{cloud-ide-layout-sidedrawer-notes.component-DMzp8tJc.mjs.map → cloud-ide-layout-sidedrawer-notes.component-C2tOsEC4.mjs.map} +1 -1
  13. package/fesm2022/cloud-ide-layout.mjs +1 -1
  14. package/index.d.ts +12 -2
  15. package/package.json +1 -1
  16. package/fesm2022/cloud-ide-layout-cloud-ide-layout-DJxJ2FIt.mjs.map +0 -1
@@ -1,14 +1,13 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, inject, signal, ComponentRef, ViewContainerRef, computed, effect, DestroyRef, Component, ViewChild, ElementRef, HostListener, ChangeDetectionStrategy, ViewChildren, viewChild, input, InjectionToken, PLATFORM_ID, output } from '@angular/core';
2
+ import { Injectable, inject, signal, computed, effect, DestroyRef, Component, ViewChild, ElementRef, HostListener, ChangeDetectionStrategy, ComponentRef, ViewContainerRef, ViewChildren, viewChild, input, InjectionToken, PLATFORM_ID, output } from '@angular/core';
3
3
  import { HttpClient } from '@angular/common/http';
4
4
  import { cidePath, hostManagerRoutesUrl, coreRoutesUrl, commonRoutesUrl, designConfigRoutesUrl, generateStringFromObject } from 'cloud-ide-lms-model';
5
- import { Observable, BehaviorSubject, throwError, of, interval, take as take$1, firstValueFrom } from 'rxjs';
6
- import { map, distinctUntilChanged, filter, tap, catchError, shareReplay, take } from 'rxjs/operators';
5
+ import { Observable, throwError, of, BehaviorSubject, interval, take as take$1, firstValueFrom } from 'rxjs';
6
+ import { map, filter, tap, catchError, shareReplay, take, distinctUntilChanged } from 'rxjs/operators';
7
7
  import * as i2 from '@angular/router';
8
- import { Router, RouteReuseStrategy, NavigationEnd, RouterModule, ActivatedRoute } from '@angular/router';
8
+ import { Router, NavigationEnd, RouteReuseStrategy, RouterModule, ActivatedRoute } from '@angular/router';
9
9
  import { Title, DomSanitizer } from '@angular/platform-browser';
10
- import { CideEleFloatingContainerService, CideEleFileManagerService, CideElementsService, NotificationService, CideIconComponent, CideEleButtonComponent, CideInputComponent, CideSelectComponent, CideThemeService, WebSocketNotificationService, NotificationApiService, CideEleDropdownComponent, CideEleFileImageDirective, CideEleResizerDirective, TooltipDirective, CideSpinnerComponent, CideEleSkeletonLoaderComponent, KeyboardShortcutService, FloatingContainerShortcutsService, CideEleFloatingContainerManagerComponent, CideEleGlobalNotificationsComponent, CideEleBreadcrumbComponent } from 'cloud-ide-element';
11
- import { merge } from 'lodash';
10
+ import { CideEleFileManagerService, CideElementsService, CideEleFloatingContainerService, NotificationService, CideIconComponent, CideEleButtonComponent, CideInputComponent, CideSelectComponent, CideThemeService, WebSocketNotificationService, NotificationApiService, CideEleDropdownComponent, CideEleFileImageDirective, CideEleResizerDirective, TooltipDirective, CideSpinnerComponent, CideEleSkeletonLoaderComponent, KeyboardShortcutService, FloatingContainerShortcutsService, CideEleFloatingContainerManagerComponent, CideEleGlobalNotificationsComponent, CideEleBreadcrumbComponent } from 'cloud-ide-element';
12
11
  import * as i1$1 from '@angular/common';
13
12
  import { CommonModule, NgClass, NgFor, NgIf, isPlatformBrowser } from '@angular/common';
14
13
  import { FINANCIAL_YEAR_SERVICE_TOKEN, ACADEMIC_YEAR_SERVICE_TOKEN, AUTH_SERVICE_TOKEN, authGuard, ENTITY_SERVICE_TOKEN } from 'cloud-ide-shared';
@@ -16,6 +15,7 @@ import * as i1 from '@angular/forms';
16
15
  import { FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
17
16
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
18
17
  import { trigger, state, transition, style, animate } from '@angular/animations';
18
+ import { merge } from 'lodash';
19
19
 
20
20
  class CloudIdeLayoutService {
21
21
  constructor() { }
@@ -247,1035 +247,464 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
247
247
  }]
248
248
  }] });
249
249
 
250
- /**
251
- * A custom route reuse strategy that allows storing and retrieving routes
252
- * to persist component state, especially for tabbed navigation.
253
- */
254
- class CustomRouteReuseStrategy {
255
- storedRoutes = {};
256
- // Generates a unique key for a route.
257
- // For routes marked with 'reuseTab', this key includes sorted query parameters
258
- // to ensure distinct states for tabs differing by query params.
259
- getPathKey(route) {
260
- let path = route.pathFromRoot
261
- .map(r => r.url.map(segment => segment.path).join('/'))
262
- .filter(p => p.length > 0)
263
- .join('/');
264
- // If the route is marked for tab reuse and has query parameters,
265
- // append them to the key in a sorted, consistent manner.
266
- if (route.data && route.data['reuseTab'] && route.queryParamMap.keys.length > 0) {
267
- const queryParams = route.queryParamMap.keys
268
- .sort() // Ensure consistent order of query parameters
269
- .map(key => `${key}=${route.queryParamMap.get(key)}`)
270
- .join('&');
271
- path += '?' + queryParams;
272
- }
273
- return path;
274
- }
275
- // Determines if this route (and its subtree) should be detached to be reused later.
276
- shouldDetach(route) {
277
- return !!(route.data && route.data['reuseTab']);
278
- }
279
- // Stores the detached route.
280
- store(route, handle) {
281
- const pathKey = this.getPathKey(route);
282
- if (handle && route.data && route.data['reuseTab'] && pathKey) {
283
- this.storedRoutes[pathKey] = handle;
284
- }
285
- }
286
- // Determines if this route (and its subtree) should be reattached.
287
- shouldAttach(route) {
288
- const pathKey = this.getPathKey(route);
289
- return !!pathKey && !!this.storedRoutes[pathKey];
290
- }
291
- // Retrieves the previously stored route.
292
- retrieve(route) {
293
- const pathKey = this.getPathKey(route);
294
- if (!pathKey || !this.storedRoutes[pathKey]) {
250
+ class AppStateService {
251
+ // Inject services
252
+ fileManagerService = inject(CideEleFileManagerService);
253
+ // Private signal for current user
254
+ currentUserSignal = signal(null, ...(ngDevMode ? [{ debugName: "currentUserSignal" }] : []));
255
+ // Private signal for active module
256
+ activeModuleSignal = signal(null, ...(ngDevMode ? [{ debugName: "activeModuleSignal" }] : []));
257
+ // Private signal for active entity
258
+ activeEntitySignal = signal(null, ...(ngDevMode ? [{ debugName: "activeEntitySignal" }] : []));
259
+ // Track if entity change handler is initialized (to prevent triggering on initial load)
260
+ entityChangeHandlerInitialized = false;
261
+ // Callback registration for entity change handlers (to avoid circular dependencies)
262
+ entityChangeCallbacks = [];
263
+ // Public computed signal for current user, for other services to get the user value
264
+ currentUser = computed(() => this.currentUserSignal(), ...(ngDevMode ? [{ debugName: "currentUser" }] : []));
265
+ // Public computed signal for active module
266
+ activeModule = computed(() => this.activeModuleSignal(), ...(ngDevMode ? [{ debugName: "activeModule" }] : []));
267
+ // Public computed signal for active entity
268
+ activeEntity = computed(() => this.activeEntitySignal(), ...(ngDevMode ? [{ debugName: "activeEntity" }] : []));
269
+ // Computed signals for derived state
270
+ isAuthenticated = computed(() => !!this.currentUserSignal(), ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : []));
271
+ userInfo = computed(() => {
272
+ const user = this.currentUserSignal();
273
+ if (!user)
295
274
  return null;
296
- }
297
- const handle = this.storedRoutes[pathKey];
298
- // Verify the handle is still valid (not destroyed)
299
- // If the component was destroyed, remove it from stored routes
300
- if (handle && typeof handle === 'object') {
301
- const handleAny = handle;
302
- // Check if ComponentRef is destroyed
303
- if (handleAny.destroyed === true || (handleAny.hostView && handleAny.hostView.destroyed)) {
304
- console.log(`⚠️ Stored route handle for ${pathKey} is already destroyed, removing from cache`);
305
- delete this.storedRoutes[pathKey];
306
- return null;
275
+ return {
276
+ id: user._id,
277
+ name: user.user_fullname || `${user.user_firstname} ${user.user_lastname}`,
278
+ email: user.user_emailid,
279
+ role: "" // user.user_id_role
280
+ };
281
+ }, ...(ngDevMode ? [{ debugName: "userInfo" }] : []));
282
+ // Computed signal for active module info
283
+ activeModuleInfo = computed(() => {
284
+ const module = this.activeModuleSignal();
285
+ if (!module)
286
+ return null;
287
+ return {
288
+ id: module._id,
289
+ title: module.syme_title,
290
+ icon: module.syme_icon,
291
+ path: module.syme_path,
292
+ type: module.syme_type
293
+ };
294
+ }, ...(ngDevMode ? [{ debugName: "activeModuleInfo" }] : []));
295
+ constructor() {
296
+ // Initialize file manager base URL on app startup
297
+ this.fileManagerService.setBaseUrl(cidePath.join([hostManagerRoutesUrl.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager]));
298
+ this.fileManagerService.setObjectIdEndpoint(cidePath.join([hostManagerRoutesUrl.cideSuiteHost, commonRoutesUrl?.module, commonRoutesUrl?.generateObjectId]));
299
+ // Load initial state from localStorage
300
+ this.loadFromLocalStorage();
301
+ // Save to localStorage whenever state changes
302
+ effect(() => {
303
+ const currentUser = this.currentUserSignal();
304
+ const activeModule = this.activeModuleSignal();
305
+ const activeEntity = this.activeEntitySignal();
306
+ this.saveToLocalStorage({
307
+ user: currentUser,
308
+ activeModule: activeModule,
309
+ activeEntity: activeEntity
310
+ });
311
+ });
312
+ // Automatically update file manager user ID whenever user changes
313
+ effect(() => {
314
+ const currentUser = this.currentUserSignal();
315
+ console.log('🔍 [AppStateService] User changed, updating file manager user ID:', currentUser);
316
+ if (currentUser?._id) {
317
+ this.fileManagerService.setUserId(currentUser._id);
307
318
  }
308
- }
309
- return handle;
310
- }
311
- // Determines if a route should be reused.
312
- shouldReuseRoute(future, curr) {
313
- return future.routeConfig === curr.routeConfig;
314
- }
315
- // Clears a stored route, typically when a tab is closed.
316
- // This method now properly destroys the component instance to prevent memory leaks
317
- // and ensure old data doesn't persist when reopening the same route.
318
- clearStoredRoute(pathKey) {
319
- const handle = this.storedRoutes[pathKey];
320
- if (handle) {
321
- // Properly destroy the component instance to prevent memory leaks
322
- // and ensure clean state when the route is reopened
323
- try {
324
- // In Angular, DetachedRouteHandle is typically a ComponentRef
325
- // We need to destroy it to remove the component from DOM and clean up resources
326
- // Method 1: Check if handle is a ComponentRef directly
327
- if (handle && typeof handle.destroy === 'function') {
328
- const componentRef = handle;
329
- // Verify it has hostView (ComponentRef signature)
330
- if (componentRef.hostView || componentRef.location) {
331
- console.log(`🗑️ Destroying component instance (ComponentRef) for route: ${pathKey}`);
332
- componentRef.destroy();
333
- }
334
- }
335
- // Method 2: Check if handle is a ViewContainerRef
336
- if (handle && typeof handle.clear === 'function' && typeof handle.length === 'number') {
337
- const viewContainerRef = handle;
338
- console.log(`🗑️ Clearing view container for route: ${pathKey}`);
339
- viewContainerRef.clear();
340
- }
341
- // Method 3: Check if handle is an object with nested ComponentRef
342
- if (handle && typeof handle === 'object' && !(handle instanceof ComponentRef) && !(handle instanceof ViewContainerRef)) {
343
- const handleObj = handle;
344
- // Try to find and destroy ComponentRef from various possible properties
345
- let componentRef = null;
346
- // Check common property names
347
- if (handleObj.componentRef && typeof handleObj.componentRef.destroy === 'function') {
348
- componentRef = handleObj.componentRef;
349
- }
350
- else if (handleObj.component && typeof handleObj.component.destroy === 'function') {
351
- componentRef = handleObj.component;
352
- }
353
- else if (handleObj.context && typeof handleObj.context.destroy === 'function') {
354
- componentRef = handleObj.context;
355
- }
356
- else if (handleObj._componentRef && typeof handleObj._componentRef.destroy === 'function') {
357
- componentRef = handleObj._componentRef;
319
+ else {
320
+ this.fileManagerService.setUserId('');
321
+ }
322
+ });
323
+ // GLOBAL ENTITY CHANGE HANDLER: When entity changes, close all tabs and navigate to home
324
+ // This ensures all components are destroyed and data is refreshed for the new entity
325
+ // No need to inject this service in any component - it works globally
326
+ let previousEntityId = null;
327
+ effect(() => {
328
+ const currentEntity = this.activeEntitySignal();
329
+ const currentEntityId = currentEntity?._id || null;
330
+ // Mark handler as initialized after first run (to skip initial load from localStorage)
331
+ if (!this.entityChangeHandlerInitialized) {
332
+ previousEntityId = currentEntityId;
333
+ this.entityChangeHandlerInitialized = true;
334
+ console.log('🏢 [AppStateService] Entity change handler initialized with initial entity:', currentEntityId);
335
+ return; // Skip on initial load
336
+ }
337
+ // Only trigger if entity actually changed (not on initial load)
338
+ if (previousEntityId !== null && currentEntityId !== previousEntityId && currentEntityId !== null) {
339
+ console.log('🏢 [AppStateService] Entity changed - notifying registered handlers', {
340
+ previousEntityId,
341
+ newEntityId: currentEntityId,
342
+ newEntityName: currentEntity?.syen_name
343
+ });
344
+ // Notify all registered entity change callbacks (e.g., close tabs, refresh components)
345
+ this.entityChangeCallbacks.forEach(callback => {
346
+ try {
347
+ callback(currentEntity);
358
348
  }
359
- if (componentRef) {
360
- console.log(`🗑️ Destroying component instance from handle object for route: ${pathKey}`);
361
- componentRef.destroy();
349
+ catch (error) {
350
+ console.error('Error in entity change callback:', error);
362
351
  }
363
- // Also try to clear view container if it exists
364
- if (handleObj.viewContainerRef && typeof handleObj.viewContainerRef.clear === 'function') {
365
- console.log(`🗑️ Clearing view container from handle object for route: ${pathKey}`);
366
- handleObj.viewContainerRef.clear();
352
+ });
353
+ }
354
+ else if (previousEntityId !== null && currentEntityId === null) {
355
+ // Entity was cleared - also notify callbacks
356
+ console.log('🏢 [AppStateService] Entity cleared - notifying registered handlers');
357
+ this.entityChangeCallbacks.forEach(callback => {
358
+ try {
359
+ callback(null);
367
360
  }
368
- // Try to destroy any nested components
369
- if (handleObj.components && Array.isArray(handleObj.components)) {
370
- handleObj.components.forEach((comp, index) => {
371
- if (comp && typeof comp.destroy === 'function') {
372
- console.log(`🗑️ Destroying nested component ${index} for route: ${pathKey}`);
373
- comp.destroy();
374
- }
375
- });
361
+ catch (error) {
362
+ console.error('Error in entity change callback:', error);
376
363
  }
377
- }
378
- }
379
- catch (error) {
380
- console.warn(`⚠️ Error destroying route handle for ${pathKey}:`, error);
364
+ });
381
365
  }
382
- // Remove from stored routes - this ensures shouldAttach will return false for this route
383
- delete this.storedRoutes[pathKey];
384
- console.log(`✅ Cleared stored route: ${pathKey} (removed from storedRoutes map)`);
385
- }
386
- else {
387
- console.log(`ℹ️ No stored route found for: ${pathKey}`);
388
- }
389
- }
390
- // Clears all stored routes (useful for cleanup)
391
- clearAllStoredRoutes() {
392
- Object.keys(this.storedRoutes).forEach(pathKey => {
393
- this.clearStoredRoute(pathKey);
366
+ // Update previous entity ID for next comparison
367
+ previousEntityId = currentEntityId;
394
368
  });
369
+ // Listen for localStorage changes from other tabs/windows
370
+ this.setupStorageListener();
395
371
  }
396
- // Gets the count of stored routes (useful for debugging)
397
- getStoredRoutesCount() {
398
- return Object.keys(this.storedRoutes).length;
372
+ /**
373
+ * Set the current user
374
+ */
375
+ setUser(user) {
376
+ this.currentUserSignal.set(user);
377
+ // Note: File manager user ID is automatically updated via effect() in constructor
399
378
  }
400
- }
401
-
402
- /**
403
- * Creates the default state for a new tab.
404
- */
405
- const createDefaultComponentState = () => ({
406
- sideDrawer: { isOpen: false, activeComponent: null, componentState: {} },
407
- footer: { someData: '' },
408
- console: { history: [] },
409
- });
410
- class TabStateService {
411
- // Holds the state for all open tabs, keyed by tab ID.
412
- _tabsState = new BehaviorSubject(new Map());
413
- // Holds the ID of the currently active tab.
414
- _activeTabId = new BehaviorSubject(null);
415
- /** An observable of all open tabs. */
416
- tabs$ = this._tabsState.pipe(map(tabsMap => Array.from(tabsMap.values())));
417
- /** An observable of the currently active tab's ID. */
418
- activeTabId$ = this._activeTabId.asObservable().pipe(distinctUntilChanged());
419
- /** An observable of the state of the currently active tab. */
420
- activeTabState$ = this.activeTabId$.pipe(map(activeId => (activeId ? this._tabsState.getValue().get(activeId) ?? null : null)),
421
- // Use a deep-ish comparison to prevent emissions when only the object reference changes
422
- distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)));
423
- /** An observable of just the component-specific state for the active tab. */
424
- activeComponentState$ = this.activeTabState$.pipe(map(activeTab => activeTab?.componentState ?? null));
425
379
  /**
426
- * Adds a new tab to the state.
427
- * @param title The title for the new tab.
428
- * @param id An optional unique ID. If not provided, one will be generated.
429
- * @returns The ID of the newly created tab.
380
+ * Update user data (partial update)
430
381
  */
431
- addTab(title, id) {
432
- const newTabId = id || `tab_${Date.now()}_${Math.random()}`;
433
- const newTab = {
434
- id: newTabId,
435
- title,
436
- componentState: createDefaultComponentState(),
437
- };
438
- const currentTabs = this._tabsState.getValue();
439
- const newTabs = new Map(currentTabs).set(newTabId, newTab);
440
- this._tabsState.next(newTabs);
441
- this.setActiveTab(newTabId);
442
- return newTabId;
382
+ updateUser(updates) {
383
+ const currentUser = this.currentUserSignal();
384
+ if (currentUser) {
385
+ this.currentUserSignal.set({ ...currentUser, ...updates });
386
+ }
443
387
  }
444
388
  /**
445
- * Removes a tab from the state.
446
- * @param tabIdToRemove The ID of the tab to remove.
389
+ * Clear user data
447
390
  */
448
- removeTab(tabIdToRemove) {
449
- const currentTabs = this._tabsState.getValue();
450
- if (!currentTabs.has(tabIdToRemove)) {
451
- return;
452
- }
453
- const newTabs = new Map(currentTabs);
454
- newTabs.delete(tabIdToRemove);
455
- this._tabsState.next(newTabs);
391
+ clearUser() {
392
+ this.currentUserSignal.set(null);
456
393
  }
457
- constructor() {
458
- // You can initialize with default tabs if needed
459
- // For example:
460
- // this.addTab('Welcome Tab');
394
+ /**
395
+ * Get current user value (non-reactive)
396
+ */
397
+ getUserValue() {
398
+ return this.currentUserSignal();
461
399
  }
462
400
  /**
463
- * Sets the currently active tab.
464
- * @param tabId The unique identifier of the tab to activate.
401
+ * Set the active module
465
402
  */
466
- setActiveTab(tabId) {
467
- if (this._activeTabId.getValue() !== tabId) {
468
- this._activeTabId.next(tabId);
469
- }
403
+ setActiveModule(module) {
404
+ this.activeModuleSignal.set(module);
470
405
  }
471
406
  /**
472
- * Updates the state for the currently active tab.
473
- * This performs a deep merge to avoid overwriting nested state.
474
- * @param partialState The partial state to update for the active tab.
407
+ * Get active module value (non-reactive)
475
408
  */
476
- updateActiveTabState(partialState) {
477
- const activeId = this._activeTabId.getValue();
478
- if (!activeId) {
479
- console.warn('Cannot update state, no active tab.');
480
- return;
481
- }
482
- const tabs = this._tabsState.getValue();
483
- const activeTabState = tabs.get(activeId);
484
- if (activeTabState) {
485
- // Use lodash merge for a deep merge
486
- const updatedState = merge({}, activeTabState, { componentState: partialState });
487
- const newTabsState = new Map(tabs);
488
- newTabsState.set(activeId, updatedState);
489
- this._tabsState.next(newTabsState);
490
- }
409
+ getActiveModuleValue() {
410
+ return this.activeModuleSignal();
491
411
  }
492
412
  /**
493
- * Gets an observable for a specific tab's state.
494
- * @param tabId The ID of the tab to observe.
413
+ * Set the active entity
495
414
  */
496
- getTabState(tabId) {
497
- return this._tabsState.pipe(map(tabs => tabs.get(tabId)));
415
+ setActiveEntity(entity) {
416
+ this.activeEntitySignal.set(entity);
498
417
  }
499
418
  /**
500
- * Determines the ID of the next tab to activate when a tab is closed.
501
- * @param idToRemove The ID of the tab being removed.
502
- * @returns The ID of the next tab to activate, or null if no tabs are left.
419
+ * Get active entity value (non-reactive)
503
420
  */
504
- getNextActiveTabId(idToRemove) {
505
- const tabsMap = this._tabsState.getValue();
506
- if (tabsMap.size <= 1) {
507
- return null;
508
- }
509
- const tabIds = Array.from(tabsMap.keys());
510
- const removedIndex = tabIds.indexOf(idToRemove);
511
- // If it's the last tab in the list, activate the one before it. Otherwise, activate the next one.
512
- const nextIndex = removedIndex === tabIds.length - 1 ? removedIndex - 1 : removedIndex + 1;
513
- return tabIds[nextIndex];
421
+ getActiveEntityValue() {
422
+ return this.activeEntitySignal();
514
423
  }
515
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TabStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
516
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TabStateService, providedIn: 'root' });
517
- }
518
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TabStateService, decorators: [{
519
- type: Injectable,
520
- args: [{
521
- providedIn: 'root',
522
- }]
523
- }], ctorParameters: () => [] });
524
-
525
- class CideLytRequestService {
526
- requestVisible = false;
527
- // Modern Angular v20+ pattern: Use Signals instead of BehaviorSubject
528
- tabsSignal = signal([], ...(ngDevMode ? [{ debugName: "tabsSignal" }] : []));
529
- activeTabIdSignal = signal(null, ...(ngDevMode ? [{ debugName: "activeTabIdSignal" }] : []));
530
- // Computed signals for derived state and public readonly access
531
- tabs = computed(() => this.tabsSignal(), ...(ngDevMode ? [{ debugName: "tabs" }] : []));
532
- activeTabId = computed(() => this.activeTabIdSignal(), ...(ngDevMode ? [{ debugName: "activeTabId" }] : []));
533
- activeTab = computed(() => this.tabsSignal().find(tab => tab.active), ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
534
- // Use inject() for all dependencies
535
- router = inject(Router);
536
- routeReuseStrategy = inject(RouteReuseStrategy);
537
- tabStateService = inject(TabStateService);
538
- sidedrawerService = inject(CideLytSidedrawerService);
539
- sharedService = inject(CideLytSharedService);
540
- floatingContainerService = inject(CideEleFloatingContainerService);
541
- constructor() {
542
- // Initialize router
543
- this.router = inject(Router);
544
- this.router.routeReuseStrategy = new CustomRouteReuseStrategy();
545
- // Register tab management callback with shared service to avoid circular dependency
546
- this.sharedService.registerTabManagement(this.handleRouteBasedTabManagement.bind(this));
547
- // Register request visibility callback
548
- this.sharedService.registerRequestVisibility(this.handleRequestVisibility.bind(this));
424
+ /**
425
+ * Register a callback to be called when entity changes
426
+ * This allows other services to react to entity changes without creating circular dependencies
427
+ * @param callback Function to call when entity changes, receives the new entity (or null if cleared)
428
+ */
429
+ registerEntityChangeHandler(callback) {
430
+ this.entityChangeCallbacks.push(callback);
431
+ console.log('📝 [AppStateService] Registered entity change handler, total handlers:', this.entityChangeCallbacks.length);
549
432
  }
550
433
  /**
551
- * Handle request visibility changes from shared service
434
+ * Unregister an entity change callback
435
+ * @param callback The callback function to remove
552
436
  */
553
- handleRequestVisibility(show) {
554
- if (show) {
555
- this.showRequest();
556
- }
557
- else {
558
- this.hideRequest();
437
+ unregisterEntityChangeHandler(callback) {
438
+ const index = this.entityChangeCallbacks.indexOf(callback);
439
+ if (index > -1) {
440
+ this.entityChangeCallbacks.splice(index, 1);
441
+ console.log('📝 [AppStateService] Unregistered entity change handler, remaining handlers:', this.entityChangeCallbacks.length);
559
442
  }
560
443
  }
561
444
  /**
562
- * Handle sidebar sync when tab becomes active
445
+ * Manually refresh state from localStorage
446
+ * Call this method when you need to sync with external localStorage changes
563
447
  */
564
- handleSidebarSync(tabInfo) {
565
- // The sidebar sync will be handled automatically by the shared service
566
- // when page data is loaded, so we don't need additional logic here
567
- console.log('🔗 REQUEST SERVICE: Tab activated, sidebar sync handled by shared service:', tabInfo.title);
448
+ refreshFromLocalStorage() {
449
+ this.loadFromLocalStorage();
450
+ // Note: File manager user ID is automatically updated via effect() when user signal changes
568
451
  }
569
452
  /**
570
- * Handle route-based tab management - called by shared service
571
- * This method checks for existing tabs and creates/activates as needed
453
+ * Check if localStorage is available (browser environment)
572
454
  */
573
- handleRouteBasedTabManagement(currentRoutePath, queryParams, layout, pageData, pageCode) {
574
- console.log('handleRouteBasedTabManagement', currentRoutePath, queryParams, layout, pageData, pageCode);
575
- const currentTabs = this.tabsSignal();
576
- const tabAlreadyExists = currentTabs.find((t) => {
577
- const tRoutePath = t.route.startsWith('/') ? t.route : '/' + t.route;
578
- const tParamsMatch = JSON.stringify(t.params || {}) === JSON.stringify(queryParams || {});
579
- return tRoutePath === currentRoutePath && tParamsMatch;
580
- });
581
- console.log('tabAlreadyExists', tabAlreadyExists);
582
- if (!tabAlreadyExists) {
583
- const title = pageData.data?.page?.sypg_title || pageCode.toString() || 'Untitled Tab';
584
- console.log(`🆕 Adding new tab: ${title}`);
585
- this.addTab(title, currentRoutePath, queryParams, layout);
586
- }
587
- else if (tabAlreadyExists && !tabAlreadyExists.active) {
588
- console.log(`🔄 Activating existing tab: ${tabAlreadyExists.title}`);
589
- this.activateTab(tabAlreadyExists.id);
590
- }
591
- else if (tabAlreadyExists && tabAlreadyExists.active) {
592
- console.log(`🔄 Activating existing tab: ${tabAlreadyExists.title}`);
593
- this.activateTab(tabAlreadyExists.id);
594
- }
595
- }
596
- addTab(title, route, params, layout) {
597
- console.log('addTab', title, route, params, layout);
598
- const id = this.generateId();
599
- const currentTabs = this.tabsSignal();
600
- console.log('currentTabs', currentTabs, route);
601
- // Tab reuse disabled - always create new tab
602
- // Removed existing tab check to prevent tab reuse
603
- // Create new tab
604
- const newTab = {
605
- id,
606
- title,
607
- route,
608
- params,
609
- active: true,
610
- layout,
611
- sytm_page_id_sypg: '',
612
- themeId: ''
613
- };
614
- // Deactivate all other tabs
615
- currentTabs.forEach((tab) => tab.active = false);
616
- console.log('currentTabs after deactivate', currentTabs, 'activateTab', id);
617
- // Add new tab
618
- this.tabsSignal.set([...currentTabs, newTab]);
619
- this.activeTabIdSignal.set(id);
620
- // Sync with TabStateService
621
- this.tabStateService.addTab(title, id);
622
- // Request visibility is now controlled by layout configuration in setPageData
623
- // No need to automatically show request wrapper here
624
- // Navigate to the new route
625
- if (params) {
626
- this.router.navigate([route], { queryParams: params });
455
+ isLocalStorageAvailable() {
456
+ try {
457
+ return typeof window !== 'undefined' && typeof localStorage !== 'undefined';
627
458
  }
628
- else {
629
- this.router.navigate([route]);
459
+ catch {
460
+ return false;
630
461
  }
631
- return id;
632
462
  }
633
- activateTab(tabId) {
634
- console.log('🔍 REQUEST SERVICE: Activating tab:', tabId);
635
- const tabs = this.tabsSignal();
636
- const tabToActivate = tabs.find(tab => tab.id === tabId);
637
- if (!tabToActivate) {
638
- console.warn('⚠️ Tab not found:', tabId);
463
+ /**
464
+ * Save state to localStorage
465
+ */
466
+ saveToLocalStorage(data) {
467
+ if (!this.isLocalStorageAvailable()) {
639
468
  return;
640
469
  }
641
- // Before navigating, ensure the route is fresh by clearing any cached state
642
- // This prevents old component data from persisting when reopening a tab
643
- if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
644
- let pathKey = tabToActivate.route.startsWith('/') ? tabToActivate.route.substring(1) : tabToActivate.route;
645
- if (tabToActivate.params && Object.keys(tabToActivate.params).length > 0) {
646
- const queryParamsString = Object.keys(tabToActivate.params)
647
- .sort()
648
- .map(key => `${key}=${tabToActivate.params[key]}`)
649
- .join('&');
650
- pathKey += '?' + queryParamsString;
651
- }
652
- // Clear any cached route to ensure fresh component state
653
- this.routeReuseStrategy.clearStoredRoute(pathKey);
654
- console.log(`🔄 REQUEST SERVICE: Cleared cached route ${pathKey} before activating tab`);
470
+ try {
471
+ if (data.user !== undefined) {
472
+ if (data.user) {
473
+ localStorage.setItem('cide_auth_user', JSON.stringify(data.user));
474
+ }
475
+ else {
476
+ localStorage.removeItem('cide_auth_user');
477
+ }
478
+ }
479
+ if (data.activeModule !== undefined) {
480
+ if (data.activeModule) {
481
+ localStorage.setItem('cide_active_module', JSON.stringify(data.activeModule));
482
+ }
483
+ else {
484
+ localStorage.removeItem('cide_active_module');
485
+ }
486
+ }
487
+ if (data.activeEntity !== undefined) {
488
+ if (data.activeEntity) {
489
+ localStorage.setItem('cide_active_entity', JSON.stringify(data.activeEntity));
490
+ }
491
+ else {
492
+ localStorage.removeItem('cide_active_entity');
493
+ }
494
+ }
495
+ }
496
+ catch (error) {
497
+ console.error('Error saving to localStorage:', error);
655
498
  }
656
- // Update tabs: deactivate all, then activate the target
657
- const updatedTabs = tabs.map(tab => ({
658
- ...tab,
659
- active: tab.id === tabId
660
- }));
661
- this.tabsSignal.set(updatedTabs);
662
- this.activeTabIdSignal.set(tabId);
663
- console.log('✅ REQUEST SERVICE: Tab activated:', tabToActivate.title);
664
- // Navigate to the tab's route
665
- this.router.navigate([tabToActivate.route], {
666
- queryParams: tabToActivate.params || {},
667
- replaceUrl: false
668
- });
669
- // Trigger sidebar sync for the activated tab by calling shared service
670
- // The shared service will handle the sidebar sync when the route loads
671
- console.log('🔗 REQUEST SERVICE: Sidebar sync will be handled by route change');
672
499
  }
673
- closeTab(id) {
674
- const currentTabs = this.tabsSignal();
675
- const tabIndex = currentTabs.findIndex((tab) => tab.id === id);
676
- if (tabIndex === -1)
500
+ /**
501
+ * Load state from localStorage
502
+ */
503
+ loadFromLocalStorage() {
504
+ if (!this.isLocalStorageAvailable()) {
677
505
  return;
678
- const tabToClose = currentTabs[tabIndex];
679
- const wasActive = tabToClose.active;
680
- // Sync with TabStateService by removing the tab state
681
- this.tabStateService.removeTab(id);
682
- // Clear stored route from strategy and ensure proper component cleanup
683
- if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
684
- // Generate pathKey in the same format as CustomRouteReuseStrategy.getPathKey()
685
- // Remove leading slash to match the format used by getPathKey
686
- let pathKey = tabToClose.route.startsWith('/') ? tabToClose.route.substring(1) : tabToClose.route;
687
- // Only add query params if the route is marked for reuse (matching getPathKey logic)
688
- // But we'll try to clear with both formats to be safe
689
- if (tabToClose.params && Object.keys(tabToClose.params).length > 0) {
690
- const queryParamsString = Object.keys(tabToClose.params)
691
- .sort()
692
- .map(key => `${key}=${tabToClose.params[key]}`)
693
- .join('&');
694
- pathKey += '?' + queryParamsString;
506
+ }
507
+ try {
508
+ const userData = localStorage.getItem('cide_auth_user');
509
+ if (userData) {
510
+ const user = JSON.parse(userData);
511
+ this.currentUserSignal.set(user);
695
512
  }
696
- // Clear the stored route and destroy the component instance
697
- this.routeReuseStrategy.clearStoredRoute(pathKey);
698
- // Also try clearing without query params in case the route wasn't marked for reuse
699
- // This ensures we catch all possible pathKey variations
700
- const pathKeyWithoutParams = tabToClose.route.startsWith('/') ? tabToClose.route.substring(1) : tabToClose.route;
701
- if (pathKeyWithoutParams !== pathKey) {
702
- this.routeReuseStrategy.clearStoredRoute(pathKeyWithoutParams);
513
+ const moduleData = localStorage.getItem('cide_active_module');
514
+ if (moduleData) {
515
+ const module = JSON.parse(moduleData);
516
+ this.activeModuleSignal.set(module);
703
517
  }
704
- // Force a route refresh to ensure clean component state
705
- // This prevents old data from persisting when the same route is reopened
706
- console.log(`🧹 REQUEST SERVICE: Cleared stored route for ${pathKey} and destroyed component instance`);
707
- // If this was the active tab, navigate away to ensure component is removed from DOM
708
- if (wasActive) {
709
- // The navigation to the new active tab will happen below, which will properly clear the router outlet
710
- // But we also want to ensure the component is removed immediately
711
- console.log(`🧹 REQUEST SERVICE: Active tab closed, component will be removed on navigation`);
518
+ const entityData = localStorage.getItem('cide_active_entity');
519
+ if (entityData) {
520
+ const entity = JSON.parse(entityData);
521
+ this.activeEntitySignal.set(entity);
712
522
  }
713
523
  }
714
- // Remove the tab from this service's list
715
- const newTabs = currentTabs.filter((tab) => tab.id !== id);
716
- // If we're closing the active tab, activate the next available tab
717
- if (wasActive && newTabs.length > 0) {
718
- const newActiveIndex = Math.max(0, Math.min(tabIndex, newTabs.length - 1));
719
- const newActiveTab = newTabs[newActiveIndex];
720
- // Update sidedrawer for the new active tab
721
- this.sidedrawerService.updateDrawerItems(newActiveTab.layout);
722
- newTabs[newActiveIndex].active = true;
723
- this.activeTabIdSignal.set(newActiveTab.id);
724
- this.tabStateService.setActiveTab(newActiveTab.id);
725
- // Navigate to the new active tab's route
726
- if (newActiveTab.params) {
727
- this.router.navigate([newActiveTab.route], { queryParams: newActiveTab.params });
728
- }
729
- else {
730
- this.router.navigate([newActiveTab.route]);
524
+ catch (error) {
525
+ console.error('Error loading from localStorage:', error);
526
+ // Clear corrupted data
527
+ if (this.isLocalStorageAvailable()) {
528
+ localStorage.removeItem('cide_auth_user');
529
+ localStorage.removeItem('cide_active_module');
530
+ localStorage.removeItem('cide_active_entity');
731
531
  }
732
532
  }
733
- else if (newTabs.length === 0) {
734
- this.activeTabIdSignal.set(null);
735
- this.tabStateService.setActiveTab(null);
736
- // Clear sidedrawer as no tabs are open
737
- this.sidedrawerService.updateDrawerItems(undefined);
738
- // Redirect to home screen when all tabs are closed
739
- this.router.navigate(['/control-panel']);
740
- }
741
- this.tabsSignal.set(newTabs);
742
- // Request wrapper visibility is now controlled by layout configuration
743
- // The wrapper will be hidden/shown based on sytm_layout_request.status in setPageData
744
533
  }
745
534
  /**
746
- * Close all tabs and navigate to home
747
- * This is used when entity changes to ensure all components are destroyed and refreshed
535
+ * Setup storage event listener to handle external localStorage changes
748
536
  */
749
- closeAllTabs() {
750
- console.log('🧹 REQUEST SERVICE: Closing all tabs due to entity change');
751
- const currentTabs = this.tabsSignal();
752
- // Close all floating containers first
753
- this.floatingContainerService.hideAll();
754
- console.log('🧹 REQUEST SERVICE: Closed all floating containers');
755
- // Clear all tabs from TabStateService
756
- currentTabs.forEach(tab => {
757
- this.tabStateService.removeTab(tab.id);
758
- // Clear stored routes from route reuse strategy
759
- if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
760
- let pathKey = tab.route.startsWith('/') ? tab.route.substring(1) : tab.route;
761
- if (tab.params && Object.keys(tab.params).length > 0) {
762
- const queryParamsString = Object.keys(tab.params)
763
- .sort()
764
- .map(key => `${key}=${tab.params[key]}`)
765
- .join('&');
766
- pathKey += '?' + queryParamsString;
537
+ setupStorageListener() {
538
+ if (!this.isLocalStorageAvailable()) {
539
+ return;
540
+ }
541
+ // Listen for storage events (triggered when localStorage is modified from other tabs/windows)
542
+ window.addEventListener('storage', (event) => {
543
+ if (!event.key)
544
+ return;
545
+ try {
546
+ // Handle user data changes
547
+ if (event.key === 'cide_auth_user') {
548
+ if (event.newValue) {
549
+ const user = JSON.parse(event.newValue);
550
+ this.currentUserSignal.set(user);
551
+ }
552
+ else {
553
+ this.currentUserSignal.set(null);
554
+ }
767
555
  }
768
- this.routeReuseStrategy.clearStoredRoute(pathKey);
769
- // Also clear without query params
770
- const pathKeyWithoutParams = tab.route.startsWith('/') ? tab.route.substring(1) : tab.route;
771
- if (pathKeyWithoutParams !== pathKey) {
772
- this.routeReuseStrategy.clearStoredRoute(pathKeyWithoutParams);
556
+ // Handle active module changes
557
+ if (event.key === 'cide_active_module') {
558
+ if (event.newValue) {
559
+ const module = JSON.parse(event.newValue);
560
+ this.activeModuleSignal.set(module);
561
+ }
562
+ else {
563
+ this.activeModuleSignal.set(null);
564
+ }
565
+ }
566
+ // Handle active entity changes
567
+ if (event.key === 'cide_active_entity') {
568
+ if (event.newValue) {
569
+ const entity = JSON.parse(event.newValue);
570
+ this.activeEntitySignal.set(entity);
571
+ }
572
+ else {
573
+ this.activeEntitySignal.set(null);
574
+ }
773
575
  }
774
576
  }
577
+ catch (error) {
578
+ console.error('Error parsing localStorage update:', error);
579
+ }
775
580
  });
776
- // Clear all tabs
777
- this.tabsSignal.set([]);
778
- this.activeTabIdSignal.set(null);
779
- this.tabStateService.setActiveTab(null);
780
- // Clear sidedrawer
781
- this.sidedrawerService.updateDrawerItems(undefined);
782
- // Navigate to home
783
- this.router.navigate(['/control-panel']);
784
- console.log('✅ REQUEST SERVICE: All tabs and floating containers closed, navigated to home');
785
- }
786
- // Hide Request
787
- hideRequest() {
788
- console.log('🚫 REQUEST SERVICE - Hiding request wrapper');
789
- this.requestVisible = false;
790
- document.querySelector(`#cide-lyt-request-wrapper`)?.classList.add('cide-lyt-request-wrapper-hide');
791
- document.querySelector(`body`)?.classList.remove('cide-lyt-request-exist');
792
581
  }
793
- // Show Request
794
- showRequest() {
795
- console.log('✅ REQUEST SERVICE - Showing request wrapper');
796
- this.requestVisible = true;
797
- document.querySelector(`#cide-lyt-request-wrapper`)?.classList.remove('cide-lyt-request-wrapper-hide');
798
- document.querySelector(`body`)?.classList.add('cide-lyt-request-exist');
582
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AppStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
583
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AppStateService, providedIn: 'root' });
584
+ }
585
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AppStateService, decorators: [{
586
+ type: Injectable,
587
+ args: [{
588
+ providedIn: 'root'
589
+ }]
590
+ }], ctorParameters: () => [] });
591
+
592
+ class AppStateHelperService {
593
+ appStateService = inject(AppStateService);
594
+ sidebarService = inject(CideLytSidebarService);
595
+ // Computed signals for derived state
596
+ currentUser = this.appStateService.currentUser;
597
+ isAuthenticated = this.appStateService.isAuthenticated;
598
+ userInfo = this.appStateService.userInfo;
599
+ activeModule = this.appStateService.activeModule;
600
+ activeModuleInfo = this.appStateService.activeModuleInfo;
601
+ activeEntity = this.appStateService.activeEntity;
602
+ /**
603
+ * Get current user value (non-reactive)
604
+ */
605
+ getCurrentUser() {
606
+ return this.appStateService.getUserValue();
799
607
  }
800
- generateId() {
801
- return 'tab-' + Math.random().toString(36).substr(2, 9);
608
+ /**
609
+ * Set user data
610
+ */
611
+ setUser(user) {
612
+ this.appStateService.setUser(user);
802
613
  }
803
- updateTabScrollPosition(tabId, scrollTop, scrollLeft) {
804
- const currentTabs = this.tabsSignal();
805
- const tabIndex = currentTabs.findIndex((t) => t.id === tabId);
806
- if (tabIndex !== -1) {
807
- const tabToUpdate = currentTabs[tabIndex];
808
- tabToUpdate.scrollTop = scrollTop;
809
- tabToUpdate.scrollLeft = scrollLeft;
810
- // Create a new array reference to trigger change detection
811
- this.tabsSignal.set([...currentTabs]);
812
- }
614
+ /**
615
+ * Update user data (partial update)
616
+ */
617
+ updateUser(updates) {
618
+ this.appStateService.updateUser(updates);
813
619
  }
814
620
  /**
815
- * Force refresh a route by clearing its cached state
816
- * This is useful when you want to ensure a clean component state
817
- * @param route The route path to refresh
818
- * @param params Optional query parameters
621
+ * Clear user data
819
622
  */
820
- forceRefreshRoute(route, params) {
821
- if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
822
- let pathKey = route.startsWith('/') ? route.substring(1) : route;
823
- if (params && Object.keys(params).length > 0) {
824
- const queryParamsString = Object.keys(params)
825
- .sort()
826
- .map(key => `${key}=${params[key]}`)
827
- .join('&');
828
- pathKey += '?' + queryParamsString;
829
- }
830
- // Clear the stored route to force a fresh component instance
831
- this.routeReuseStrategy.clearStoredRoute(pathKey);
832
- console.log(`🔄 REQUEST SERVICE: Force refreshed route ${pathKey}`);
833
- }
623
+ clearUser() {
624
+ this.appStateService.clearUser();
834
625
  }
835
626
  /**
836
- * Get information about cached routes (useful for debugging)
627
+ * Check if user is authenticated
837
628
  */
838
- getCachedRoutesInfo() {
839
- if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
840
- const count = this.routeReuseStrategy.getStoredRoutesCount();
841
- return { count, routes: [] }; // Could be enhanced to return actual route paths
842
- }
843
- return { count: 0, routes: [] };
629
+ isUserAuthenticated() {
630
+ return this.appStateService.isAuthenticated();
844
631
  }
845
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytRequestService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
846
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytRequestService, providedIn: 'root' });
847
- }
848
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytRequestService, decorators: [{
849
- type: Injectable,
850
- args: [{
851
- providedIn: 'root'
852
- }]
853
- }], ctorParameters: () => [] });
854
-
855
- class AppStateService {
856
- // Inject services
857
- fileManagerService = inject(CideEleFileManagerService);
858
- router = inject(Router);
859
- requestService = inject(CideLytRequestService);
860
- // Private signal for current user
861
- currentUserSignal = signal(null, ...(ngDevMode ? [{ debugName: "currentUserSignal" }] : []));
862
- // Private signal for active module
863
- activeModuleSignal = signal(null, ...(ngDevMode ? [{ debugName: "activeModuleSignal" }] : []));
864
- // Private signal for active entity
865
- activeEntitySignal = signal(null, ...(ngDevMode ? [{ debugName: "activeEntitySignal" }] : []));
866
- // Track if entity change handler is initialized (to prevent triggering on initial load)
867
- entityChangeHandlerInitialized = false;
868
- // Public computed signal for current user, for other services to get the user value
869
- currentUser = computed(() => this.currentUserSignal(), ...(ngDevMode ? [{ debugName: "currentUser" }] : []));
870
- // Public computed signal for active module
871
- activeModule = computed(() => this.activeModuleSignal(), ...(ngDevMode ? [{ debugName: "activeModule" }] : []));
872
- // Public computed signal for active entity
873
- activeEntity = computed(() => this.activeEntitySignal(), ...(ngDevMode ? [{ debugName: "activeEntity" }] : []));
874
- // Computed signals for derived state
875
- isAuthenticated = computed(() => !!this.currentUserSignal(), ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : []));
876
- userInfo = computed(() => {
877
- const user = this.currentUserSignal();
878
- if (!user)
879
- return null;
880
- return {
881
- id: user._id,
882
- name: user.user_fullname || `${user.user_firstname} ${user.user_lastname}`,
883
- email: user.user_emailid,
884
- role: "" // user.user_id_role
885
- };
886
- }, ...(ngDevMode ? [{ debugName: "userInfo" }] : []));
887
- // Computed signal for active module info
888
- activeModuleInfo = computed(() => {
889
- const module = this.activeModuleSignal();
890
- if (!module)
891
- return null;
892
- return {
893
- id: module._id,
894
- title: module.syme_title,
895
- icon: module.syme_icon,
896
- path: module.syme_path,
897
- type: module.syme_type
898
- };
899
- }, ...(ngDevMode ? [{ debugName: "activeModuleInfo" }] : []));
900
- constructor() {
901
- // Initialize file manager base URL on app startup
902
- this.fileManagerService.setBaseUrl(cidePath.join([hostManagerRoutesUrl.cideSuiteHost, coreRoutesUrl?.module, coreRoutesUrl?.fileManager]));
903
- this.fileManagerService.setObjectIdEndpoint(cidePath.join([hostManagerRoutesUrl.cideSuiteHost, commonRoutesUrl?.module, commonRoutesUrl?.generateObjectId]));
904
- // Load initial state from localStorage
905
- this.loadFromLocalStorage();
906
- // Save to localStorage whenever state changes
907
- effect(() => {
908
- const currentUser = this.currentUserSignal();
909
- const activeModule = this.activeModuleSignal();
910
- const activeEntity = this.activeEntitySignal();
911
- this.saveToLocalStorage({
912
- user: currentUser,
913
- activeModule: activeModule,
914
- activeEntity: activeEntity
915
- });
916
- });
917
- // Automatically update file manager user ID whenever user changes
918
- effect(() => {
919
- const currentUser = this.currentUserSignal();
920
- console.log('🔍 [AppStateService] User changed, updating file manager user ID:', currentUser);
921
- if (currentUser?._id) {
922
- this.fileManagerService.setUserId(currentUser._id);
923
- }
924
- else {
925
- this.fileManagerService.setUserId('');
926
- }
927
- });
928
- // GLOBAL ENTITY CHANGE HANDLER: When entity changes, close all tabs and navigate to home
929
- // This ensures all components are destroyed and data is refreshed for the new entity
930
- // No need to inject this service in any component - it works globally
931
- let previousEntityId = null;
932
- effect(() => {
933
- const currentEntity = this.activeEntitySignal();
934
- const currentEntityId = currentEntity?._id || null;
935
- // Mark handler as initialized after first run (to skip initial load from localStorage)
936
- if (!this.entityChangeHandlerInitialized) {
937
- previousEntityId = currentEntityId;
938
- this.entityChangeHandlerInitialized = true;
939
- console.log('🏢 [AppStateService] Entity change handler initialized with initial entity:', currentEntityId);
940
- return; // Skip on initial load
941
- }
942
- // Only trigger if entity actually changed (not on initial load)
943
- if (previousEntityId !== null && currentEntityId !== previousEntityId && currentEntityId !== null) {
944
- console.log('🏢 [AppStateService] Entity changed - closing all tabs and navigating to home', {
945
- previousEntityId,
946
- newEntityId: currentEntityId,
947
- newEntityName: currentEntity?.syen_name
948
- });
949
- // Close all tabs - this will destroy all components and navigate to home
950
- this.requestService.closeAllTabs();
951
- }
952
- else if (previousEntityId !== null && currentEntityId === null) {
953
- // Entity was cleared - also close tabs
954
- console.log('🏢 [AppStateService] Entity cleared - closing all tabs');
955
- this.requestService.closeAllTabs();
956
- }
957
- // Update previous entity ID for next comparison
958
- previousEntityId = currentEntityId;
959
- });
960
- // Listen for localStorage changes from other tabs/windows
961
- this.setupStorageListener();
632
+ /**
633
+ * Get user info object
634
+ */
635
+ getUserInfo() {
636
+ return this.appStateService.userInfo();
962
637
  }
963
638
  /**
964
- * Set the current user
639
+ * Get user ID
965
640
  */
966
- setUser(user) {
967
- this.currentUserSignal.set(user);
968
- // Note: File manager user ID is automatically updated via effect() in constructor
641
+ getUserId() {
642
+ const user = this.getCurrentUser();
643
+ return user?._id || null;
969
644
  }
970
645
  /**
971
- * Update user data (partial update)
646
+ * Get user name
972
647
  */
973
- updateUser(updates) {
974
- const currentUser = this.currentUserSignal();
975
- if (currentUser) {
976
- this.currentUserSignal.set({ ...currentUser, ...updates });
977
- }
648
+ getUserName() {
649
+ const userInfo = this.getUserInfo();
650
+ return userInfo?.name || null;
978
651
  }
979
652
  /**
980
- * Clear user data
653
+ * Get user email
981
654
  */
982
- clearUser() {
983
- this.currentUserSignal.set(null);
655
+ getUserEmail() {
656
+ const userInfo = this.getUserInfo();
657
+ return userInfo?.email || null;
984
658
  }
985
659
  /**
986
- * Get current user value (non-reactive)
660
+ * Get user username
987
661
  */
988
- getUserValue() {
989
- return this.currentUserSignal();
662
+ getUserUsername() {
663
+ const user = this.getCurrentUser();
664
+ return user?.user_username || null;
990
665
  }
991
666
  /**
992
- * Set the active module
667
+ * Get user full name
993
668
  */
994
- setActiveModule(module) {
995
- this.activeModuleSignal.set(module);
669
+ getUserFullName() {
670
+ const user = this.getCurrentUser();
671
+ return user?.user_fullname || null;
996
672
  }
997
673
  /**
998
- * Get active module value (non-reactive)
674
+ * Get user first name
999
675
  */
1000
- getActiveModuleValue() {
1001
- return this.activeModuleSignal();
676
+ getUserFirstName() {
677
+ const user = this.getCurrentUser();
678
+ return user?.user_firstname || null;
1002
679
  }
1003
680
  /**
1004
- * Set the active entity
681
+ * Get user last name
1005
682
  */
1006
- setActiveEntity(entity) {
1007
- this.activeEntitySignal.set(entity);
683
+ getUserLastName() {
684
+ const user = this.getCurrentUser();
685
+ return user?.user_lastname || null;
1008
686
  }
1009
687
  /**
1010
- * Get active entity value (non-reactive)
688
+ * Get user mobile number
1011
689
  */
1012
- getActiveEntityValue() {
1013
- return this.activeEntitySignal();
690
+ getUserMobile() {
691
+ const user = this.getCurrentUser();
692
+ return user?.user_mobileno || null;
1014
693
  }
1015
694
  /**
1016
- * Manually refresh state from localStorage
1017
- * Call this method when you need to sync with external localStorage changes
695
+ * Set the active module
1018
696
  */
1019
- refreshFromLocalStorage() {
1020
- this.loadFromLocalStorage();
1021
- // Note: File manager user ID is automatically updated via effect() when user signal changes
697
+ setActiveModule(module) {
698
+ this.appStateService.setActiveModule(module);
1022
699
  }
1023
700
  /**
1024
- * Check if localStorage is available (browser environment)
701
+ * Get active module value (non-reactive)
1025
702
  */
1026
- isLocalStorageAvailable() {
1027
- try {
1028
- return typeof window !== 'undefined' && typeof localStorage !== 'undefined';
1029
- }
1030
- catch {
1031
- return false;
1032
- }
703
+ getActiveModule() {
704
+ return this.appStateService.getActiveModuleValue();
1033
705
  }
1034
706
  /**
1035
- * Save state to localStorage
1036
- */
1037
- saveToLocalStorage(data) {
1038
- if (!this.isLocalStorageAvailable()) {
1039
- return;
1040
- }
1041
- try {
1042
- if (data.user !== undefined) {
1043
- if (data.user) {
1044
- localStorage.setItem('cide_auth_user', JSON.stringify(data.user));
1045
- }
1046
- else {
1047
- localStorage.removeItem('cide_auth_user');
1048
- }
1049
- }
1050
- if (data.activeModule !== undefined) {
1051
- if (data.activeModule) {
1052
- localStorage.setItem('cide_active_module', JSON.stringify(data.activeModule));
1053
- }
1054
- else {
1055
- localStorage.removeItem('cide_active_module');
1056
- }
1057
- }
1058
- if (data.activeEntity !== undefined) {
1059
- if (data.activeEntity) {
1060
- localStorage.setItem('cide_active_entity', JSON.stringify(data.activeEntity));
1061
- }
1062
- else {
1063
- localStorage.removeItem('cide_active_entity');
1064
- }
1065
- }
1066
- }
1067
- catch (error) {
1068
- console.error('Error saving to localStorage:', error);
1069
- }
1070
- }
1071
- /**
1072
- * Load state from localStorage
1073
- */
1074
- loadFromLocalStorage() {
1075
- if (!this.isLocalStorageAvailable()) {
1076
- return;
1077
- }
1078
- try {
1079
- const userData = localStorage.getItem('cide_auth_user');
1080
- if (userData) {
1081
- const user = JSON.parse(userData);
1082
- this.currentUserSignal.set(user);
1083
- }
1084
- const moduleData = localStorage.getItem('cide_active_module');
1085
- if (moduleData) {
1086
- const module = JSON.parse(moduleData);
1087
- this.activeModuleSignal.set(module);
1088
- }
1089
- const entityData = localStorage.getItem('cide_active_entity');
1090
- if (entityData) {
1091
- const entity = JSON.parse(entityData);
1092
- this.activeEntitySignal.set(entity);
1093
- }
1094
- }
1095
- catch (error) {
1096
- console.error('Error loading from localStorage:', error);
1097
- // Clear corrupted data
1098
- if (this.isLocalStorageAvailable()) {
1099
- localStorage.removeItem('cide_auth_user');
1100
- localStorage.removeItem('cide_active_module');
1101
- localStorage.removeItem('cide_active_entity');
1102
- }
1103
- }
1104
- }
1105
- /**
1106
- * Setup storage event listener to handle external localStorage changes
1107
- */
1108
- setupStorageListener() {
1109
- if (!this.isLocalStorageAvailable()) {
1110
- return;
1111
- }
1112
- // Listen for storage events (triggered when localStorage is modified from other tabs/windows)
1113
- window.addEventListener('storage', (event) => {
1114
- if (!event.key)
1115
- return;
1116
- try {
1117
- // Handle user data changes
1118
- if (event.key === 'cide_auth_user') {
1119
- if (event.newValue) {
1120
- const user = JSON.parse(event.newValue);
1121
- this.currentUserSignal.set(user);
1122
- }
1123
- else {
1124
- this.currentUserSignal.set(null);
1125
- }
1126
- }
1127
- // Handle active module changes
1128
- if (event.key === 'cide_active_module') {
1129
- if (event.newValue) {
1130
- const module = JSON.parse(event.newValue);
1131
- this.activeModuleSignal.set(module);
1132
- }
1133
- else {
1134
- this.activeModuleSignal.set(null);
1135
- }
1136
- }
1137
- // Handle active entity changes
1138
- if (event.key === 'cide_active_entity') {
1139
- if (event.newValue) {
1140
- const entity = JSON.parse(event.newValue);
1141
- this.activeEntitySignal.set(entity);
1142
- }
1143
- else {
1144
- this.activeEntitySignal.set(null);
1145
- }
1146
- }
1147
- }
1148
- catch (error) {
1149
- console.error('Error parsing localStorage update:', error);
1150
- }
1151
- });
1152
- }
1153
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AppStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1154
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AppStateService, providedIn: 'root' });
1155
- }
1156
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AppStateService, decorators: [{
1157
- type: Injectable,
1158
- args: [{
1159
- providedIn: 'root'
1160
- }]
1161
- }], ctorParameters: () => [] });
1162
-
1163
- class AppStateHelperService {
1164
- appStateService = inject(AppStateService);
1165
- sidebarService = inject(CideLytSidebarService);
1166
- // Computed signals for derived state
1167
- currentUser = this.appStateService.currentUser;
1168
- isAuthenticated = this.appStateService.isAuthenticated;
1169
- userInfo = this.appStateService.userInfo;
1170
- activeModule = this.appStateService.activeModule;
1171
- activeModuleInfo = this.appStateService.activeModuleInfo;
1172
- activeEntity = this.appStateService.activeEntity;
1173
- /**
1174
- * Get current user value (non-reactive)
1175
- */
1176
- getCurrentUser() {
1177
- return this.appStateService.getUserValue();
1178
- }
1179
- /**
1180
- * Set user data
1181
- */
1182
- setUser(user) {
1183
- this.appStateService.setUser(user);
1184
- }
1185
- /**
1186
- * Update user data (partial update)
1187
- */
1188
- updateUser(updates) {
1189
- this.appStateService.updateUser(updates);
1190
- }
1191
- /**
1192
- * Clear user data
1193
- */
1194
- clearUser() {
1195
- this.appStateService.clearUser();
1196
- }
1197
- /**
1198
- * Check if user is authenticated
1199
- */
1200
- isUserAuthenticated() {
1201
- return this.appStateService.isAuthenticated();
1202
- }
1203
- /**
1204
- * Get user info object
1205
- */
1206
- getUserInfo() {
1207
- return this.appStateService.userInfo();
1208
- }
1209
- /**
1210
- * Get user ID
1211
- */
1212
- getUserId() {
1213
- const user = this.getCurrentUser();
1214
- return user?._id || null;
1215
- }
1216
- /**
1217
- * Get user name
1218
- */
1219
- getUserName() {
1220
- const userInfo = this.getUserInfo();
1221
- return userInfo?.name || null;
1222
- }
1223
- /**
1224
- * Get user email
1225
- */
1226
- getUserEmail() {
1227
- const userInfo = this.getUserInfo();
1228
- return userInfo?.email || null;
1229
- }
1230
- /**
1231
- * Get user username
1232
- */
1233
- getUserUsername() {
1234
- const user = this.getCurrentUser();
1235
- return user?.user_username || null;
1236
- }
1237
- /**
1238
- * Get user full name
1239
- */
1240
- getUserFullName() {
1241
- const user = this.getCurrentUser();
1242
- return user?.user_fullname || null;
1243
- }
1244
- /**
1245
- * Get user first name
1246
- */
1247
- getUserFirstName() {
1248
- const user = this.getCurrentUser();
1249
- return user?.user_firstname || null;
1250
- }
1251
- /**
1252
- * Get user last name
1253
- */
1254
- getUserLastName() {
1255
- const user = this.getCurrentUser();
1256
- return user?.user_lastname || null;
1257
- }
1258
- /**
1259
- * Get user mobile number
1260
- */
1261
- getUserMobile() {
1262
- const user = this.getCurrentUser();
1263
- return user?.user_mobileno || null;
1264
- }
1265
- /**
1266
- * Set the active module
1267
- */
1268
- setActiveModule(module) {
1269
- this.appStateService.setActiveModule(module);
1270
- }
1271
- /**
1272
- * Get active module value (non-reactive)
1273
- */
1274
- getActiveModule() {
1275
- return this.appStateService.getActiveModuleValue();
1276
- }
1277
- /**
1278
- * Get active module info
707
+ * Get active module info
1279
708
  */
1280
709
  getActiveModuleInfo() {
1281
710
  return this.appStateService.activeModuleInfo();
@@ -1924,7 +1353,7 @@ class CideLytFloatingEntitySelectionService {
1924
1353
  }
1925
1354
  try {
1926
1355
  // Use relative import to avoid circular dependency
1927
- const module = await import('./cloud-ide-layout-floating-entity-selection.component-CGoIPT5y.mjs');
1356
+ const module = await import('./cloud-ide-layout-floating-entity-selection.component-DteJtUqk.mjs');
1928
1357
  if (module.CideLytFloatingEntitySelectionComponent) {
1929
1358
  this.containerService.registerComponent('entity-selection-header', module.CideLytFloatingEntitySelectionComponent);
1930
1359
  console.log('✅ Entity selection component registered successfully');
@@ -4830,6 +4259,618 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImpor
4830
4259
  args: ['document:click', ['$event']]
4831
4260
  }] } });
4832
4261
 
4262
+ /**
4263
+ * A custom route reuse strategy that allows storing and retrieving routes
4264
+ * to persist component state, especially for tabbed navigation.
4265
+ */
4266
+ class CustomRouteReuseStrategy {
4267
+ storedRoutes = {};
4268
+ // Generates a unique key for a route.
4269
+ // For routes marked with 'reuseTab', this key includes sorted query parameters
4270
+ // to ensure distinct states for tabs differing by query params.
4271
+ getPathKey(route) {
4272
+ let path = route.pathFromRoot
4273
+ .map(r => r.url.map(segment => segment.path).join('/'))
4274
+ .filter(p => p.length > 0)
4275
+ .join('/');
4276
+ // If the route is marked for tab reuse and has query parameters,
4277
+ // append them to the key in a sorted, consistent manner.
4278
+ if (route.data && route.data['reuseTab'] && route.queryParamMap.keys.length > 0) {
4279
+ const queryParams = route.queryParamMap.keys
4280
+ .sort() // Ensure consistent order of query parameters
4281
+ .map(key => `${key}=${route.queryParamMap.get(key)}`)
4282
+ .join('&');
4283
+ path += '?' + queryParams;
4284
+ }
4285
+ return path;
4286
+ }
4287
+ // Determines if this route (and its subtree) should be detached to be reused later.
4288
+ shouldDetach(route) {
4289
+ return !!(route.data && route.data['reuseTab']);
4290
+ }
4291
+ // Stores the detached route.
4292
+ store(route, handle) {
4293
+ const pathKey = this.getPathKey(route);
4294
+ if (handle && route.data && route.data['reuseTab'] && pathKey) {
4295
+ this.storedRoutes[pathKey] = handle;
4296
+ }
4297
+ }
4298
+ // Determines if this route (and its subtree) should be reattached.
4299
+ shouldAttach(route) {
4300
+ const pathKey = this.getPathKey(route);
4301
+ return !!pathKey && !!this.storedRoutes[pathKey];
4302
+ }
4303
+ // Retrieves the previously stored route.
4304
+ retrieve(route) {
4305
+ const pathKey = this.getPathKey(route);
4306
+ if (!pathKey || !this.storedRoutes[pathKey]) {
4307
+ return null;
4308
+ }
4309
+ const handle = this.storedRoutes[pathKey];
4310
+ // Verify the handle is still valid (not destroyed)
4311
+ // If the component was destroyed, remove it from stored routes
4312
+ if (handle && typeof handle === 'object') {
4313
+ const handleAny = handle;
4314
+ // Check if ComponentRef is destroyed
4315
+ if (handleAny.destroyed === true || (handleAny.hostView && handleAny.hostView.destroyed)) {
4316
+ console.log(`⚠️ Stored route handle for ${pathKey} is already destroyed, removing from cache`);
4317
+ delete this.storedRoutes[pathKey];
4318
+ return null;
4319
+ }
4320
+ }
4321
+ return handle;
4322
+ }
4323
+ // Determines if a route should be reused.
4324
+ shouldReuseRoute(future, curr) {
4325
+ return future.routeConfig === curr.routeConfig;
4326
+ }
4327
+ // Clears a stored route, typically when a tab is closed.
4328
+ // This method now properly destroys the component instance to prevent memory leaks
4329
+ // and ensure old data doesn't persist when reopening the same route.
4330
+ clearStoredRoute(pathKey) {
4331
+ const handle = this.storedRoutes[pathKey];
4332
+ if (handle) {
4333
+ // Properly destroy the component instance to prevent memory leaks
4334
+ // and ensure clean state when the route is reopened
4335
+ try {
4336
+ // In Angular, DetachedRouteHandle is typically a ComponentRef
4337
+ // We need to destroy it to remove the component from DOM and clean up resources
4338
+ // Method 1: Check if handle is a ComponentRef directly
4339
+ if (handle && typeof handle.destroy === 'function') {
4340
+ const componentRef = handle;
4341
+ // Verify it has hostView (ComponentRef signature)
4342
+ if (componentRef.hostView || componentRef.location) {
4343
+ console.log(`🗑️ Destroying component instance (ComponentRef) for route: ${pathKey}`);
4344
+ componentRef.destroy();
4345
+ }
4346
+ }
4347
+ // Method 2: Check if handle is a ViewContainerRef
4348
+ if (handle && typeof handle.clear === 'function' && typeof handle.length === 'number') {
4349
+ const viewContainerRef = handle;
4350
+ console.log(`🗑️ Clearing view container for route: ${pathKey}`);
4351
+ viewContainerRef.clear();
4352
+ }
4353
+ // Method 3: Check if handle is an object with nested ComponentRef
4354
+ if (handle && typeof handle === 'object' && !(handle instanceof ComponentRef) && !(handle instanceof ViewContainerRef)) {
4355
+ const handleObj = handle;
4356
+ // Try to find and destroy ComponentRef from various possible properties
4357
+ let componentRef = null;
4358
+ // Check common property names
4359
+ if (handleObj.componentRef && typeof handleObj.componentRef.destroy === 'function') {
4360
+ componentRef = handleObj.componentRef;
4361
+ }
4362
+ else if (handleObj.component && typeof handleObj.component.destroy === 'function') {
4363
+ componentRef = handleObj.component;
4364
+ }
4365
+ else if (handleObj.context && typeof handleObj.context.destroy === 'function') {
4366
+ componentRef = handleObj.context;
4367
+ }
4368
+ else if (handleObj._componentRef && typeof handleObj._componentRef.destroy === 'function') {
4369
+ componentRef = handleObj._componentRef;
4370
+ }
4371
+ if (componentRef) {
4372
+ console.log(`🗑️ Destroying component instance from handle object for route: ${pathKey}`);
4373
+ componentRef.destroy();
4374
+ }
4375
+ // Also try to clear view container if it exists
4376
+ if (handleObj.viewContainerRef && typeof handleObj.viewContainerRef.clear === 'function') {
4377
+ console.log(`🗑️ Clearing view container from handle object for route: ${pathKey}`);
4378
+ handleObj.viewContainerRef.clear();
4379
+ }
4380
+ // Try to destroy any nested components
4381
+ if (handleObj.components && Array.isArray(handleObj.components)) {
4382
+ handleObj.components.forEach((comp, index) => {
4383
+ if (comp && typeof comp.destroy === 'function') {
4384
+ console.log(`🗑️ Destroying nested component ${index} for route: ${pathKey}`);
4385
+ comp.destroy();
4386
+ }
4387
+ });
4388
+ }
4389
+ }
4390
+ }
4391
+ catch (error) {
4392
+ console.warn(`⚠️ Error destroying route handle for ${pathKey}:`, error);
4393
+ }
4394
+ // Remove from stored routes - this ensures shouldAttach will return false for this route
4395
+ delete this.storedRoutes[pathKey];
4396
+ console.log(`✅ Cleared stored route: ${pathKey} (removed from storedRoutes map)`);
4397
+ }
4398
+ else {
4399
+ console.log(`ℹ️ No stored route found for: ${pathKey}`);
4400
+ }
4401
+ }
4402
+ // Clears all stored routes (useful for cleanup)
4403
+ clearAllStoredRoutes() {
4404
+ Object.keys(this.storedRoutes).forEach(pathKey => {
4405
+ this.clearStoredRoute(pathKey);
4406
+ });
4407
+ }
4408
+ // Gets the count of stored routes (useful for debugging)
4409
+ getStoredRoutesCount() {
4410
+ return Object.keys(this.storedRoutes).length;
4411
+ }
4412
+ }
4413
+
4414
+ /**
4415
+ * Creates the default state for a new tab.
4416
+ */
4417
+ const createDefaultComponentState = () => ({
4418
+ sideDrawer: { isOpen: false, activeComponent: null, componentState: {} },
4419
+ footer: { someData: '' },
4420
+ console: { history: [] },
4421
+ });
4422
+ class TabStateService {
4423
+ // Holds the state for all open tabs, keyed by tab ID.
4424
+ _tabsState = new BehaviorSubject(new Map());
4425
+ // Holds the ID of the currently active tab.
4426
+ _activeTabId = new BehaviorSubject(null);
4427
+ /** An observable of all open tabs. */
4428
+ tabs$ = this._tabsState.pipe(map(tabsMap => Array.from(tabsMap.values())));
4429
+ /** An observable of the currently active tab's ID. */
4430
+ activeTabId$ = this._activeTabId.asObservable().pipe(distinctUntilChanged());
4431
+ /** An observable of the state of the currently active tab. */
4432
+ activeTabState$ = this.activeTabId$.pipe(map(activeId => (activeId ? this._tabsState.getValue().get(activeId) ?? null : null)),
4433
+ // Use a deep-ish comparison to prevent emissions when only the object reference changes
4434
+ distinctUntilChanged((prev, curr) => JSON.stringify(prev) === JSON.stringify(curr)));
4435
+ /** An observable of just the component-specific state for the active tab. */
4436
+ activeComponentState$ = this.activeTabState$.pipe(map(activeTab => activeTab?.componentState ?? null));
4437
+ /**
4438
+ * Adds a new tab to the state.
4439
+ * @param title The title for the new tab.
4440
+ * @param id An optional unique ID. If not provided, one will be generated.
4441
+ * @returns The ID of the newly created tab.
4442
+ */
4443
+ addTab(title, id) {
4444
+ const newTabId = id || `tab_${Date.now()}_${Math.random()}`;
4445
+ const newTab = {
4446
+ id: newTabId,
4447
+ title,
4448
+ componentState: createDefaultComponentState(),
4449
+ };
4450
+ const currentTabs = this._tabsState.getValue();
4451
+ const newTabs = new Map(currentTabs).set(newTabId, newTab);
4452
+ this._tabsState.next(newTabs);
4453
+ this.setActiveTab(newTabId);
4454
+ return newTabId;
4455
+ }
4456
+ /**
4457
+ * Removes a tab from the state.
4458
+ * @param tabIdToRemove The ID of the tab to remove.
4459
+ */
4460
+ removeTab(tabIdToRemove) {
4461
+ const currentTabs = this._tabsState.getValue();
4462
+ if (!currentTabs.has(tabIdToRemove)) {
4463
+ return;
4464
+ }
4465
+ const newTabs = new Map(currentTabs);
4466
+ newTabs.delete(tabIdToRemove);
4467
+ this._tabsState.next(newTabs);
4468
+ }
4469
+ constructor() {
4470
+ // You can initialize with default tabs if needed
4471
+ // For example:
4472
+ // this.addTab('Welcome Tab');
4473
+ }
4474
+ /**
4475
+ * Sets the currently active tab.
4476
+ * @param tabId The unique identifier of the tab to activate.
4477
+ */
4478
+ setActiveTab(tabId) {
4479
+ if (this._activeTabId.getValue() !== tabId) {
4480
+ this._activeTabId.next(tabId);
4481
+ }
4482
+ }
4483
+ /**
4484
+ * Updates the state for the currently active tab.
4485
+ * This performs a deep merge to avoid overwriting nested state.
4486
+ * @param partialState The partial state to update for the active tab.
4487
+ */
4488
+ updateActiveTabState(partialState) {
4489
+ const activeId = this._activeTabId.getValue();
4490
+ if (!activeId) {
4491
+ console.warn('Cannot update state, no active tab.');
4492
+ return;
4493
+ }
4494
+ const tabs = this._tabsState.getValue();
4495
+ const activeTabState = tabs.get(activeId);
4496
+ if (activeTabState) {
4497
+ // Use lodash merge for a deep merge
4498
+ const updatedState = merge({}, activeTabState, { componentState: partialState });
4499
+ const newTabsState = new Map(tabs);
4500
+ newTabsState.set(activeId, updatedState);
4501
+ this._tabsState.next(newTabsState);
4502
+ }
4503
+ }
4504
+ /**
4505
+ * Gets an observable for a specific tab's state.
4506
+ * @param tabId The ID of the tab to observe.
4507
+ */
4508
+ getTabState(tabId) {
4509
+ return this._tabsState.pipe(map(tabs => tabs.get(tabId)));
4510
+ }
4511
+ /**
4512
+ * Determines the ID of the next tab to activate when a tab is closed.
4513
+ * @param idToRemove The ID of the tab being removed.
4514
+ * @returns The ID of the next tab to activate, or null if no tabs are left.
4515
+ */
4516
+ getNextActiveTabId(idToRemove) {
4517
+ const tabsMap = this._tabsState.getValue();
4518
+ if (tabsMap.size <= 1) {
4519
+ return null;
4520
+ }
4521
+ const tabIds = Array.from(tabsMap.keys());
4522
+ const removedIndex = tabIds.indexOf(idToRemove);
4523
+ // If it's the last tab in the list, activate the one before it. Otherwise, activate the next one.
4524
+ const nextIndex = removedIndex === tabIds.length - 1 ? removedIndex - 1 : removedIndex + 1;
4525
+ return tabIds[nextIndex];
4526
+ }
4527
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TabStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4528
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TabStateService, providedIn: 'root' });
4529
+ }
4530
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: TabStateService, decorators: [{
4531
+ type: Injectable,
4532
+ args: [{
4533
+ providedIn: 'root',
4534
+ }]
4535
+ }], ctorParameters: () => [] });
4536
+
4537
+ class CideLytRequestService {
4538
+ requestVisible = false;
4539
+ // Modern Angular v20+ pattern: Use Signals instead of BehaviorSubject
4540
+ tabsSignal = signal([], ...(ngDevMode ? [{ debugName: "tabsSignal" }] : []));
4541
+ activeTabIdSignal = signal(null, ...(ngDevMode ? [{ debugName: "activeTabIdSignal" }] : []));
4542
+ // Computed signals for derived state and public readonly access
4543
+ tabs = computed(() => this.tabsSignal(), ...(ngDevMode ? [{ debugName: "tabs" }] : []));
4544
+ activeTabId = computed(() => this.activeTabIdSignal(), ...(ngDevMode ? [{ debugName: "activeTabId" }] : []));
4545
+ activeTab = computed(() => this.tabsSignal().find(tab => tab.active), ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
4546
+ // Use inject() for all dependencies
4547
+ router = inject(Router);
4548
+ routeReuseStrategy = inject(RouteReuseStrategy);
4549
+ tabStateService = inject(TabStateService);
4550
+ sidedrawerService = inject(CideLytSidedrawerService);
4551
+ sharedService = inject(CideLytSharedService);
4552
+ floatingContainerService = inject(CideEleFloatingContainerService);
4553
+ appStateService = inject(AppStateService);
4554
+ constructor() {
4555
+ // Initialize router
4556
+ this.router = inject(Router);
4557
+ this.router.routeReuseStrategy = new CustomRouteReuseStrategy();
4558
+ // Register tab management callback with shared service to avoid circular dependency
4559
+ this.sharedService.registerTabManagement(this.handleRouteBasedTabManagement.bind(this));
4560
+ // Register request visibility callback
4561
+ this.sharedService.registerRequestVisibility(this.handleRequestVisibility.bind(this));
4562
+ // Register entity change handler with AppStateService to close tabs when entity changes
4563
+ // This is safe because AppStateService no longer imports CideLytRequestService
4564
+ this.appStateService.registerEntityChangeHandler((entity) => {
4565
+ console.log('🏢 [RequestService] Entity changed, closing all tabs:', entity?._id);
4566
+ this.closeAllTabs();
4567
+ });
4568
+ }
4569
+ /**
4570
+ * Handle request visibility changes from shared service
4571
+ */
4572
+ handleRequestVisibility(show) {
4573
+ if (show) {
4574
+ this.showRequest();
4575
+ }
4576
+ else {
4577
+ this.hideRequest();
4578
+ }
4579
+ }
4580
+ /**
4581
+ * Handle sidebar sync when tab becomes active
4582
+ */
4583
+ handleSidebarSync(tabInfo) {
4584
+ // The sidebar sync will be handled automatically by the shared service
4585
+ // when page data is loaded, so we don't need additional logic here
4586
+ console.log('🔗 REQUEST SERVICE: Tab activated, sidebar sync handled by shared service:', tabInfo.title);
4587
+ }
4588
+ /**
4589
+ * Handle route-based tab management - called by shared service
4590
+ * This method checks for existing tabs and creates/activates as needed
4591
+ */
4592
+ handleRouteBasedTabManagement(currentRoutePath, queryParams, layout, pageData, pageCode) {
4593
+ console.log('handleRouteBasedTabManagement', currentRoutePath, queryParams, layout, pageData, pageCode);
4594
+ const currentTabs = this.tabsSignal();
4595
+ const tabAlreadyExists = currentTabs.find((t) => {
4596
+ const tRoutePath = t.route.startsWith('/') ? t.route : '/' + t.route;
4597
+ const tParamsMatch = JSON.stringify(t.params || {}) === JSON.stringify(queryParams || {});
4598
+ return tRoutePath === currentRoutePath && tParamsMatch;
4599
+ });
4600
+ console.log('tabAlreadyExists', tabAlreadyExists);
4601
+ if (!tabAlreadyExists) {
4602
+ const title = pageData.data?.page?.sypg_title || pageCode.toString() || 'Untitled Tab';
4603
+ console.log(`🆕 Adding new tab: ${title}`);
4604
+ this.addTab(title, currentRoutePath, queryParams, layout);
4605
+ }
4606
+ else if (tabAlreadyExists && !tabAlreadyExists.active) {
4607
+ console.log(`🔄 Activating existing tab: ${tabAlreadyExists.title}`);
4608
+ this.activateTab(tabAlreadyExists.id);
4609
+ }
4610
+ else if (tabAlreadyExists && tabAlreadyExists.active) {
4611
+ console.log(`🔄 Activating existing tab: ${tabAlreadyExists.title}`);
4612
+ this.activateTab(tabAlreadyExists.id);
4613
+ }
4614
+ }
4615
+ addTab(title, route, params, layout) {
4616
+ console.log('addTab', title, route, params, layout);
4617
+ const id = this.generateId();
4618
+ const currentTabs = this.tabsSignal();
4619
+ console.log('currentTabs', currentTabs, route);
4620
+ // Tab reuse disabled - always create new tab
4621
+ // Removed existing tab check to prevent tab reuse
4622
+ // Create new tab
4623
+ const newTab = {
4624
+ id,
4625
+ title,
4626
+ route,
4627
+ params,
4628
+ active: true,
4629
+ layout,
4630
+ sytm_page_id_sypg: '',
4631
+ themeId: ''
4632
+ };
4633
+ // Deactivate all other tabs
4634
+ currentTabs.forEach((tab) => tab.active = false);
4635
+ console.log('currentTabs after deactivate', currentTabs, 'activateTab', id);
4636
+ // Add new tab
4637
+ this.tabsSignal.set([...currentTabs, newTab]);
4638
+ this.activeTabIdSignal.set(id);
4639
+ // Sync with TabStateService
4640
+ this.tabStateService.addTab(title, id);
4641
+ // Request visibility is now controlled by layout configuration in setPageData
4642
+ // No need to automatically show request wrapper here
4643
+ // Navigate to the new route
4644
+ if (params) {
4645
+ this.router.navigate([route], { queryParams: params });
4646
+ }
4647
+ else {
4648
+ this.router.navigate([route]);
4649
+ }
4650
+ return id;
4651
+ }
4652
+ activateTab(tabId) {
4653
+ console.log('🔍 REQUEST SERVICE: Activating tab:', tabId);
4654
+ const tabs = this.tabsSignal();
4655
+ const tabToActivate = tabs.find(tab => tab.id === tabId);
4656
+ if (!tabToActivate) {
4657
+ console.warn('⚠️ Tab not found:', tabId);
4658
+ return;
4659
+ }
4660
+ // Before navigating, ensure the route is fresh by clearing any cached state
4661
+ // This prevents old component data from persisting when reopening a tab
4662
+ if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
4663
+ let pathKey = tabToActivate.route.startsWith('/') ? tabToActivate.route.substring(1) : tabToActivate.route;
4664
+ if (tabToActivate.params && Object.keys(tabToActivate.params).length > 0) {
4665
+ const queryParamsString = Object.keys(tabToActivate.params)
4666
+ .sort()
4667
+ .map(key => `${key}=${tabToActivate.params[key]}`)
4668
+ .join('&');
4669
+ pathKey += '?' + queryParamsString;
4670
+ }
4671
+ // Clear any cached route to ensure fresh component state
4672
+ this.routeReuseStrategy.clearStoredRoute(pathKey);
4673
+ console.log(`🔄 REQUEST SERVICE: Cleared cached route ${pathKey} before activating tab`);
4674
+ }
4675
+ // Update tabs: deactivate all, then activate the target
4676
+ const updatedTabs = tabs.map(tab => ({
4677
+ ...tab,
4678
+ active: tab.id === tabId
4679
+ }));
4680
+ this.tabsSignal.set(updatedTabs);
4681
+ this.activeTabIdSignal.set(tabId);
4682
+ console.log('✅ REQUEST SERVICE: Tab activated:', tabToActivate.title);
4683
+ // Navigate to the tab's route
4684
+ this.router.navigate([tabToActivate.route], {
4685
+ queryParams: tabToActivate.params || {},
4686
+ replaceUrl: false
4687
+ });
4688
+ // Trigger sidebar sync for the activated tab by calling shared service
4689
+ // The shared service will handle the sidebar sync when the route loads
4690
+ console.log('🔗 REQUEST SERVICE: Sidebar sync will be handled by route change');
4691
+ }
4692
+ closeTab(id) {
4693
+ const currentTabs = this.tabsSignal();
4694
+ const tabIndex = currentTabs.findIndex((tab) => tab.id === id);
4695
+ if (tabIndex === -1)
4696
+ return;
4697
+ const tabToClose = currentTabs[tabIndex];
4698
+ const wasActive = tabToClose.active;
4699
+ // Sync with TabStateService by removing the tab state
4700
+ this.tabStateService.removeTab(id);
4701
+ // Clear stored route from strategy and ensure proper component cleanup
4702
+ if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
4703
+ // Generate pathKey in the same format as CustomRouteReuseStrategy.getPathKey()
4704
+ // Remove leading slash to match the format used by getPathKey
4705
+ let pathKey = tabToClose.route.startsWith('/') ? tabToClose.route.substring(1) : tabToClose.route;
4706
+ // Only add query params if the route is marked for reuse (matching getPathKey logic)
4707
+ // But we'll try to clear with both formats to be safe
4708
+ if (tabToClose.params && Object.keys(tabToClose.params).length > 0) {
4709
+ const queryParamsString = Object.keys(tabToClose.params)
4710
+ .sort()
4711
+ .map(key => `${key}=${tabToClose.params[key]}`)
4712
+ .join('&');
4713
+ pathKey += '?' + queryParamsString;
4714
+ }
4715
+ // Clear the stored route and destroy the component instance
4716
+ this.routeReuseStrategy.clearStoredRoute(pathKey);
4717
+ // Also try clearing without query params in case the route wasn't marked for reuse
4718
+ // This ensures we catch all possible pathKey variations
4719
+ const pathKeyWithoutParams = tabToClose.route.startsWith('/') ? tabToClose.route.substring(1) : tabToClose.route;
4720
+ if (pathKeyWithoutParams !== pathKey) {
4721
+ this.routeReuseStrategy.clearStoredRoute(pathKeyWithoutParams);
4722
+ }
4723
+ // Force a route refresh to ensure clean component state
4724
+ // This prevents old data from persisting when the same route is reopened
4725
+ console.log(`🧹 REQUEST SERVICE: Cleared stored route for ${pathKey} and destroyed component instance`);
4726
+ // If this was the active tab, navigate away to ensure component is removed from DOM
4727
+ if (wasActive) {
4728
+ // The navigation to the new active tab will happen below, which will properly clear the router outlet
4729
+ // But we also want to ensure the component is removed immediately
4730
+ console.log(`🧹 REQUEST SERVICE: Active tab closed, component will be removed on navigation`);
4731
+ }
4732
+ }
4733
+ // Remove the tab from this service's list
4734
+ const newTabs = currentTabs.filter((tab) => tab.id !== id);
4735
+ // If we're closing the active tab, activate the next available tab
4736
+ if (wasActive && newTabs.length > 0) {
4737
+ const newActiveIndex = Math.max(0, Math.min(tabIndex, newTabs.length - 1));
4738
+ const newActiveTab = newTabs[newActiveIndex];
4739
+ // Update sidedrawer for the new active tab
4740
+ this.sidedrawerService.updateDrawerItems(newActiveTab.layout);
4741
+ newTabs[newActiveIndex].active = true;
4742
+ this.activeTabIdSignal.set(newActiveTab.id);
4743
+ this.tabStateService.setActiveTab(newActiveTab.id);
4744
+ // Navigate to the new active tab's route
4745
+ if (newActiveTab.params) {
4746
+ this.router.navigate([newActiveTab.route], { queryParams: newActiveTab.params });
4747
+ }
4748
+ else {
4749
+ this.router.navigate([newActiveTab.route]);
4750
+ }
4751
+ }
4752
+ else if (newTabs.length === 0) {
4753
+ this.activeTabIdSignal.set(null);
4754
+ this.tabStateService.setActiveTab(null);
4755
+ // Clear sidedrawer as no tabs are open
4756
+ this.sidedrawerService.updateDrawerItems(undefined);
4757
+ // Redirect to home screen when all tabs are closed
4758
+ this.router.navigate(['/control-panel']);
4759
+ }
4760
+ this.tabsSignal.set(newTabs);
4761
+ // Request wrapper visibility is now controlled by layout configuration
4762
+ // The wrapper will be hidden/shown based on sytm_layout_request.status in setPageData
4763
+ }
4764
+ /**
4765
+ * Close all tabs and navigate to home
4766
+ * This is used when entity changes to ensure all components are destroyed and refreshed
4767
+ */
4768
+ closeAllTabs() {
4769
+ console.log('🧹 REQUEST SERVICE: Closing all tabs due to entity change');
4770
+ const currentTabs = this.tabsSignal();
4771
+ // Close all floating containers first
4772
+ this.floatingContainerService.hideAll();
4773
+ console.log('🧹 REQUEST SERVICE: Closed all floating containers');
4774
+ // Clear all tabs from TabStateService
4775
+ currentTabs.forEach(tab => {
4776
+ this.tabStateService.removeTab(tab.id);
4777
+ // Clear stored routes from route reuse strategy
4778
+ if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
4779
+ let pathKey = tab.route.startsWith('/') ? tab.route.substring(1) : tab.route;
4780
+ if (tab.params && Object.keys(tab.params).length > 0) {
4781
+ const queryParamsString = Object.keys(tab.params)
4782
+ .sort()
4783
+ .map(key => `${key}=${tab.params[key]}`)
4784
+ .join('&');
4785
+ pathKey += '?' + queryParamsString;
4786
+ }
4787
+ this.routeReuseStrategy.clearStoredRoute(pathKey);
4788
+ // Also clear without query params
4789
+ const pathKeyWithoutParams = tab.route.startsWith('/') ? tab.route.substring(1) : tab.route;
4790
+ if (pathKeyWithoutParams !== pathKey) {
4791
+ this.routeReuseStrategy.clearStoredRoute(pathKeyWithoutParams);
4792
+ }
4793
+ }
4794
+ });
4795
+ // Clear all tabs
4796
+ this.tabsSignal.set([]);
4797
+ this.activeTabIdSignal.set(null);
4798
+ this.tabStateService.setActiveTab(null);
4799
+ // Clear sidedrawer
4800
+ this.sidedrawerService.updateDrawerItems(undefined);
4801
+ // Navigate to home
4802
+ this.router.navigate(['/control-panel']);
4803
+ console.log('✅ REQUEST SERVICE: All tabs and floating containers closed, navigated to home');
4804
+ }
4805
+ // Hide Request
4806
+ hideRequest() {
4807
+ console.log('🚫 REQUEST SERVICE - Hiding request wrapper');
4808
+ this.requestVisible = false;
4809
+ document.querySelector(`#cide-lyt-request-wrapper`)?.classList.add('cide-lyt-request-wrapper-hide');
4810
+ document.querySelector(`body`)?.classList.remove('cide-lyt-request-exist');
4811
+ }
4812
+ // Show Request
4813
+ showRequest() {
4814
+ console.log('✅ REQUEST SERVICE - Showing request wrapper');
4815
+ this.requestVisible = true;
4816
+ document.querySelector(`#cide-lyt-request-wrapper`)?.classList.remove('cide-lyt-request-wrapper-hide');
4817
+ document.querySelector(`body`)?.classList.add('cide-lyt-request-exist');
4818
+ }
4819
+ generateId() {
4820
+ return 'tab-' + Math.random().toString(36).substr(2, 9);
4821
+ }
4822
+ updateTabScrollPosition(tabId, scrollTop, scrollLeft) {
4823
+ const currentTabs = this.tabsSignal();
4824
+ const tabIndex = currentTabs.findIndex((t) => t.id === tabId);
4825
+ if (tabIndex !== -1) {
4826
+ const tabToUpdate = currentTabs[tabIndex];
4827
+ tabToUpdate.scrollTop = scrollTop;
4828
+ tabToUpdate.scrollLeft = scrollLeft;
4829
+ // Create a new array reference to trigger change detection
4830
+ this.tabsSignal.set([...currentTabs]);
4831
+ }
4832
+ }
4833
+ /**
4834
+ * Force refresh a route by clearing its cached state
4835
+ * This is useful when you want to ensure a clean component state
4836
+ * @param route The route path to refresh
4837
+ * @param params Optional query parameters
4838
+ */
4839
+ forceRefreshRoute(route, params) {
4840
+ if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
4841
+ let pathKey = route.startsWith('/') ? route.substring(1) : route;
4842
+ if (params && Object.keys(params).length > 0) {
4843
+ const queryParamsString = Object.keys(params)
4844
+ .sort()
4845
+ .map(key => `${key}=${params[key]}`)
4846
+ .join('&');
4847
+ pathKey += '?' + queryParamsString;
4848
+ }
4849
+ // Clear the stored route to force a fresh component instance
4850
+ this.routeReuseStrategy.clearStoredRoute(pathKey);
4851
+ console.log(`🔄 REQUEST SERVICE: Force refreshed route ${pathKey}`);
4852
+ }
4853
+ }
4854
+ /**
4855
+ * Get information about cached routes (useful for debugging)
4856
+ */
4857
+ getCachedRoutesInfo() {
4858
+ if (this.routeReuseStrategy instanceof CustomRouteReuseStrategy) {
4859
+ const count = this.routeReuseStrategy.getStoredRoutesCount();
4860
+ return { count, routes: [] }; // Could be enhanced to return actual route paths
4861
+ }
4862
+ return { count: 0, routes: [] };
4863
+ }
4864
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytRequestService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
4865
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytRequestService, providedIn: 'root' });
4866
+ }
4867
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytRequestService, decorators: [{
4868
+ type: Injectable,
4869
+ args: [{
4870
+ providedIn: 'root'
4871
+ }]
4872
+ }], ctorParameters: () => [] });
4873
+
4833
4874
  // Type guard for setState/getState
4834
4875
  function hasStateMethods(instance) {
4835
4876
  return !!instance && typeof instance.getState === 'function' && typeof instance.setState === 'function';
@@ -4913,8 +4954,8 @@ class CideLytSidedrawerWrapperComponent {
4913
4954
  }
4914
4955
  ngOnInit() {
4915
4956
  // Initialize the component map (You'd likely populate this from a config or service)
4916
- this.componentMap['drowar_notes'] = () => import('./cloud-ide-layout-sidedrawer-notes.component-DMzp8tJc.mjs').then(m => m.CideLytSidedrawerNotesComponent);
4917
- this.componentMap['drawer_theme'] = () => import('./cloud-ide-layout-drawer-theme.component-J2GTGGFO.mjs').then(m => m.CideLytDrawerThemeComponent);
4957
+ this.componentMap['drowar_notes'] = () => import('./cloud-ide-layout-sidedrawer-notes.component-C2tOsEC4.mjs').then(m => m.CideLytSidedrawerNotesComponent);
4958
+ this.componentMap['drawer_theme'] = () => import('./cloud-ide-layout-drawer-theme.component-DV0QsDsO.mjs').then(m => m.CideLytDrawerThemeComponent);
4918
4959
  }
4919
4960
  async loadComponent(configFor) {
4920
4961
  console.log('🔍 SIDEDRAWER - Loading component:', configFor, 'Current tab:', this.currentTabId);
@@ -6581,7 +6622,7 @@ const layoutControlPannelChildRoutes = [{
6581
6622
  },
6582
6623
  {
6583
6624
  path: "home",
6584
- loadComponent: () => import('./cloud-ide-layout-home-wrapper.component-DaonafYv.mjs').then(c => c.CideLytHomeWrapperComponent),
6625
+ loadComponent: () => import('./cloud-ide-layout-home-wrapper.component-DjRHVU59.mjs').then(c => c.CideLytHomeWrapperComponent),
6585
6626
  canActivate: [authGuard],
6586
6627
  data: {
6587
6628
  sypg_page_code: "cide_lyt_home" // Used by RequestService to fetch tab properties
@@ -6589,7 +6630,7 @@ const layoutControlPannelChildRoutes = [{
6589
6630
  },
6590
6631
  {
6591
6632
  path: "dashboard-manager",
6592
- loadComponent: () => import('./cloud-ide-layout-dashboard-manager.component-C-kubr7Z.mjs').then(c => c.DashboardManagerComponent),
6633
+ loadComponent: () => import('./cloud-ide-layout-dashboard-manager.component-C8rnVZpP.mjs').then(c => c.DashboardManagerComponent),
6593
6634
  canActivate: [authGuard],
6594
6635
  data: {
6595
6636
  sypg_page_code: "cide_lyt_dashboard_manager"
@@ -8156,4 +8197,4 @@ var floatingEntityRightsSharing_component = /*#__PURE__*/Object.freeze({
8156
8197
  */
8157
8198
 
8158
8199
  export { AppStateHelperService as A, CideLytSharedWrapperComponent as C, ENVIRONMENT_CONFIG as E, NotificationSettingsService as N, RightsService as R, CideLytSidebarService as a, CideLytRequestService as b, CideLytSidedrawerService as c, CideLytThemeService as d, AppStateService as e, CloudIdeLayoutService as f, CloudIdeLayoutComponent as g, CideLytSharedService as h, ComponentContextService as i, layoutControlPannelChildRoutes as j, CustomRouteReuseStrategy as k, layoutRoutes as l, CideLytUserStatusService as m, CacheManagerService as n, CideLytFileManagerService as o, processThemeVariable as p, CideLytFloatingEntityRightsSharingComponent as q, CideLytFloatingEntityRightsSharingService as r, setCSSVariable as s, themeFactory as t };
8159
- //# sourceMappingURL=cloud-ide-layout-cloud-ide-layout-DJxJ2FIt.mjs.map
8200
+ //# sourceMappingURL=cloud-ide-layout-cloud-ide-layout-DdMf1j7_.mjs.map