@memberjunction/ng-explorer-settings 2.108.0 → 2.110.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.
@@ -0,0 +1,696 @@
1
+ import { Component } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { FormsModule } from '@angular/forms';
4
+ import { Metadata } from '@memberjunction/core';
5
+ import { MJGlobal, MJEventType } from '@memberjunction/global';
6
+ import { EventCodes } from '@memberjunction/ng-shared';
7
+ import * as i0 from "@angular/core";
8
+ import * as i1 from "@memberjunction/ng-user-avatar";
9
+ import * as i2 from "@memberjunction/ng-shared";
10
+ import * as i3 from "@angular/forms";
11
+ const _forTrack0 = ($index, $item) => $item.name;
12
+ function UserProfileSettingsComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
13
+ const _r1 = i0.ɵɵgetCurrentView();
14
+ i0.ɵɵelementStart(0, "div", 4);
15
+ i0.ɵɵelement(1, "i", 26);
16
+ i0.ɵɵelementStart(2, "span");
17
+ i0.ɵɵtext(3);
18
+ i0.ɵɵelementEnd();
19
+ i0.ɵɵelementStart(4, "button", 27);
20
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Conditional_6_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.errorMessage = ""); });
21
+ i0.ɵɵelement(5, "i", 28);
22
+ i0.ɵɵelementEnd()();
23
+ } if (rf & 2) {
24
+ const ctx_r1 = i0.ɵɵnextContext();
25
+ i0.ɵɵadvance(3);
26
+ i0.ɵɵtextInterpolate(ctx_r1.errorMessage);
27
+ } }
28
+ function UserProfileSettingsComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
29
+ i0.ɵɵelementStart(0, "div", 5);
30
+ i0.ɵɵelement(1, "i", 29);
31
+ i0.ɵɵelementStart(2, "span");
32
+ i0.ɵɵtext(3, "Avatar updated successfully!");
33
+ i0.ɵɵelementEnd()();
34
+ } }
35
+ function UserProfileSettingsComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
36
+ i0.ɵɵelement(0, "img", 9);
37
+ } if (rf & 2) {
38
+ const ctx_r1 = i0.ɵɵnextContext();
39
+ i0.ɵɵproperty("src", ctx_r1.previewUrl, i0.ɵɵsanitizeUrl);
40
+ } }
41
+ function UserProfileSettingsComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
42
+ i0.ɵɵelementStart(0, "div", 10);
43
+ i0.ɵɵelement(1, "i");
44
+ i0.ɵɵelementEnd();
45
+ } if (rf & 2) {
46
+ const ctx_r1 = i0.ɵɵnextContext();
47
+ i0.ɵɵadvance();
48
+ i0.ɵɵclassMap(ctx_r1.previewIconClass + " preview-icon");
49
+ } }
50
+ function UserProfileSettingsComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
51
+ i0.ɵɵelementStart(0, "div", 11);
52
+ i0.ɵɵelement(1, "i", 30);
53
+ i0.ɵɵelementStart(2, "span");
54
+ i0.ɵɵtext(3, "No avatar selected");
55
+ i0.ɵɵelementEnd()();
56
+ } }
57
+ function UserProfileSettingsComponent_Conditional_33_Conditional_9_Template(rf, ctx) { if (rf & 1) {
58
+ const _r5 = i0.ɵɵgetCurrentView();
59
+ i0.ɵɵelementStart(0, "div", 35);
60
+ i0.ɵɵelement(1, "i", 36);
61
+ i0.ɵɵelementStart(2, "span");
62
+ i0.ɵɵtext(3);
63
+ i0.ɵɵelementEnd();
64
+ i0.ɵɵelementStart(4, "button", 37);
65
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Conditional_33_Conditional_9_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r1.clearUpload()); });
66
+ i0.ɵɵelement(5, "i", 28);
67
+ i0.ɵɵelementEnd()();
68
+ } if (rf & 2) {
69
+ const ctx_r1 = i0.ɵɵnextContext(2);
70
+ i0.ɵɵadvance(3);
71
+ i0.ɵɵtextInterpolate(ctx_r1.uploadedFileName);
72
+ } }
73
+ function UserProfileSettingsComponent_Conditional_33_Template(rf, ctx) { if (rf & 1) {
74
+ const _r3 = i0.ɵɵgetCurrentView();
75
+ i0.ɵɵelementStart(0, "div", 19)(1, "input", 31, 0);
76
+ i0.ɵɵlistener("change", function UserProfileSettingsComponent_Conditional_33_Template_input_change_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onFileSelected($event)); });
77
+ i0.ɵɵelementEnd();
78
+ i0.ɵɵelementStart(3, "button", 32);
79
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Conditional_33_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r3); const fileInput_r4 = i0.ɵɵreference(2); return i0.ɵɵresetView(fileInput_r4.click()); });
80
+ i0.ɵɵelement(4, "i", 14);
81
+ i0.ɵɵtext(5, " Choose Image ");
82
+ i0.ɵɵelementEnd();
83
+ i0.ɵɵelementStart(6, "div", 33);
84
+ i0.ɵɵelement(7, "i", 34);
85
+ i0.ɵɵtext(8, " Max 200KB \u2022 PNG, JPG, GIF, WEBP ");
86
+ i0.ɵɵelementEnd();
87
+ i0.ɵɵtemplate(9, UserProfileSettingsComponent_Conditional_33_Conditional_9_Template, 6, 1, "div", 35);
88
+ i0.ɵɵelementEnd();
89
+ } if (rf & 2) {
90
+ const ctx_r1 = i0.ɵɵnextContext();
91
+ i0.ɵɵadvance(9);
92
+ i0.ɵɵconditional(ctx_r1.uploadedFileName ? 9 : -1);
93
+ } }
94
+ function UserProfileSettingsComponent_Conditional_34_Template(rf, ctx) { if (rf & 1) {
95
+ const _r6 = i0.ɵɵgetCurrentView();
96
+ i0.ɵɵelementStart(0, "div", 20)(1, "label", 38);
97
+ i0.ɵɵtext(2, "Image URL");
98
+ i0.ɵɵelementEnd();
99
+ i0.ɵɵelementStart(3, "input", 39);
100
+ i0.ɵɵtwoWayListener("ngModelChange", function UserProfileSettingsComponent_Conditional_34_Template_input_ngModelChange_3_listener($event) { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r1.imageUrlInput, $event) || (ctx_r1.imageUrlInput = $event); return i0.ɵɵresetView($event); });
101
+ i0.ɵɵlistener("input", function UserProfileSettingsComponent_Conditional_34_Template_input_input_3_listener() { i0.ɵɵrestoreView(_r6); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.onUrlChange()); });
102
+ i0.ɵɵelementEnd();
103
+ i0.ɵɵelementStart(4, "div", 40);
104
+ i0.ɵɵelement(5, "i", 34);
105
+ i0.ɵɵtext(6, " Enter a URL to an image hosted online ");
106
+ i0.ɵɵelementEnd()();
107
+ } if (rf & 2) {
108
+ const ctx_r1 = i0.ɵɵnextContext();
109
+ i0.ɵɵadvance(3);
110
+ i0.ɵɵtwoWayProperty("ngModel", ctx_r1.imageUrlInput);
111
+ } }
112
+ function UserProfileSettingsComponent_Conditional_35_For_2_For_5_Template(rf, ctx) { if (rf & 1) {
113
+ const _r7 = i0.ɵɵgetCurrentView();
114
+ i0.ɵɵelementStart(0, "button", 45);
115
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Conditional_35_For_2_For_5_Template_button_click_0_listener() { const icon_r8 = i0.ɵɵrestoreView(_r7).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.selectIcon(icon_r8)); });
116
+ i0.ɵɵelement(1, "i");
117
+ i0.ɵɵelementEnd();
118
+ } if (rf & 2) {
119
+ const icon_r8 = ctx.$implicit;
120
+ const ctx_r1 = i0.ɵɵnextContext(3);
121
+ i0.ɵɵclassProp("selected", ctx_r1.isIconSelected(icon_r8));
122
+ i0.ɵɵproperty("title", icon_r8);
123
+ i0.ɵɵadvance();
124
+ i0.ɵɵclassMap(icon_r8);
125
+ } }
126
+ function UserProfileSettingsComponent_Conditional_35_For_2_Template(rf, ctx) { if (rf & 1) {
127
+ i0.ɵɵelementStart(0, "div", 41)(1, "h4", 42);
128
+ i0.ɵɵtext(2);
129
+ i0.ɵɵelementEnd();
130
+ i0.ɵɵelementStart(3, "div", 43);
131
+ i0.ɵɵrepeaterCreate(4, UserProfileSettingsComponent_Conditional_35_For_2_For_5_Template, 2, 5, "button", 44, i0.ɵɵrepeaterTrackByIdentity);
132
+ i0.ɵɵelementEnd()();
133
+ } if (rf & 2) {
134
+ const category_r9 = ctx.$implicit;
135
+ i0.ɵɵadvance(2);
136
+ i0.ɵɵtextInterpolate(category_r9.name);
137
+ i0.ɵɵadvance(2);
138
+ i0.ɵɵrepeater(category_r9.icons);
139
+ } }
140
+ function UserProfileSettingsComponent_Conditional_35_Template(rf, ctx) { if (rf & 1) {
141
+ i0.ɵɵelementStart(0, "div", 21);
142
+ i0.ɵɵrepeaterCreate(1, UserProfileSettingsComponent_Conditional_35_For_2_Template, 6, 1, "div", 41, _forTrack0);
143
+ i0.ɵɵelementEnd();
144
+ } if (rf & 2) {
145
+ const ctx_r1 = i0.ɵɵnextContext();
146
+ i0.ɵɵadvance();
147
+ i0.ɵɵrepeater(ctx_r1.iconCategories);
148
+ } }
149
+ function UserProfileSettingsComponent_Conditional_36_Conditional_7_Template(rf, ctx) { if (rf & 1) {
150
+ i0.ɵɵelementStart(0, "span");
151
+ i0.ɵɵtext(1, "Syncing...");
152
+ i0.ɵɵelementEnd();
153
+ } }
154
+ function UserProfileSettingsComponent_Conditional_36_Conditional_8_Template(rf, ctx) { if (rf & 1) {
155
+ i0.ɵɵelementStart(0, "span");
156
+ i0.ɵɵtext(1, "Sync from Microsoft Account");
157
+ i0.ɵɵelementEnd();
158
+ } }
159
+ function UserProfileSettingsComponent_Conditional_36_Conditional_16_Template(rf, ctx) { if (rf & 1) {
160
+ i0.ɵɵelementStart(0, "span");
161
+ i0.ɵɵtext(1, "Reverting...");
162
+ i0.ɵɵelementEnd();
163
+ } }
164
+ function UserProfileSettingsComponent_Conditional_36_Conditional_17_Template(rf, ctx) { if (rf & 1) {
165
+ i0.ɵɵelementStart(0, "span");
166
+ i0.ɵɵtext(1, "Revert to Default");
167
+ i0.ɵɵelementEnd();
168
+ } }
169
+ function UserProfileSettingsComponent_Conditional_36_Template(rf, ctx) { if (rf & 1) {
170
+ const _r10 = i0.ɵɵgetCurrentView();
171
+ i0.ɵɵelementStart(0, "div", 22)(1, "div", 46);
172
+ i0.ɵɵelement(2, "i", 34);
173
+ i0.ɵɵelementStart(3, "p");
174
+ i0.ɵɵtext(4, "Sync your avatar from your authentication provider account");
175
+ i0.ɵɵelementEnd()();
176
+ i0.ɵɵelementStart(5, "button", 47);
177
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Conditional_36_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.syncFromProvider()); });
178
+ i0.ɵɵelement(6, "i", 17);
179
+ i0.ɵɵtemplate(7, UserProfileSettingsComponent_Conditional_36_Conditional_7_Template, 2, 0, "span")(8, UserProfileSettingsComponent_Conditional_36_Conditional_8_Template, 2, 0, "span");
180
+ i0.ɵɵelementEnd();
181
+ i0.ɵɵelementStart(9, "div", 48);
182
+ i0.ɵɵelement(10, "i", 49);
183
+ i0.ɵɵelementStart(11, "small");
184
+ i0.ɵɵtext(12, "This will download your profile photo from Microsoft and save it to your profile");
185
+ i0.ɵɵelementEnd()();
186
+ i0.ɵɵelement(13, "div", 50);
187
+ i0.ɵɵelementStart(14, "button", 51);
188
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Conditional_36_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r10); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.revertToDefault()); });
189
+ i0.ɵɵelement(15, "i", 52);
190
+ i0.ɵɵtemplate(16, UserProfileSettingsComponent_Conditional_36_Conditional_16_Template, 2, 0, "span")(17, UserProfileSettingsComponent_Conditional_36_Conditional_17_Template, 2, 0, "span");
191
+ i0.ɵɵelementEnd();
192
+ i0.ɵɵelementStart(18, "div", 48);
193
+ i0.ɵɵelement(19, "i", 34);
194
+ i0.ɵɵelementStart(20, "small");
195
+ i0.ɵɵtext(21, "Clears your avatar settings and restores the automatic sync from your auth provider");
196
+ i0.ɵɵelementEnd()()();
197
+ } if (rf & 2) {
198
+ const ctx_r1 = i0.ɵɵnextContext();
199
+ i0.ɵɵadvance(5);
200
+ i0.ɵɵproperty("disabled", ctx_r1.isSaving);
201
+ i0.ɵɵadvance(2);
202
+ i0.ɵɵconditional(ctx_r1.isSaving ? 7 : 8);
203
+ i0.ɵɵadvance(7);
204
+ i0.ɵɵproperty("disabled", ctx_r1.isSaving);
205
+ i0.ɵɵadvance(2);
206
+ i0.ɵɵconditional(ctx_r1.isSaving ? 16 : 17);
207
+ } }
208
+ function UserProfileSettingsComponent_Conditional_41_Template(rf, ctx) { if (rf & 1) {
209
+ i0.ɵɵelement(0, "i", 53);
210
+ i0.ɵɵelementStart(1, "span");
211
+ i0.ɵɵtext(2, "Saving...");
212
+ i0.ɵɵelementEnd();
213
+ } }
214
+ function UserProfileSettingsComponent_Conditional_42_Template(rf, ctx) { if (rf & 1) {
215
+ i0.ɵɵelement(0, "i", 54);
216
+ i0.ɵɵelementStart(1, "span");
217
+ i0.ɵɵtext(2, "Save Changes");
218
+ i0.ɵɵelementEnd();
219
+ } }
220
+ export class UserProfileSettingsComponent {
221
+ userAvatarService;
222
+ sharedService;
223
+ currentUser;
224
+ selectedTab = 'url';
225
+ // Form state
226
+ imageUrlInput = '';
227
+ selectedIconClass = '';
228
+ uploadedImageBase64 = '';
229
+ uploadedFileName = '';
230
+ previewUrl = '';
231
+ previewIconClass = '';
232
+ // UI state
233
+ isSaving = false;
234
+ showSuccessMessage = false;
235
+ errorMessage = '';
236
+ // Icon picker data
237
+ iconCategories = [
238
+ {
239
+ name: 'Users',
240
+ icons: [
241
+ 'fa-solid fa-user',
242
+ 'fa-solid fa-user-tie',
243
+ 'fa-solid fa-user-astronaut',
244
+ 'fa-solid fa-user-ninja',
245
+ 'fa-solid fa-user-secret',
246
+ 'fa-solid fa-user-graduate',
247
+ 'fa-solid fa-user-doctor',
248
+ 'fa-solid fa-user-gear',
249
+ 'fa-regular fa-circle-user',
250
+ 'fa-solid fa-user-check',
251
+ 'fa-solid fa-user-shield',
252
+ 'fa-solid fa-user-crown',
253
+ 'fa-solid fa-user-pilot',
254
+ 'fa-solid fa-user-cowboy',
255
+ 'fa-solid fa-user-chef'
256
+ ]
257
+ },
258
+ {
259
+ name: 'Business',
260
+ icons: [
261
+ 'fa-solid fa-briefcase',
262
+ 'fa-solid fa-building',
263
+ 'fa-solid fa-chart-line',
264
+ 'fa-solid fa-handshake',
265
+ 'fa-solid fa-trophy',
266
+ 'fa-solid fa-medal',
267
+ 'fa-solid fa-award',
268
+ 'fa-solid fa-lightbulb',
269
+ 'fa-solid fa-rocket',
270
+ 'fa-solid fa-star'
271
+ ]
272
+ },
273
+ {
274
+ name: 'Tech',
275
+ icons: [
276
+ 'fa-solid fa-laptop-code',
277
+ 'fa-solid fa-terminal',
278
+ 'fa-solid fa-microchip',
279
+ 'fa-solid fa-robot',
280
+ 'fa-solid fa-brain',
281
+ 'fa-solid fa-code',
282
+ 'fa-solid fa-server',
283
+ 'fa-solid fa-database',
284
+ 'fa-solid fa-network-wired',
285
+ 'fa-solid fa-bug'
286
+ ]
287
+ },
288
+ {
289
+ name: 'Fun',
290
+ icons: [
291
+ 'fa-solid fa-face-smile',
292
+ 'fa-solid fa-face-grin',
293
+ 'fa-solid fa-face-laugh',
294
+ 'fa-solid fa-face-wink',
295
+ 'fa-solid fa-heart',
296
+ 'fa-solid fa-fire',
297
+ 'fa-solid fa-bolt',
298
+ 'fa-solid fa-gem',
299
+ 'fa-solid fa-crown',
300
+ 'fa-solid fa-hat-wizard'
301
+ ]
302
+ },
303
+ {
304
+ name: 'Animals',
305
+ icons: [
306
+ 'fa-solid fa-cat',
307
+ 'fa-solid fa-dog',
308
+ 'fa-solid fa-dragon',
309
+ 'fa-solid fa-dove',
310
+ 'fa-solid fa-fish'
311
+ ]
312
+ }
313
+ ];
314
+ constructor(userAvatarService, sharedService) {
315
+ this.userAvatarService = userAvatarService;
316
+ this.sharedService = sharedService;
317
+ }
318
+ async ngOnInit() {
319
+ const md = new Metadata();
320
+ const currentUserInfo = md.CurrentUser;
321
+ // Load the full UserEntity to access avatar fields
322
+ this.currentUser = await md.GetEntityObject('Users');
323
+ await this.currentUser.Load(currentUserInfo.ID);
324
+ this.loadCurrentAvatar();
325
+ }
326
+ /**
327
+ * Loads the current avatar settings from the user entity
328
+ */
329
+ loadCurrentAvatar() {
330
+ if (this.currentUser.UserImageURL) {
331
+ this.imageUrlInput = this.currentUser.UserImageURL;
332
+ this.previewUrl = this.currentUser.UserImageURL;
333
+ // Determine if it's a Base64 upload or URL
334
+ if (this.userAvatarService.isValidBase64DataUri(this.currentUser.UserImageURL)) {
335
+ this.selectedTab = 'upload';
336
+ this.uploadedImageBase64 = this.currentUser.UserImageURL;
337
+ this.uploadedFileName = 'Current uploaded image';
338
+ }
339
+ else {
340
+ this.selectedTab = 'url';
341
+ }
342
+ }
343
+ else if (this.currentUser.UserImageIconClass) {
344
+ this.selectedIconClass = this.currentUser.UserImageIconClass;
345
+ this.previewIconClass = this.currentUser.UserImageIconClass;
346
+ this.selectedTab = 'icon';
347
+ }
348
+ else {
349
+ // Default to URL tab with empty state
350
+ this.selectedTab = 'url';
351
+ }
352
+ }
353
+ /**
354
+ * Switches between tabs and updates preview
355
+ */
356
+ selectTab(tab) {
357
+ this.selectedTab = tab;
358
+ this.updatePreview();
359
+ }
360
+ /**
361
+ * Handles file selection from native input
362
+ */
363
+ async onFileSelected(event) {
364
+ const input = event.target;
365
+ const file = input.files?.[0];
366
+ if (!file) {
367
+ return;
368
+ }
369
+ // Clear any previous errors
370
+ this.errorMessage = '';
371
+ // Validate file type
372
+ if (!file.type.match(/^image\/(png|jpeg|jpg|gif|webp)$/)) {
373
+ this.errorMessage = 'Please select a valid image file (PNG, JPG, GIF, WEBP)';
374
+ input.value = ''; // Clear the input
375
+ return;
376
+ }
377
+ // Validate file size (200KB)
378
+ const maxSize = 200 * 1024;
379
+ if (file.size > maxSize) {
380
+ this.errorMessage = `Image must be smaller than 200KB. Your image is ${Math.round(file.size / 1024)}KB`;
381
+ input.value = ''; // Clear the input
382
+ return;
383
+ }
384
+ // Convert to Base64
385
+ try {
386
+ this.uploadedImageBase64 = await this.userAvatarService.fileToBase64(file);
387
+ this.uploadedFileName = file.name;
388
+ this.previewUrl = this.uploadedImageBase64;
389
+ this.previewIconClass = ''; // Clear icon preview
390
+ }
391
+ catch (error) {
392
+ this.errorMessage = 'Failed to process image. Please try again.';
393
+ console.error('Error converting file to Base64:', error);
394
+ }
395
+ }
396
+ /**
397
+ * Clears uploaded file
398
+ */
399
+ clearUpload() {
400
+ this.uploadedFileName = '';
401
+ this.uploadedImageBase64 = '';
402
+ this.previewUrl = '';
403
+ this.errorMessage = '';
404
+ // Reset file input
405
+ const fileInput = document.querySelector('input[type="file"]');
406
+ if (fileInput) {
407
+ fileInput.value = '';
408
+ }
409
+ }
410
+ /**
411
+ * Handles URL input changes
412
+ */
413
+ onUrlChange() {
414
+ this.errorMessage = '';
415
+ if (this.imageUrlInput && this.imageUrlInput.trim().length > 0) {
416
+ if (this.userAvatarService.isValidUrl(this.imageUrlInput)) {
417
+ this.previewUrl = this.imageUrlInput;
418
+ this.previewIconClass = ''; // Clear icon preview
419
+ }
420
+ else {
421
+ this.errorMessage = 'Please enter a valid URL';
422
+ this.previewUrl = '';
423
+ }
424
+ }
425
+ else {
426
+ this.previewUrl = '';
427
+ }
428
+ }
429
+ /**
430
+ * Handles icon selection
431
+ */
432
+ selectIcon(iconClass) {
433
+ this.selectedIconClass = iconClass;
434
+ this.previewIconClass = iconClass;
435
+ this.previewUrl = ''; // Clear image preview
436
+ this.errorMessage = '';
437
+ }
438
+ /**
439
+ * Checks if an icon is currently selected
440
+ */
441
+ isIconSelected(iconClass) {
442
+ return this.selectedIconClass === iconClass;
443
+ }
444
+ /**
445
+ * Syncs avatar from authentication provider
446
+ * NOTE: This is a placeholder - actual implementation should be done
447
+ * in the calling application which has access to auth services
448
+ */
449
+ async syncFromProvider() {
450
+ this.errorMessage = 'Avatar sync from provider is not yet implemented in settings. Please use the automatic sync on login or manually upload an image.';
451
+ // TODO: Implement auth provider integration
452
+ // The calling application should provide a way to get auth claims
453
+ // and call userAvatarService.syncFromImageUrl() with the appropriate URL and headers
454
+ }
455
+ /**
456
+ * Reverts avatar to default (clears both fields)
457
+ * This will trigger auto-sync from auth provider on next login
458
+ */
459
+ async revertToDefault() {
460
+ this.isSaving = true;
461
+ this.errorMessage = '';
462
+ this.showSuccessMessage = false;
463
+ try {
464
+ // Clear both avatar fields
465
+ this.currentUser.UserImageURL = null;
466
+ this.currentUser.UserImageIconClass = null;
467
+ // Save to database
468
+ const saved = await this.currentUser.Save();
469
+ if (saved) {
470
+ // Clear local state
471
+ this.imageUrlInput = '';
472
+ this.selectedIconClass = '';
473
+ this.uploadedImageBase64 = '';
474
+ this.uploadedFileName = '';
475
+ this.previewUrl = '';
476
+ this.previewIconClass = '';
477
+ this.showSuccess('Avatar reverted to default! Your auth provider image will sync on next login.');
478
+ // Notify header component to update avatar display
479
+ MJGlobal.Instance.RaiseEvent({
480
+ event: MJEventType.ComponentEvent,
481
+ eventCode: EventCodes.AvatarUpdated,
482
+ component: this,
483
+ args: {
484
+ imageUrl: null,
485
+ iconClass: null
486
+ }
487
+ });
488
+ }
489
+ else {
490
+ this.errorMessage = 'Failed to revert avatar. Please try again.';
491
+ }
492
+ }
493
+ catch (error) {
494
+ this.errorMessage = 'An error occurred while reverting. Please try again.';
495
+ console.error('Error reverting avatar:', error);
496
+ }
497
+ finally {
498
+ this.isSaving = false;
499
+ }
500
+ }
501
+ /**
502
+ * Updates the preview based on current tab
503
+ */
504
+ updatePreview() {
505
+ switch (this.selectedTab) {
506
+ case 'upload':
507
+ if (this.uploadedImageBase64) {
508
+ this.previewUrl = this.uploadedImageBase64;
509
+ this.previewIconClass = '';
510
+ }
511
+ break;
512
+ case 'url':
513
+ if (this.imageUrlInput && this.userAvatarService.isValidUrl(this.imageUrlInput)) {
514
+ this.previewUrl = this.imageUrlInput;
515
+ this.previewIconClass = '';
516
+ }
517
+ break;
518
+ case 'icon':
519
+ if (this.selectedIconClass) {
520
+ this.previewIconClass = this.selectedIconClass;
521
+ this.previewUrl = '';
522
+ }
523
+ break;
524
+ }
525
+ }
526
+ /**
527
+ * Saves avatar settings to database
528
+ */
529
+ async save() {
530
+ this.isSaving = true;
531
+ this.errorMessage = '';
532
+ this.showSuccessMessage = false;
533
+ try {
534
+ // Update user entity based on selected tab
535
+ switch (this.selectedTab) {
536
+ case 'upload':
537
+ if (!this.uploadedImageBase64) {
538
+ this.errorMessage = 'Please select an image to upload';
539
+ this.isSaving = false;
540
+ return;
541
+ }
542
+ this.currentUser.UserImageURL = this.uploadedImageBase64;
543
+ this.currentUser.UserImageIconClass = null;
544
+ break;
545
+ case 'url':
546
+ if (!this.imageUrlInput || !this.userAvatarService.isValidUrl(this.imageUrlInput)) {
547
+ this.errorMessage = 'Please enter a valid image URL';
548
+ this.isSaving = false;
549
+ return;
550
+ }
551
+ this.currentUser.UserImageURL = this.imageUrlInput;
552
+ this.currentUser.UserImageIconClass = null;
553
+ break;
554
+ case 'icon':
555
+ if (!this.selectedIconClass) {
556
+ this.errorMessage = 'Please select an icon';
557
+ this.isSaving = false;
558
+ return;
559
+ }
560
+ this.currentUser.UserImageURL = null;
561
+ this.currentUser.UserImageIconClass = this.selectedIconClass;
562
+ break;
563
+ }
564
+ // Save to database
565
+ const saved = await this.currentUser.Save();
566
+ if (saved) {
567
+ this.showSuccess('Avatar updated successfully!');
568
+ // Notify header component to update avatar display
569
+ MJGlobal.Instance.RaiseEvent({
570
+ event: MJEventType.ComponentEvent,
571
+ eventCode: EventCodes.AvatarUpdated,
572
+ component: this,
573
+ args: {
574
+ imageUrl: this.currentUser.UserImageURL,
575
+ iconClass: this.currentUser.UserImageIconClass
576
+ }
577
+ });
578
+ }
579
+ else {
580
+ this.errorMessage = 'Failed to save avatar. Please try again.';
581
+ }
582
+ }
583
+ catch (error) {
584
+ this.errorMessage = 'An error occurred while saving. Please try again.';
585
+ console.error('Error saving avatar:', error);
586
+ }
587
+ finally {
588
+ this.isSaving = false;
589
+ }
590
+ }
591
+ /**
592
+ * Cancels changes and reverts to saved state
593
+ */
594
+ cancel() {
595
+ this.loadCurrentAvatar();
596
+ this.errorMessage = '';
597
+ this.showSuccessMessage = false;
598
+ }
599
+ /**
600
+ * Shows success message temporarily
601
+ */
602
+ showSuccess(message) {
603
+ this.showSuccessMessage = true;
604
+ this.sharedService.CreateSimpleNotification(message, 'success', 3000);
605
+ // Hide success message after 3 seconds
606
+ setTimeout(() => {
607
+ this.showSuccessMessage = false;
608
+ }, 3000);
609
+ }
610
+ static ɵfac = function UserProfileSettingsComponent_Factory(t) { return new (t || UserProfileSettingsComponent)(i0.ɵɵdirectiveInject(i1.UserAvatarService), i0.ɵɵdirectiveInject(i2.SharedService)); };
611
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: UserProfileSettingsComponent, selectors: [["mj-user-profile-settings"]], standalone: true, features: [i0.ɵɵStandaloneFeature], decls: 43, vars: 18, consts: [["fileInput", ""], [1, "user-profile-settings"], [1, "profile-header"], [1, "description"], [1, "alert", "alert-error"], [1, "alert", "alert-success"], [1, "avatar-preview-section"], [1, "preview-label"], [1, "preview-container"], ["alt", "Avatar preview", 1, "preview-image", 3, "src"], [1, "preview-icon-wrapper"], [1, "preview-placeholder"], [1, "tab-navigation"], [1, "tab-btn", 3, "click"], [1, "fa-solid", "fa-upload"], [1, "fa-solid", "fa-link"], [1, "fa-solid", "fa-icons"], [1, "fa-brands", "fa-microsoft"], [1, "tab-content"], [1, "upload-section"], [1, "url-section"], [1, "icon-section"], [1, "provider-section"], [1, "action-buttons"], [1, "btn", "btn-secondary", 3, "click", "disabled"], [1, "btn", "btn-primary", 3, "click", "disabled"], [1, "fa-solid", "fa-exclamation-triangle"], [1, "close-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "fa-solid", "fa-check-circle"], [1, "fa-solid", "fa-user"], ["type", "file", "accept", "image/png,image/jpeg,image/jpg,image/gif,image/webp", 2, "display", "none", 3, "change"], [1, "upload-btn", 3, "click"], [1, "upload-info"], [1, "fa-solid", "fa-info-circle"], [1, "selected-file"], [1, "fa-solid", "fa-file-image"], [1, "remove-btn", 3, "click"], ["for", "imageUrl"], ["type", "text", "id", "imageUrl", "placeholder", "https://example.com/avatar.jpg", 1, "url-input", 3, "ngModelChange", "input", "ngModel"], [1, "url-info"], [1, "icon-category"], [1, "category-title"], [1, "icon-grid"], [1, "icon-option", 3, "selected", "title"], [1, "icon-option", 3, "click", "title"], [1, "provider-info"], [1, "sync-btn", 3, "click", "disabled"], [1, "provider-note"], [1, "fa-solid", "fa-lightbulb"], [2, "border-top", "1px solid #ddd", "width", "100%", "max-width", "500px", "margin", "1rem 0"], [1, "revert-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-rotate-left"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-save"]], template: function UserProfileSettingsComponent_Template(rf, ctx) { if (rf & 1) {
612
+ i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "h3");
613
+ i0.ɵɵtext(3, "Avatar Settings");
614
+ i0.ɵɵelementEnd();
615
+ i0.ɵɵelementStart(4, "p", 3);
616
+ i0.ɵɵtext(5, "Customize your profile picture with an image, URL, or icon");
617
+ i0.ɵɵelementEnd()();
618
+ i0.ɵɵtemplate(6, UserProfileSettingsComponent_Conditional_6_Template, 6, 1, "div", 4)(7, UserProfileSettingsComponent_Conditional_7_Template, 4, 0, "div", 5);
619
+ i0.ɵɵelementStart(8, "div", 6)(9, "div", 7);
620
+ i0.ɵɵtext(10, "Preview");
621
+ i0.ɵɵelementEnd();
622
+ i0.ɵɵelementStart(11, "div", 8);
623
+ i0.ɵɵtemplate(12, UserProfileSettingsComponent_Conditional_12_Template, 1, 1, "img", 9)(13, UserProfileSettingsComponent_Conditional_13_Template, 2, 2, "div", 10)(14, UserProfileSettingsComponent_Conditional_14_Template, 4, 0, "div", 11);
624
+ i0.ɵɵelementEnd()();
625
+ i0.ɵɵelementStart(15, "div", 12)(16, "button", 13);
626
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Template_button_click_16_listener() { return ctx.selectTab("upload"); });
627
+ i0.ɵɵelement(17, "i", 14);
628
+ i0.ɵɵelementStart(18, "span");
629
+ i0.ɵɵtext(19, "Upload");
630
+ i0.ɵɵelementEnd()();
631
+ i0.ɵɵelementStart(20, "button", 13);
632
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Template_button_click_20_listener() { return ctx.selectTab("url"); });
633
+ i0.ɵɵelement(21, "i", 15);
634
+ i0.ɵɵelementStart(22, "span");
635
+ i0.ɵɵtext(23, "URL");
636
+ i0.ɵɵelementEnd()();
637
+ i0.ɵɵelementStart(24, "button", 13);
638
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Template_button_click_24_listener() { return ctx.selectTab("icon"); });
639
+ i0.ɵɵelement(25, "i", 16);
640
+ i0.ɵɵelementStart(26, "span");
641
+ i0.ɵɵtext(27, "Icon");
642
+ i0.ɵɵelementEnd()();
643
+ i0.ɵɵelementStart(28, "button", 13);
644
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Template_button_click_28_listener() { return ctx.selectTab("provider"); });
645
+ i0.ɵɵelement(29, "i", 17);
646
+ i0.ɵɵelementStart(30, "span");
647
+ i0.ɵɵtext(31, "Sync");
648
+ i0.ɵɵelementEnd()()();
649
+ i0.ɵɵelementStart(32, "div", 18);
650
+ i0.ɵɵtemplate(33, UserProfileSettingsComponent_Conditional_33_Template, 10, 1, "div", 19)(34, UserProfileSettingsComponent_Conditional_34_Template, 7, 1, "div", 20)(35, UserProfileSettingsComponent_Conditional_35_Template, 3, 0, "div", 21)(36, UserProfileSettingsComponent_Conditional_36_Template, 22, 4, "div", 22);
651
+ i0.ɵɵelementEnd();
652
+ i0.ɵɵelementStart(37, "div", 23)(38, "button", 24);
653
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Template_button_click_38_listener() { return ctx.cancel(); });
654
+ i0.ɵɵtext(39, " Cancel ");
655
+ i0.ɵɵelementEnd();
656
+ i0.ɵɵelementStart(40, "button", 25);
657
+ i0.ɵɵlistener("click", function UserProfileSettingsComponent_Template_button_click_40_listener() { return ctx.save(); });
658
+ i0.ɵɵtemplate(41, UserProfileSettingsComponent_Conditional_41_Template, 3, 0)(42, UserProfileSettingsComponent_Conditional_42_Template, 3, 0);
659
+ i0.ɵɵelementEnd()()();
660
+ } if (rf & 2) {
661
+ i0.ɵɵadvance(6);
662
+ i0.ɵɵconditional(ctx.errorMessage ? 6 : -1);
663
+ i0.ɵɵadvance();
664
+ i0.ɵɵconditional(ctx.showSuccessMessage ? 7 : -1);
665
+ i0.ɵɵadvance(5);
666
+ i0.ɵɵconditional(ctx.previewUrl ? 12 : ctx.previewIconClass ? 13 : 14);
667
+ i0.ɵɵadvance(4);
668
+ i0.ɵɵclassProp("active", ctx.selectedTab === "upload");
669
+ i0.ɵɵadvance(4);
670
+ i0.ɵɵclassProp("active", ctx.selectedTab === "url");
671
+ i0.ɵɵadvance(4);
672
+ i0.ɵɵclassProp("active", ctx.selectedTab === "icon");
673
+ i0.ɵɵadvance(4);
674
+ i0.ɵɵclassProp("active", ctx.selectedTab === "provider");
675
+ i0.ɵɵadvance(5);
676
+ i0.ɵɵconditional(ctx.selectedTab === "upload" ? 33 : -1);
677
+ i0.ɵɵadvance();
678
+ i0.ɵɵconditional(ctx.selectedTab === "url" ? 34 : -1);
679
+ i0.ɵɵadvance();
680
+ i0.ɵɵconditional(ctx.selectedTab === "icon" ? 35 : -1);
681
+ i0.ɵɵadvance();
682
+ i0.ɵɵconditional(ctx.selectedTab === "provider" ? 36 : -1);
683
+ i0.ɵɵadvance(2);
684
+ i0.ɵɵproperty("disabled", ctx.isSaving);
685
+ i0.ɵɵadvance(2);
686
+ i0.ɵɵproperty("disabled", ctx.isSaving || ctx.selectedTab === "provider");
687
+ i0.ɵɵadvance();
688
+ i0.ɵɵconditional(ctx.isSaving ? 41 : 42);
689
+ } }, dependencies: [CommonModule, FormsModule, i3.DefaultValueAccessor, i3.NgControlStatus, i3.NgModel], styles: [".user-profile-settings[_ngcontent-%COMP%] {\n max-width: 800px;\n margin: 0 auto;\n padding: 1.5rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .profile-header[_ngcontent-%COMP%] {\n margin-bottom: 1.5rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .profile-header[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 0.5rem 0;\n font-size: 1.5rem;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .profile-header[_ngcontent-%COMP%] .description[_ngcontent-%COMP%] {\n margin: 0;\n color: #666;\n font-size: 0.9rem;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .alert[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n border-radius: 6px;\n margin-bottom: 1rem;\n font-size: 0.9rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .alert[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n flex-shrink: 0;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .alert[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .alert[_ngcontent-%COMP%] .close-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem;\n color: inherit;\n opacity: 0.7;\n transition: opacity 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .alert[_ngcontent-%COMP%] .close-btn[_ngcontent-%COMP%]:hover {\n opacity: 1;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .alert.alert-error[_ngcontent-%COMP%] {\n background-color: #fee;\n border: 1px solid #fcc;\n color: #c33;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .alert.alert-success[_ngcontent-%COMP%] {\n background-color: #efe;\n border: 1px solid #cfc;\n color: #3c3;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-bottom: 2rem;\n padding: 1.5rem;\n background: #f8f9fa;\n border-radius: 8px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-label[_ngcontent-%COMP%] {\n font-size: 0.85rem;\n font-weight: 600;\n color: #666;\n margin-bottom: 1rem;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n overflow: hidden;\n background: white;\n border: 3px solid #e0e0e0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] .preview-image[_ngcontent-%COMP%] {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] .preview-icon-wrapper[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] .preview-icon-wrapper[_ngcontent-%COMP%] .preview-icon[_ngcontent-%COMP%] {\n font-size: 3.5rem;\n color: #666;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] .preview-placeholder[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n color: #999;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] .preview-placeholder[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 2.5rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .avatar-preview-section[_ngcontent-%COMP%] .preview-container[_ngcontent-%COMP%] .preview-placeholder[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 0.75rem;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n border-bottom: 2px solid #e0e0e0;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: none;\n border: none;\n border-bottom: 3px solid transparent;\n cursor: pointer;\n font-size: 0.9rem;\n font-weight: 500;\n color: #666;\n transition: all 0.2s;\n margin-bottom: -2px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n background: #f8f9fa;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn.active[_ngcontent-%COMP%] {\n color: #007bff;\n border-bottom-color: #007bff;\n background: #f8f9fa;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .tab-content[_ngcontent-%COMP%] {\n min-height: 300px;\n padding: 1.5rem;\n background: #f8f9fa;\n border-radius: 8px;\n margin-bottom: 1.5rem;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .upload-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n background: #007bff;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 0.95rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .upload-btn[_ngcontent-%COMP%]:hover {\n background: #0056b3;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .upload-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .upload-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n color: #666;\n font-size: 0.85rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .upload-info[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #007bff;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .selected-file[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n background: white;\n border: 1px solid #ddd;\n border-radius: 6px;\n width: 100%;\n max-width: 400px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .selected-file[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #007bff;\n font-size: 1.2rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .selected-file[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n flex: 1;\n font-size: 0.9rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .selected-file[_ngcontent-%COMP%] .remove-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem;\n color: #999;\n transition: color 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .selected-file[_ngcontent-%COMP%] .remove-btn[_ngcontent-%COMP%]:hover {\n color: #c33;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .upload-section[_ngcontent-%COMP%] .selected-file[_ngcontent-%COMP%] .remove-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n color: inherit;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .url-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .url-section[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n font-weight: 600;\n color: #333;\n font-size: 0.9rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .url-section[_ngcontent-%COMP%] .url-input[_ngcontent-%COMP%] {\n padding: 0.75rem;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 0.95rem;\n transition: border-color 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .url-section[_ngcontent-%COMP%] .url-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #007bff;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .url-section[_ngcontent-%COMP%] .url-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n color: #666;\n font-size: 0.85rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .url-section[_ngcontent-%COMP%] .url-info[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #007bff;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n max-height: 400px;\n overflow-y: auto;\n padding-right: 0.5rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .category-title[_ngcontent-%COMP%] {\n font-size: 0.9rem;\n font-weight: 600;\n color: #333;\n margin: 0 0 0.75rem 0;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid #ddd;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));\n gap: 0.5rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] .icon-option[_ngcontent-%COMP%] {\n aspect-ratio: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0.75rem;\n background: white;\n border: 2px solid #ddd;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] .icon-option[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1.5rem;\n color: #666;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] .icon-option[_ngcontent-%COMP%]:hover {\n border-color: #007bff;\n background: #f0f8ff;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] .icon-option[_ngcontent-%COMP%]:hover i[_ngcontent-%COMP%] {\n color: #007bff;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] .icon-option.selected[_ngcontent-%COMP%] {\n border-color: #007bff;\n background: #007bff;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] .icon-option.selected[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: white;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%]::-webkit-scrollbar {\n width: 6px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%]::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%]::-webkit-scrollbar-thumb {\n background: #ccc;\n border-radius: 3px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%]::-webkit-scrollbar-thumb:hover {\n background: #999;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1.5rem;\n padding: 2rem 1rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .provider-info[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1rem;\n background: white;\n border: 1px solid #ddd;\n border-radius: 6px;\n width: 100%;\n max-width: 500px;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .provider-info[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #007bff;\n font-size: 1.2rem;\n flex-shrink: 0;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .provider-info[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n color: #666;\n font-size: 0.9rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .sync-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1rem 2rem;\n background: #0078d4;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 1rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .sync-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #005a9e;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .sync-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .sync-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1.2rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .provider-note[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n color: #666;\n font-size: 0.85rem;\n max-width: 500px;\n text-align: center;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .provider-note[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #ffc107;\n margin-top: 0.1rem;\n flex-shrink: 0;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .revert-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1.5rem;\n background: #6c757d;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 0.95rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .revert-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #5a6268;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .revert-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .provider-section[_ngcontent-%COMP%] .revert-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1rem;\n}\n\n\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] {\n display: flex;\n gap: 1rem;\n justify-content: flex-end;\n padding-top: 1rem;\n border-top: 1px solid #e0e0e0;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n border: none;\n border-radius: 6px;\n font-size: 0.95rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 0.9rem;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] i.fa-spin[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_spin 1s linear infinite;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn.btn-secondary[_ngcontent-%COMP%] {\n background: #6c757d;\n color: white;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn.btn-secondary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #5a6268;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn.btn-primary[_ngcontent-%COMP%] {\n background: #28a745;\n color: white;\n}\n\n.user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn.btn-primary[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #218838;\n}\n\n@keyframes _ngcontent-%COMP%_spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n\n\n@media (max-width: 768px) {\n .user-profile-settings[_ngcontent-%COMP%] {\n padding: 1rem;\n }\n\n .user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 0.25rem;\n padding: 0.5rem;\n font-size: 0.75rem;\n }\n\n .user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 1.2rem;\n }\n\n .user-profile-settings[_ngcontent-%COMP%] .tab-navigation[_ngcontent-%COMP%] .tab-btn[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 0.7rem;\n }\n\n .user-profile-settings[_ngcontent-%COMP%] .icon-section[_ngcontent-%COMP%] .icon-category[_ngcontent-%COMP%] .icon-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(auto-fill, minmax(45px, 1fr));\n }\n\n .user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] {\n flex-direction: column-reverse;\n }\n\n .user-profile-settings[_ngcontent-%COMP%] .action-buttons[_ngcontent-%COMP%] .btn[_ngcontent-%COMP%] {\n width: 100%;\n justify-content: center;\n }\n}"] });
690
+ }
691
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(UserProfileSettingsComponent, [{
692
+ type: Component,
693
+ args: [{ selector: 'mj-user-profile-settings', standalone: true, imports: [CommonModule, FormsModule], template: "<div class=\"user-profile-settings\">\n <div class=\"profile-header\">\n <h3>Avatar Settings</h3>\n <p class=\"description\">Customize your profile picture with an image, URL, or icon</p>\n </div>\n\n <!-- Error Message -->\n @if (errorMessage) {\n <div class=\"alert alert-error\">\n <i class=\"fa-solid fa-exclamation-triangle\"></i>\n <span>{{ errorMessage }}</span>\n <button class=\"close-btn\" (click)=\"errorMessage = ''\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n\n <!-- Success Message -->\n @if (showSuccessMessage) {\n <div class=\"alert alert-success\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <span>Avatar updated successfully!</span>\n </div>\n }\n\n <!-- Preview Section -->\n <div class=\"avatar-preview-section\">\n <div class=\"preview-label\">Preview</div>\n <div class=\"preview-container\">\n @if (previewUrl) {\n <img [src]=\"previewUrl\" alt=\"Avatar preview\" class=\"preview-image\" />\n } @else if (previewIconClass) {\n <div class=\"preview-icon-wrapper\">\n <i [class]=\"previewIconClass + ' preview-icon'\"></i>\n </div>\n } @else {\n <div class=\"preview-placeholder\">\n <i class=\"fa-solid fa-user\"></i>\n <span>No avatar selected</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Tab Navigation -->\n <div class=\"tab-navigation\">\n <button\n class=\"tab-btn\"\n [class.active]=\"selectedTab === 'upload'\"\n (click)=\"selectTab('upload')\"\n >\n <i class=\"fa-solid fa-upload\"></i>\n <span>Upload</span>\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"selectedTab === 'url'\"\n (click)=\"selectTab('url')\"\n >\n <i class=\"fa-solid fa-link\"></i>\n <span>URL</span>\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"selectedTab === 'icon'\"\n (click)=\"selectTab('icon')\"\n >\n <i class=\"fa-solid fa-icons\"></i>\n <span>Icon</span>\n </button>\n <button\n class=\"tab-btn\"\n [class.active]=\"selectedTab === 'provider'\"\n (click)=\"selectTab('provider')\"\n >\n <i class=\"fa-brands fa-microsoft\"></i>\n <span>Sync</span>\n </button>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tab-content\">\n <!-- Upload Tab -->\n @if (selectedTab === 'upload') {\n <div class=\"upload-section\">\n <input\n type=\"file\"\n #fileInput\n accept=\"image/png,image/jpeg,image/jpg,image/gif,image/webp\"\n (change)=\"onFileSelected($event)\"\n style=\"display: none\"\n />\n\n <button class=\"upload-btn\" (click)=\"fileInput.click()\">\n <i class=\"fa-solid fa-upload\"></i>\n Choose Image\n </button>\n\n <div class=\"upload-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n Max 200KB \u2022 PNG, JPG, GIF, WEBP\n </div>\n\n @if (uploadedFileName) {\n <div class=\"selected-file\">\n <i class=\"fa-solid fa-file-image\"></i>\n <span>{{ uploadedFileName }}</span>\n <button class=\"remove-btn\" (click)=\"clearUpload()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n }\n </div>\n }\n\n <!-- URL Tab -->\n @if (selectedTab === 'url') {\n <div class=\"url-section\">\n <label for=\"imageUrl\">Image URL</label>\n <input\n type=\"text\"\n id=\"imageUrl\"\n class=\"url-input\"\n placeholder=\"https://example.com/avatar.jpg\"\n [(ngModel)]=\"imageUrlInput\"\n (input)=\"onUrlChange()\"\n />\n <div class=\"url-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n Enter a URL to an image hosted online\n </div>\n </div>\n }\n\n <!-- Icon Tab -->\n @if (selectedTab === 'icon') {\n <div class=\"icon-section\">\n @for (category of iconCategories; track category.name) {\n <div class=\"icon-category\">\n <h4 class=\"category-title\">{{ category.name }}</h4>\n <div class=\"icon-grid\">\n @for (icon of category.icons; track icon) {\n <button\n class=\"icon-option\"\n [class.selected]=\"isIconSelected(icon)\"\n (click)=\"selectIcon(icon)\"\n [title]=\"icon\"\n >\n <i [class]=\"icon\"></i>\n </button>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Provider Sync Tab -->\n @if (selectedTab === 'provider') {\n <div class=\"provider-section\">\n <div class=\"provider-info\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <p>Sync your avatar from your authentication provider account</p>\n </div>\n\n <button\n class=\"sync-btn\"\n (click)=\"syncFromProvider()\"\n [disabled]=\"isSaving\"\n >\n <i class=\"fa-brands fa-microsoft\"></i>\n @if (isSaving) {\n <span>Syncing...</span>\n } @else {\n <span>Sync from Microsoft Account</span>\n }\n </button>\n\n <div class=\"provider-note\">\n <i class=\"fa-solid fa-lightbulb\"></i>\n <small>This will download your profile photo from Microsoft and save it to your profile</small>\n </div>\n\n <div style=\"border-top: 1px solid #ddd; width: 100%; max-width: 500px; margin: 1rem 0;\"></div>\n\n <button\n class=\"revert-btn\"\n (click)=\"revertToDefault()\"\n [disabled]=\"isSaving\"\n >\n <i class=\"fa-solid fa-rotate-left\"></i>\n @if (isSaving) {\n <span>Reverting...</span>\n } @else {\n <span>Revert to Default</span>\n }\n </button>\n\n <div class=\"provider-note\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <small>Clears your avatar settings and restores the automatic sync from your auth provider</small>\n </div>\n </div>\n }\n </div>\n\n <!-- Action Buttons -->\n <div class=\"action-buttons\">\n <button\n class=\"btn btn-secondary\"\n (click)=\"cancel()\"\n [disabled]=\"isSaving\"\n >\n Cancel\n </button>\n <button\n class=\"btn btn-primary\"\n (click)=\"save()\"\n [disabled]=\"isSaving || selectedTab === 'provider'\"\n >\n @if (isSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Saving...</span>\n } @else {\n <i class=\"fa-solid fa-save\"></i>\n <span>Save Changes</span>\n }\n </button>\n </div>\n</div>\n", styles: [".user-profile-settings {\n max-width: 800px;\n margin: 0 auto;\n padding: 1.5rem;\n}\n\n.user-profile-settings .profile-header {\n margin-bottom: 1.5rem;\n}\n\n.user-profile-settings .profile-header h3 {\n margin: 0 0 0.5rem 0;\n font-size: 1.5rem;\n font-weight: 600;\n color: #1a1a1a;\n}\n\n.user-profile-settings .profile-header .description {\n margin: 0;\n color: #666;\n font-size: 0.9rem;\n}\n\n/* Alert Messages */\n.user-profile-settings .alert {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n border-radius: 6px;\n margin-bottom: 1rem;\n font-size: 0.9rem;\n}\n\n.user-profile-settings .alert i {\n flex-shrink: 0;\n}\n\n.user-profile-settings .alert span {\n flex: 1;\n}\n\n.user-profile-settings .alert .close-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem;\n color: inherit;\n opacity: 0.7;\n transition: opacity 0.2s;\n}\n\n.user-profile-settings .alert .close-btn:hover {\n opacity: 1;\n}\n\n.user-profile-settings .alert.alert-error {\n background-color: #fee;\n border: 1px solid #fcc;\n color: #c33;\n}\n\n.user-profile-settings .alert.alert-success {\n background-color: #efe;\n border: 1px solid #cfc;\n color: #3c3;\n}\n\n/* Avatar Preview */\n.user-profile-settings .avatar-preview-section {\n display: flex;\n flex-direction: column;\n align-items: center;\n margin-bottom: 2rem;\n padding: 1.5rem;\n background: #f8f9fa;\n border-radius: 8px;\n}\n\n.user-profile-settings .avatar-preview-section .preview-label {\n font-size: 0.85rem;\n font-weight: 600;\n color: #666;\n margin-bottom: 1rem;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container {\n width: 120px;\n height: 120px;\n border-radius: 50%;\n overflow: hidden;\n background: white;\n border: 3px solid #e0e0e0;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container .preview-image {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container .preview-icon-wrapper {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container .preview-icon-wrapper .preview-icon {\n font-size: 3.5rem;\n color: #666;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container .preview-placeholder {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 0.5rem;\n color: #999;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container .preview-placeholder i {\n font-size: 2.5rem;\n}\n\n.user-profile-settings .avatar-preview-section .preview-container .preview-placeholder span {\n font-size: 0.75rem;\n}\n\n/* Tab Navigation */\n.user-profile-settings .tab-navigation {\n display: flex;\n gap: 0.5rem;\n margin-bottom: 1.5rem;\n border-bottom: 2px solid #e0e0e0;\n}\n\n.user-profile-settings .tab-navigation .tab-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background: none;\n border: none;\n border-bottom: 3px solid transparent;\n cursor: pointer;\n font-size: 0.9rem;\n font-weight: 500;\n color: #666;\n transition: all 0.2s;\n margin-bottom: -2px;\n}\n\n.user-profile-settings .tab-navigation .tab-btn i {\n font-size: 1rem;\n}\n\n.user-profile-settings .tab-navigation .tab-btn:hover {\n color: #333;\n background: #f8f9fa;\n}\n\n.user-profile-settings .tab-navigation .tab-btn.active {\n color: #007bff;\n border-bottom-color: #007bff;\n background: #f8f9fa;\n}\n\n/* Tab Content */\n.user-profile-settings .tab-content {\n min-height: 300px;\n padding: 1.5rem;\n background: #f8f9fa;\n border-radius: 8px;\n margin-bottom: 1.5rem;\n}\n\n/* Upload Section */\n.user-profile-settings .upload-section {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1rem;\n}\n\n.user-profile-settings .upload-section .upload-btn {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n background: #007bff;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 0.95rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.user-profile-settings .upload-section .upload-btn:hover {\n background: #0056b3;\n}\n\n.user-profile-settings .upload-section .upload-btn i {\n font-size: 1rem;\n}\n\n.user-profile-settings .upload-section .upload-info {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n color: #666;\n font-size: 0.85rem;\n}\n\n.user-profile-settings .upload-section .upload-info i {\n color: #007bff;\n}\n\n.user-profile-settings .upload-section .selected-file {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1rem;\n background: white;\n border: 1px solid #ddd;\n border-radius: 6px;\n width: 100%;\n max-width: 400px;\n}\n\n.user-profile-settings .upload-section .selected-file i {\n color: #007bff;\n font-size: 1.2rem;\n}\n\n.user-profile-settings .upload-section .selected-file span {\n flex: 1;\n font-size: 0.9rem;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.user-profile-settings .upload-section .selected-file .remove-btn {\n background: none;\n border: none;\n cursor: pointer;\n padding: 0.25rem;\n color: #999;\n transition: color 0.2s;\n}\n\n.user-profile-settings .upload-section .selected-file .remove-btn:hover {\n color: #c33;\n}\n\n.user-profile-settings .upload-section .selected-file .remove-btn i {\n font-size: 1rem;\n color: inherit;\n}\n\n/* URL Section */\n.user-profile-settings .url-section {\n display: flex;\n flex-direction: column;\n gap: 0.75rem;\n}\n\n.user-profile-settings .url-section label {\n font-weight: 600;\n color: #333;\n font-size: 0.9rem;\n}\n\n.user-profile-settings .url-section .url-input {\n padding: 0.75rem;\n border: 1px solid #ddd;\n border-radius: 6px;\n font-size: 0.95rem;\n transition: border-color 0.2s;\n}\n\n.user-profile-settings .url-section .url-input:focus {\n outline: none;\n border-color: #007bff;\n}\n\n.user-profile-settings .url-section .url-info {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n color: #666;\n font-size: 0.85rem;\n}\n\n.user-profile-settings .url-section .url-info i {\n color: #007bff;\n}\n\n/* Icon Section */\n.user-profile-settings .icon-section {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n max-height: 400px;\n overflow-y: auto;\n padding-right: 0.5rem;\n}\n\n.user-profile-settings .icon-section .icon-category .category-title {\n font-size: 0.9rem;\n font-weight: 600;\n color: #333;\n margin: 0 0 0.75rem 0;\n padding-bottom: 0.5rem;\n border-bottom: 1px solid #ddd;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(50px, 1fr));\n gap: 0.5rem;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid .icon-option {\n aspect-ratio: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0.75rem;\n background: white;\n border: 2px solid #ddd;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid .icon-option i {\n font-size: 1.5rem;\n color: #666;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid .icon-option:hover {\n border-color: #007bff;\n background: #f0f8ff;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid .icon-option:hover i {\n color: #007bff;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid .icon-option.selected {\n border-color: #007bff;\n background: #007bff;\n}\n\n.user-profile-settings .icon-section .icon-category .icon-grid .icon-option.selected i {\n color: white;\n}\n\n/* Custom scrollbar */\n.user-profile-settings .icon-section::-webkit-scrollbar {\n width: 6px;\n}\n\n.user-profile-settings .icon-section::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n}\n\n.user-profile-settings .icon-section::-webkit-scrollbar-thumb {\n background: #ccc;\n border-radius: 3px;\n}\n\n.user-profile-settings .icon-section::-webkit-scrollbar-thumb:hover {\n background: #999;\n}\n\n/* Provider Section */\n.user-profile-settings .provider-section {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 1.5rem;\n padding: 2rem 1rem;\n}\n\n.user-profile-settings .provider-section .provider-info {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1rem;\n background: white;\n border: 1px solid #ddd;\n border-radius: 6px;\n width: 100%;\n max-width: 500px;\n}\n\n.user-profile-settings .provider-section .provider-info i {\n color: #007bff;\n font-size: 1.2rem;\n flex-shrink: 0;\n}\n\n.user-profile-settings .provider-section .provider-info p {\n margin: 0;\n color: #666;\n font-size: 0.9rem;\n}\n\n.user-profile-settings .provider-section .sync-btn {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 1rem 2rem;\n background: #0078d4;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 1rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.user-profile-settings .provider-section .sync-btn:hover:not(:disabled) {\n background: #005a9e;\n}\n\n.user-profile-settings .provider-section .sync-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.user-profile-settings .provider-section .sync-btn i {\n font-size: 1.2rem;\n}\n\n.user-profile-settings .provider-section .provider-note {\n display: flex;\n align-items: flex-start;\n gap: 0.5rem;\n color: #666;\n font-size: 0.85rem;\n max-width: 500px;\n text-align: center;\n}\n\n.user-profile-settings .provider-section .provider-note i {\n color: #ffc107;\n margin-top: 0.1rem;\n flex-shrink: 0;\n}\n\n.user-profile-settings .provider-section .revert-btn {\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.75rem 1.5rem;\n background: #6c757d;\n color: white;\n border: none;\n border-radius: 6px;\n font-size: 0.95rem;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n}\n\n.user-profile-settings .provider-section .revert-btn:hover:not(:disabled) {\n background: #5a6268;\n}\n\n.user-profile-settings .provider-section .revert-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.user-profile-settings .provider-section .revert-btn i {\n font-size: 1rem;\n}\n\n/* Action Buttons */\n.user-profile-settings .action-buttons {\n display: flex;\n gap: 1rem;\n justify-content: flex-end;\n padding-top: 1rem;\n border-top: 1px solid #e0e0e0;\n}\n\n.user-profile-settings .action-buttons .btn {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.5rem;\n border: none;\n border-radius: 6px;\n font-size: 0.95rem;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s;\n}\n\n.user-profile-settings .action-buttons .btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.user-profile-settings .action-buttons .btn i {\n font-size: 0.9rem;\n}\n\n.user-profile-settings .action-buttons .btn i.fa-spin {\n animation: spin 1s linear infinite;\n}\n\n.user-profile-settings .action-buttons .btn.btn-secondary {\n background: #6c757d;\n color: white;\n}\n\n.user-profile-settings .action-buttons .btn.btn-secondary:hover:not(:disabled) {\n background: #5a6268;\n}\n\n.user-profile-settings .action-buttons .btn.btn-primary {\n background: #28a745;\n color: white;\n}\n\n.user-profile-settings .action-buttons .btn.btn-primary:hover:not(:disabled) {\n background: #218838;\n}\n\n@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n}\n\n/* Responsive Design */\n@media (max-width: 768px) {\n .user-profile-settings {\n padding: 1rem;\n }\n\n .user-profile-settings .tab-navigation .tab-btn {\n flex-direction: column;\n gap: 0.25rem;\n padding: 0.5rem;\n font-size: 0.75rem;\n }\n\n .user-profile-settings .tab-navigation .tab-btn i {\n font-size: 1.2rem;\n }\n\n .user-profile-settings .tab-navigation .tab-btn span {\n font-size: 0.7rem;\n }\n\n .user-profile-settings .icon-section .icon-category .icon-grid {\n grid-template-columns: repeat(auto-fill, minmax(45px, 1fr));\n }\n\n .user-profile-settings .action-buttons {\n flex-direction: column-reverse;\n }\n\n .user-profile-settings .action-buttons .btn {\n width: 100%;\n justify-content: center;\n }\n}\n"] }]
694
+ }], () => [{ type: i1.UserAvatarService }, { type: i2.SharedService }], null); })();
695
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(UserProfileSettingsComponent, { className: "UserProfileSettingsComponent", filePath: "src/lib/user-profile-settings/user-profile-settings.component.ts", lineNumber: 22 }); })();
696
+ //# sourceMappingURL=user-profile-settings.component.js.map