cloud-ide-layout 1.0.143 → 1.0.146

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 (18) hide show
  1. package/fesm2022/{cloud-ide-layout-cloud-ide-layout-ChGVLIdx.mjs → cloud-ide-layout-cloud-ide-layout-JYQBTRrb.mjs} +7 -10
  2. package/fesm2022/cloud-ide-layout-cloud-ide-layout-JYQBTRrb.mjs.map +1 -0
  3. package/fesm2022/cloud-ide-layout-dashboard-cards.service-BGaKcq7H.mjs.map +1 -1
  4. package/fesm2022/{cloud-ide-layout-dashboard-manager.component-CexN59ty.mjs → cloud-ide-layout-dashboard-manager.component-CpjdIeuX.mjs} +2 -2
  5. package/fesm2022/{cloud-ide-layout-dashboard-manager.component-CexN59ty.mjs.map → cloud-ide-layout-dashboard-manager.component-CpjdIeuX.mjs.map} +1 -1
  6. package/fesm2022/{cloud-ide-layout-drawer-theme.component-CBRIZvx2.mjs → cloud-ide-layout-drawer-theme.component-BavBnxPb.mjs} +2 -2
  7. package/fesm2022/{cloud-ide-layout-drawer-theme.component-CBRIZvx2.mjs.map → cloud-ide-layout-drawer-theme.component-BavBnxPb.mjs.map} +1 -1
  8. package/fesm2022/{cloud-ide-layout-floating-entity-selection.component-CcAdnHFQ.mjs → cloud-ide-layout-floating-entity-selection.component-D9xDFZ2U.mjs} +118 -74
  9. package/fesm2022/cloud-ide-layout-floating-entity-selection.component-D9xDFZ2U.mjs.map +1 -0
  10. package/fesm2022/{cloud-ide-layout-home-wrapper.component-B0kKQ3pC.mjs → cloud-ide-layout-home-wrapper.component-CcZAQSRK.mjs} +66 -18
  11. package/fesm2022/cloud-ide-layout-home-wrapper.component-CcZAQSRK.mjs.map +1 -0
  12. package/fesm2022/{cloud-ide-layout-sidedrawer-notes.component-BE7biT8I.mjs → cloud-ide-layout-sidedrawer-notes.component-CfMjTTs9.mjs} +2 -2
  13. package/fesm2022/{cloud-ide-layout-sidedrawer-notes.component-BE7biT8I.mjs.map → cloud-ide-layout-sidedrawer-notes.component-CfMjTTs9.mjs.map} +1 -1
  14. package/fesm2022/cloud-ide-layout.mjs +1 -1
  15. package/package.json +1 -1
  16. package/fesm2022/cloud-ide-layout-cloud-ide-layout-ChGVLIdx.mjs.map +0 -1
  17. package/fesm2022/cloud-ide-layout-floating-entity-selection.component-CcAdnHFQ.mjs.map +0 -1
  18. package/fesm2022/cloud-ide-layout-home-wrapper.component-B0kKQ3pC.mjs.map +0 -1
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { CommonModule } from '@angular/common';
4
- import { CideEleFloatingContainerService, NotificationService, CideEleButtonComponent, CideIconComponent } from 'cloud-ide-element';
5
- import { e as AppStateService, A as AppStateHelperService } from './cloud-ide-layout-cloud-ide-layout-ChGVLIdx.mjs';
4
+ import { Router } from '@angular/router';
5
+ import { CideEleFloatingContainerService, NotificationService, CideEleButtonComponent, CideIconComponent, CideSpinnerComponent } from 'cloud-ide-element';
6
+ import { d as AppStateService, A as AppStateHelperService } from './cloud-ide-layout-cloud-ide-layout-JYQBTRrb.mjs';
6
7
  import { CideSharedOrgStructureComponent } from 'cloud-ide-shared';
7
8
  import { CloudIdeAuthService } from 'cloud-ide-auth';
8
9
 
@@ -17,6 +18,7 @@ class CideLytFloatingEntitySelectionComponent {
17
18
  floatingContainerService = inject(CideEleFloatingContainerService);
18
19
  authService = inject(CloudIdeAuthService);
19
20
  notificationService = inject(NotificationService);
21
+ router = inject(Router);
20
22
  // Loading state
21
23
  isSwitchingEntity = signal(false, ...(ngDevMode ? [{ debugName: "isSwitchingEntity" }] : []));
22
24
  ngOnInit() {
@@ -46,41 +48,63 @@ class CideLytFloatingEntitySelectionComponent {
46
48
  this.isSwitchingEntity.set(true);
47
49
  // Call switch entity API
48
50
  this.authService.switchEntity(entity._id).subscribe({
49
- next: (response) => {
50
- this.isSwitchingEntity.set(false);
51
+ next: async (response) => {
52
+ // Wait for response and check if successful
51
53
  if (response?.success === true && response?.data) {
52
54
  // CRITICAL: Update token FIRST so subsequent API calls can use it
55
+ // The setter automatically saves to localStorage (same as login flow)
53
56
  // This prevents 401 errors on other API calls
54
57
  if (response.token) {
55
58
  this.authService.auth_token = response.token;
56
- console.log('✅ Token updated after entity switch');
59
+ console.log('✅ Token updated and saved to localStorage after entity switch');
57
60
  }
58
61
  else {
59
62
  console.warn('⚠️ No token received in switch entity response');
63
+ this.notificationService.error('No token received. Entity switch failed.');
64
+ this.isSwitchingEntity.set(false);
65
+ return;
60
66
  }
61
- // Update user data if provided
67
+ // Store user data in auth service (saves to localStorage)
68
+ // Follow same pattern as sign-in component
62
69
  if (response.data.auth_user_mst) {
63
70
  this.authService.storeUserData(response.data.auth_user_mst);
64
- this.appStateHelperService.setUser(response.data.auth_user_mst);
65
71
  }
66
- // Update active entity in AppStateService
72
+ // Synchronize AppStateService with the same user data (saves to localStorage via effect)
73
+ // Follow same pattern as sign-in component
74
+ if (response.data.auth_user_mst) {
75
+ this.appStateService.setUser(response.data.auth_user_mst);
76
+ }
77
+ // Store active entity data (saves to localStorage via effect)
67
78
  // Use entity from response if available, otherwise use the selected entity
79
+ // Follow same pattern as sign-in component
68
80
  const entityToSet = response.data.core_system_entity || entity;
69
81
  if (entityToSet && entityToSet._id) {
70
82
  this.appStateService.setActiveEntity(entityToSet);
71
- this.appStateHelperService.setActiveEntity(entityToSet);
72
- console.log('✅ Active entity updated:', entityToSet._id);
83
+ console.log('✅ Active entity updated and saved to localStorage:', entityToSet._id);
73
84
  }
74
85
  else {
75
86
  console.warn('⚠️ No entity data available to set as active entity');
76
87
  }
88
+ // Wait a moment to ensure token and state are fully stored
89
+ await new Promise(resolve => setTimeout(resolve, 200));
77
90
  // Show success notification
78
91
  this.notificationService.success('Entity switched successfully');
79
92
  // Close drawer
80
93
  this.closeContainer();
94
+ // Navigate to home after successful entity switch and token storage
95
+ // Wait a bit more to ensure all state updates are complete
96
+ await new Promise(resolve => setTimeout(resolve, 100));
97
+ this.router.navigate(['/control-panel']);
98
+ console.log('✅ Navigated to home after successful entity switch');
99
+ // Hide loader after navigation completes
100
+ this.isSwitchingEntity.set(false);
81
101
  }
82
102
  else {
83
- this.notificationService.error(response?.message || 'Failed to switch entity');
103
+ // Entity switch was not successful
104
+ const errorMessage = response?.message || 'Failed to switch entity';
105
+ this.notificationService.error(errorMessage);
106
+ console.error('❌ Entity switch failed:', errorMessage);
107
+ this.isSwitchingEntity.set(false);
84
108
  }
85
109
  },
86
110
  error: (error) => {
@@ -88,6 +112,7 @@ class CideLytFloatingEntitySelectionComponent {
88
112
  console.error('Error switching entity:', error);
89
113
  const errorMessage = error?.error?.message || error?.message || 'Failed to switch entity';
90
114
  this.notificationService.error(errorMessage);
115
+ // Do not navigate on error - stay on current page
91
116
  }
92
117
  });
93
118
  }
@@ -112,37 +137,46 @@ class CideLytFloatingEntitySelectionComponent {
112
137
  this.floatingContainerService.hideAll();
113
138
  }
114
139
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CideLytFloatingEntitySelectionComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
115
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: CideLytFloatingEntitySelectionComponent, isStandalone: true, selector: "cide-lyt-floating-entity-selection", inputs: { allowSwitching: { classPropertyName: "allowSwitching", publicName: "allowSwitching", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
116
- <!-- Entity Selection Content using Org Structure Component -->
117
- <div class="tw-flex tw-flex-col tw-h-full">
118
- <!-- Header -->
119
- <div class="tw-bg-white tw-border-b tw-border-gray-200 tw-px-4 tw-py-3 tw-flex-shrink-0">
120
- <div class="tw-flex tw-items-center tw-justify-between">
121
- <div class="tw-flex tw-items-center tw-space-x-2">
122
- <cide-ele-icon class="tw-text-blue-600">account_tree</cide-ele-icon>
123
- <h3 class="tw-text-sm tw-font-semibold tw-text-gray-900">Entity Structure</h3>
124
- </div>
125
- @if (mode() === 'selection') {
126
- <button cideEleButton variant="secondary" size="xs" type="button" (click)="onCancel()">
127
- Cancel
128
- </button>
129
- }
130
- </div>
131
- </div>
132
-
133
- <!-- Org Structure Component -->
134
- <div class="tw-flex-1 tw-overflow-hidden">
135
- <cide-shared-org-structure
136
- [allowSwitching]="allowSwitching()"
137
- [showActions]="showActions()"
138
- [mode]="mode()"
139
- (entityClick)="onEntityClick($event)"
140
- (entitySelect)="onEntitySelect($event)"
141
- (entityView)="onEntityView($event)">
142
- </cide-shared-org-structure>
143
- </div>
144
- </div>
145
- `, isInline: true, styles: [".tw-dashed{border-style:dashed}.tw-bg-gray-300{background-color:#d1d5db}.level-0{border-top-color:#3b82f6!important}.level-1{border-top-color:#f59e0b!important}.level-2{border-top-color:#10b981!important}.level-3{border-top-color:#8b5cf6!important}.level-4{border-top-color:#ef4444!important}.level-5{border-top-color:#06b6d4!important}.connection-line-compact{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton], cide-ele-button", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideSharedOrgStructureComponent, selector: "cide-shared-org-structure", inputs: ["allowSwitching", "showActions", "mode"], outputs: ["entityClick", "entitySelect", "entityView"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
140
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: CideLytFloatingEntitySelectionComponent, isStandalone: true, selector: "cide-lyt-floating-entity-selection", inputs: { allowSwitching: { classPropertyName: "allowSwitching", publicName: "allowSwitching", isSignal: true, isRequired: false, transformFunction: null }, showActions: { classPropertyName: "showActions", publicName: "showActions", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
141
+ <!-- Entity Selection Content using Org Structure Component -->
142
+ <div class="tw-flex tw-flex-col tw-h-full tw-relative">
143
+ <!-- Loading Overlay -->
144
+ @if (isSwitchingEntity()) {
145
+ <div class="tw-absolute tw-inset-0 tw-bg-white/80 tw-backdrop-blur-sm tw-z-50 tw-flex tw-items-center tw-justify-center tw-flex-col tw-gap-3">
146
+ <cide-ele-spinner [size]="'lg'"></cide-ele-spinner>
147
+ <p class="tw-text-sm tw-font-medium tw-text-gray-700">Switching entity...</p>
148
+ <p class="tw-text-xs tw-text-gray-500">Please wait</p>
149
+ </div>
150
+ }
151
+
152
+ <!-- Header -->
153
+ <div class="tw-bg-white tw-border-b tw-border-gray-200 tw-px-4 tw-py-3 tw-flex-shrink-0" [class.tw-opacity-50]="isSwitchingEntity()" [class.tw-pointer-events-none]="isSwitchingEntity()">
154
+ <div class="tw-flex tw-items-center tw-justify-between">
155
+ <div class="tw-flex tw-items-center tw-space-x-2">
156
+ <cide-ele-icon class="tw-text-blue-600">account_tree</cide-ele-icon>
157
+ <h3 class="tw-text-sm tw-font-semibold tw-text-gray-900">Entity Structure</h3>
158
+ </div>
159
+ @if (mode() === 'selection') {
160
+ <button cideEleButton variant="secondary" size="xs" type="button" (click)="onCancel()" [disabled]="isSwitchingEntity()">
161
+ Cancel
162
+ </button>
163
+ }
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Org Structure Component -->
168
+ <div class="tw-flex-1 tw-overflow-hidden" [class.tw-opacity-50]="isSwitchingEntity()" [class.tw-pointer-events-none]="isSwitchingEntity()">
169
+ <cide-shared-org-structure
170
+ [allowSwitching]="allowSwitching()"
171
+ [showActions]="showActions()"
172
+ [mode]="mode()"
173
+ (entityClick)="onEntityClick($event)"
174
+ (entitySelect)="onEntitySelect($event)"
175
+ (entityView)="onEntityView($event)">
176
+ </cide-shared-org-structure>
177
+ </div>
178
+ </div>
179
+ `, isInline: true, styles: [".tw-dashed{border-style:dashed}.tw-bg-gray-300{background-color:#d1d5db}.level-0{border-top-color:#3b82f6!important}.level-1{border-top-color:#f59e0b!important}.level-2{border-top-color:#10b981!important}.level-3{border-top-color:#8b5cf6!important}.level-4{border-top-color:#ef4444!important}.level-5{border-top-color:#06b6d4!important}.connection-line-compact{pointer-events:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton], cide-ele-button", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideSharedOrgStructureComponent, selector: "cide-shared-org-structure", inputs: ["allowSwitching", "showActions", "mode"], outputs: ["entityClick", "entitySelect", "entityView"] }, { kind: "component", type: CideSpinnerComponent, selector: "cide-ele-spinner", inputs: ["size", "type"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
146
180
  }
147
181
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CideLytFloatingEntitySelectionComponent, decorators: [{
148
182
  type: Component,
@@ -150,39 +184,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
150
184
  CommonModule,
151
185
  CideEleButtonComponent,
152
186
  CideIconComponent,
153
- CideSharedOrgStructureComponent
154
- ], template: `
155
- <!-- Entity Selection Content using Org Structure Component -->
156
- <div class="tw-flex tw-flex-col tw-h-full">
157
- <!-- Header -->
158
- <div class="tw-bg-white tw-border-b tw-border-gray-200 tw-px-4 tw-py-3 tw-flex-shrink-0">
159
- <div class="tw-flex tw-items-center tw-justify-between">
160
- <div class="tw-flex tw-items-center tw-space-x-2">
161
- <cide-ele-icon class="tw-text-blue-600">account_tree</cide-ele-icon>
162
- <h3 class="tw-text-sm tw-font-semibold tw-text-gray-900">Entity Structure</h3>
163
- </div>
164
- @if (mode() === 'selection') {
165
- <button cideEleButton variant="secondary" size="xs" type="button" (click)="onCancel()">
166
- Cancel
167
- </button>
168
- }
169
- </div>
170
- </div>
171
-
172
- <!-- Org Structure Component -->
173
- <div class="tw-flex-1 tw-overflow-hidden">
174
- <cide-shared-org-structure
175
- [allowSwitching]="allowSwitching()"
176
- [showActions]="showActions()"
177
- [mode]="mode()"
178
- (entityClick)="onEntityClick($event)"
179
- (entitySelect)="onEntitySelect($event)"
180
- (entityView)="onEntityView($event)">
181
- </cide-shared-org-structure>
182
- </div>
183
- </div>
187
+ CideSharedOrgStructureComponent,
188
+ CideSpinnerComponent
189
+ ], template: `
190
+ <!-- Entity Selection Content using Org Structure Component -->
191
+ <div class="tw-flex tw-flex-col tw-h-full tw-relative">
192
+ <!-- Loading Overlay -->
193
+ @if (isSwitchingEntity()) {
194
+ <div class="tw-absolute tw-inset-0 tw-bg-white/80 tw-backdrop-blur-sm tw-z-50 tw-flex tw-items-center tw-justify-center tw-flex-col tw-gap-3">
195
+ <cide-ele-spinner [size]="'lg'"></cide-ele-spinner>
196
+ <p class="tw-text-sm tw-font-medium tw-text-gray-700">Switching entity...</p>
197
+ <p class="tw-text-xs tw-text-gray-500">Please wait</p>
198
+ </div>
199
+ }
200
+
201
+ <!-- Header -->
202
+ <div class="tw-bg-white tw-border-b tw-border-gray-200 tw-px-4 tw-py-3 tw-flex-shrink-0" [class.tw-opacity-50]="isSwitchingEntity()" [class.tw-pointer-events-none]="isSwitchingEntity()">
203
+ <div class="tw-flex tw-items-center tw-justify-between">
204
+ <div class="tw-flex tw-items-center tw-space-x-2">
205
+ <cide-ele-icon class="tw-text-blue-600">account_tree</cide-ele-icon>
206
+ <h3 class="tw-text-sm tw-font-semibold tw-text-gray-900">Entity Structure</h3>
207
+ </div>
208
+ @if (mode() === 'selection') {
209
+ <button cideEleButton variant="secondary" size="xs" type="button" (click)="onCancel()" [disabled]="isSwitchingEntity()">
210
+ Cancel
211
+ </button>
212
+ }
213
+ </div>
214
+ </div>
215
+
216
+ <!-- Org Structure Component -->
217
+ <div class="tw-flex-1 tw-overflow-hidden" [class.tw-opacity-50]="isSwitchingEntity()" [class.tw-pointer-events-none]="isSwitchingEntity()">
218
+ <cide-shared-org-structure
219
+ [allowSwitching]="allowSwitching()"
220
+ [showActions]="showActions()"
221
+ [mode]="mode()"
222
+ (entityClick)="onEntityClick($event)"
223
+ (entitySelect)="onEntitySelect($event)"
224
+ (entityView)="onEntityView($event)">
225
+ </cide-shared-org-structure>
226
+ </div>
227
+ </div>
184
228
  `, styles: [".tw-dashed{border-style:dashed}.tw-bg-gray-300{background-color:#d1d5db}.level-0{border-top-color:#3b82f6!important}.level-1{border-top-color:#f59e0b!important}.level-2{border-top-color:#10b981!important}.level-3{border-top-color:#8b5cf6!important}.level-4{border-top-color:#ef4444!important}.level-5{border-top-color:#06b6d4!important}.connection-line-compact{pointer-events:none}\n"] }]
185
229
  }], propDecorators: { allowSwitching: [{ type: i0.Input, args: [{ isSignal: true, alias: "allowSwitching", required: false }] }], showActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showActions", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }] } });
186
230
 
187
231
  export { CideLytFloatingEntitySelectionComponent };
188
- //# sourceMappingURL=cloud-ide-layout-floating-entity-selection.component-CcAdnHFQ.mjs.map
232
+ //# sourceMappingURL=cloud-ide-layout-floating-entity-selection.component-D9xDFZ2U.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloud-ide-layout-floating-entity-selection.component-D9xDFZ2U.mjs","sources":["../../../projects/cloud-ide-layout/src/lib/components/floating-entity-selection/floating-entity-selection.component.ts"],"sourcesContent":["import { Component, OnInit, inject, signal, computed, ChangeDetectionStrategy, input } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { Router } from '@angular/router';\nimport { CideEleButtonComponent, NotificationService, CideSpinnerComponent } from 'cloud-ide-element';\nimport { CideIconComponent } from 'cloud-ide-element';\nimport { AppStateService } from '../../services/app-state/app-state.service';\nimport { CideEleFloatingContainerService } from 'cloud-ide-element';\nimport { CideSharedOrgStructureComponent } from 'cloud-ide-shared';\nimport { CoreSystemEntityListResponse, switchEntityControllerResponse } from 'cloud-ide-lms-model';\nimport { CloudIdeAuthService } from 'cloud-ide-auth';\nimport { AppStateHelperService } from '../../services/app-state/app-state-helper.service';\nimport { HttpErrorResponse } from '@angular/common/http';\n\n@Component({\n selector: 'cide-lyt-floating-entity-selection',\n changeDetection: ChangeDetectionStrategy.OnPush,\n imports: [\n CommonModule,\n CideEleButtonComponent,\n CideIconComponent,\n CideSharedOrgStructureComponent,\n CideSpinnerComponent\n ],\n template: `\n <!-- Entity Selection Content using Org Structure Component -->\n <div class=\"tw-flex tw-flex-col tw-h-full tw-relative\">\n <!-- Loading Overlay -->\n @if (isSwitchingEntity()) {\n <div class=\"tw-absolute tw-inset-0 tw-bg-white/80 tw-backdrop-blur-sm tw-z-50 tw-flex tw-items-center tw-justify-center tw-flex-col tw-gap-3\">\n <cide-ele-spinner [size]=\"'lg'\"></cide-ele-spinner>\n <p class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Switching entity...</p>\n <p class=\"tw-text-xs tw-text-gray-500\">Please wait</p>\n </div>\n }\n \n <!-- Header -->\n <div class=\"tw-bg-white tw-border-b tw-border-gray-200 tw-px-4 tw-py-3 tw-flex-shrink-0\" [class.tw-opacity-50]=\"isSwitchingEntity()\" [class.tw-pointer-events-none]=\"isSwitchingEntity()\">\n <div class=\"tw-flex tw-items-center tw-justify-between\">\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <cide-ele-icon class=\"tw-text-blue-600\">account_tree</cide-ele-icon>\n <h3 class=\"tw-text-sm tw-font-semibold tw-text-gray-900\">Entity Structure</h3>\n </div>\n @if (mode() === 'selection') {\n <button cideEleButton variant=\"secondary\" size=\"xs\" type=\"button\" (click)=\"onCancel()\" [disabled]=\"isSwitchingEntity()\">\n Cancel\n </button>\n }\n </div>\n </div>\n \n <!-- Org Structure Component -->\n <div class=\"tw-flex-1 tw-overflow-hidden\" [class.tw-opacity-50]=\"isSwitchingEntity()\" [class.tw-pointer-events-none]=\"isSwitchingEntity()\">\n <cide-shared-org-structure\n [allowSwitching]=\"allowSwitching()\"\n [showActions]=\"showActions()\"\n [mode]=\"mode()\"\n (entityClick)=\"onEntityClick($event)\"\n (entitySelect)=\"onEntitySelect($event)\"\n (entityView)=\"onEntityView($event)\">\n </cide-shared-org-structure>\n </div>\n </div>\n `,\n styles: [`\n .tw-dashed {\n border-style: dashed;\n }\n .tw-bg-gray-300 {\n background-color: #d1d5db;\n }\n \n /* Level-based styling for entity cards */\n .level-0 {\n border-top-color: #3b82f6 !important; /* Blue */\n }\n .level-1 {\n border-top-color: #f59e0b !important; /* Amber */\n }\n .level-2 {\n border-top-color: #10b981 !important; /* Emerald */\n }\n .level-3 {\n border-top-color: #8b5cf6 !important; /* Violet */\n }\n .level-4 {\n border-top-color: #ef4444 !important; /* Red */\n }\n .level-5 {\n border-top-color: #06b6d4 !important; /* Cyan */\n }\n \n /* Connection line styling */\n .connection-line-compact {\n pointer-events: none;\n }\n `]\n})\nexport class CideLytFloatingEntitySelectionComponent implements OnInit {\n // Input parameters for configuration\n allowSwitching = input<boolean>(true); // Allow entity switching (default: true)\n showActions = input<boolean>(true); // Show action buttons (default: true)\n mode = input<'selection' | 'view'>('selection'); // Mode: selection or view-only\n\n // Service injections\n private appStateService = inject(AppStateService);\n private appStateHelperService = inject(AppStateHelperService);\n private floatingContainerService = inject(CideEleFloatingContainerService);\n private authService = inject(CloudIdeAuthService);\n private notificationService = inject(NotificationService);\n private router = inject(Router);\n\n // Loading state\n isSwitchingEntity = signal<boolean>(false);\n\n ngOnInit() {\n // No initialization needed - shared org structure component handles everything\n }\n\n /**\n * Handle entity click event from org structure\n */\n onEntityClick(entity: CoreSystemEntityListResponse): void {\n console.log('🏢 Entity clicked:', entity);\n // This is just a click event, no action needed unless specific handling is required\n }\n\n /**\n * Handle entity select event from org structure (for switching)\n * Calls the switch entity API to create new auth_logs entry\n */\n onEntitySelect(entity: CoreSystemEntityListResponse): void {\n console.log('🏢 Entity selected for switching:', entity);\n \n if (!entity._id) {\n this.notificationService.error('Invalid entity selected');\n return;\n }\n\n // Check if already switching\n if (this.isSwitchingEntity()) {\n return;\n }\n\n this.isSwitchingEntity.set(true);\n\n // Call switch entity API\n this.authService.switchEntity(entity._id).subscribe({\n next: async (response: switchEntityControllerResponse) => {\n // Wait for response and check if successful\n if (response?.success === true && response?.data) {\n // CRITICAL: Update token FIRST so subsequent API calls can use it\n // The setter automatically saves to localStorage (same as login flow)\n // This prevents 401 errors on other API calls\n if (response.token) {\n this.authService.auth_token = response.token;\n console.log('✅ Token updated and saved to localStorage after entity switch');\n } else {\n console.warn('⚠️ No token received in switch entity response');\n this.notificationService.error('No token received. Entity switch failed.');\n this.isSwitchingEntity.set(false);\n return;\n }\n\n // Store user data in auth service (saves to localStorage)\n // Follow same pattern as sign-in component\n if (response.data.auth_user_mst) {\n this.authService.storeUserData(response.data.auth_user_mst);\n }\n\n // Synchronize AppStateService with the same user data (saves to localStorage via effect)\n // Follow same pattern as sign-in component\n if (response.data.auth_user_mst) {\n this.appStateService.setUser(response.data.auth_user_mst);\n }\n\n // Store active entity data (saves to localStorage via effect)\n // Use entity from response if available, otherwise use the selected entity\n // Follow same pattern as sign-in component\n const entityToSet = response.data.core_system_entity || entity;\n if (entityToSet && entityToSet._id) {\n this.appStateService.setActiveEntity(entityToSet);\n console.log('✅ Active entity updated and saved to localStorage:', entityToSet._id);\n } else {\n console.warn('⚠️ No entity data available to set as active entity');\n }\n\n // Wait a moment to ensure token and state are fully stored\n await new Promise(resolve => setTimeout(resolve, 200));\n\n // Show success notification\n this.notificationService.success('Entity switched successfully');\n\n // Close drawer\n this.closeContainer();\n\n // Navigate to home after successful entity switch and token storage\n // Wait a bit more to ensure all state updates are complete\n await new Promise(resolve => setTimeout(resolve, 100));\n this.router.navigate(['/control-panel']);\n console.log('✅ Navigated to home after successful entity switch');\n \n // Hide loader after navigation completes\n this.isSwitchingEntity.set(false);\n } else {\n // Entity switch was not successful\n const errorMessage = response?.message || 'Failed to switch entity';\n this.notificationService.error(errorMessage);\n console.error('❌ Entity switch failed:', errorMessage);\n this.isSwitchingEntity.set(false);\n }\n },\n error: (error: HttpErrorResponse) => {\n this.isSwitchingEntity.set(false);\n console.error('Error switching entity:', error);\n const errorMessage = error?.error?.message || error?.message || 'Failed to switch entity';\n this.notificationService.error(errorMessage);\n // Do not navigate on error - stay on current page\n }\n });\n }\n\n /**\n * Handle entity view event from org structure (for viewing)\n */\n onEntityView(entity: CoreSystemEntityListResponse): void {\n console.log('👁️ Entity viewed:', entity);\n // Handle view-only mode - could show entity details or navigate\n // This can be customized based on your needs\n }\n\n /**\n * Cancel selection\n */\n onCancel(): void {\n this.closeContainer();\n }\n\n /**\n * Close the floating container\n */\n private closeContainer(): void {\n this.floatingContainerService.hideAll();\n }\n}"],"names":[],"mappings":"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"}
@@ -1,15 +1,14 @@
1
1
  import * as i1 from '@angular/common';
2
2
  import { CommonModule } from '@angular/common';
3
- import { C as CideLytSharedWrapperComponent, a as CideLytSidebarService, A as AppStateHelperService, b as CideLytRequestService } from './cloud-ide-layout-cloud-ide-layout-ChGVLIdx.mjs';
3
+ import { C as CideLytSharedWrapperComponent, a as CideLytSidebarService, A as AppStateHelperService } from './cloud-ide-layout-cloud-ide-layout-JYQBTRrb.mjs';
4
4
  import * as i0 from '@angular/core';
5
- import { input, inject, Renderer2, ChangeDetectorRef, signal, computed, ViewChild, Component } from '@angular/core';
5
+ import { input, inject, Renderer2, ChangeDetectorRef, signal, computed, effect, ViewChild, Component } from '@angular/core';
6
6
  import { DomSanitizer } from '@angular/platform-browser';
7
7
  import { CideIconComponent, TooltipDirective, CideSpinnerComponent } from 'cloud-ide-element';
8
8
  import { Router } from '@angular/router';
9
9
  import { D as DashboardCardsService } from './cloud-ide-layout-dashboard-cards.service-BGaKcq7H.mjs';
10
10
 
11
11
  class CideLytHomeWrapperComponent extends CideLytSharedWrapperComponent {
12
- requestService;
13
12
  shared_wrapper_setup_param = input({
14
13
  sypg_page_code: "cide_lyt_home"
15
14
  }, ...(ngDevMode ? [{ debugName: "shared_wrapper_setup_param" }] : []));
@@ -36,18 +35,6 @@ class CideLytHomeWrapperComponent extends CideLytSharedWrapperComponent {
36
35
  { accentColor: "#8B5CF6" }, // violet (same as purple for fallback)
37
36
  { accentColor: "#D946EF" } // fuchsia
38
37
  ];
39
- constructor(requestService) {
40
- super();
41
- this.requestService = requestService;
42
- // Sanitize SVG icons in the hardcoded cardsData
43
- this.cardsData = this.cardsData.map(card => ({
44
- ...card,
45
- items: card.items?.map(item => ({
46
- ...item,
47
- safeIconSVG: item.iconSVG ? this.sanitizer.bypassSecurityTrustHtml(item.iconSVG) : undefined
48
- }))
49
- }));
50
- }
51
38
  modulesContainerRef;
52
39
  scrollPrevModuleBtnRef;
53
40
  scrollNextModuleBtnRef;
@@ -66,6 +53,59 @@ class CideLytHomeWrapperComponent extends CideLytSharedWrapperComponent {
66
53
  isLoadingCards = signal(true, ...(ngDevMode ? [{ debugName: "isLoadingCards" }] : []));
67
54
  // Computed signal for overall loading state
68
55
  isLoading = computed(() => this.isLoadingModules() || this.isLoadingCards(), ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
56
+ // Track previous entity ID for change detection
57
+ previousEntityId = null;
58
+ // Flag to prevent multiple simultaneous API calls
59
+ isModuleLoadInProgress = false;
60
+ // Debounce timer for entity change
61
+ entityChangeDebounceTimer = null;
62
+ constructor() {
63
+ super();
64
+ // Sanitize SVG icons in the hardcoded cardsData
65
+ this.cardsData = this.cardsData.map(card => ({
66
+ ...card,
67
+ items: card.items?.map(item => ({
68
+ ...item,
69
+ safeIconSVG: item.iconSVG ? this.sanitizer.bypassSecurityTrustHtml(item.iconSVG) : undefined
70
+ }))
71
+ }));
72
+ // React to active entity changes - reload modules when entity changes
73
+ effect(() => {
74
+ const activeEntity = this.appState.activeEntity();
75
+ const currentEntityId = activeEntity?._id || null;
76
+ // Skip on initial load (when previousEntityId is null)
77
+ if (this.previousEntityId === null) {
78
+ this.previousEntityId = currentEntityId;
79
+ console.log('🏢 HOME: Entity change handler initialized with initial entity:', currentEntityId);
80
+ return;
81
+ }
82
+ // Only reload if entity actually changed and no load is in progress
83
+ if (this.previousEntityId !== currentEntityId && currentEntityId !== null && !this.isModuleLoadInProgress) {
84
+ console.log('🏢 HOME: Entity changed from', this.previousEntityId, 'to', currentEntityId, '- reloading modules');
85
+ this.previousEntityId = currentEntityId;
86
+ // Clear any existing debounce timer
87
+ if (this.entityChangeDebounceTimer) {
88
+ clearTimeout(this.entityChangeDebounceTimer);
89
+ }
90
+ // Debounce the API call to prevent multiple rapid calls
91
+ this.entityChangeDebounceTimer = setTimeout(() => {
92
+ // Reload modules when entity changes (menu API call)
93
+ this.loadModules();
94
+ this.entityChangeDebounceTimer = null;
95
+ }, 100); // 100ms debounce
96
+ }
97
+ else if (this.previousEntityId !== null && currentEntityId === null) {
98
+ // Entity was cleared
99
+ console.log('🏢 HOME: Entity cleared');
100
+ this.previousEntityId = null;
101
+ this.modulesData = [];
102
+ this.isLoadingModules.set(false);
103
+ }
104
+ else if (this.isModuleLoadInProgress) {
105
+ console.log('⏭️ HOME: Module load already in progress, skipping duplicate call');
106
+ }
107
+ });
108
+ }
69
109
  ngOnInit() {
70
110
  super.ngOnInit?.();
71
111
  // Set loading states to true initially
@@ -87,6 +127,12 @@ class CideLytHomeWrapperComponent extends CideLytSharedWrapperComponent {
87
127
  this.setupDragAndDrop();
88
128
  }
89
129
  loadModules() {
130
+ // Prevent multiple simultaneous calls
131
+ if (this.isModuleLoadInProgress) {
132
+ console.log('⏭️ HOME: Module load already in progress, skipping duplicate call');
133
+ return;
134
+ }
135
+ this.isModuleLoadInProgress = true;
90
136
  this.isLoadingModules.set(true);
91
137
  this.sidebarService?.getSidebarMenues({
92
138
  syme_type: "module",
@@ -105,12 +151,14 @@ class CideLytHomeWrapperComponent extends CideLytSharedWrapperComponent {
105
151
  };
106
152
  });
107
153
  this.isLoadingModules.set(false);
154
+ this.isModuleLoadInProgress = false;
108
155
  this.cdr.detectChanges();
109
156
  },
110
157
  error: (err) => {
111
158
  console.error('Error loading sidebar modules:', err);
112
159
  this.modulesData = [];
113
160
  this.isLoadingModules.set(false);
161
+ this.isModuleLoadInProgress = false;
114
162
  this.cdr.detectChanges();
115
163
  }
116
164
  });
@@ -252,13 +300,13 @@ class CideLytHomeWrapperComponent extends CideLytSharedWrapperComponent {
252
300
  this.draggedItem = null;
253
301
  });
254
302
  }
255
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CideLytHomeWrapperComponent, deps: [{ token: CideLytRequestService }], target: i0.ɵɵFactoryTarget.Component });
303
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CideLytHomeWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
256
304
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: CideLytHomeWrapperComponent, isStandalone: true, selector: "cide-lyt-home-wrapper", inputs: { shared_wrapper_setup_param: { classPropertyName: "shared_wrapper_setup_param", publicName: "shared_wrapper_setup_param", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "modulesContainerRef", first: true, predicate: ["modulesContainer"], descendants: true }, { propertyName: "scrollPrevModuleBtnRef", first: true, predicate: ["scrollPrevModuleBtn"], descendants: true }, { propertyName: "scrollNextModuleBtnRef", first: true, predicate: ["scrollNextModuleBtn"], descendants: true }, { propertyName: "cardsContainerElRef", first: true, predicate: ["cardsContainerEl"], descendants: true }], usesInheritance: true, ngImport: i0, template: "<!-- Fullscreen Loader Overlay -->\n@if (isLoading()) {\n <div class=\"tw-fixed tw-inset-0 tw-z-[9999] tw-backdrop-blur-sm tw-flex tw-items-center tw-justify-center\">\n <div class=\"tw-text-center\">\n <cide-ele-spinner size=\"sm\" type=\"spinner\"></cide-ele-spinner>\n <p class=\"tw-mt-4 tw-text-gray-600 dark:tw-text-gray-300 tw-text-sm tw-font-medium\">Loading dashboard...</p>\n </div>\n </div>\n}\n\n<div class=\"tw-p-4 sm:tw-p-6 md:tw-p-8 tw-w-full\" [class.tw-opacity-0]=\"isLoading()\" [class.tw-transition-opacity]=\"!isLoading()\">\n <header class=\"tw-mb-8 md:tw-mb-10\">\n <h1\n class=\"tw-text-3xl sm:tw-text-4xl tw-font-bold tw-text-transparent tw-bg-clip-text tw-bg-gradient-to-r tw-from-purple-600 tw-via-pink-500 tw-to-orange-500\">\n Welcome, {{ appState.getUserName() || 'User' }}!\n </h1>\n <p class=\"tw-text-sm tw-text-gray-500 tw-mt-1\">Your central hub for all things college.</p>\n <p *ngIf=\"appState.getUserEmail()\" class=\"tw-text-xs tw-text-gray-400 tw-mt-1\">{{ appState.getUserEmail() }}</p>\n </header>\n\n <div class=\"tw-mb-8 md:tw-mb-10\">\n <div class=\"tw-flex tw-justify-between tw-items-center tw-mb-5\">\n <h2 class=\"tw-text-xl tw-font-bold tw-text-gray-700\">{{ modulesSectionConfig.title }}</h2>\n <a *ngIf=\"modulesSectionConfig.showViewAllLink\" [href]=\"modulesSectionConfig.viewAllLinkHref\"\n class=\"tw-text-sm tw-font-medium tw-text-purple-600 hover:tw-text-purple-800 hover:tw-underline tw-transition-colors\">\n {{ modulesSectionConfig.viewAllLinkText }}\n </a>\n </div>\n <div class=\"tw-relative group/modules tw-max-w-full\">\n <button #scrollPrevModuleBtn aria-label=\"Scroll previous modules\"\n class=\"tw-absolute tw--left-4 tw-z-50 tw-p-2 tw-bg-white/90 tw-bg-opacity-90 tw-backdrop-blur-sm tw-rounded-full tw-shadow-lg hover:tw-bg-gray-100 tw-transition-all tw-opacity-100 md:group-hover/modules:tw-opacity-100 disabled:tw-opacity-30 disabled:tw-cursor-not-allowed tw-pointer-events-auto\"\n style=\"top: 2.875rem;\">\n <svg class=\"tw-w-5 tw-h-5 tw-text-gray-700\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n </svg>\n </button>\n\n <div #modulesContainer\n class=\"tw-flex tw-space-x-4 tw-pb-6 tw-overflow-x-auto tw-overflow-y-visible tw-scroll-smooth tw-snap-x tw-snap-mandatory hide-scrollbar\"\n style=\"touch-action: none; pointer-events: auto;\">\n <a *ngFor=\"let module of modulesData\" \n (click)=\"openModule(module)\"\n (keydown.enter)=\"openModule(module)\"\n (keydown.space)=\"openModule(module)\"\n class=\"module-card tw-relative tw-overflow-hidden group tw-flex-shrink-0 tw-w-60 tw-h-28 tw-bg-white tw-border tw-border-gray-200/80 tw-rounded-xl tw-p-3 tw-shadow-md hover:tw-shadow-xl tw-transition-all tw-duration-300 tw-ease-out tw-flex tw-items-center tw-space-x-4 tw-snap-start\"\n [style.--accent-color]=\"module.accentColor\" \n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'Open module: ' + module.syme_title\">\n <div class=\"tw-relative tw-flex-shrink-0\">\n <div class=\"tw-w-16 tw-h-16 tw-rounded-lg tw-flex tw-items-center tw-justify-center tw-transition-all tw-duration-300 group-hover:tw-scale-105\"\n [style.backgroundColor]=\"module?.accentColor + '22'\">\n <!-- icon rendering here if needed -->\n <cide-ele-icon class=\"mpro-icon-field\" type=\"none\">{{module?.syme_icon}}</cide-ele-icon>\n </div>\n <div *ngIf=\"(module?.pillCount || 0) > 0\" class=\"tw-absolute tw--top-1.5 tw--right-1.5\">\n <span\n class=\"tw-flex tw-h-5 tw-w-5 tw-items-center tw-justify-center tw-rounded-full tw-text-white tw-text-xs tw-font-bold tw-ring-2 tw-ring-white\"\n [style.backgroundColor]=\"module?.accentColor\">{{module?.pillCount}}</span>\n </div>\n </div>\n <div class=\"tw-flex tw-flex-col tw-flex-grow tw-min-w-0\">\n <h4 class=\"tw-font-semibold tw-text-xs tw-text-gray-800 tw-truncate line-clamp-1\" cideEleTooltip\n [cideEleTooltip]=\"module.syme_title\" [tooltipDelay]=\"300\" [tooltipPlacement]=\"'top'\">\n {{module?.syme_title}}\n </h4>\n <p\n class=\"tw-text-xs tw-text-gray-400 tw-mt-0.5 tw-overflow-hidden tw-text-ellipsis tw-line-clamp-2 tw-text-justify min-h-[2.5rem]\">\n {{module?.syme_desc}}\n </p>\n </div>\n </a>\n </div>\n\n <button #scrollNextModuleBtn aria-label=\"Scroll next modules\"\n class=\"tw-absolute tw--right-4 tw-z-50 tw-p-2 tw-bg-white/90 tw-bg-opacity-90 tw-backdrop-blur-sm tw-rounded-full tw-shadow-lg hover:tw-bg-gray-100 tw-transition-all tw-opacity-100 md:group-hover/modules:tw-opacity-100 disabled:tw-opacity-30 disabled:tw-cursor-not-allowed tw-pointer-events-auto\"\n style=\"top: 2.875rem;\">\n <svg class=\"tw-w-5 tw-h-5 tw-text-gray-700\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n </button>\n </div>\n </div>\n\n <main class=\"tw--mt-8 md:tw--mt-10\">\n <div #cardsContainerEl class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-2 xl:tw-grid-cols-3 tw-gap-5\">\n <div *ngFor=\"let card of cardsData\" draggable=\"true\" [id]=\"card.id\"\n class=\"dashboard-card tw-bg-white tw-border tw-border-gray-200/70 tw-rounded-xl tw-p-4 tw-shadow-lg hover:tw-shadow-xl hover:tw--translate-y-0.5 tw-transition-all tw-cursor-grab\"\n [ngClass]=\"card.colSpan || 'tw-col-span-1'\">\n\n <div *ngIf=\"card.type !== 'studyStreak' && card.type !== 'quickStats'\"\n class=\"tw-flex tw-justify-between tw-items-center tw-mb-3\">\n <h3 class=\"tw-text-md tw-font-semibold\" [ngClass]=\"'tw-text-' + card.accentColor + '-700'\">\n {{card.title}}</h3>\n <span *ngIf=\"card.urgentText\"\n class=\"tw-text-xs tw-bg-red-100 tw-text-red-700 tw-px-2.5 tw-py-1 tw-rounded-full tw-font-semibold\">{{card.urgentText}}</span>\n <span *ngIf=\"card.insightTag\" class=\"tw-text-xs tw-px-2.5 tw-py-1 tw-rounded-full tw-font-semibold\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-100 tw-text-' + card.accentColor + '-700'\">{{card.insightTag}}</span>\n </div>\n\n <div class=\"card-content-area tw-space-y-3 tw-text-sm tw-pr-1\">\n <ng-container [ngSwitch]=\"card.type\">\n <ul *ngSwitchCase=\"'deadlines'\">\n <li *ngFor=\"let item of card.items\"\n class=\"tw-flex tw-justify-between tw-items-center tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <div>\n <p class=\"tw-text-gray-700 tw-font-medium\">{{item.text}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.subtext}}</p>\n </div>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'news'\">\n <li *ngFor=\"let item of card.items\" class=\"tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <p class=\"tw-font-medium tw-text-gray-800\">{{item.title}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.details}}</p>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'grades'\">\n <li *ngFor=\"let item of card.items\"\n class=\"tw-flex tw-justify-between tw-items-center tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <span class=\"tw-text-gray-700\">{{item.subject}}</span><span class=\"tw-font-bold\"\n [ngClass]=\"item.color\">{{item.grade}}</span>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'professorConnect'\">\n <li *ngFor=\"let item of card.items\" class=\"tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <p class=\"tw-font-medium tw-text-gray-700\">{{item.name}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.officeHours}}</p><a\n *ngIf=\"item.messageLink\" [href]=\"item.messageLink\"\n class=\"tw-text-xs hover:tw-underline\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600'\">Send Message</a><a\n *ngIf=\"item.appointmentLink\" [href]=\"item.appointmentLink\"\n class=\"tw-text-xs hover:tw-underline tw-ml-2\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600'\">Book Appointment</a>\n </li>\n </ul>\n <div *ngSwitchCase=\"'courses'\" class=\"tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 tw-gap-2.5\"><a\n *ngFor=\"let item of card.items\" href=\"#\"\n class=\"tw-block tw-p-2.5 tw-rounded-lg tw-transition-all group tw-ring-1\"\n [ngClass]=\"'tw-bg-' + item.colorTheme + '-50/70 hover:tw-bg-' + item.colorTheme + '-100/80 tw-ring-' + item.colorTheme + '-200/70 hover:tw-ring-' + item.colorTheme + '-300'\">\n <p class=\"tw-font-semibold group-hover:tw-text-{{item.colorTheme}}-800\"\n [ngClass]=\"'tw-text-' + item.colorTheme + '-700'\">{{item.name}}</p>\n <p class=\"tw-text-xs group-hover:tw-text-{{item.colorTheme}}-600 tw-mt-0.5\"\n [ngClass]=\"'tw-text-' + item.colorTheme + '-500'\">{{item.professor}}</p>\n </a></div>\n <ul *ngSwitchCase=\"'events'\">\n <li *ngFor=\"let item of card.items\"\n class=\"tw-flex tw-items-start tw-space-x-2.5 tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <div class=\"tw-text-white tw-text-xs tw-font-bold tw-p-2 tw-rounded-md tw-flex tw-flex-col tw-items-center tw-justify-center tw-w-12 tw-h-12 tw-flex-shrink-0 tw-shadow\"\n [ngClass]=\"item.themeColor\"><span>{{item.date?.month}}</span><span\n class=\"tw-text-sm\">{{item.date?.day}}</span></div>\n <div>\n <p class=\"tw-text-gray-700 tw-font-medium\">{{item.title}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.details}}</p>\n </div>\n </li>\n </ul>\n <div *ngSwitchCase=\"'studyStreak'\"\n class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-text-center tw-flex-grow tw-p-4\">\n <svg class=\"tw-w-12 tw-h-12 tw-mb-2\" [ngClass]=\"'tw-text-' + card.accentColor + '-500'\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7.014A8.003 8.003 0 0122 12c0 3-1 7-6.657 7.343A7.975 7.975 0 0117.657 18.657z\">\n </path>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M9.879 16.121A3 3 0 1012.015 11L11 14H9c0 .768.293 1.536.879 2.121z\"></path>\n </svg>\n <h3 class=\"tw-text-md tw-font-semibold tw-mb-1\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600'\">{{card.title}}</h3>\n <p class=\"tw-text-xs tw-text-gray-500\">{{card.description}}</p>\n <div class=\"tw-mt-3 tw-w-full tw-bg-gray-200 tw-rounded-full tw-h-1.5\">\n <div class=\"tw-h-1.5 tw-rounded-full\" [ngClass]=\"'tw-bg-' + card.accentColor + '-500'\"\n [style.width]=\"card.progress\"></div>\n </div>\n </div>\n <div *ngSwitchCase=\"'quickStats'\" class=\"tw-space-y-3.5 tw-flex-grow\">\n <h2 class=\"tw-text-md tw-font-semibold tw-mb-3 tw-border-b tw-border-gray-200/80 tw-pb-2\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-700'\">{{card.title}}</h2>\n <div *ngFor=\"let stat of card.stats\" class=\"tw-text-sm\">\n <div *ngIf=\"stat.gradient\" class=\"tw-text-sm\">\n <div class=\"tw-flex tw-justify-between tw-items-center tw-mb-0.5\">\n <p class=\"tw-text-gray-600 tw-font-medium\">{{stat.label}}</p>\n <p class=\"tw-text-xs tw-text-green-600 tw-font-semibold\">{{stat.value}}</p>\n </div>\n <div class=\"tw-w-full tw-bg-gray-200 tw-rounded-full tw-h-2\">\n <div class=\"tw-h-2 tw-rounded-full\" [ngClass]=\"stat.gradient\"\n [style.width]=\"stat.value\"></div>\n </div>\n </div>\n <div *ngIf=\"!stat.gradient\">\n <p class=\"tw-text-gray-600 tw-font-medium tw-mb-0.5\">{{stat.label}}</p>\n <p [ngClass]=\"stat.valueClass || ''\">{{stat.value}}</p>\n </div>\n </div>\n </div>\n <ul *ngSwitchCase=\"'discussions'\">\n <li *ngFor=\"let item of card.items\" class=\"tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <a href=\"#\" class=\"tw-block group\">\n <p class=\"tw-font-medium tw-text-gray-700\"\n [ngClass]=\"'group-hover:tw-text-' + card.accentColor + '-700'\">{{item.topic}}\n </p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.forum}} - Last post: {{item.lastPost}}\n </p>\n </a>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'quickLinks'\">\n <li *ngFor=\"let item of card.items\"><a href=\"#\"\n class=\"tw-flex tw-items-center tw-p-2 tw-rounded-lg tw-text-gray-700 tw-transition-colors\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70 hover:tw-text-' + card.accentColor + '-700'\"><svg\n class=\"tw-w-4 tw-h-4 tw-mr-2\" [ngClass]=\"'tw-text-' + card.accentColor + '-500'\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n [innerHTML]=\"item.safeIconSVG\"></svg>{{item.text}}</a></li>\n </ul>\n </ng-container>\n </div>\n\n <a *ngIf=\"card.link\" [href]=\"card.link.href\"\n class=\"tw-mt-auto tw-pt-3 tw-text-xs tw-font-medium tw-w-full tw-text-left hover:tw-underline\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600 hover:tw-text-' + card.accentColor + '-800'\">\n {{card.link.text}}\n </a>\n </div>\n </div>\n </main>\n</div>", styles: [".module-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background-color:var(--accent-color, transparent);opacity:0;pointer-events:none;transition:opacity .3s,transform .3s;transform:scaleX(0);z-index:1}.module-card:hover:before{opacity:1;transform:scaleX(1)}.card-content-area::-webkit-scrollbar{width:6px}.card-content-area::-webkit-scrollbar-track{background:#f1f5f9;border-radius:3px}.card-content-area::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.card-content-area::-webkit-scrollbar-thumb:hover{background:#94a3b8}.dashboard-card{overflow:hidden}.hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.hide-scrollbar::-webkit-scrollbar{display:none}\n", ":host{height:100%;display:flex;flex-direction:column;overflow-y:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "directive", type: TooltipDirective, selector: "[cideEleTooltip]", inputs: ["cideEleTooltip", "tooltipColor", "tooltipBg", "tooltipPlacement", "tooltipType", "tooltipDelay", "tooltipDir", "tooltipShowArrow", "tooltipMultiline", "tooltipMaxWidth", "tooltipInteractive", "tooltipClass"] }, { kind: "component", type: CideSpinnerComponent, selector: "cide-ele-spinner", inputs: ["size", "type"] }] });
257
305
  }
258
306
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: CideLytHomeWrapperComponent, decorators: [{
259
307
  type: Component,
260
308
  args: [{ selector: 'cide-lyt-home-wrapper', imports: [CommonModule, CideIconComponent, TooltipDirective, CideSpinnerComponent], template: "<!-- Fullscreen Loader Overlay -->\n@if (isLoading()) {\n <div class=\"tw-fixed tw-inset-0 tw-z-[9999] tw-backdrop-blur-sm tw-flex tw-items-center tw-justify-center\">\n <div class=\"tw-text-center\">\n <cide-ele-spinner size=\"sm\" type=\"spinner\"></cide-ele-spinner>\n <p class=\"tw-mt-4 tw-text-gray-600 dark:tw-text-gray-300 tw-text-sm tw-font-medium\">Loading dashboard...</p>\n </div>\n </div>\n}\n\n<div class=\"tw-p-4 sm:tw-p-6 md:tw-p-8 tw-w-full\" [class.tw-opacity-0]=\"isLoading()\" [class.tw-transition-opacity]=\"!isLoading()\">\n <header class=\"tw-mb-8 md:tw-mb-10\">\n <h1\n class=\"tw-text-3xl sm:tw-text-4xl tw-font-bold tw-text-transparent tw-bg-clip-text tw-bg-gradient-to-r tw-from-purple-600 tw-via-pink-500 tw-to-orange-500\">\n Welcome, {{ appState.getUserName() || 'User' }}!\n </h1>\n <p class=\"tw-text-sm tw-text-gray-500 tw-mt-1\">Your central hub for all things college.</p>\n <p *ngIf=\"appState.getUserEmail()\" class=\"tw-text-xs tw-text-gray-400 tw-mt-1\">{{ appState.getUserEmail() }}</p>\n </header>\n\n <div class=\"tw-mb-8 md:tw-mb-10\">\n <div class=\"tw-flex tw-justify-between tw-items-center tw-mb-5\">\n <h2 class=\"tw-text-xl tw-font-bold tw-text-gray-700\">{{ modulesSectionConfig.title }}</h2>\n <a *ngIf=\"modulesSectionConfig.showViewAllLink\" [href]=\"modulesSectionConfig.viewAllLinkHref\"\n class=\"tw-text-sm tw-font-medium tw-text-purple-600 hover:tw-text-purple-800 hover:tw-underline tw-transition-colors\">\n {{ modulesSectionConfig.viewAllLinkText }}\n </a>\n </div>\n <div class=\"tw-relative group/modules tw-max-w-full\">\n <button #scrollPrevModuleBtn aria-label=\"Scroll previous modules\"\n class=\"tw-absolute tw--left-4 tw-z-50 tw-p-2 tw-bg-white/90 tw-bg-opacity-90 tw-backdrop-blur-sm tw-rounded-full tw-shadow-lg hover:tw-bg-gray-100 tw-transition-all tw-opacity-100 md:group-hover/modules:tw-opacity-100 disabled:tw-opacity-30 disabled:tw-cursor-not-allowed tw-pointer-events-auto\"\n style=\"top: 2.875rem;\">\n <svg class=\"tw-w-5 tw-h-5 tw-text-gray-700\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M15 19l-7-7 7-7\"></path>\n </svg>\n </button>\n\n <div #modulesContainer\n class=\"tw-flex tw-space-x-4 tw-pb-6 tw-overflow-x-auto tw-overflow-y-visible tw-scroll-smooth tw-snap-x tw-snap-mandatory hide-scrollbar\"\n style=\"touch-action: none; pointer-events: auto;\">\n <a *ngFor=\"let module of modulesData\" \n (click)=\"openModule(module)\"\n (keydown.enter)=\"openModule(module)\"\n (keydown.space)=\"openModule(module)\"\n class=\"module-card tw-relative tw-overflow-hidden group tw-flex-shrink-0 tw-w-60 tw-h-28 tw-bg-white tw-border tw-border-gray-200/80 tw-rounded-xl tw-p-3 tw-shadow-md hover:tw-shadow-xl tw-transition-all tw-duration-300 tw-ease-out tw-flex tw-items-center tw-space-x-4 tw-snap-start\"\n [style.--accent-color]=\"module.accentColor\" \n tabindex=\"0\"\n role=\"button\"\n [attr.aria-label]=\"'Open module: ' + module.syme_title\">\n <div class=\"tw-relative tw-flex-shrink-0\">\n <div class=\"tw-w-16 tw-h-16 tw-rounded-lg tw-flex tw-items-center tw-justify-center tw-transition-all tw-duration-300 group-hover:tw-scale-105\"\n [style.backgroundColor]=\"module?.accentColor + '22'\">\n <!-- icon rendering here if needed -->\n <cide-ele-icon class=\"mpro-icon-field\" type=\"none\">{{module?.syme_icon}}</cide-ele-icon>\n </div>\n <div *ngIf=\"(module?.pillCount || 0) > 0\" class=\"tw-absolute tw--top-1.5 tw--right-1.5\">\n <span\n class=\"tw-flex tw-h-5 tw-w-5 tw-items-center tw-justify-center tw-rounded-full tw-text-white tw-text-xs tw-font-bold tw-ring-2 tw-ring-white\"\n [style.backgroundColor]=\"module?.accentColor\">{{module?.pillCount}}</span>\n </div>\n </div>\n <div class=\"tw-flex tw-flex-col tw-flex-grow tw-min-w-0\">\n <h4 class=\"tw-font-semibold tw-text-xs tw-text-gray-800 tw-truncate line-clamp-1\" cideEleTooltip\n [cideEleTooltip]=\"module.syme_title\" [tooltipDelay]=\"300\" [tooltipPlacement]=\"'top'\">\n {{module?.syme_title}}\n </h4>\n <p\n class=\"tw-text-xs tw-text-gray-400 tw-mt-0.5 tw-overflow-hidden tw-text-ellipsis tw-line-clamp-2 tw-text-justify min-h-[2.5rem]\">\n {{module?.syme_desc}}\n </p>\n </div>\n </a>\n </div>\n\n <button #scrollNextModuleBtn aria-label=\"Scroll next modules\"\n class=\"tw-absolute tw--right-4 tw-z-50 tw-p-2 tw-bg-white/90 tw-bg-opacity-90 tw-backdrop-blur-sm tw-rounded-full tw-shadow-lg hover:tw-bg-gray-100 tw-transition-all tw-opacity-100 md:group-hover/modules:tw-opacity-100 disabled:tw-opacity-30 disabled:tw-cursor-not-allowed tw-pointer-events-auto\"\n style=\"top: 2.875rem;\">\n <svg class=\"tw-w-5 tw-h-5 tw-text-gray-700\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 5l7 7-7 7\"></path>\n </svg>\n </button>\n </div>\n </div>\n\n <main class=\"tw--mt-8 md:tw--mt-10\">\n <div #cardsContainerEl class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-2 xl:tw-grid-cols-3 tw-gap-5\">\n <div *ngFor=\"let card of cardsData\" draggable=\"true\" [id]=\"card.id\"\n class=\"dashboard-card tw-bg-white tw-border tw-border-gray-200/70 tw-rounded-xl tw-p-4 tw-shadow-lg hover:tw-shadow-xl hover:tw--translate-y-0.5 tw-transition-all tw-cursor-grab\"\n [ngClass]=\"card.colSpan || 'tw-col-span-1'\">\n\n <div *ngIf=\"card.type !== 'studyStreak' && card.type !== 'quickStats'\"\n class=\"tw-flex tw-justify-between tw-items-center tw-mb-3\">\n <h3 class=\"tw-text-md tw-font-semibold\" [ngClass]=\"'tw-text-' + card.accentColor + '-700'\">\n {{card.title}}</h3>\n <span *ngIf=\"card.urgentText\"\n class=\"tw-text-xs tw-bg-red-100 tw-text-red-700 tw-px-2.5 tw-py-1 tw-rounded-full tw-font-semibold\">{{card.urgentText}}</span>\n <span *ngIf=\"card.insightTag\" class=\"tw-text-xs tw-px-2.5 tw-py-1 tw-rounded-full tw-font-semibold\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-100 tw-text-' + card.accentColor + '-700'\">{{card.insightTag}}</span>\n </div>\n\n <div class=\"card-content-area tw-space-y-3 tw-text-sm tw-pr-1\">\n <ng-container [ngSwitch]=\"card.type\">\n <ul *ngSwitchCase=\"'deadlines'\">\n <li *ngFor=\"let item of card.items\"\n class=\"tw-flex tw-justify-between tw-items-center tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <div>\n <p class=\"tw-text-gray-700 tw-font-medium\">{{item.text}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.subtext}}</p>\n </div>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'news'\">\n <li *ngFor=\"let item of card.items\" class=\"tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <p class=\"tw-font-medium tw-text-gray-800\">{{item.title}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.details}}</p>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'grades'\">\n <li *ngFor=\"let item of card.items\"\n class=\"tw-flex tw-justify-between tw-items-center tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <span class=\"tw-text-gray-700\">{{item.subject}}</span><span class=\"tw-font-bold\"\n [ngClass]=\"item.color\">{{item.grade}}</span>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'professorConnect'\">\n <li *ngFor=\"let item of card.items\" class=\"tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <p class=\"tw-font-medium tw-text-gray-700\">{{item.name}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.officeHours}}</p><a\n *ngIf=\"item.messageLink\" [href]=\"item.messageLink\"\n class=\"tw-text-xs hover:tw-underline\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600'\">Send Message</a><a\n *ngIf=\"item.appointmentLink\" [href]=\"item.appointmentLink\"\n class=\"tw-text-xs hover:tw-underline tw-ml-2\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600'\">Book Appointment</a>\n </li>\n </ul>\n <div *ngSwitchCase=\"'courses'\" class=\"tw-grid tw-grid-cols-1 sm:tw-grid-cols-2 tw-gap-2.5\"><a\n *ngFor=\"let item of card.items\" href=\"#\"\n class=\"tw-block tw-p-2.5 tw-rounded-lg tw-transition-all group tw-ring-1\"\n [ngClass]=\"'tw-bg-' + item.colorTheme + '-50/70 hover:tw-bg-' + item.colorTheme + '-100/80 tw-ring-' + item.colorTheme + '-200/70 hover:tw-ring-' + item.colorTheme + '-300'\">\n <p class=\"tw-font-semibold group-hover:tw-text-{{item.colorTheme}}-800\"\n [ngClass]=\"'tw-text-' + item.colorTheme + '-700'\">{{item.name}}</p>\n <p class=\"tw-text-xs group-hover:tw-text-{{item.colorTheme}}-600 tw-mt-0.5\"\n [ngClass]=\"'tw-text-' + item.colorTheme + '-500'\">{{item.professor}}</p>\n </a></div>\n <ul *ngSwitchCase=\"'events'\">\n <li *ngFor=\"let item of card.items\"\n class=\"tw-flex tw-items-start tw-space-x-2.5 tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <div class=\"tw-text-white tw-text-xs tw-font-bold tw-p-2 tw-rounded-md tw-flex tw-flex-col tw-items-center tw-justify-center tw-w-12 tw-h-12 tw-flex-shrink-0 tw-shadow\"\n [ngClass]=\"item.themeColor\"><span>{{item.date?.month}}</span><span\n class=\"tw-text-sm\">{{item.date?.day}}</span></div>\n <div>\n <p class=\"tw-text-gray-700 tw-font-medium\">{{item.title}}</p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.details}}</p>\n </div>\n </li>\n </ul>\n <div *ngSwitchCase=\"'studyStreak'\"\n class=\"tw-flex tw-flex-col tw-items-center tw-justify-center tw-text-center tw-flex-grow tw-p-4\">\n <svg class=\"tw-w-12 tw-h-12 tw-mb-2\" [ngClass]=\"'tw-text-' + card.accentColor + '-500'\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7.014A8.003 8.003 0 0122 12c0 3-1 7-6.657 7.343A7.975 7.975 0 0117.657 18.657z\">\n </path>\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\"\n d=\"M9.879 16.121A3 3 0 1012.015 11L11 14H9c0 .768.293 1.536.879 2.121z\"></path>\n </svg>\n <h3 class=\"tw-text-md tw-font-semibold tw-mb-1\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600'\">{{card.title}}</h3>\n <p class=\"tw-text-xs tw-text-gray-500\">{{card.description}}</p>\n <div class=\"tw-mt-3 tw-w-full tw-bg-gray-200 tw-rounded-full tw-h-1.5\">\n <div class=\"tw-h-1.5 tw-rounded-full\" [ngClass]=\"'tw-bg-' + card.accentColor + '-500'\"\n [style.width]=\"card.progress\"></div>\n </div>\n </div>\n <div *ngSwitchCase=\"'quickStats'\" class=\"tw-space-y-3.5 tw-flex-grow\">\n <h2 class=\"tw-text-md tw-font-semibold tw-mb-3 tw-border-b tw-border-gray-200/80 tw-pb-2\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-700'\">{{card.title}}</h2>\n <div *ngFor=\"let stat of card.stats\" class=\"tw-text-sm\">\n <div *ngIf=\"stat.gradient\" class=\"tw-text-sm\">\n <div class=\"tw-flex tw-justify-between tw-items-center tw-mb-0.5\">\n <p class=\"tw-text-gray-600 tw-font-medium\">{{stat.label}}</p>\n <p class=\"tw-text-xs tw-text-green-600 tw-font-semibold\">{{stat.value}}</p>\n </div>\n <div class=\"tw-w-full tw-bg-gray-200 tw-rounded-full tw-h-2\">\n <div class=\"tw-h-2 tw-rounded-full\" [ngClass]=\"stat.gradient\"\n [style.width]=\"stat.value\"></div>\n </div>\n </div>\n <div *ngIf=\"!stat.gradient\">\n <p class=\"tw-text-gray-600 tw-font-medium tw-mb-0.5\">{{stat.label}}</p>\n <p [ngClass]=\"stat.valueClass || ''\">{{stat.value}}</p>\n </div>\n </div>\n </div>\n <ul *ngSwitchCase=\"'discussions'\">\n <li *ngFor=\"let item of card.items\" class=\"tw-p-2.5 tw-rounded-lg\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70'\">\n <a href=\"#\" class=\"tw-block group\">\n <p class=\"tw-font-medium tw-text-gray-700\"\n [ngClass]=\"'group-hover:tw-text-' + card.accentColor + '-700'\">{{item.topic}}\n </p>\n <p class=\"tw-text-xs tw-text-gray-500\">{{item.forum}} - Last post: {{item.lastPost}}\n </p>\n </a>\n </li>\n </ul>\n <ul *ngSwitchCase=\"'quickLinks'\">\n <li *ngFor=\"let item of card.items\"><a href=\"#\"\n class=\"tw-flex tw-items-center tw-p-2 tw-rounded-lg tw-text-gray-700 tw-transition-colors\"\n [ngClass]=\"'tw-bg-' + card.accentColor + '-50/60 hover:tw-bg-' + card.accentColor + '-100/70 hover:tw-text-' + card.accentColor + '-700'\"><svg\n class=\"tw-w-4 tw-h-4 tw-mr-2\" [ngClass]=\"'tw-text-' + card.accentColor + '-500'\"\n fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\"\n [innerHTML]=\"item.safeIconSVG\"></svg>{{item.text}}</a></li>\n </ul>\n </ng-container>\n </div>\n\n <a *ngIf=\"card.link\" [href]=\"card.link.href\"\n class=\"tw-mt-auto tw-pt-3 tw-text-xs tw-font-medium tw-w-full tw-text-left hover:tw-underline\"\n [ngClass]=\"'tw-text-' + card.accentColor + '-600 hover:tw-text-' + card.accentColor + '-800'\">\n {{card.link.text}}\n </a>\n </div>\n </div>\n </main>\n</div>", styles: [".module-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background-color:var(--accent-color, transparent);opacity:0;pointer-events:none;transition:opacity .3s,transform .3s;transform:scaleX(0);z-index:1}.module-card:hover:before{opacity:1;transform:scaleX(1)}.card-content-area::-webkit-scrollbar{width:6px}.card-content-area::-webkit-scrollbar-track{background:#f1f5f9;border-radius:3px}.card-content-area::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:3px}.card-content-area::-webkit-scrollbar-thumb:hover{background:#94a3b8}.dashboard-card{overflow:hidden}.hide-scrollbar{scrollbar-width:none;-ms-overflow-style:none}.hide-scrollbar::-webkit-scrollbar{display:none}\n", ":host{height:100%;display:flex;flex-direction:column;overflow-y:auto}\n"] }]
261
- }], ctorParameters: () => [{ type: CideLytRequestService }], propDecorators: { shared_wrapper_setup_param: [{ type: i0.Input, args: [{ isSignal: true, alias: "shared_wrapper_setup_param", required: false }] }], modulesContainerRef: [{
309
+ }], ctorParameters: () => [], propDecorators: { shared_wrapper_setup_param: [{ type: i0.Input, args: [{ isSignal: true, alias: "shared_wrapper_setup_param", required: false }] }], modulesContainerRef: [{
262
310
  type: ViewChild,
263
311
  args: ['modulesContainer']
264
312
  }], scrollPrevModuleBtnRef: [{
@@ -273,4 +321,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
273
321
  }] } });
274
322
 
275
323
  export { CideLytHomeWrapperComponent };
276
- //# sourceMappingURL=cloud-ide-layout-home-wrapper.component-B0kKQ3pC.mjs.map
324
+ //# sourceMappingURL=cloud-ide-layout-home-wrapper.component-CcZAQSRK.mjs.map