@flusys/ng-auth 9.1.0 → 9.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +2 -0
  2. package/fesm2022/flusys-ng-auth-administration-page.component-DmN4KqiR.mjs +146 -0
  3. package/fesm2022/flusys-ng-auth-administration-page.component-DmN4KqiR.mjs.map +1 -0
  4. package/fesm2022/flusys-ng-auth-branch-form.component-Df_sMC2G.mjs +743 -0
  5. package/fesm2022/flusys-ng-auth-branch-form.component-Df_sMC2G.mjs.map +1 -0
  6. package/fesm2022/flusys-ng-auth-branch-list.component-CWat6QWN.mjs +775 -0
  7. package/fesm2022/flusys-ng-auth-branch-list.component-CWat6QWN.mjs.map +1 -0
  8. package/fesm2022/flusys-ng-auth-company-form.component-BvZ4Pn8j.mjs +198 -0
  9. package/fesm2022/flusys-ng-auth-company-form.component-BvZ4Pn8j.mjs.map +1 -0
  10. package/fesm2022/flusys-ng-auth-company-list.component-Corh9AIh.mjs +241 -0
  11. package/fesm2022/flusys-ng-auth-company-list.component-Corh9AIh.mjs.map +1 -0
  12. package/fesm2022/flusys-ng-auth-extension-order.util-2XWSLqii.mjs +6 -0
  13. package/fesm2022/flusys-ng-auth-extension-order.util-2XWSLqii.mjs.map +1 -0
  14. package/fesm2022/flusys-ng-auth-forgot-password-page.component-BRTVZPX6.mjs +384 -0
  15. package/fesm2022/flusys-ng-auth-forgot-password-page.component-BRTVZPX6.mjs.map +1 -0
  16. package/fesm2022/flusys-ng-auth-login-page.component-DQl52Qau.mjs +1074 -0
  17. package/fesm2022/flusys-ng-auth-login-page.component-DQl52Qau.mjs.map +1 -0
  18. package/fesm2022/flusys-ng-auth-profile-page.component-b-8rPEXQ.mjs +1792 -0
  19. package/fesm2022/flusys-ng-auth-profile-page.component-b-8rPEXQ.mjs.map +1 -0
  20. package/fesm2022/flusys-ng-auth-register-page.component-D99y1jiA.mjs +1513 -0
  21. package/fesm2022/flusys-ng-auth-register-page.component-D99y1jiA.mjs.map +1 -0
  22. package/fesm2022/flusys-ng-auth-reset-password-page.component-C0tN_AW1.mjs +538 -0
  23. package/fesm2022/flusys-ng-auth-reset-password-page.component-C0tN_AW1.mjs.map +1 -0
  24. package/fesm2022/flusys-ng-auth-social-callback-page.component-gTD7_LJY.mjs +217 -0
  25. package/fesm2022/flusys-ng-auth-social-callback-page.component-gTD7_LJY.mjs.map +1 -0
  26. package/fesm2022/flusys-ng-auth-social-config-page.component-DpGmcb9n.mjs +541 -0
  27. package/fesm2022/flusys-ng-auth-social-config-page.component-DpGmcb9n.mjs.map +1 -0
  28. package/fesm2022/flusys-ng-auth-user-form.component-CM9UYc0q.mjs +1171 -0
  29. package/fesm2022/flusys-ng-auth-user-form.component-CM9UYc0q.mjs.map +1 -0
  30. package/fesm2022/flusys-ng-auth-user-list.component-Bew7gz-H.mjs +1046 -0
  31. package/fesm2022/flusys-ng-auth-user-list.component-Bew7gz-H.mjs.map +1 -0
  32. package/fesm2022/flusys-ng-auth-verify-email-page.component-DB9iKvt3.mjs +545 -0
  33. package/fesm2022/flusys-ng-auth-verify-email-page.component-DB9iKvt3.mjs.map +1 -0
  34. package/fesm2022/flusys-ng-auth.mjs +3030 -10735
  35. package/fesm2022/flusys-ng-auth.mjs.map +1 -1
  36. package/package.json +5 -5
  37. package/types/flusys-ng-auth.d.ts +978 -1558
@@ -0,0 +1,541 @@
1
+ import { SOCIAL_PROVIDERS, SocialAuthApiService, SocialAuthConfigApiService, SocialProviderEnum } from "./flusys-ng-auth.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { Component, inject, signal } from "@angular/core";
4
+ import { TranslatePipe, TranslateService } from "@flusys/ng-shared";
5
+ import { FButtonDirective, InputTextDirective, MessageService, PasswordComponent, ToggleSwitchComponent, UiIconComponent } from "@flusys/ng-ui";
6
+ import * as i1 from "@angular/forms";
7
+ import { FormsModule } from "@angular/forms";
8
+ const TENANT_PROVIDER = SocialProviderEnum.MICROSOFT;
9
+ var SocialConfigPageComponent = class SocialConfigPageComponent {
10
+ configService = inject(SocialAuthConfigApiService);
11
+ socialApi = inject(SocialAuthApiService);
12
+ messageService = inject(MessageService);
13
+ translateAdapter = inject(TranslateService, { optional: true });
14
+ tenantProvider = TENANT_PROVIDER;
15
+ _forms = signal(SOCIAL_PROVIDERS.map(emptyForm), ...ngDevMode ? [{ debugName: "_forms" }] : /* istanbul ignore next */ []);
16
+ forms = this._forms.asReadonly();
17
+ _isLoading = signal(true, ...ngDevMode ? [{ debugName: "_isLoading" }] : /* istanbul ignore next */ []);
18
+ isLoading = this._isLoading.asReadonly();
19
+ _isSaving = signal(false, ...ngDevMode ? [{ debugName: "_isSaving" }] : /* istanbul ignore next */ []);
20
+ isSaving = this._isSaving.asReadonly();
21
+ redirectUri = this.socialApi.redirectUri;
22
+ constructor() {
23
+ this.loadConfigs();
24
+ }
25
+ updateField(provider, field, value) {
26
+ this._forms.update((forms) => forms.map((form) => form.provider === provider ? {
27
+ ...form,
28
+ [field]: value
29
+ } : form));
30
+ }
31
+ async onSave() {
32
+ const invalid = this._forms().find((form) => form.isEnabled && (!form.clientId.trim() || !form.hasClientSecret && !form.clientSecret.trim()));
33
+ if (invalid) {
34
+ this.messageService.add({
35
+ severity: "warn",
36
+ summary: this.translate("shared.validation"),
37
+ detail: this.translate("social.config.credentials.required", { provider: this.translate(`auth.social.provider.${invalid.provider}`) })
38
+ });
39
+ return;
40
+ }
41
+ this._isSaving.set(true);
42
+ try {
43
+ const pending = this._forms().filter((form) => form.id || form.isEnabled);
44
+ await this.configService.bulkUpsert(pending.filter((form) => !form.id).map(toCreateDto), pending.filter((form) => form.id).map(toUpdateDto));
45
+ this.messageService.add({
46
+ severity: "success",
47
+ summary: this.translate("shared.success"),
48
+ detail: this.translate("social.config.save.success")
49
+ });
50
+ await this.loadConfigs();
51
+ } catch {} finally {
52
+ this._isSaving.set(false);
53
+ }
54
+ }
55
+ async loadConfigs() {
56
+ this._isLoading.set(true);
57
+ try {
58
+ const response = await this.configService.getAll({
59
+ pagination: {
60
+ currentPage: 0,
61
+ pageSize: SOCIAL_PROVIDERS.length
62
+ },
63
+ filter: {},
64
+ select: [],
65
+ sort: {}
66
+ });
67
+ this._forms.set(mergeConfigs(response.data ?? []));
68
+ } catch {} finally {
69
+ this._isLoading.set(false);
70
+ }
71
+ }
72
+ translate(key, vars) {
73
+ return this.translateAdapter?.translate(key, vars) ?? key;
74
+ }
75
+ static ɵfac = i0.ɵɵngDeclareFactory({
76
+ minVersion: "12.0.0",
77
+ version: "22.2.1",
78
+ ngImport: i0,
79
+ type: SocialConfigPageComponent,
80
+ deps: [],
81
+ target: i0.ɵɵFactoryTarget.Component
82
+ });
83
+ static ɵcmp = i0.ɵɵngDeclareComponent({
84
+ minVersion: "17.0.0",
85
+ version: "22.2.1",
86
+ type: SocialConfigPageComponent,
87
+ isStandalone: true,
88
+ selector: "lib-social-config-page",
89
+ providers: [MessageService],
90
+ ngImport: i0,
91
+ template: `
92
+ <div class="card">
93
+ <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
94
+ <div>
95
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
96
+ {{ 'social.config.title' | translate }}
97
+ </h3>
98
+ <p class="text-sm text-muted-color mt-1">
99
+ {{ 'social.config.subtitle' | translate }}
100
+ </p>
101
+ </div>
102
+ <button fButton (click)="onSave()" [disabled]="isSaving()" [loading]="isSaving()">
103
+ @if (!isSaving()) {
104
+ <ui-icon pButtonIcon name="save"></ui-icon>
105
+ }
106
+ <span pButtonLabel>{{ 'shared.save' | translate }}</span>
107
+ </button>
108
+ </div>
109
+
110
+ <p class="text-sm text-muted-color mb-4">
111
+ {{ 'social.config.redirect.uri' | translate }}:
112
+ <code class="text-color">{{ redirectUri }}</code>
113
+ </p>
114
+
115
+ @if (isLoading()) {
116
+ <div class="flex justify-center py-8">
117
+ <ui-icon name="spinner" [spin]="true" class="text-3xl text-primary"></ui-icon>
118
+ </div>
119
+ } @else {
120
+ <div class="flex flex-col gap-4">
121
+ @for (form of forms(); track form.provider) {
122
+ <div class="border border-surface rounded p-4">
123
+ <div class="flex items-center justify-between gap-3">
124
+ <div>
125
+ <div class="font-semibold">
126
+ {{ 'auth.social.provider.' + form.provider | translate }}
127
+ </div>
128
+ <div class="text-sm text-muted-color">
129
+ {{ 'social.config.provider.hint.' + form.provider | translate }}
130
+ </div>
131
+ </div>
132
+ <f-toggleswitch
133
+ [ngModel]="form.isEnabled"
134
+ (ngModelChange)="updateField(form.provider, 'isEnabled', $event)"
135
+ />
136
+ </div>
137
+
138
+ @if (form.isEnabled) {
139
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 pt-4 border-t border-surface">
140
+ <div class="field">
141
+ <label [for]="form.provider + '-client-id'" class="block font-medium mb-2">
142
+ {{ 'social.config.client.id' | translate }} *
143
+ </label>
144
+ <input
145
+ fInputText
146
+ [id]="form.provider + '-client-id'"
147
+ class="w-full"
148
+ [ngModel]="form.clientId"
149
+ (ngModelChange)="updateField(form.provider, 'clientId', $event)"
150
+ />
151
+ </div>
152
+
153
+ <div class="field">
154
+ <label [for]="form.provider + '-client-secret'" class="block font-medium mb-2">
155
+ {{ 'social.config.client.secret' | translate }}
156
+ @if (!form.hasClientSecret) {
157
+ *
158
+ }
159
+ </label>
160
+ <f-password
161
+ [inputId]="form.provider + '-client-secret'"
162
+ styleClass="w-full"
163
+ [placeholder]="
164
+ form.hasClientSecret
165
+ ? ('social.config.client.secret.unchanged' | translate)
166
+ : ''
167
+ "
168
+ [ngModel]="form.clientSecret"
169
+ (ngModelChange)="updateField(form.provider, 'clientSecret', $event)"
170
+ />
171
+ </div>
172
+
173
+ @if (form.provider === tenantProvider) {
174
+ <div class="field">
175
+ <label [for]="form.provider + '-tenant'" class="block font-medium mb-2">
176
+ {{ 'social.config.tenant.id' | translate }}
177
+ </label>
178
+ <input
179
+ fInputText
180
+ [id]="form.provider + '-tenant'"
181
+ class="w-full"
182
+ [placeholder]="'social.config.tenant.id.example' | translate"
183
+ [ngModel]="form.tenantId"
184
+ (ngModelChange)="updateField(form.provider, 'tenantId', $event)"
185
+ />
186
+ </div>
187
+ }
188
+
189
+ <div class="field">
190
+ <label [for]="form.provider + '-scopes'" class="block font-medium mb-2">
191
+ {{ 'social.config.scopes' | translate }}
192
+ </label>
193
+ <input
194
+ fInputText
195
+ [id]="form.provider + '-scopes'"
196
+ class="w-full"
197
+ [placeholder]="'social.config.scopes.placeholder' | translate"
198
+ [ngModel]="form.scopes"
199
+ (ngModelChange)="updateField(form.provider, 'scopes', $event)"
200
+ />
201
+ </div>
202
+
203
+ </div>
204
+ }
205
+ </div>
206
+ }
207
+ </div>
208
+ }
209
+ </div>
210
+ `,
211
+ isInline: true,
212
+ dependencies: [
213
+ {
214
+ kind: "directive",
215
+ type: FButtonDirective,
216
+ selector: "[fButton]",
217
+ inputs: [
218
+ "severity",
219
+ "text",
220
+ "outlined",
221
+ "link",
222
+ "variant",
223
+ "rounded",
224
+ "raised",
225
+ "fluid",
226
+ "disabled",
227
+ "loading",
228
+ "loadingIcon",
229
+ "size",
230
+ "label",
231
+ "icon",
232
+ "iconPos",
233
+ "ariaLabel",
234
+ "plain"
235
+ ]
236
+ },
237
+ {
238
+ kind: "directive",
239
+ type: InputTextDirective,
240
+ selector: "[fInputText]",
241
+ inputs: [
242
+ "invalid",
243
+ "touched",
244
+ "variant",
245
+ "size",
246
+ "fluid"
247
+ ]
248
+ },
249
+ {
250
+ kind: "component",
251
+ type: PasswordComponent,
252
+ selector: "f-password",
253
+ inputs: [
254
+ "toggleMask",
255
+ "feedback",
256
+ "disabled",
257
+ "placeholder",
258
+ "inputId",
259
+ "invalid",
260
+ "touched",
261
+ "inputStyleClass",
262
+ "styleClass",
263
+ "style",
264
+ "ariaDescribedby",
265
+ "autocomplete",
266
+ "variant",
267
+ "size",
268
+ "fluid",
269
+ "appendTo",
270
+ "promptLabel",
271
+ "weakLabel",
272
+ "mediumLabel",
273
+ "strongLabel",
274
+ "mediumRegex",
275
+ "strongRegex"
276
+ ],
277
+ outputs: ["onBlur", "onFocus"]
278
+ },
279
+ {
280
+ kind: "component",
281
+ type: ToggleSwitchComponent,
282
+ selector: "f-toggleswitch",
283
+ inputs: [
284
+ "disabled",
285
+ "readonly",
286
+ "invalid",
287
+ "ariaLabel",
288
+ "trueValue",
289
+ "falseValue",
290
+ "inputId",
291
+ "size",
292
+ "tabindex",
293
+ "styleClass",
294
+ "style"
295
+ ]
296
+ },
297
+ {
298
+ kind: "component",
299
+ type: UiIconComponent,
300
+ selector: "ui-icon",
301
+ inputs: [
302
+ "name",
303
+ "size",
304
+ "spin",
305
+ "fill",
306
+ "severity"
307
+ ]
308
+ },
309
+ {
310
+ kind: "ngmodule",
311
+ type: FormsModule
312
+ },
313
+ {
314
+ kind: "directive",
315
+ type: i1.DefaultValueAccessor,
316
+ selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]"
317
+ },
318
+ {
319
+ kind: "directive",
320
+ type: i1.NgControlStatus,
321
+ selector: "[formControlName],[ngModel],[formControl]"
322
+ },
323
+ {
324
+ kind: "directive",
325
+ type: i1.NgModel,
326
+ selector: "[ngModel]:not([formControlName]):not([formControl])",
327
+ inputs: [
328
+ "name",
329
+ "disabled",
330
+ "ngModel",
331
+ "ngModelOptions"
332
+ ],
333
+ outputs: ["ngModelChange"],
334
+ exportAs: ["ngModel"]
335
+ },
336
+ {
337
+ kind: "pipe",
338
+ type: TranslatePipe,
339
+ name: "translate"
340
+ }
341
+ ]
342
+ });
343
+ };
344
+ i0.ɵɵngDeclareClassMetadata({
345
+ minVersion: "12.0.0",
346
+ version: "22.2.1",
347
+ ngImport: i0,
348
+ type: SocialConfigPageComponent,
349
+ decorators: [{
350
+ type: Component,
351
+ args: [{
352
+ selector: "lib-social-config-page",
353
+ imports: [
354
+ FButtonDirective,
355
+ InputTextDirective,
356
+ PasswordComponent,
357
+ ToggleSwitchComponent,
358
+ UiIconComponent,
359
+ FormsModule,
360
+ TranslatePipe
361
+ ],
362
+ providers: [MessageService],
363
+ template: `
364
+ <div class="card">
365
+ <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4">
366
+ <div>
367
+ <h3 class="text-lg sm:text-xl font-semibold m-0">
368
+ {{ 'social.config.title' | translate }}
369
+ </h3>
370
+ <p class="text-sm text-muted-color mt-1">
371
+ {{ 'social.config.subtitle' | translate }}
372
+ </p>
373
+ </div>
374
+ <button fButton (click)="onSave()" [disabled]="isSaving()" [loading]="isSaving()">
375
+ @if (!isSaving()) {
376
+ <ui-icon pButtonIcon name="save"></ui-icon>
377
+ }
378
+ <span pButtonLabel>{{ 'shared.save' | translate }}</span>
379
+ </button>
380
+ </div>
381
+
382
+ <p class="text-sm text-muted-color mb-4">
383
+ {{ 'social.config.redirect.uri' | translate }}:
384
+ <code class="text-color">{{ redirectUri }}</code>
385
+ </p>
386
+
387
+ @if (isLoading()) {
388
+ <div class="flex justify-center py-8">
389
+ <ui-icon name="spinner" [spin]="true" class="text-3xl text-primary"></ui-icon>
390
+ </div>
391
+ } @else {
392
+ <div class="flex flex-col gap-4">
393
+ @for (form of forms(); track form.provider) {
394
+ <div class="border border-surface rounded p-4">
395
+ <div class="flex items-center justify-between gap-3">
396
+ <div>
397
+ <div class="font-semibold">
398
+ {{ 'auth.social.provider.' + form.provider | translate }}
399
+ </div>
400
+ <div class="text-sm text-muted-color">
401
+ {{ 'social.config.provider.hint.' + form.provider | translate }}
402
+ </div>
403
+ </div>
404
+ <f-toggleswitch
405
+ [ngModel]="form.isEnabled"
406
+ (ngModelChange)="updateField(form.provider, 'isEnabled', $event)"
407
+ />
408
+ </div>
409
+
410
+ @if (form.isEnabled) {
411
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 pt-4 border-t border-surface">
412
+ <div class="field">
413
+ <label [for]="form.provider + '-client-id'" class="block font-medium mb-2">
414
+ {{ 'social.config.client.id' | translate }} *
415
+ </label>
416
+ <input
417
+ fInputText
418
+ [id]="form.provider + '-client-id'"
419
+ class="w-full"
420
+ [ngModel]="form.clientId"
421
+ (ngModelChange)="updateField(form.provider, 'clientId', $event)"
422
+ />
423
+ </div>
424
+
425
+ <div class="field">
426
+ <label [for]="form.provider + '-client-secret'" class="block font-medium mb-2">
427
+ {{ 'social.config.client.secret' | translate }}
428
+ @if (!form.hasClientSecret) {
429
+ *
430
+ }
431
+ </label>
432
+ <f-password
433
+ [inputId]="form.provider + '-client-secret'"
434
+ styleClass="w-full"
435
+ [placeholder]="
436
+ form.hasClientSecret
437
+ ? ('social.config.client.secret.unchanged' | translate)
438
+ : ''
439
+ "
440
+ [ngModel]="form.clientSecret"
441
+ (ngModelChange)="updateField(form.provider, 'clientSecret', $event)"
442
+ />
443
+ </div>
444
+
445
+ @if (form.provider === tenantProvider) {
446
+ <div class="field">
447
+ <label [for]="form.provider + '-tenant'" class="block font-medium mb-2">
448
+ {{ 'social.config.tenant.id' | translate }}
449
+ </label>
450
+ <input
451
+ fInputText
452
+ [id]="form.provider + '-tenant'"
453
+ class="w-full"
454
+ [placeholder]="'social.config.tenant.id.example' | translate"
455
+ [ngModel]="form.tenantId"
456
+ (ngModelChange)="updateField(form.provider, 'tenantId', $event)"
457
+ />
458
+ </div>
459
+ }
460
+
461
+ <div class="field">
462
+ <label [for]="form.provider + '-scopes'" class="block font-medium mb-2">
463
+ {{ 'social.config.scopes' | translate }}
464
+ </label>
465
+ <input
466
+ fInputText
467
+ [id]="form.provider + '-scopes'"
468
+ class="w-full"
469
+ [placeholder]="'social.config.scopes.placeholder' | translate"
470
+ [ngModel]="form.scopes"
471
+ (ngModelChange)="updateField(form.provider, 'scopes', $event)"
472
+ />
473
+ </div>
474
+
475
+ </div>
476
+ }
477
+ </div>
478
+ }
479
+ </div>
480
+ }
481
+ </div>
482
+ `
483
+ }]
484
+ }],
485
+ ctorParameters: () => []
486
+ });
487
+ function emptyForm(provider) {
488
+ return {
489
+ provider,
490
+ id: null,
491
+ isEnabled: false,
492
+ clientId: "",
493
+ clientSecret: "",
494
+ hasClientSecret: false,
495
+ tenantId: "",
496
+ scopes: ""
497
+ };
498
+ }
499
+ function mergeConfigs(configs) {
500
+ return SOCIAL_PROVIDERS.map((provider) => {
501
+ const config = configs.find((entry) => entry.provider === provider);
502
+ if (!config) return emptyForm(provider);
503
+ return {
504
+ provider,
505
+ id: config.id,
506
+ isEnabled: config.isEnabled,
507
+ clientId: config.clientId ?? "",
508
+ clientSecret: "",
509
+ hasClientSecret: config.hasClientSecret,
510
+ tenantId: config.extraConfig?.["tenantId"] ?? "",
511
+ scopes: (config.scopes ?? []).join(", ")
512
+ };
513
+ });
514
+ }
515
+ function toBaseDto(form) {
516
+ const scopes = form.scopes.split(",").map((scope) => scope.trim()).filter(Boolean);
517
+ return {
518
+ provider: form.provider,
519
+ isEnabled: form.isEnabled,
520
+ clientId: form.clientId.trim(),
521
+ ...scopes.length ? { scopes } : {},
522
+ ...form.provider === TENANT_PROVIDER && form.tenantId.trim() ? { extraConfig: { tenantId: form.tenantId.trim() } } : {}
523
+ };
524
+ }
525
+ function toCreateDto(form) {
526
+ return {
527
+ ...toBaseDto(form),
528
+ clientSecret: form.clientSecret.trim()
529
+ };
530
+ }
531
+ function toUpdateDto(form) {
532
+ const trimmedSecret = form.clientSecret.trim();
533
+ return {
534
+ id: form.id,
535
+ ...toBaseDto(form),
536
+ ...trimmedSecret ? { clientSecret: trimmedSecret } : {}
537
+ };
538
+ }
539
+ export { SocialConfigPageComponent };
540
+
541
+ //# sourceMappingURL=flusys-ng-auth-social-config-page.component-DpGmcb9n.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flusys-ng-auth-social-config-page.component-DpGmcb9n.mjs","names":[],"sources":["../../../projects/ng-auth/pages/social-config/social-config-page.component.ts"],"sourcesContent":["import { Component, inject, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport {\n TranslatePipe,\n TranslateService,\n} from '@flusys/ng-shared';\nimport {\n FButtonDirective,\n InputTextDirective,\n MessageService,\n PasswordComponent,\n ToggleSwitchComponent,\n UiIconComponent,\n} from '@flusys/ng-ui';\nimport {\n SOCIAL_PROVIDERS,\n SocialProviderEnum,\n} from '../../enums/social-provider.enum';\nimport {\n ICreateSocialAuthConfigDto,\n ISocialAuthConfig,\n IUpdateSocialAuthConfigDto,\n} from '../../interfaces/social-auth.interface';\nimport { SocialAuthApiService } from '../../services/social-auth-api.service';\nimport { SocialAuthConfigApiService } from '../../services/social-auth-config-api.service';\n\ninterface IProviderFormModel {\n provider: SocialProviderEnum;\n id: string | null;\n isEnabled: boolean;\n clientId: string;\n /** A newly typed secret to save. Left blank keeps the previously stored one. */\n clientSecret: string;\n hasClientSecret: boolean;\n tenantId: string;\n scopes: string;\n}\n\nconst TENANT_PROVIDER = SocialProviderEnum.MICROSOFT;\n\n@Component({\n selector: 'lib-social-config-page',\n imports: [\n FButtonDirective,\n InputTextDirective,\n PasswordComponent,\n ToggleSwitchComponent,\n UiIconComponent,\n FormsModule,\n TranslatePipe,\n ],\n providers: [MessageService],\n template: `\n <div class=\"card\">\n <div class=\"flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 mb-4\">\n <div>\n <h3 class=\"text-lg sm:text-xl font-semibold m-0\">\n {{ 'social.config.title' | translate }}\n </h3>\n <p class=\"text-sm text-muted-color mt-1\">\n {{ 'social.config.subtitle' | translate }}\n </p>\n </div>\n <button fButton (click)=\"onSave()\" [disabled]=\"isSaving()\" [loading]=\"isSaving()\">\n @if (!isSaving()) {\n <ui-icon pButtonIcon name=\"save\"></ui-icon>\n }\n <span pButtonLabel>{{ 'shared.save' | translate }}</span>\n </button>\n </div>\n\n <p class=\"text-sm text-muted-color mb-4\">\n {{ 'social.config.redirect.uri' | translate }}:\n <code class=\"text-color\">{{ redirectUri }}</code>\n </p>\n\n @if (isLoading()) {\n <div class=\"flex justify-center py-8\">\n <ui-icon name=\"spinner\" [spin]=\"true\" class=\"text-3xl text-primary\"></ui-icon>\n </div>\n } @else {\n <div class=\"flex flex-col gap-4\">\n @for (form of forms(); track form.provider) {\n <div class=\"border border-surface rounded p-4\">\n <div class=\"flex items-center justify-between gap-3\">\n <div>\n <div class=\"font-semibold\">\n {{ 'auth.social.provider.' + form.provider | translate }}\n </div>\n <div class=\"text-sm text-muted-color\">\n {{ 'social.config.provider.hint.' + form.provider | translate }}\n </div>\n </div>\n <f-toggleswitch\n [ngModel]=\"form.isEnabled\"\n (ngModelChange)=\"updateField(form.provider, 'isEnabled', $event)\"\n />\n </div>\n\n @if (form.isEnabled) {\n <div class=\"grid grid-cols-1 md:grid-cols-2 gap-4 mt-4 pt-4 border-t border-surface\">\n <div class=\"field\">\n <label [for]=\"form.provider + '-client-id'\" class=\"block font-medium mb-2\">\n {{ 'social.config.client.id' | translate }} *\n </label>\n <input\n fInputText\n [id]=\"form.provider + '-client-id'\"\n class=\"w-full\"\n [ngModel]=\"form.clientId\"\n (ngModelChange)=\"updateField(form.provider, 'clientId', $event)\"\n />\n </div>\n\n <div class=\"field\">\n <label [for]=\"form.provider + '-client-secret'\" class=\"block font-medium mb-2\">\n {{ 'social.config.client.secret' | translate }}\n @if (!form.hasClientSecret) {\n *\n }\n </label>\n <f-password\n [inputId]=\"form.provider + '-client-secret'\"\n styleClass=\"w-full\"\n [placeholder]=\"\n form.hasClientSecret\n ? ('social.config.client.secret.unchanged' | translate)\n : ''\n \"\n [ngModel]=\"form.clientSecret\"\n (ngModelChange)=\"updateField(form.provider, 'clientSecret', $event)\"\n />\n </div>\n\n @if (form.provider === tenantProvider) {\n <div class=\"field\">\n <label [for]=\"form.provider + '-tenant'\" class=\"block font-medium mb-2\">\n {{ 'social.config.tenant.id' | translate }}\n </label>\n <input\n fInputText\n [id]=\"form.provider + '-tenant'\"\n class=\"w-full\"\n [placeholder]=\"'social.config.tenant.id.example' | translate\"\n [ngModel]=\"form.tenantId\"\n (ngModelChange)=\"updateField(form.provider, 'tenantId', $event)\"\n />\n </div>\n }\n\n <div class=\"field\">\n <label [for]=\"form.provider + '-scopes'\" class=\"block font-medium mb-2\">\n {{ 'social.config.scopes' | translate }}\n </label>\n <input\n fInputText\n [id]=\"form.provider + '-scopes'\"\n class=\"w-full\"\n [placeholder]=\"'social.config.scopes.placeholder' | translate\"\n [ngModel]=\"form.scopes\"\n (ngModelChange)=\"updateField(form.provider, 'scopes', $event)\"\n />\n </div>\n\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n `,\n})\nexport class SocialConfigPageComponent {\n private readonly configService = inject(SocialAuthConfigApiService);\n private readonly socialApi = inject(SocialAuthApiService);\n private readonly messageService = inject(MessageService);\n private readonly translateAdapter = inject(TranslateService, {\n optional: true,\n });\n\n readonly tenantProvider = TENANT_PROVIDER;\n\n private readonly _forms = signal<IProviderFormModel[]>(\n SOCIAL_PROVIDERS.map(emptyForm),\n );\n readonly forms = this._forms.asReadonly();\n\n private readonly _isLoading = signal(true);\n readonly isLoading = this._isLoading.asReadonly();\n\n private readonly _isSaving = signal(false);\n readonly isSaving = this._isSaving.asReadonly();\n\n readonly redirectUri = this.socialApi.redirectUri;\n\n constructor() {\n void this.loadConfigs();\n }\n\n updateField<K extends keyof IProviderFormModel>(\n provider: SocialProviderEnum,\n field: K,\n value: IProviderFormModel[K],\n ): void {\n this._forms.update((forms) =>\n forms.map((form) =>\n form.provider === provider ? { ...form, [field]: value } : form,\n ),\n );\n }\n\n async onSave(): Promise<void> {\n const invalid = this._forms().find(\n (form) =>\n form.isEnabled &&\n (!form.clientId.trim() || (!form.hasClientSecret && !form.clientSecret.trim())),\n );\n\n if (invalid) {\n this.messageService.add({\n severity: 'warn',\n summary: this.translate('shared.validation'),\n detail: this.translate('social.config.credentials.required', {\n provider: this.translate(`auth.social.provider.${invalid.provider}`),\n }),\n });\n return;\n }\n\n this._isSaving.set(true);\n try {\n const pending = this._forms().filter((form) => form.id || form.isEnabled);\n await this.configService.bulkUpsert(\n pending.filter((form) => !form.id).map(toCreateDto),\n pending.filter((form) => form.id).map(toUpdateDto),\n );\n\n this.messageService.add({\n severity: 'success',\n summary: this.translate('shared.success'),\n detail: this.translate('social.config.save.success'),\n });\n await this.loadConfigs();\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this._isSaving.set(false);\n }\n }\n\n private async loadConfigs(): Promise<void> {\n this._isLoading.set(true);\n try {\n const response = await this.configService.getAll({\n pagination: { currentPage: 0, pageSize: SOCIAL_PROVIDERS.length },\n filter: {},\n select: [],\n sort: {},\n });\n this._forms.set(mergeConfigs(response.data ?? []));\n } catch {\n // Error toast handled by global interceptor\n } finally {\n this._isLoading.set(false);\n }\n }\n\n private translate(\n key: string,\n vars?: Record<string, string | number>,\n ): string {\n return this.translateAdapter?.translate(key, vars) ?? key;\n }\n}\n\nfunction emptyForm(provider: SocialProviderEnum): IProviderFormModel {\n return {\n provider,\n id: null,\n isEnabled: false,\n clientId: '',\n clientSecret: '',\n hasClientSecret: false,\n tenantId: '',\n scopes: '',\n };\n}\n\nfunction mergeConfigs(configs: ISocialAuthConfig[]): IProviderFormModel[] {\n return SOCIAL_PROVIDERS.map((provider) => {\n const config = configs.find((entry) => entry.provider === provider);\n if (!config) return emptyForm(provider);\n\n return {\n provider,\n id: config.id,\n isEnabled: config.isEnabled,\n clientId: config.clientId ?? '',\n clientSecret: '',\n hasClientSecret: config.hasClientSecret,\n tenantId: (config.extraConfig?.['tenantId'] as string) ?? '',\n scopes: (config.scopes ?? []).join(', '),\n };\n });\n}\n\n/** clientSecret is omitted here; callers decide whether to include a newly typed value. */\nfunction toBaseDto(form: IProviderFormModel): Omit<ICreateSocialAuthConfigDto, 'clientSecret'> {\n const scopes = form.scopes\n .split(',')\n .map((scope) => scope.trim())\n .filter(Boolean);\n\n return {\n provider: form.provider,\n isEnabled: form.isEnabled,\n clientId: form.clientId.trim(),\n ...(scopes.length ? { scopes } : {}),\n ...(form.provider === TENANT_PROVIDER && form.tenantId.trim()\n ? { extraConfig: { tenantId: form.tenantId.trim() } }\n : {}),\n };\n}\n\nfunction toCreateDto(form: IProviderFormModel): ICreateSocialAuthConfigDto {\n return { ...toBaseDto(form), clientSecret: form.clientSecret.trim() };\n}\n\nfunction toUpdateDto(form: IProviderFormModel): IUpdateSocialAuthConfigDto {\n const trimmedSecret = form.clientSecret.trim();\n return {\n id: form.id!,\n ...toBaseDto(form),\n // Blank means \"keep the previously stored secret\" - never overwrite it with an empty value.\n ...(trimmedSecret ? { clientSecret: trimmedSecret } : {}),\n };\n}\n"],"mappings":"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"}