@memberjunction/ng-explorer-core 2.47.0 → 2.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-routing.module.js +40 -53
- package/dist/app-routing.module.js.map +1 -1
- package/dist/generic/Events.types.js +104 -0
- package/dist/generic/Events.types.js.map +1 -1
- package/dist/generic/Item.types.js +28 -14
- package/dist/generic/Item.types.js.map +1 -1
- package/dist/generic/PathData.types.js +5 -0
- package/dist/generic/PathData.types.js.map +1 -1
- package/dist/generic/app-nav-view.types.js +3 -1
- package/dist/generic/app-nav-view.types.js.map +1 -1
- package/dist/lib/app-view/application-view.component.js +273 -294
- package/dist/lib/app-view/application-view.component.js.map +1 -1
- package/dist/lib/auth-button/auth-button.component.js +13 -22
- package/dist/lib/auth-button/auth-button.component.js.map +1 -1
- package/dist/lib/base-browser-component/base-browser-component.js +96 -108
- package/dist/lib/base-browser-component/base-browser-component.js.map +1 -1
- package/dist/lib/dashboard-browser-component/dashboard-browser.component.js +106 -124
- package/dist/lib/dashboard-browser-component/dashboard-browser.component.js.map +1 -1
- package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js +257 -281
- package/dist/lib/dashboard-preferences-dialog/dashboard-preferences-dialog.component.js.map +1 -1
- package/dist/lib/data-browser-component/data-browser.component.js +122 -137
- package/dist/lib/data-browser-component/data-browser.component.js.map +1 -1
- package/dist/lib/expansion-panel-component/expansion-panel-component.js +100 -117
- package/dist/lib/expansion-panel-component/expansion-panel-component.js.map +1 -1
- package/dist/lib/favorites/favorites.component.js +44 -54
- package/dist/lib/favorites/favorites.component.js.map +1 -1
- package/dist/lib/files/files.component.js +12 -11
- package/dist/lib/files/files.component.js.map +1 -1
- package/dist/lib/generic/form-toolbar.js +21 -20
- package/dist/lib/generic/form-toolbar.js.map +1 -1
- package/dist/lib/generic/resource-container-component.js +23 -20
- package/dist/lib/generic/resource-container-component.js.map +1 -1
- package/dist/lib/generic-browse-list/generic-browse-list.component.js +48 -46
- package/dist/lib/generic-browse-list/generic-browse-list.component.js.map +1 -1
- package/dist/lib/generic-browser-list/generic-browser-list.component.js +353 -386
- package/dist/lib/generic-browser-list/generic-browser-list.component.js.map +1 -1
- package/dist/lib/guards/auth-guard.service.js +4 -2
- package/dist/lib/guards/auth-guard.service.js.map +1 -1
- package/dist/lib/guards/entities.guard.js +1 -1
- package/dist/lib/guards/entities.guard.js.map +1 -1
- package/dist/lib/header/MSFT_UserImageService.js +4 -3
- package/dist/lib/header/MSFT_UserImageService.js.map +1 -1
- package/dist/lib/header/header.component.js +111 -121
- package/dist/lib/header/header.component.js.map +1 -1
- package/dist/lib/home-component/home.component.js +34 -42
- package/dist/lib/home-component/home.component.js.map +1 -1
- package/dist/lib/home-wrapper/home-wrapper.component.js +6 -6
- package/dist/lib/home-wrapper/home-wrapper.component.js.map +1 -1
- package/dist/lib/list-view/list-view.component.js +132 -152
- package/dist/lib/list-view/list-view.component.js.map +1 -1
- package/dist/lib/navigation/navigation.component.js +568 -615
- package/dist/lib/navigation/navigation.component.js.map +1 -1
- package/dist/lib/query-browser-component/query-browser.component.js +30 -39
- package/dist/lib/query-browser-component/query-browser.component.js.map +1 -1
- package/dist/lib/report-browser-component/report-browser.component.js +18 -30
- package/dist/lib/report-browser-component/report-browser.component.js.map +1 -1
- package/dist/lib/resource-browser/resource-browser.component.js +435 -457
- package/dist/lib/resource-browser/resource-browser.component.js.map +1 -1
- package/dist/lib/resource-wrappers/dashboard-resource.component.js +12 -25
- package/dist/lib/resource-wrappers/dashboard-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/list-detail-resource.component.js +18 -31
- package/dist/lib/resource-wrappers/list-detail-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/query-resource.component.js +15 -28
- package/dist/lib/resource-wrappers/query-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/record-resource.component.js +35 -47
- package/dist/lib/resource-wrappers/record-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/report-resource.component.js +15 -28
- package/dist/lib/resource-wrappers/report-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/search-results-resource.component.js +21 -34
- package/dist/lib/resource-wrappers/search-results-resource.component.js.map +1 -1
- package/dist/lib/resource-wrappers/view-resource.component.js +23 -37
- package/dist/lib/resource-wrappers/view-resource.component.js.map +1 -1
- package/dist/lib/single-application/single-application.component.js +15 -20
- package/dist/lib/single-application/single-application.component.js.map +1 -1
- package/dist/lib/single-dashboard/Components/add-item/add-item.component.js +81 -95
- package/dist/lib/single-dashboard/Components/add-item/add-item.component.js.map +1 -1
- package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js +28 -29
- package/dist/lib/single-dashboard/Components/delete-item/delete-item.component.js.map +1 -1
- package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js +51 -64
- package/dist/lib/single-dashboard/Components/edit-dashboard/edit-dashboard.component.js.map +1 -1
- package/dist/lib/single-dashboard/single-dashboard.component.js +158 -165
- package/dist/lib/single-dashboard/single-dashboard.component.js.map +1 -1
- package/dist/lib/single-entity/single-entity.component.js +106 -118
- package/dist/lib/single-entity/single-entity.component.js.map +1 -1
- package/dist/lib/single-list-detail/single-list-detail.component.js +265 -287
- package/dist/lib/single-list-detail/single-list-detail.component.js.map +1 -1
- package/dist/lib/single-query/single-query.component.js +35 -44
- package/dist/lib/single-query/single-query.component.js.map +1 -1
- package/dist/lib/single-record/single-record.component.js +64 -73
- package/dist/lib/single-record/single-record.component.js.map +1 -1
- package/dist/lib/single-report/single-report.component.js +33 -43
- package/dist/lib/single-report/single-report.component.js.map +1 -1
- package/dist/lib/single-search-result/single-search-result.component.js +18 -30
- package/dist/lib/single-search-result/single-search-result.component.js.map +1 -1
- package/dist/lib/single-view/single-view.component.js +107 -124
- package/dist/lib/single-view/single-view.component.js.map +1 -1
- package/dist/lib/tabbed-dashboard/tabbed-dashboard.component.js +197 -210
- package/dist/lib/tabbed-dashboard/tabbed-dashboard.component.js.map +1 -1
- package/dist/lib/user-notifications/user-notifications.component.js +137 -155
- package/dist/lib/user-notifications/user-notifications.component.js.map +1 -1
- package/dist/lib/user-profile/user-profile.component.js +10 -8
- package/dist/lib/user-profile/user-profile.component.js.map +1 -1
- package/dist/module.js +51 -51
- package/dist/module.js.map +1 -1
- package/package.json +25 -25
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
-
});
|
|
9
|
-
};
|
|
10
1
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
|
11
2
|
import { moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
|
|
12
3
|
import { LogError, Metadata, RunView } from '@memberjunction/core';
|
|
@@ -190,122 +181,112 @@ function DashboardPreferencesDialogComponent_Conditional_20_Template(rf, ctx) {
|
|
|
190
181
|
i0.ɵɵtext(1, " Save Changes ");
|
|
191
182
|
} }
|
|
192
183
|
export class DashboardPreferencesDialogComponent {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
184
|
+
applicationId = null;
|
|
185
|
+
scope = 'Global';
|
|
186
|
+
result = new EventEmitter();
|
|
187
|
+
availableDashboards = [];
|
|
188
|
+
configuredDashboards = [];
|
|
189
|
+
applicationName = '';
|
|
190
|
+
loading = true;
|
|
191
|
+
saving = false;
|
|
192
|
+
error = null;
|
|
193
|
+
hasChanges = false;
|
|
194
|
+
isSysAdmin = false;
|
|
195
|
+
preferenceMode = 'personal';
|
|
196
|
+
originalConfiguredIds = [];
|
|
197
|
+
currentUserPreferences = [];
|
|
198
|
+
allAvailableDashboards = [];
|
|
199
|
+
async ngOnInit() {
|
|
200
|
+
try {
|
|
201
|
+
await this.loadData();
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
LogError('Error initializing dashboard preferences dialog', null, error);
|
|
205
|
+
this.error = 'Failed to load dashboard preferences';
|
|
206
|
+
}
|
|
207
|
+
finally {
|
|
208
|
+
this.loading = false;
|
|
209
|
+
}
|
|
209
210
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
211
|
+
async loadData() {
|
|
212
|
+
const md = new Metadata();
|
|
213
|
+
// Check if current user is sysadmin
|
|
214
|
+
this.isSysAdmin = md.CurrentUser.Type.trim().toLowerCase() === 'owner';
|
|
215
|
+
console.log('User is sysadmin:', this.isSysAdmin);
|
|
216
|
+
// Default to personal preferences for all users (including sysadmin)
|
|
217
|
+
this.preferenceMode = 'personal';
|
|
218
|
+
// Load application name if we're in app scope
|
|
219
|
+
if (this.scope === 'App' && this.applicationId) {
|
|
220
|
+
await this.loadApplicationName();
|
|
221
|
+
}
|
|
222
|
+
// Get cached dashboards from MJ_Metadata dataset
|
|
223
|
+
const ds = await md.GetAndCacheDatasetByName("MJ_Metadata");
|
|
224
|
+
if (!ds || !ds.Success) {
|
|
225
|
+
throw new Error(ds?.Status || 'Failed to load metadata dataset');
|
|
226
|
+
}
|
|
227
|
+
const dashList = ds.Results.find(r => r.Code === 'Dashboards');
|
|
228
|
+
if (!dashList) {
|
|
229
|
+
throw new Error('Dashboards dataset not found');
|
|
230
|
+
}
|
|
231
|
+
// Filter dashboards by scope
|
|
232
|
+
const appFilter = this.applicationId ? ` AND ApplicationID='${this.applicationId}'` : ' AND ApplicationID IS NULL';
|
|
233
|
+
this.allAvailableDashboards = dashList.Results.filter((d) => {
|
|
234
|
+
if (this.scope === 'Global') {
|
|
235
|
+
return d.Scope === 'Global' && !d.ApplicationID;
|
|
218
236
|
}
|
|
219
|
-
|
|
220
|
-
this.
|
|
237
|
+
else {
|
|
238
|
+
return d.ApplicationID === this.applicationId; // ignore scope for dashboards that match app id, sometimes they have a global scope as they can be shown globally as well as app specific
|
|
221
239
|
}
|
|
222
240
|
});
|
|
241
|
+
// Load current user preferences
|
|
242
|
+
await this.loadCurrentPreferences();
|
|
243
|
+
// Split dashboards into available and configured
|
|
244
|
+
this.splitDashboards();
|
|
245
|
+
// Store original state to detect changes
|
|
246
|
+
this.originalConfiguredIds = this.configuredDashboards.map(d => d.ID);
|
|
223
247
|
}
|
|
224
|
-
|
|
225
|
-
|
|
248
|
+
async loadApplicationName() {
|
|
249
|
+
if (!this.applicationId)
|
|
250
|
+
return;
|
|
251
|
+
try {
|
|
226
252
|
const md = new Metadata();
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
// Load application name if we're in app scope
|
|
233
|
-
if (this.scope === 'App' && this.applicationId) {
|
|
234
|
-
yield this.loadApplicationName();
|
|
235
|
-
}
|
|
236
|
-
// Get cached dashboards from MJ_Metadata dataset
|
|
237
|
-
const ds = yield md.GetAndCacheDatasetByName("MJ_Metadata");
|
|
238
|
-
if (!ds || !ds.Success) {
|
|
239
|
-
throw new Error((ds === null || ds === void 0 ? void 0 : ds.Status) || 'Failed to load metadata dataset');
|
|
240
|
-
}
|
|
241
|
-
const dashList = ds.Results.find(r => r.Code === 'Dashboards');
|
|
242
|
-
if (!dashList) {
|
|
243
|
-
throw new Error('Dashboards dataset not found');
|
|
244
|
-
}
|
|
245
|
-
// Filter dashboards by scope
|
|
246
|
-
const appFilter = this.applicationId ? ` AND ApplicationID='${this.applicationId}'` : ' AND ApplicationID IS NULL';
|
|
247
|
-
this.allAvailableDashboards = dashList.Results.filter((d) => {
|
|
248
|
-
if (this.scope === 'Global') {
|
|
249
|
-
return d.Scope === 'Global' && !d.ApplicationID;
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
return d.ApplicationID === this.applicationId; // ignore scope for dashboards that match app id, sometimes they have a global scope as they can be shown globally as well as app specific
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
// Load current user preferences
|
|
256
|
-
yield this.loadCurrentPreferences();
|
|
257
|
-
// Split dashboards into available and configured
|
|
258
|
-
this.splitDashboards();
|
|
259
|
-
// Store original state to detect changes
|
|
260
|
-
this.originalConfiguredIds = this.configuredDashboards.map(d => d.ID);
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
loadApplicationName() {
|
|
264
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
265
|
-
if (!this.applicationId)
|
|
266
|
-
return;
|
|
267
|
-
try {
|
|
268
|
-
const md = new Metadata();
|
|
269
|
-
const ds = yield md.GetAndCacheDatasetByName("MJ_Metadata");
|
|
270
|
-
const appList = ds.Results.find(r => r.Code === 'Applications');
|
|
271
|
-
if (appList) {
|
|
272
|
-
const app = appList.Results.find((a) => a.ID === this.applicationId);
|
|
273
|
-
this.applicationName = (app === null || app === void 0 ? void 0 : app.Name) || 'Unknown Application';
|
|
274
|
-
}
|
|
253
|
+
const ds = await md.GetAndCacheDatasetByName("MJ_Metadata");
|
|
254
|
+
const appList = ds.Results.find(r => r.Code === 'Applications');
|
|
255
|
+
if (appList) {
|
|
256
|
+
const app = appList.Results.find((a) => a.ID === this.applicationId);
|
|
257
|
+
this.applicationName = app?.Name || 'Unknown Application';
|
|
275
258
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
LogError('Error loading application name', null, error);
|
|
262
|
+
this.applicationName = 'Unknown Application';
|
|
263
|
+
}
|
|
281
264
|
}
|
|
282
|
-
loadCurrentPreferences() {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
OrderBy: 'DisplayOrder',
|
|
305
|
-
});
|
|
306
|
-
this.currentUserPreferences = (prefsResult === null || prefsResult === void 0 ? void 0 : prefsResult.Results) || [];
|
|
307
|
-
console.log('Loaded preferences:', this.currentUserPreferences.length);
|
|
265
|
+
async loadCurrentPreferences() {
|
|
266
|
+
const rv = new RunView();
|
|
267
|
+
const md = new Metadata();
|
|
268
|
+
const appFilter = this.applicationId ? ` AND ApplicationID='${this.applicationId}'` : '';
|
|
269
|
+
const baseCondition = `Scope='${this.scope}'${appFilter}`;
|
|
270
|
+
let filter;
|
|
271
|
+
if (this.isSysAdmin && this.scope === 'Global' && this.preferenceMode === 'system') {
|
|
272
|
+
// Load system defaults only (UserID IS NULL)
|
|
273
|
+
filter = `UserID IS NULL AND ${baseCondition}`;
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
// Load personal user preferences (including for sysadmin when in personal mode)
|
|
277
|
+
// For personal mode, we ONLY load the user's specific preferences, no fallback to system defaults
|
|
278
|
+
// This allows sysadmin to see their actual personal preferences vs system defaults
|
|
279
|
+
filter = `UserID='${md.CurrentUser.ID}' AND ${baseCondition}`;
|
|
280
|
+
}
|
|
281
|
+
console.log('Loading preferences with filter:', filter);
|
|
282
|
+
const prefsResult = await rv.RunView({
|
|
283
|
+
EntityName: 'MJ: Dashboard User Preferences',
|
|
284
|
+
ExtraFilter: filter,
|
|
285
|
+
ResultType: 'entity_object',
|
|
286
|
+
OrderBy: 'DisplayOrder',
|
|
308
287
|
});
|
|
288
|
+
this.currentUserPreferences = prefsResult?.Results || [];
|
|
289
|
+
console.log('Loaded preferences:', this.currentUserPreferences.length);
|
|
309
290
|
}
|
|
310
291
|
splitDashboards() {
|
|
311
292
|
const configuredIds = new Set(this.currentUserPreferences.map(p => p.DashboardID));
|
|
@@ -380,25 +361,23 @@ export class DashboardPreferencesDialogComponent {
|
|
|
380
361
|
}, 3000);
|
|
381
362
|
}
|
|
382
363
|
}
|
|
383
|
-
onPreferenceModeChange() {
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
});
|
|
364
|
+
async onPreferenceModeChange() {
|
|
365
|
+
try {
|
|
366
|
+
this.loading = true;
|
|
367
|
+
this.error = null;
|
|
368
|
+
// Reload preferences with new mode
|
|
369
|
+
await this.loadCurrentPreferences();
|
|
370
|
+
this.splitDashboards();
|
|
371
|
+
this.originalConfiguredIds = this.configuredDashboards.map(d => d.ID);
|
|
372
|
+
this.hasChanges = false;
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
LogError('Error changing preference mode', null, error);
|
|
376
|
+
this.error = 'Error loading preferences. Please try again.';
|
|
377
|
+
}
|
|
378
|
+
finally {
|
|
379
|
+
this.loading = false;
|
|
380
|
+
}
|
|
402
381
|
}
|
|
403
382
|
checkForChanges() {
|
|
404
383
|
try {
|
|
@@ -418,166 +397,163 @@ export class DashboardPreferencesDialogComponent {
|
|
|
418
397
|
this.hasChanges = false;
|
|
419
398
|
}
|
|
420
399
|
}
|
|
421
|
-
onSave() {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
400
|
+
async onSave() {
|
|
401
|
+
if (this.saving || !this.hasChanges) {
|
|
402
|
+
console.log('Save cancelled:', { saving: this.saving, hasChanges: this.hasChanges });
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
try {
|
|
406
|
+
this.saving = true;
|
|
407
|
+
console.log('Starting save process with configured dashboards:', this.configuredDashboards.map(d => ({ id: d.ID, name: d.Name })));
|
|
408
|
+
const md = new Metadata();
|
|
409
|
+
const rv = new RunView();
|
|
410
|
+
// Get existing preferences for this scope
|
|
411
|
+
const baseCondition = this.scope === 'Global'
|
|
412
|
+
? `Scope='Global' AND ApplicationID IS NULL`
|
|
413
|
+
: `Scope='App' AND ApplicationID='${this.applicationId}'`;
|
|
414
|
+
let userFilter;
|
|
415
|
+
if (this.isSysAdmin && this.scope === 'Global' && this.preferenceMode === 'system') {
|
|
416
|
+
// Managing system defaults
|
|
417
|
+
userFilter = `UserID IS NULL AND ${baseCondition}`;
|
|
427
418
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
419
|
+
else {
|
|
420
|
+
// Managing personal preferences
|
|
421
|
+
userFilter = `UserID='${md.CurrentUser.ID}' AND ${baseCondition}`;
|
|
422
|
+
}
|
|
423
|
+
console.log('Loading existing preferences with filter:', userFilter);
|
|
424
|
+
const existingPrefs = await rv.RunView({
|
|
425
|
+
EntityName: 'MJ: Dashboard User Preferences',
|
|
426
|
+
ExtraFilter: userFilter,
|
|
427
|
+
ResultType: 'entity_object',
|
|
428
|
+
});
|
|
429
|
+
const existingPreferences = existingPrefs?.Results || [];
|
|
430
|
+
console.log('Found existing preferences:', existingPreferences.length);
|
|
431
|
+
// Create maps for efficient lookups
|
|
432
|
+
const existingByDashboardId = new Map();
|
|
433
|
+
existingPreferences.forEach(pref => {
|
|
434
|
+
existingByDashboardId.set(pref.DashboardID, pref);
|
|
435
|
+
});
|
|
436
|
+
const configuredDashboardIds = new Set(this.configuredDashboards.map(d => d.ID));
|
|
437
|
+
// Step 1: Delete preferences that are no longer configured
|
|
438
|
+
const prefsToDelete = existingPreferences.filter(pref => !configuredDashboardIds.has(pref.DashboardID));
|
|
439
|
+
console.log('Preferences to delete:', prefsToDelete.length);
|
|
440
|
+
for (const pref of prefsToDelete) {
|
|
441
|
+
console.log('Deleting preference for dashboard:', pref.DashboardID);
|
|
442
|
+
if (!await pref.Delete()) {
|
|
443
|
+
const errorMsg = pref.LatestResult?.Error || pref.LatestResult?.Message || 'Unknown error';
|
|
444
|
+
throw new Error(`Failed to delete preference: ${errorMsg}`);
|
|
445
445
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
// Step 1: Delete preferences that are no longer configured
|
|
461
|
-
const prefsToDelete = existingPreferences.filter(pref => !configuredDashboardIds.has(pref.DashboardID));
|
|
462
|
-
console.log('Preferences to delete:', prefsToDelete.length);
|
|
463
|
-
for (const pref of prefsToDelete) {
|
|
464
|
-
console.log('Deleting preference for dashboard:', pref.DashboardID);
|
|
465
|
-
if (!(yield pref.Delete())) {
|
|
466
|
-
const errorMsg = ((_a = pref.LatestResult) === null || _a === void 0 ? void 0 : _a.Error) || ((_b = pref.LatestResult) === null || _b === void 0 ? void 0 : _b.Message) || 'Unknown error';
|
|
467
|
-
throw new Error(`Failed to delete preference: ${errorMsg}`);
|
|
446
|
+
}
|
|
447
|
+
// Step 2: Update existing preferences or create new ones
|
|
448
|
+
const newPreferences = [];
|
|
449
|
+
for (let i = 0; i < this.configuredDashboards.length; i++) {
|
|
450
|
+
const dashboard = this.configuredDashboards[i];
|
|
451
|
+
const newDisplayOrder = i + 1;
|
|
452
|
+
let prefEntity = existingByDashboardId.get(dashboard.ID);
|
|
453
|
+
if (prefEntity) {
|
|
454
|
+
// Update existing preference
|
|
455
|
+
console.log(`Updating existing preference for dashboard ${dashboard.Name}, new order: ${newDisplayOrder}`);
|
|
456
|
+
prefEntity.DisplayOrder = newDisplayOrder;
|
|
457
|
+
if (!await prefEntity.Save()) {
|
|
458
|
+
const errorMsg = prefEntity.LatestResult?.Error || prefEntity.LatestResult?.Message || 'Unknown error';
|
|
459
|
+
throw new Error(`Failed to update preference for dashboard ${dashboard.Name}: ${errorMsg}`);
|
|
468
460
|
}
|
|
469
461
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
// Update existing preference
|
|
478
|
-
console.log(`Updating existing preference for dashboard ${dashboard.Name}, new order: ${newDisplayOrder}`);
|
|
479
|
-
prefEntity.DisplayOrder = newDisplayOrder;
|
|
480
|
-
if (!(yield prefEntity.Save())) {
|
|
481
|
-
const errorMsg = ((_c = prefEntity.LatestResult) === null || _c === void 0 ? void 0 : _c.Error) || ((_d = prefEntity.LatestResult) === null || _d === void 0 ? void 0 : _d.Message) || 'Unknown error';
|
|
482
|
-
throw new Error(`Failed to update preference for dashboard ${dashboard.Name}: ${errorMsg}`);
|
|
483
|
-
}
|
|
462
|
+
else {
|
|
463
|
+
// Create new preference
|
|
464
|
+
console.log(`Creating new preference for dashboard ${dashboard.Name}, order: ${newDisplayOrder}`);
|
|
465
|
+
prefEntity = await md.GetEntityObject('MJ: Dashboard User Preferences');
|
|
466
|
+
// Set UserID based on preference mode
|
|
467
|
+
if (this.isSysAdmin && this.scope === 'Global' && this.preferenceMode === 'system') {
|
|
468
|
+
prefEntity.UserID = null; // System default
|
|
484
469
|
}
|
|
485
470
|
else {
|
|
486
|
-
//
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
prefEntity.
|
|
497
|
-
prefEntity.
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
DashboardID: prefEntity.DashboardID,
|
|
503
|
-
DisplayOrder: prefEntity.DisplayOrder,
|
|
504
|
-
Scope: prefEntity.Scope,
|
|
505
|
-
ApplicationID: prefEntity.ApplicationID
|
|
506
|
-
});
|
|
507
|
-
if (!(yield prefEntity.Save())) {
|
|
508
|
-
const errorMsg = ((_e = prefEntity.LatestResult) === null || _e === void 0 ? void 0 : _e.Error) || ((_f = prefEntity.LatestResult) === null || _f === void 0 ? void 0 : _f.Message) || 'Unknown error';
|
|
509
|
-
throw new Error(`Failed to create preference for dashboard ${dashboard.Name}: ${errorMsg}`);
|
|
510
|
-
}
|
|
471
|
+
prefEntity.UserID = md.CurrentUser.ID; // Personal preference
|
|
472
|
+
}
|
|
473
|
+
prefEntity.DashboardID = dashboard.ID;
|
|
474
|
+
prefEntity.DisplayOrder = newDisplayOrder;
|
|
475
|
+
prefEntity.Scope = this.scope;
|
|
476
|
+
prefEntity.ApplicationID = this.applicationId;
|
|
477
|
+
console.log('Creating preference entity:', {
|
|
478
|
+
UserID: prefEntity.UserID,
|
|
479
|
+
DashboardID: prefEntity.DashboardID,
|
|
480
|
+
DisplayOrder: prefEntity.DisplayOrder,
|
|
481
|
+
Scope: prefEntity.Scope,
|
|
482
|
+
ApplicationID: prefEntity.ApplicationID
|
|
483
|
+
});
|
|
484
|
+
if (!await prefEntity.Save()) {
|
|
485
|
+
const errorMsg = prefEntity.LatestResult?.Error || prefEntity.LatestResult?.Message || 'Unknown error';
|
|
486
|
+
throw new Error(`Failed to create preference for dashboard ${dashboard.Name}: ${errorMsg}`);
|
|
511
487
|
}
|
|
512
|
-
newPreferences.push(prefEntity);
|
|
513
488
|
}
|
|
514
|
-
|
|
515
|
-
// Emit success result
|
|
516
|
-
this.result.emit({
|
|
517
|
-
saved: true,
|
|
518
|
-
preferences: newPreferences
|
|
519
|
-
});
|
|
489
|
+
newPreferences.push(prefEntity);
|
|
520
490
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
491
|
+
console.log('Successfully processed', newPreferences.length, 'preferences');
|
|
492
|
+
// Emit success result
|
|
493
|
+
this.result.emit({
|
|
494
|
+
saved: true,
|
|
495
|
+
preferences: newPreferences
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
catch (error) {
|
|
499
|
+
console.error('Save error:', error);
|
|
500
|
+
LogError('Error saving dashboard preferences', null, error);
|
|
501
|
+
this.error = `Failed to save preferences: ${error instanceof Error ? error.message : 'Unknown error'}`;
|
|
502
|
+
// Clear error after 5 seconds
|
|
503
|
+
setTimeout(() => {
|
|
504
|
+
this.error = null;
|
|
505
|
+
}, 5000);
|
|
506
|
+
}
|
|
507
|
+
finally {
|
|
508
|
+
this.saving = false;
|
|
509
|
+
}
|
|
534
510
|
}
|
|
535
511
|
onCancel() {
|
|
536
512
|
this.result.emit({ saved: false });
|
|
537
513
|
}
|
|
514
|
+
static ɵfac = function DashboardPreferencesDialogComponent_Factory(t) { return new (t || DashboardPreferencesDialogComponent)(); };
|
|
515
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DashboardPreferencesDialogComponent, selectors: [["mj-dashboard-preferences-dialog"]], inputs: { applicationId: "applicationId", scope: "scope" }, outputs: { result: "result" }, decls: 21, vars: 11, consts: [["availableList", "cdkDropList"], ["configuredList", "cdkDropList"], ["title", "Dashboard Preferences", 3, "close", "width", "height", "minWidth", "minHeight"], [1, "dashboard-preferences-dialog"], [1, "dialog-header"], [1, "fa-solid", "fa-sliders"], [1, "scope-indicator"], [1, "preference-mode-selector"], [1, "dialog-content"], [1, "loading-container"], [1, "error-container"], [1, "preferences-panels"], [1, "dialog-footer"], ["type", "button", 1, "btn", "btn-secondary", 3, "click"], [1, "fa-solid", "fa-times"], ["type", "button", 1, "btn", "btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-globe"], [1, "fa-solid", "fa-layer-group"], ["type", "radio", "name", "preferenceMode", "value", "personal", 3, "ngModelChange", "change", "ngModel"], ["type", "radio", "name", "preferenceMode", "value", "system", 3, "ngModelChange", "change", "ngModel"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "panel", "available-panel"], [1, "fa-solid", "fa-list"], ["cdkDropList", "", 1, "dashboard-list", 3, "cdkDropListDropped", "cdkDropListData", "cdkDropListConnectedTo"], [1, "empty-state"], ["cdkDrag", "", 1, "dashboard-item", "available-item", 3, "cdkDragData"], [1, "panel", "configured-panel"], [1, "fa-solid", "fa-list-ol"], ["cdkDrag", "", 1, "dashboard-item", "configured-item", 3, "cdkDragData"], [1, "fa-solid", "fa-info-circle"], [1, "dashboard-item-content"], [1, "fa-solid", "fa-grip-vertical", "drag-handle"], [1, "dashboard-info"], [1, "dashboard-name"], [1, "dashboard-description"], ["type", "button", "title", "Add to configured dashboards", 1, "add-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "order-number"], ["type", "button", "title", "Remove from configured dashboards", 1, "remove-button", 3, "click"], ["size", "small"], [1, "fa-solid", "fa-check"]], template: function DashboardPreferencesDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
516
|
+
i0.ɵɵelementStart(0, "kendo-dialog", 2);
|
|
517
|
+
i0.ɵɵlistener("close", function DashboardPreferencesDialogComponent_Template_kendo_dialog_close_0_listener() { return ctx.onCancel(); });
|
|
518
|
+
i0.ɵɵelementStart(1, "div", 3)(2, "div", 4)(3, "h3");
|
|
519
|
+
i0.ɵɵelement(4, "i", 5);
|
|
520
|
+
i0.ɵɵtext(5, " Dashboard Preferences");
|
|
521
|
+
i0.ɵɵelementEnd();
|
|
522
|
+
i0.ɵɵelementStart(6, "p", 6);
|
|
523
|
+
i0.ɵɵtemplate(7, DashboardPreferencesDialogComponent_Conditional_7_Template, 2, 0)(8, DashboardPreferencesDialogComponent_Conditional_8_Template, 2, 1);
|
|
524
|
+
i0.ɵɵelementEnd();
|
|
525
|
+
i0.ɵɵtemplate(9, DashboardPreferencesDialogComponent_Conditional_9_Template, 7, 2, "div", 7);
|
|
526
|
+
i0.ɵɵelementEnd();
|
|
527
|
+
i0.ɵɵelementStart(10, "div", 8);
|
|
528
|
+
i0.ɵɵtemplate(11, DashboardPreferencesDialogComponent_Conditional_11_Template, 3, 0, "div", 9)(12, DashboardPreferencesDialogComponent_Conditional_12_Template, 3, 1, "div", 10)(13, DashboardPreferencesDialogComponent_Conditional_13_Template, 19, 10, "div", 11);
|
|
529
|
+
i0.ɵɵelementEnd();
|
|
530
|
+
i0.ɵɵelementStart(14, "div", 12)(15, "button", 13);
|
|
531
|
+
i0.ɵɵlistener("click", function DashboardPreferencesDialogComponent_Template_button_click_15_listener() { return ctx.onCancel(); });
|
|
532
|
+
i0.ɵɵelement(16, "i", 14);
|
|
533
|
+
i0.ɵɵtext(17, " Cancel ");
|
|
534
|
+
i0.ɵɵelementEnd();
|
|
535
|
+
i0.ɵɵelementStart(18, "button", 15);
|
|
536
|
+
i0.ɵɵlistener("click", function DashboardPreferencesDialogComponent_Template_button_click_18_listener() { return ctx.onSave(); });
|
|
537
|
+
i0.ɵɵtemplate(19, DashboardPreferencesDialogComponent_Conditional_19_Template, 2, 0)(20, DashboardPreferencesDialogComponent_Conditional_20_Template, 2, 0);
|
|
538
|
+
i0.ɵɵelementEnd()()()();
|
|
539
|
+
} if (rf & 2) {
|
|
540
|
+
i0.ɵɵproperty("width", 800)("height", 600)("minWidth", 600)("minHeight", 400);
|
|
541
|
+
i0.ɵɵadvance(7);
|
|
542
|
+
i0.ɵɵconditional(ctx.scope === "Global" ? 7 : 8);
|
|
543
|
+
i0.ɵɵadvance(2);
|
|
544
|
+
i0.ɵɵconditional(ctx.isSysAdmin && ctx.scope === "Global" ? 9 : -1);
|
|
545
|
+
i0.ɵɵadvance(2);
|
|
546
|
+
i0.ɵɵconditional(ctx.loading ? 11 : -1);
|
|
547
|
+
i0.ɵɵadvance();
|
|
548
|
+
i0.ɵɵconditional(ctx.error ? 12 : -1);
|
|
549
|
+
i0.ɵɵadvance();
|
|
550
|
+
i0.ɵɵconditional(!ctx.loading && !ctx.error ? 13 : -1);
|
|
551
|
+
i0.ɵɵadvance(5);
|
|
552
|
+
i0.ɵɵproperty("disabled", !ctx.hasChanges || ctx.saving);
|
|
553
|
+
i0.ɵɵadvance();
|
|
554
|
+
i0.ɵɵconditional(ctx.saving ? 19 : 20);
|
|
555
|
+
} }, dependencies: [i1.DefaultValueAccessor, i1.RadioControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.DialogComponent, i3.LoaderComponent, i4.CdkDropList, i4.CdkDrag], styles: [".dashboard-preferences-dialog[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.dialog-header[_ngcontent-%COMP%] {\n padding: 20px 24px 16px;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 8px;\n color: #666;\n}\n\n.scope-indicator[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n.scope-indicator[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 6px;\n}\n\n.preference-mode-selector[_ngcontent-%COMP%] {\n margin-top: 12px;\n display: flex;\n gap: 20px;\n}\n\n.preference-mode-selector[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n font-size: 14px;\n color: #495057;\n}\n\n.preference-mode-selector[_ngcontent-%COMP%] input[type=\"radio\"][_ngcontent-%COMP%] {\n margin: 0;\n}\n\n.dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n padding: 20px 24px;\n}\n\n.loading-container[_ngcontent-%COMP%], .error-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n flex-direction: column;\n gap: 12px;\n}\n\n.error-container[_ngcontent-%COMP%] {\n color: #dc3545;\n}\n\n.error-container[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n margin-bottom: 8px;\n}\n\n.preferences-panels[_ngcontent-%COMP%] {\n display: flex;\n gap: 20px;\n height: 100%;\n}\n\n.panel[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.panel[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0;\n padding: 12px 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n font-size: 14px;\n font-weight: 600;\n color: #495057;\n}\n\n.panel[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 8px;\n color: #6c757d;\n}\n\n.dashboard-list[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100px;\n color: #6c757d;\n font-style: italic;\n flex-direction: column;\n gap: 8px;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n opacity: 0.5;\n}\n\n.dashboard-item[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n background: white;\n transition: all 0.2s ease;\n cursor: move;\n}\n\n.dashboard-item[_ngcontent-%COMP%]:hover {\n border-color: #007bff;\n box-shadow: 0 2px 4px rgba(0, 123, 255, 0.1);\n}\n\n.dashboard-item.cdk-drag-preview[_ngcontent-%COMP%] {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform: rotate(2deg);\n}\n\n.dashboard-item.cdk-drag-placeholder[_ngcontent-%COMP%] {\n opacity: 0.3;\n border-style: dashed;\n}\n\n.dashboard-item-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px;\n gap: 12px;\n}\n\n.drag-handle[_ngcontent-%COMP%] {\n color: #adb5bd;\n cursor: move;\n font-size: 14px;\n}\n\n.order-number[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n background: #007bff;\n color: white;\n border-radius: 50%;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.dashboard-info[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.dashboard-name[_ngcontent-%COMP%] {\n font-weight: 500;\n color: #333;\n font-size: 14px;\n}\n\n.dashboard-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #6c757d;\n line-height: 1.3;\n}\n\n.add-button[_ngcontent-%COMP%] {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #28a745;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n opacity: 0.7;\n}\n\n.add-button[_ngcontent-%COMP%]:hover {\n background-color: rgba(40, 167, 69, 0.1);\n opacity: 1;\n}\n\n.remove-button[_ngcontent-%COMP%] {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #dc3545;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n}\n\n.remove-button[_ngcontent-%COMP%]:hover {\n background-color: rgba(220, 53, 69, 0.1);\n}\n\n.cdk-drop-list-dragging[_ngcontent-%COMP%] .dashboard-item[_ngcontent-%COMP%]:not(.cdk-drag-placeholder) {\n transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);\n}\n\n.dialog-footer[_ngcontent-%COMP%] {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n background: #f8f9fa;\n}\n\n.btn[_ngcontent-%COMP%] {\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n border: 1px solid transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-secondary[_ngcontent-%COMP%] {\n background: white;\n border-color: #6c757d;\n color: #6c757d;\n}\n\n.btn-secondary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #6c757d;\n color: white;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n background: #007bff;\n color: white;\n border-color: #007bff;\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #0056b3;\n border-color: #0056b3;\n}\n\n\n\n@media (max-width: 768px) {\n .dashboard-preferences-dialog[_ngcontent-%COMP%] {\n width: 95vw;\n max-height: 90vh;\n }\n \n .preferences-panels[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 16px;\n }\n \n .panel[_ngcontent-%COMP%] {\n min-height: 200px;\n }\n \n .dashboard-list[_ngcontent-%COMP%] {\n min-height: 150px;\n }\n}"] });
|
|
538
556
|
}
|
|
539
|
-
DashboardPreferencesDialogComponent.ɵfac = function DashboardPreferencesDialogComponent_Factory(t) { return new (t || DashboardPreferencesDialogComponent)(); };
|
|
540
|
-
DashboardPreferencesDialogComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: DashboardPreferencesDialogComponent, selectors: [["mj-dashboard-preferences-dialog"]], inputs: { applicationId: "applicationId", scope: "scope" }, outputs: { result: "result" }, decls: 21, vars: 11, consts: [["availableList", "cdkDropList"], ["configuredList", "cdkDropList"], ["title", "Dashboard Preferences", 3, "close", "width", "height", "minWidth", "minHeight"], [1, "dashboard-preferences-dialog"], [1, "dialog-header"], [1, "fa-solid", "fa-sliders"], [1, "scope-indicator"], [1, "preference-mode-selector"], [1, "dialog-content"], [1, "loading-container"], [1, "error-container"], [1, "preferences-panels"], [1, "dialog-footer"], ["type", "button", 1, "btn", "btn-secondary", 3, "click"], [1, "fa-solid", "fa-times"], ["type", "button", 1, "btn", "btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-globe"], [1, "fa-solid", "fa-layer-group"], ["type", "radio", "name", "preferenceMode", "value", "personal", 3, "ngModelChange", "change", "ngModel"], ["type", "radio", "name", "preferenceMode", "value", "system", 3, "ngModelChange", "change", "ngModel"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "panel", "available-panel"], [1, "fa-solid", "fa-list"], ["cdkDropList", "", 1, "dashboard-list", 3, "cdkDropListDropped", "cdkDropListData", "cdkDropListConnectedTo"], [1, "empty-state"], ["cdkDrag", "", 1, "dashboard-item", "available-item", 3, "cdkDragData"], [1, "panel", "configured-panel"], [1, "fa-solid", "fa-list-ol"], ["cdkDrag", "", 1, "dashboard-item", "configured-item", 3, "cdkDragData"], [1, "fa-solid", "fa-info-circle"], [1, "dashboard-item-content"], [1, "fa-solid", "fa-grip-vertical", "drag-handle"], [1, "dashboard-info"], [1, "dashboard-name"], [1, "dashboard-description"], ["type", "button", "title", "Add to configured dashboards", 1, "add-button", 3, "click"], [1, "fa-solid", "fa-plus"], [1, "order-number"], ["type", "button", "title", "Remove from configured dashboards", 1, "remove-button", 3, "click"], ["size", "small"], [1, "fa-solid", "fa-check"]], template: function DashboardPreferencesDialogComponent_Template(rf, ctx) { if (rf & 1) {
|
|
541
|
-
i0.ɵɵelementStart(0, "kendo-dialog", 2);
|
|
542
|
-
i0.ɵɵlistener("close", function DashboardPreferencesDialogComponent_Template_kendo_dialog_close_0_listener() { return ctx.onCancel(); });
|
|
543
|
-
i0.ɵɵelementStart(1, "div", 3)(2, "div", 4)(3, "h3");
|
|
544
|
-
i0.ɵɵelement(4, "i", 5);
|
|
545
|
-
i0.ɵɵtext(5, " Dashboard Preferences");
|
|
546
|
-
i0.ɵɵelementEnd();
|
|
547
|
-
i0.ɵɵelementStart(6, "p", 6);
|
|
548
|
-
i0.ɵɵtemplate(7, DashboardPreferencesDialogComponent_Conditional_7_Template, 2, 0)(8, DashboardPreferencesDialogComponent_Conditional_8_Template, 2, 1);
|
|
549
|
-
i0.ɵɵelementEnd();
|
|
550
|
-
i0.ɵɵtemplate(9, DashboardPreferencesDialogComponent_Conditional_9_Template, 7, 2, "div", 7);
|
|
551
|
-
i0.ɵɵelementEnd();
|
|
552
|
-
i0.ɵɵelementStart(10, "div", 8);
|
|
553
|
-
i0.ɵɵtemplate(11, DashboardPreferencesDialogComponent_Conditional_11_Template, 3, 0, "div", 9)(12, DashboardPreferencesDialogComponent_Conditional_12_Template, 3, 1, "div", 10)(13, DashboardPreferencesDialogComponent_Conditional_13_Template, 19, 10, "div", 11);
|
|
554
|
-
i0.ɵɵelementEnd();
|
|
555
|
-
i0.ɵɵelementStart(14, "div", 12)(15, "button", 13);
|
|
556
|
-
i0.ɵɵlistener("click", function DashboardPreferencesDialogComponent_Template_button_click_15_listener() { return ctx.onCancel(); });
|
|
557
|
-
i0.ɵɵelement(16, "i", 14);
|
|
558
|
-
i0.ɵɵtext(17, " Cancel ");
|
|
559
|
-
i0.ɵɵelementEnd();
|
|
560
|
-
i0.ɵɵelementStart(18, "button", 15);
|
|
561
|
-
i0.ɵɵlistener("click", function DashboardPreferencesDialogComponent_Template_button_click_18_listener() { return ctx.onSave(); });
|
|
562
|
-
i0.ɵɵtemplate(19, DashboardPreferencesDialogComponent_Conditional_19_Template, 2, 0)(20, DashboardPreferencesDialogComponent_Conditional_20_Template, 2, 0);
|
|
563
|
-
i0.ɵɵelementEnd()()()();
|
|
564
|
-
} if (rf & 2) {
|
|
565
|
-
i0.ɵɵproperty("width", 800)("height", 600)("minWidth", 600)("minHeight", 400);
|
|
566
|
-
i0.ɵɵadvance(7);
|
|
567
|
-
i0.ɵɵconditional(ctx.scope === "Global" ? 7 : 8);
|
|
568
|
-
i0.ɵɵadvance(2);
|
|
569
|
-
i0.ɵɵconditional(ctx.isSysAdmin && ctx.scope === "Global" ? 9 : -1);
|
|
570
|
-
i0.ɵɵadvance(2);
|
|
571
|
-
i0.ɵɵconditional(ctx.loading ? 11 : -1);
|
|
572
|
-
i0.ɵɵadvance();
|
|
573
|
-
i0.ɵɵconditional(ctx.error ? 12 : -1);
|
|
574
|
-
i0.ɵɵadvance();
|
|
575
|
-
i0.ɵɵconditional(!ctx.loading && !ctx.error ? 13 : -1);
|
|
576
|
-
i0.ɵɵadvance(5);
|
|
577
|
-
i0.ɵɵproperty("disabled", !ctx.hasChanges || ctx.saving);
|
|
578
|
-
i0.ɵɵadvance();
|
|
579
|
-
i0.ɵɵconditional(ctx.saving ? 19 : 20);
|
|
580
|
-
} }, dependencies: [i1.DefaultValueAccessor, i1.RadioControlValueAccessor, i1.NgControlStatus, i1.NgModel, i2.DialogComponent, i3.LoaderComponent, i4.CdkDropList, i4.CdkDrag], styles: [".dashboard-preferences-dialog[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.dialog-header[_ngcontent-%COMP%] {\n padding: 20px 24px 16px;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n}\n\n.dialog-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 8px;\n color: #666;\n}\n\n.scope-indicator[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n.scope-indicator[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 6px;\n}\n\n.preference-mode-selector[_ngcontent-%COMP%] {\n margin-top: 12px;\n display: flex;\n gap: 20px;\n}\n\n.preference-mode-selector[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n font-size: 14px;\n color: #495057;\n}\n\n.preference-mode-selector[_ngcontent-%COMP%] input[type=\"radio\"][_ngcontent-%COMP%] {\n margin: 0;\n}\n\n.dialog-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow: hidden;\n padding: 20px 24px;\n}\n\n.loading-container[_ngcontent-%COMP%], .error-container[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n flex-direction: column;\n gap: 12px;\n}\n\n.error-container[_ngcontent-%COMP%] {\n color: #dc3545;\n}\n\n.error-container[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n margin-bottom: 8px;\n}\n\n.preferences-panels[_ngcontent-%COMP%] {\n display: flex;\n gap: 20px;\n height: 100%;\n}\n\n.panel[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.panel[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0;\n padding: 12px 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n font-size: 14px;\n font-weight: 600;\n color: #495057;\n}\n\n.panel[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 8px;\n color: #6c757d;\n}\n\n.dashboard-list[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n}\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100px;\n color: #6c757d;\n font-style: italic;\n flex-direction: column;\n gap: 8px;\n}\n\n.empty-state[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 24px;\n opacity: 0.5;\n}\n\n.dashboard-item[_ngcontent-%COMP%] {\n margin-bottom: 8px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n background: white;\n transition: all 0.2s ease;\n cursor: move;\n}\n\n.dashboard-item[_ngcontent-%COMP%]:hover {\n border-color: #007bff;\n box-shadow: 0 2px 4px rgba(0, 123, 255, 0.1);\n}\n\n.dashboard-item.cdk-drag-preview[_ngcontent-%COMP%] {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform: rotate(2deg);\n}\n\n.dashboard-item.cdk-drag-placeholder[_ngcontent-%COMP%] {\n opacity: 0.3;\n border-style: dashed;\n}\n\n.dashboard-item-content[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 12px;\n gap: 12px;\n}\n\n.drag-handle[_ngcontent-%COMP%] {\n color: #adb5bd;\n cursor: move;\n font-size: 14px;\n}\n\n.order-number[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n background: #007bff;\n color: white;\n border-radius: 50%;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.dashboard-info[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.dashboard-name[_ngcontent-%COMP%] {\n font-weight: 500;\n color: #333;\n font-size: 14px;\n}\n\n.dashboard-description[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #6c757d;\n line-height: 1.3;\n}\n\n.add-button[_ngcontent-%COMP%] {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #28a745;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n opacity: 0.7;\n}\n\n.add-button[_ngcontent-%COMP%]:hover {\n background-color: rgba(40, 167, 69, 0.1);\n opacity: 1;\n}\n\n.remove-button[_ngcontent-%COMP%] {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #dc3545;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n}\n\n.remove-button[_ngcontent-%COMP%]:hover {\n background-color: rgba(220, 53, 69, 0.1);\n}\n\n.cdk-drop-list-dragging[_ngcontent-%COMP%] .dashboard-item[_ngcontent-%COMP%]:not(.cdk-drag-placeholder) {\n transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);\n}\n\n.dialog-footer[_ngcontent-%COMP%] {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n background: #f8f9fa;\n}\n\n.btn[_ngcontent-%COMP%] {\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n border: 1px solid transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-secondary[_ngcontent-%COMP%] {\n background: white;\n border-color: #6c757d;\n color: #6c757d;\n}\n\n.btn-secondary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #6c757d;\n color: white;\n}\n\n.btn-primary[_ngcontent-%COMP%] {\n background: #007bff;\n color: white;\n border-color: #007bff;\n}\n\n.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #0056b3;\n border-color: #0056b3;\n}\n\n\n\n@media (max-width: 768px) {\n .dashboard-preferences-dialog[_ngcontent-%COMP%] {\n width: 95vw;\n max-height: 90vh;\n }\n \n .preferences-panels[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 16px;\n }\n \n .panel[_ngcontent-%COMP%] {\n min-height: 200px;\n }\n \n .dashboard-list[_ngcontent-%COMP%] {\n min-height: 150px;\n }\n}"] });
|
|
581
557
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(DashboardPreferencesDialogComponent, [{
|
|
582
558
|
type: Component,
|
|
583
559
|
args: [{ selector: 'mj-dashboard-preferences-dialog', template: "<kendo-dialog title=\"Dashboard Preferences\" \n [width]=\"800\" \n [height]=\"600\"\n [minWidth]=\"600\"\n [minHeight]=\"400\"\n (close)=\"onCancel()\">\n<div class=\"dashboard-preferences-dialog\">\n <div class=\"dialog-header\">\n <h3><i class=\"fa-solid fa-sliders\"></i> Dashboard Preferences</h3>\n <p class=\"scope-indicator\">\n @if (scope === 'Global') {\n <i class=\"fa-solid fa-globe\"></i> Global Scope\n } @else {\n <i class=\"fa-solid fa-layer-group\"></i> Application: {{ applicationName }}\n }\n </p>\n @if (isSysAdmin && scope === 'Global') {\n <div class=\"preference-mode-selector\">\n <label>\n <input type=\"radio\" \n name=\"preferenceMode\" \n value=\"personal\" \n [(ngModel)]=\"preferenceMode\"\n (change)=\"onPreferenceModeChange()\">\n Personal Preferences\n </label>\n <label>\n <input type=\"radio\" \n name=\"preferenceMode\" \n value=\"system\" \n [(ngModel)]=\"preferenceMode\"\n (change)=\"onPreferenceModeChange()\">\n System Defaults\n </label>\n </div>\n }\n </div>\n\n <div class=\"dialog-content\">\n @if (loading) {\n <div class=\"loading-container\">\n <kendo-loader></kendo-loader>\n Loading dashboards...\n </div>\n }\n\n @if (error) {\n <div class=\"error-container\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n {{ error }}\n </div>\n }\n\n @if (!loading && !error) {\n <div class=\"preferences-panels\">\n <!-- Available Dashboards Panel -->\n <div class=\"panel available-panel\">\n <h4><i class=\"fa-solid fa-list\"></i> Available Dashboards</h4>\n <div class=\"dashboard-list\" \n cdkDropList \n #availableList=\"cdkDropList\"\n [cdkDropListData]=\"availableDashboards\"\n [cdkDropListConnectedTo]=\"[configuredList]\"\n (cdkDropListDropped)=\"onDrop($event)\">\n \n @if (availableDashboards.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-info-circle\"></i>\n All dashboards are configured\n </div>\n }\n\n @for (dashboard of availableDashboards; track dashboard.ID) {\n <div class=\"dashboard-item available-item\" \n cdkDrag\n [cdkDragData]=\"dashboard\">\n <div class=\"dashboard-item-content\">\n <i class=\"fa-solid fa-grip-vertical drag-handle\"></i>\n <div class=\"dashboard-info\">\n <span class=\"dashboard-name\">{{ dashboard.Name }}</span>\n @if (dashboard.Description) {\n <span class=\"dashboard-description\">{{ dashboard.Description }}</span>\n }\n </div>\n <button type=\"button\" \n class=\"add-button\"\n (click)=\"addDashboard(dashboard)\"\n title=\"Add to configured dashboards\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Configured Dashboards Panel -->\n <div class=\"panel configured-panel\">\n <h4><i class=\"fa-solid fa-list-ol\"></i> Configured Dashboards</h4>\n <div class=\"dashboard-list\" \n cdkDropList \n #configuredList=\"cdkDropList\"\n [cdkDropListData]=\"configuredDashboards\"\n [cdkDropListConnectedTo]=\"[availableList]\"\n (cdkDropListDropped)=\"onDrop($event)\">\n \n @if (configuredDashboards.length === 0) {\n <div class=\"empty-state\">\n <i class=\"fa-solid fa-info-circle\"></i>\n No dashboards configured\n </div>\n }\n\n @for (dashboard of configuredDashboards; track dashboard.ID; let i = $index) {\n <div class=\"dashboard-item configured-item\" \n cdkDrag\n [cdkDragData]=\"dashboard\">\n <div class=\"dashboard-item-content\">\n <i class=\"fa-solid fa-grip-vertical drag-handle\"></i>\n <span class=\"order-number\">{{ i + 1 }}</span>\n <div class=\"dashboard-info\">\n <span class=\"dashboard-name\">{{ dashboard.Name }}</span>\n @if (dashboard.Description) {\n <span class=\"dashboard-description\">{{ dashboard.Description }}</span>\n }\n </div>\n <button type=\"button\" \n class=\"remove-button\"\n (click)=\"removeDashboard(dashboard)\"\n title=\"Remove from configured dashboards\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n }\n </div>\n\n <div class=\"dialog-footer\">\n <button type=\"button\" \n class=\"btn btn-secondary\" \n (click)=\"onCancel()\">\n <i class=\"fa-solid fa-times\"></i> Cancel\n </button>\n <button type=\"button\" \n class=\"btn btn-primary\" \n [disabled]=\"!hasChanges || saving\"\n (click)=\"onSave()\">\n @if (saving) {\n <kendo-loader size=\"small\"></kendo-loader>\n Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i> Save Changes\n }\n </button>\n </div>\n</div>\n</kendo-dialog>", styles: [".dashboard-preferences-dialog {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.dialog-header {\n padding: 20px 24px 16px;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.dialog-header h3 {\n margin: 0 0 8px 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n}\n\n.dialog-header h3 i {\n margin-right: 8px;\n color: #666;\n}\n\n.scope-indicator {\n margin: 0;\n font-size: 14px;\n color: #666;\n}\n\n.scope-indicator i {\n margin-right: 6px;\n}\n\n.preference-mode-selector {\n margin-top: 12px;\n display: flex;\n gap: 20px;\n}\n\n.preference-mode-selector label {\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n font-size: 14px;\n color: #495057;\n}\n\n.preference-mode-selector input[type=\"radio\"] {\n margin: 0;\n}\n\n.dialog-content {\n flex: 1;\n overflow: hidden;\n padding: 20px 24px;\n}\n\n.loading-container, .error-container {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n flex-direction: column;\n gap: 12px;\n}\n\n.error-container {\n color: #dc3545;\n}\n\n.error-container i {\n font-size: 24px;\n margin-bottom: 8px;\n}\n\n.preferences-panels {\n display: flex;\n gap: 20px;\n height: 100%;\n}\n\n.panel {\n flex: 1;\n display: flex;\n flex-direction: column;\n border: 1px solid #e0e0e0;\n border-radius: 8px;\n overflow: hidden;\n}\n\n.panel h4 {\n margin: 0;\n padding: 12px 16px;\n background: #f8f9fa;\n border-bottom: 1px solid #e0e0e0;\n font-size: 14px;\n font-weight: 600;\n color: #495057;\n}\n\n.panel h4 i {\n margin-right: 8px;\n color: #6c757d;\n}\n\n.dashboard-list {\n flex: 1;\n overflow-y: auto;\n padding: 8px;\n min-height: 300px;\n}\n\n.empty-state {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100px;\n color: #6c757d;\n font-style: italic;\n flex-direction: column;\n gap: 8px;\n}\n\n.empty-state i {\n font-size: 24px;\n opacity: 0.5;\n}\n\n.dashboard-item {\n margin-bottom: 8px;\n border: 1px solid #e0e0e0;\n border-radius: 6px;\n background: white;\n transition: all 0.2s ease;\n cursor: move;\n}\n\n.dashboard-item:hover {\n border-color: #007bff;\n box-shadow: 0 2px 4px rgba(0, 123, 255, 0.1);\n}\n\n.dashboard-item.cdk-drag-preview {\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n transform: rotate(2deg);\n}\n\n.dashboard-item.cdk-drag-placeholder {\n opacity: 0.3;\n border-style: dashed;\n}\n\n.dashboard-item-content {\n display: flex;\n align-items: center;\n padding: 12px;\n gap: 12px;\n}\n\n.drag-handle {\n color: #adb5bd;\n cursor: move;\n font-size: 14px;\n}\n\n.order-number {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n background: #007bff;\n color: white;\n border-radius: 50%;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.dashboard-info {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.dashboard-name {\n font-weight: 500;\n color: #333;\n font-size: 14px;\n}\n\n.dashboard-description {\n font-size: 12px;\n color: #6c757d;\n line-height: 1.3;\n}\n\n.add-button {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #28a745;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n opacity: 0.7;\n}\n\n.add-button:hover {\n background-color: rgba(40, 167, 69, 0.1);\n opacity: 1;\n}\n\n.remove-button {\n background: none;\n border: none;\n padding: 4px;\n cursor: pointer;\n color: #dc3545;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 24px;\n height: 24px;\n}\n\n.remove-button:hover {\n background-color: rgba(220, 53, 69, 0.1);\n}\n\n.cdk-drop-list-dragging .dashboard-item:not(.cdk-drag-placeholder) {\n transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);\n}\n\n.dialog-footer {\n padding: 16px 24px;\n border-top: 1px solid #e0e0e0;\n display: flex;\n justify-content: flex-end;\n gap: 12px;\n background: #f8f9fa;\n}\n\n.btn {\n padding: 8px 16px;\n border-radius: 6px;\n font-size: 14px;\n font-weight: 500;\n border: 1px solid transparent;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.btn-secondary {\n background: white;\n border-color: #6c757d;\n color: #6c757d;\n}\n\n.btn-secondary:hover:not(:disabled) {\n background: #6c757d;\n color: white;\n}\n\n.btn-primary {\n background: #007bff;\n color: white;\n border-color: #007bff;\n}\n\n.btn-primary:hover:not(:disabled) {\n background: #0056b3;\n border-color: #0056b3;\n}\n\n/* Responsive design */\n@media (max-width: 768px) {\n .dashboard-preferences-dialog {\n width: 95vw;\n max-height: 90vh;\n }\n \n .preferences-panels {\n flex-direction: column;\n gap: 16px;\n }\n \n .panel {\n min-height: 200px;\n }\n \n .dashboard-list {\n min-height: 150px;\n }\n}"] }]
|