@flusys/ng-auth 9.1.0 → 9.1.1

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,1074 @@
1
+ import { AuthApiService, AuthStateService, EmailPolicyService, SocialLoginButtonsComponent, isCompanySelectionResponse, isEmailVerificationRequired, isLoginResponse } from "./flusys-ng-auth.mjs";
2
+ import * as i0 from "@angular/core";
3
+ import { Component, DestroyRef, computed, effect, inject, input, signal } from "@angular/core";
4
+ import { APP_CONFIG, isCompanyFeatureEnabled, isServiceEnabled, isSignUpEnabled } from "@flusys/ng-core";
5
+ import { RouterLink } from "@angular/router";
6
+ import { TranslatePipe, TranslateService, buildBranchTreeNodes } from "@flusys/ng-shared";
7
+ import { CheckboxComponent, FButtonDirective, InputTextDirective, MessageService, PasswordComponent, SelectComponent, TreeSelectComponent, UiIconComponent } from "@flusys/ng-ui";
8
+ import { AppFloatingConfigurator } from "@flusys/ng-layout";
9
+ import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
10
+ import { FormField, form, required } from "@angular/forms/signals";
11
+ var LoginPageComponent = class LoginPageComponent {
12
+ destroyRef = inject(DestroyRef);
13
+ appConfig = inject(APP_CONFIG);
14
+ messageService = inject(MessageService);
15
+ authApi = inject(AuthApiService);
16
+ authState = inject(AuthStateService);
17
+ translateAdapter = inject(TranslateService, { optional: true });
18
+ emailPolicy = inject(EmailPolicyService);
19
+ translate(key, vars) {
20
+ return this.translateAdapter?.translate(key, vars) ?? key;
21
+ }
22
+ returnUrl = input("", ...ngDevMode ? [{ debugName: "returnUrl" }] : /* istanbul ignore next */ []);
23
+ currentStep = signal("credentials", ...ngDevMode ? [{ debugName: "currentStep" }] : /* istanbul ignore next */ []);
24
+ isLoading = signal(false, ...ngDevMode ? [{ debugName: "isLoading" }] : /* istanbul ignore next */ []);
25
+ unverifiedEmail = signal("", ...ngDevMode ? [{ debugName: "unverifiedEmail" }] : /* istanbul ignore next */ []);
26
+ savedUserName = signal(this.authState.getSavedUserName(), ...ngDevMode ? [{ debugName: "savedUserName" }] : /* istanbul ignore next */ []);
27
+ welcomeBackText = computed(() => this.translate("auth.login.welcome.back", { name: this.savedUserName() }), ...ngDevMode ? [{ debugName: "welcomeBackText" }] : /* istanbul ignore next */ []);
28
+ loginFormModel = signal({
29
+ email: "",
30
+ password: "",
31
+ rememberMe: true
32
+ }, ...ngDevMode ? [{ debugName: "loginFormModel" }] : /* istanbul ignore next */ []);
33
+ loginForm = form(this.loginFormModel, (f) => {
34
+ required(f.email, { message: this.translate("auth.validation.email.required") });
35
+ required(f.password, { message: this.translate("auth.validation.password.required") });
36
+ });
37
+ emailValidationError = computed(() => this.emailPolicy.validate(this.loginFormModel().email), ...ngDevMode ? [{ debugName: "emailValidationError" }] : /* istanbul ignore next */ []);
38
+ isLoginFormValid = computed(() => {
39
+ return this.loginForm().valid() && !this.emailValidationError();
40
+ }, ...ngDevMode ? [{ debugName: "isLoginFormValid" }] : /* istanbul ignore next */ []);
41
+ selectionFormModel = signal({
42
+ companyId: "",
43
+ branchId: "",
44
+ rememberSelection: true
45
+ }, ...ngDevMode ? [{ debugName: "selectionFormModel" }] : /* istanbul ignore next */ []);
46
+ selectionForm = form(this.selectionFormModel, (f) => {
47
+ required(f.companyId, { message: this.translate("auth.validation.company.required") });
48
+ required(f.branchId, { message: this.translate("auth.validation.branch.required") });
49
+ });
50
+ isSelectionFormValid = computed(() => this.selectionForm().valid(), ...ngDevMode ? [{ debugName: "isSelectionFormValid" }] : /* istanbul ignore next */ []);
51
+ companyFeatureEnabled = computed(() => isCompanyFeatureEnabled(this.appConfig), ...ngDevMode ? [{ debugName: "companyFeatureEnabled" }] : /* istanbul ignore next */ []);
52
+ emailFeatureEnabled = computed(() => isServiceEnabled(this.appConfig, "email"), ...ngDevMode ? [{ debugName: "emailFeatureEnabled" }] : /* istanbul ignore next */ []);
53
+ signUpEnabled = computed(() => isSignUpEnabled(this.appConfig), ...ngDevMode ? [{ debugName: "signUpEnabled" }] : /* istanbul ignore next */ []);
54
+ sessionId = null;
55
+ companies = signal([], ...ngDevMode ? [{ debugName: "companies" }] : /* istanbul ignore next */ []);
56
+ userName = signal("", ...ngDevMode ? [{ debugName: "userName" }] : /* istanbul ignore next */ []);
57
+ branches = computed(() => {
58
+ const companyId = this.selectionFormModel().companyId;
59
+ if (!companyId) return [];
60
+ return this.companies().find((c) => c.id === companyId)?.branches ?? [];
61
+ }, ...ngDevMode ? [{ debugName: "branches" }] : /* istanbul ignore next */ []);
62
+ branchTreeNodes = computed(() => buildBranchTreeNodes(this.branches()), ...ngDevMode ? [{ debugName: "branchTreeNodes" }] : /* istanbul ignore next */ []);
63
+ constructor() {
64
+ effect(() => {
65
+ if (this.authState.requiresCompanySelection()) this.restorePendingSelectionState();
66
+ });
67
+ }
68
+ restorePendingSelectionState() {
69
+ const availableCompanies = this.authState.availableCompanies();
70
+ const sessionId = this.authState.selectionSessionId();
71
+ const name = this.authState.userName();
72
+ if (availableCompanies.length > 0 && sessionId) {
73
+ this.companies.set(availableCompanies);
74
+ this.sessionId = sessionId;
75
+ this.userName.set(name);
76
+ this.currentStep.set("company-selection");
77
+ this.autoSelectIfSingle();
78
+ }
79
+ }
80
+ onSubmit(event) {
81
+ event.preventDefault();
82
+ if (!this.isLoginFormValid()) {
83
+ this.messageService.add({
84
+ severity: "error",
85
+ summary: this.translate("shared.validation.error"),
86
+ detail: this.translate("shared.fill.required.fields")
87
+ });
88
+ return;
89
+ }
90
+ this.isLoading.set(true);
91
+ const formData = this.loginFormModel();
92
+ const loginData = {
93
+ email: formData.email,
94
+ password: formData.password,
95
+ rememberMe: formData.rememberMe
96
+ };
97
+ this.authApi.login(loginData).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
98
+ next: (response) => {
99
+ this.isLoading.set(false);
100
+ if (isEmailVerificationRequired(response)) {
101
+ if (!response.data) return;
102
+ this.unverifiedEmail.set(response.data.email);
103
+ this.currentStep.set("email-verification");
104
+ return;
105
+ }
106
+ if (isCompanySelectionResponse(response)) {
107
+ if (!response.data) return;
108
+ this.sessionId = response.data.sessionId;
109
+ this.companies.set(response.data.companies);
110
+ this.userName.set(response.data.user.name ?? response.data.user.email);
111
+ this.authState.setSelectionState(response.data);
112
+ this.currentStep.set("company-selection");
113
+ this.autoSelectIfSingle();
114
+ } else if (isLoginResponse(response)) {
115
+ if (!response.data) return;
116
+ this.authState.setLoginState(response.data);
117
+ this.messageService.add({
118
+ severity: "success",
119
+ summary: this.translate("shared.success"),
120
+ detail: response.messageKey ? this.translate(response.messageKey, response.messageVariables) : response.message ?? this.translate("auth.login.success")
121
+ });
122
+ this.authState.navigateAfterLogin(this.returnUrl());
123
+ }
124
+ },
125
+ error: (error) => {
126
+ this.isLoading.set(false);
127
+ const messageKey = error?.error?.messageKey;
128
+ const detail = messageKey ? this.translate(messageKey, error?.error?.messageVariables) : error?.error?.message;
129
+ this.messageService.add({
130
+ severity: "error",
131
+ summary: this.translate("auth.login.title"),
132
+ detail
133
+ });
134
+ }
135
+ });
136
+ }
137
+ onCompanyChange() {
138
+ this.selectionFormModel.update((model) => ({
139
+ ...model,
140
+ branchId: ""
141
+ }));
142
+ const branches = this.branches();
143
+ if (branches.length === 1) this.selectionFormModel.update((model) => ({
144
+ ...model,
145
+ branchId: branches[0].id
146
+ }));
147
+ }
148
+ onSelectCompany(event) {
149
+ event.preventDefault();
150
+ if (!this.isSelectionFormValid()) {
151
+ this.messageService.add({
152
+ severity: "warn",
153
+ summary: this.translate("shared.validation.error"),
154
+ detail: this.translate("auth.login.requires.selection")
155
+ });
156
+ return;
157
+ }
158
+ if (!this.sessionId) {
159
+ this.messageService.add({
160
+ severity: "error",
161
+ summary: this.translate("shared.error"),
162
+ detail: this.translate("auth.session.expired")
163
+ });
164
+ this.goBackToCredentials();
165
+ return;
166
+ }
167
+ this.isLoading.set(true);
168
+ const formData = this.selectionFormModel();
169
+ const selectData = {
170
+ sessionId: this.sessionId,
171
+ companyId: formData.companyId,
172
+ branchId: formData.branchId,
173
+ rememberMe: this.loginFormModel().rememberMe
174
+ };
175
+ this.authApi.select(selectData).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
176
+ next: (response) => {
177
+ this.isLoading.set(false);
178
+ if (!response.data) return;
179
+ this.authState.setLoginState(response.data);
180
+ this.messageService.add({
181
+ severity: "success",
182
+ summary: this.translate("shared.success"),
183
+ detail: response.messageKey ? this.translate(response.messageKey, response.messageVariables) : response.message
184
+ });
185
+ this.authState.navigateAfterLogin(this.returnUrl());
186
+ },
187
+ error: () => {
188
+ this.isLoading.set(false);
189
+ }
190
+ });
191
+ }
192
+ goBackToCredentials() {
193
+ this.sessionId = null;
194
+ this.companies.set([]);
195
+ this.unverifiedEmail.set("");
196
+ this.selectionFormModel.set({
197
+ companyId: "",
198
+ branchId: "",
199
+ rememberSelection: true
200
+ });
201
+ this.authState.resetState();
202
+ this.currentStep.set("credentials");
203
+ }
204
+ onResendVerification() {
205
+ const email = this.unverifiedEmail();
206
+ if (!email) return;
207
+ this.isLoading.set(true);
208
+ this.authApi.resendVerification(email).pipe(takeUntilDestroyed(this.destroyRef)).subscribe({
209
+ next: (response) => {
210
+ this.isLoading.set(false);
211
+ this.messageService.add({
212
+ severity: "success",
213
+ summary: this.translate("shared.success"),
214
+ detail: response.messageKey ? this.translate(response.messageKey, response.messageVariables) : response.message
215
+ });
216
+ },
217
+ error: () => {
218
+ this.isLoading.set(false);
219
+ }
220
+ });
221
+ }
222
+ autoSelectIfSingle() {
223
+ if (this.companies().length === 1) {
224
+ this.selectionFormModel.update((model) => ({
225
+ ...model,
226
+ companyId: this.companies()[0].id
227
+ }));
228
+ this.onCompanyChange();
229
+ }
230
+ const storedCompanyId = this.authState.getStoredCompanyId();
231
+ const storedBranchId = this.authState.getStoredBranchId();
232
+ if (storedCompanyId) {
233
+ const company = this.companies().find((c) => c.id === storedCompanyId);
234
+ if (company) {
235
+ this.selectionFormModel.update((model) => ({
236
+ ...model,
237
+ companyId: storedCompanyId
238
+ }));
239
+ if (storedBranchId) {
240
+ if (company.branches.find((b) => b.id === storedBranchId)) this.selectionFormModel.update((model) => ({
241
+ ...model,
242
+ branchId: storedBranchId
243
+ }));
244
+ }
245
+ }
246
+ }
247
+ }
248
+ static ɵfac = i0.ɵɵngDeclareFactory({
249
+ minVersion: "12.0.0",
250
+ version: "22.2.1",
251
+ ngImport: i0,
252
+ type: LoginPageComponent,
253
+ deps: [],
254
+ target: i0.ɵɵFactoryTarget.Component
255
+ });
256
+ static ɵcmp = i0.ɵɵngDeclareComponent({
257
+ minVersion: "17.0.0",
258
+ version: "22.2.1",
259
+ type: LoginPageComponent,
260
+ isStandalone: true,
261
+ selector: "lib-login-page",
262
+ inputs: { returnUrl: {
263
+ classPropertyName: "returnUrl",
264
+ publicName: "returnUrl",
265
+ isSignal: true,
266
+ isRequired: false,
267
+ transformFunction: null
268
+ } },
269
+ ngImport: i0,
270
+ template: `
271
+ <app-floating-configurator />
272
+ <div
273
+ class="bg-surface-ground flex items-center justify-center min-h-screen"
274
+ >
275
+ <div
276
+ class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4"
277
+ >
278
+ <div
279
+ class="w-full"
280
+ style="border-radius: var(--f-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)"
281
+ >
282
+ <div
283
+ class="w-full py-8 px-4 sm:py-12 sm:px-8 md:py-16 md:px-12"
284
+ style="border-radius: var(--f-content-border-radius); background: var(--f-content-background)"
285
+ >
286
+ <!-- Step Indicator (only when company feature enabled) -->
287
+ @if (companyFeatureEnabled()) {
288
+ <div class="flex items-center justify-center mb-6 gap-2">
289
+ <div
290
+ class="flex items-center justify-center w-8 h-8 rounded-full text-sm font-semibold transition-colors"
291
+ [class.bg-primary]="currentStep() === 'credentials'"
292
+ [class.text-primary-contrast]="
293
+ currentStep() === 'credentials'
294
+ "
295
+ [class.bg-surface-200]="currentStep() !== 'credentials'"
296
+ [class.dark:bg-surface-700]="currentStep() !== 'credentials'"
297
+ [class.text-muted-color]="currentStep() !== 'credentials'"
298
+ >
299
+ 1
300
+ </div>
301
+ <div class="w-12 h-0.5 bg-surface-200 dark:bg-surface-700"></div>
302
+ <div
303
+ class="flex items-center justify-center w-8 h-8 rounded-full text-sm font-semibold transition-colors"
304
+ [class.bg-primary]="currentStep() === 'company-selection'"
305
+ [class.text-primary-contrast]="
306
+ currentStep() === 'company-selection'
307
+ "
308
+ [class.bg-surface-200]="currentStep() !== 'company-selection'"
309
+ [class.dark:bg-surface-700]="currentStep() !== 'company-selection'"
310
+ [class.text-muted-color]="
311
+ currentStep() !== 'company-selection'
312
+ "
313
+ >
314
+ 2
315
+ </div>
316
+ </div>
317
+ }
318
+
319
+ <!-- Step 1: Credentials -->
320
+ @if (currentStep() === 'credentials') {
321
+ <div class="text-center mb-8">
322
+ <div
323
+ class="text-3xl font-medium mb-4 text-color truncate"
324
+ [title]="savedUserName() ? welcomeBackText() : null"
325
+ >
326
+ @if (savedUserName()) {
327
+ {{ welcomeBackText() }}
328
+ } @else {
329
+ {{ 'auth.login.welcome' | translate }}
330
+ }
331
+ </div>
332
+ <span class="text-muted-color font-medium">{{
333
+ 'auth.login.sign.in.to.continue' | translate
334
+ }}</span>
335
+ </div>
336
+
337
+ <form (submit)="onSubmit($event)">
338
+ <label
339
+ for="email"
340
+ class="block text-lg font-medium mb-2 text-color"
341
+ >
342
+ {{ 'auth.email.label' | translate }}
343
+ </label>
344
+ <input
345
+ fInputText
346
+ id="email"
347
+ type="email"
348
+ [placeholder]="'auth.email.placeholder' | translate"
349
+ class="w-full"
350
+ [formField]="loginForm.email"
351
+ autocomplete="email"
352
+ />
353
+ @if (emailValidationError()) {
354
+ <small class="text-danger block mt-1">{{
355
+ emailValidationError()
356
+ }}</small>
357
+ }
358
+ <div class="mb-4"></div>
359
+
360
+ <label
361
+ for="password"
362
+ class="block font-medium text-lg mb-2 text-color"
363
+ >
364
+ {{ 'auth.password.label' | translate }}
365
+ </label>
366
+ <div class="mb-4">
367
+ <f-password
368
+ [inputId]="'password'"
369
+ [placeholder]="'auth.password.placeholder' | translate"
370
+ class="w-full"
371
+ [formField]="loginForm.password"
372
+ autocomplete="current-password"
373
+ />
374
+ </div>
375
+
376
+ <div
377
+ class="flex flex-col sm:flex-row items-start sm:items-center justify-between mt-2 mb-8 gap-4"
378
+ >
379
+ <f-checkbox [formField]="loginForm.rememberMe" [label]="'auth.field.remember.me' | translate" />
380
+ @if (emailFeatureEnabled()) {
381
+ <span
382
+ class="font-medium no-underline cursor-pointer text-primary"
383
+ style="margin-inline-start: 0.5rem;"
384
+ routerLink="/auth/forgot-password"
385
+ >
386
+ {{ 'auth.password.forgot' | translate }}
387
+ </span>
388
+ }
389
+ </div>
390
+
391
+ <button
392
+ fButton
393
+ class="w-full"
394
+ type="submit"
395
+ [disabled]="!isLoginFormValid() || isLoading()"
396
+ >
397
+ @if (isLoading()) {
398
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
399
+ }
400
+ <span pButtonLabel>{{ 'auth.login.sign.in' | translate }}</span>
401
+ </button>
402
+
403
+ <lib-social-login-buttons [returnUrl]="returnUrl()" />
404
+
405
+ @if (signUpEnabled()) {
406
+ <div class="flex items-center justify-center mt-4 gap-2">
407
+ <span class="font-medium">{{
408
+ 'auth.login.no.account' | translate
409
+ }}</span>
410
+ <a
411
+ routerLink="/auth/register"
412
+ class="font-semibold text-primary cursor-pointer"
413
+ >
414
+ {{ 'auth.login.sign.up' | translate }}
415
+ </a>
416
+ </div>
417
+ }
418
+ </form>
419
+ }
420
+
421
+ <!-- Email Verification Required -->
422
+ @if (currentStep() === 'email-verification') {
423
+ <div class="text-center">
424
+ <div class="flex justify-center mb-6">
425
+ <ui-icon name="envelope" class="text-6xl text-warn"></ui-icon>
426
+ </div>
427
+ <div class="text-3xl font-medium mb-4 text-color">
428
+ {{ 'auth.login.email.not.verified' | translate }}
429
+ </div>
430
+ <p class="text-muted-color font-medium mb-6">
431
+ {{ 'auth.login.verify.email.message' | translate }}
432
+ </p>
433
+ <button
434
+ fButton
435
+ class="w-full mb-4"
436
+ (click)="onResendVerification()"
437
+ [disabled]="isLoading()"
438
+ >
439
+ @if (isLoading()) {
440
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
441
+ }
442
+ <span pButtonLabel>{{ 'auth.verify.resend' | translate }}</span>
443
+ </button>
444
+ <div class="mt-4">
445
+ <span
446
+ class="font-semibold text-primary cursor-pointer"
447
+ (click)="goBackToCredentials()"
448
+ >
449
+ {{ 'auth.login.back.to.sign.in' | translate }}
450
+ </span>
451
+ </div>
452
+ </div>
453
+ }
454
+
455
+ <!-- Step 2: Company Selection -->
456
+ @if (currentStep() === 'company-selection') {
457
+ <div class="text-center mb-8">
458
+ <div class="text-3xl font-medium mb-4 text-color">
459
+ {{ 'auth.login.select.company' | translate }}
460
+ </div>
461
+ <div class="text-muted-color font-medium">
462
+ {{ 'auth.login.select.company.message' | translate }}
463
+ </div>
464
+ </div>
465
+
466
+ <form (submit)="onSelectCompany($event)">
467
+ <label
468
+ for="company"
469
+ class="block text-lg font-medium mb-2 text-color"
470
+ >
471
+ {{ 'shared.company' | translate }}
472
+ </label>
473
+ <f-select
474
+ inputId="company"
475
+ class="w-full mb-4"
476
+ [options]="companies()"
477
+ optionLabel="name"
478
+ optionValue="id"
479
+ [formField]="selectionForm.companyId"
480
+ [placeholder]="'auth.login.select.company.option' | translate"
481
+ (onChange)="onCompanyChange()"
482
+ />
483
+
484
+ <label
485
+ for="branch"
486
+ class="block text-lg font-medium mb-2 text-color"
487
+ >
488
+ {{ 'auth.login.branch.label' | translate }}
489
+ </label>
490
+ <f-treeselect
491
+ inputId="branch"
492
+ class="w-full mb-4"
493
+ [options]="branchTreeNodes()"
494
+ [formField]="selectionForm.branchId"
495
+ [placeholder]="'auth.login.select.branch.option' | translate"
496
+ [filter]="true"
497
+ [filterPlaceholder]="'shared.search' | translate"
498
+ [emptyMessage]="'shared.no.results' | translate"
499
+ />
500
+
501
+ <div class="flex items-center mt-2 mb-8">
502
+ <f-checkbox
503
+ [formField]="selectionForm.rememberSelection"
504
+ [label]="'auth.login.remember.selection' | translate"
505
+ />
506
+ </div>
507
+
508
+ <div class="flex flex-col sm:flex-row gap-4">
509
+ <button
510
+ fButton
511
+ class="w-full"
512
+ severity="secondary"
513
+ [outlined]="true"
514
+ type="button"
515
+ (click)="goBackToCredentials()"
516
+ >
517
+ <span pButtonLabel>{{ 'shared.back' | translate }}</span>
518
+ </button>
519
+ <button
520
+ fButton
521
+ class="w-full"
522
+ type="submit"
523
+ [disabled]="!isSelectionFormValid() || isLoading()"
524
+ >
525
+ @if (isLoading()) {
526
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
527
+ }
528
+ <span pButtonLabel>{{ 'shared.continue' | translate }}</span>
529
+ </button>
530
+ </div>
531
+ </form>
532
+ }
533
+ </div>
534
+ </div>
535
+ </div>
536
+ </div>
537
+ `,
538
+ isInline: true,
539
+ dependencies: [
540
+ {
541
+ kind: "component",
542
+ type: CheckboxComponent,
543
+ selector: "f-checkbox",
544
+ inputs: [
545
+ "label",
546
+ "binary",
547
+ "disabled",
548
+ "readonly",
549
+ "inputId",
550
+ "invalid",
551
+ "indeterminate",
552
+ "variant",
553
+ "size",
554
+ "touched",
555
+ "styleClass",
556
+ "style",
557
+ "checked"
558
+ ],
559
+ outputs: [
560
+ "onChange",
561
+ "onBlur",
562
+ "touch",
563
+ "checkedChange"
564
+ ]
565
+ },
566
+ {
567
+ kind: "directive",
568
+ type: FButtonDirective,
569
+ selector: "[fButton]",
570
+ inputs: [
571
+ "severity",
572
+ "text",
573
+ "outlined",
574
+ "link",
575
+ "variant",
576
+ "rounded",
577
+ "raised",
578
+ "fluid",
579
+ "disabled",
580
+ "loading",
581
+ "loadingIcon",
582
+ "size",
583
+ "label",
584
+ "icon",
585
+ "iconPos",
586
+ "ariaLabel",
587
+ "plain"
588
+ ]
589
+ },
590
+ {
591
+ kind: "directive",
592
+ type: InputTextDirective,
593
+ selector: "[fInputText]",
594
+ inputs: [
595
+ "invalid",
596
+ "touched",
597
+ "variant",
598
+ "size",
599
+ "fluid"
600
+ ]
601
+ },
602
+ {
603
+ kind: "component",
604
+ type: PasswordComponent,
605
+ selector: "f-password",
606
+ inputs: [
607
+ "toggleMask",
608
+ "feedback",
609
+ "disabled",
610
+ "placeholder",
611
+ "inputId",
612
+ "invalid",
613
+ "touched",
614
+ "inputStyleClass",
615
+ "styleClass",
616
+ "style",
617
+ "ariaDescribedby",
618
+ "autocomplete",
619
+ "variant",
620
+ "size",
621
+ "fluid",
622
+ "appendTo",
623
+ "promptLabel",
624
+ "weakLabel",
625
+ "mediumLabel",
626
+ "strongLabel",
627
+ "mediumRegex",
628
+ "strongRegex"
629
+ ],
630
+ outputs: ["onBlur", "onFocus"]
631
+ },
632
+ {
633
+ kind: "component",
634
+ type: SelectComponent,
635
+ selector: "f-select",
636
+ inputs: [
637
+ "options",
638
+ "optionLabel",
639
+ "optionValue",
640
+ "placeholder",
641
+ "disabled",
642
+ "filter",
643
+ "filterBy",
644
+ "filterMatchMode",
645
+ "filterLocale",
646
+ "filterPlaceholder",
647
+ "resetFilterOnHide",
648
+ "autofocusFilter",
649
+ "emptyMessage",
650
+ "emptyFilterMessage",
651
+ "showClear",
652
+ "loading",
653
+ "loadingIcon",
654
+ "dropdownIcon",
655
+ "clearIcon",
656
+ "inputId",
657
+ "invalid",
658
+ "touched",
659
+ "styleClass",
660
+ "style",
661
+ "appendTo",
662
+ "editable",
663
+ "readonly",
664
+ "autofocus",
665
+ "checkmark",
666
+ "group",
667
+ "optionGroupLabel",
668
+ "optionGroupChildren",
669
+ "virtualScroll",
670
+ "virtualScrollItemSize",
671
+ "lazy",
672
+ "selectedObject"
673
+ ],
674
+ outputs: [
675
+ "onFilter",
676
+ "onChange",
677
+ "onShow",
678
+ "onHide",
679
+ "onBlur",
680
+ "onFocus",
681
+ "onClear",
682
+ "onLazyLoad"
683
+ ]
684
+ },
685
+ {
686
+ kind: "component",
687
+ type: TreeSelectComponent,
688
+ selector: "f-treeselect",
689
+ inputs: [
690
+ "options",
691
+ "placeholder",
692
+ "inputId",
693
+ "disabled",
694
+ "filter",
695
+ "filterBy",
696
+ "filterPlaceholder",
697
+ "selectionMode",
698
+ "display",
699
+ "propagateSelectionDown",
700
+ "propagateSelectionUp",
701
+ "showClear",
702
+ "virtualScroll",
703
+ "virtualScrollItemSize",
704
+ "emptyMessage",
705
+ "invalid",
706
+ "touched",
707
+ "styleClass",
708
+ "style",
709
+ "appendTo"
710
+ ]
711
+ },
712
+ {
713
+ kind: "component",
714
+ type: UiIconComponent,
715
+ selector: "ui-icon",
716
+ inputs: [
717
+ "name",
718
+ "size",
719
+ "spin",
720
+ "fill",
721
+ "severity"
722
+ ]
723
+ },
724
+ {
725
+ kind: "directive",
726
+ type: RouterLink,
727
+ selector: "[routerLink]",
728
+ inputs: [
729
+ "target",
730
+ "queryParams",
731
+ "fragment",
732
+ "queryParamsHandling",
733
+ "state",
734
+ "info",
735
+ "relativeTo",
736
+ "preserveFragment",
737
+ "skipLocationChange",
738
+ "replaceUrl",
739
+ "browserUrl",
740
+ "routerLink"
741
+ ]
742
+ },
743
+ {
744
+ kind: "directive",
745
+ type: FormField,
746
+ selector: "[formField]",
747
+ inputs: ["formField"],
748
+ exportAs: ["formField"]
749
+ },
750
+ {
751
+ kind: "component",
752
+ type: AppFloatingConfigurator,
753
+ selector: "app-floating-configurator"
754
+ },
755
+ {
756
+ kind: "component",
757
+ type: SocialLoginButtonsComponent,
758
+ selector: "lib-social-login-buttons",
759
+ inputs: ["returnUrl"]
760
+ },
761
+ {
762
+ kind: "pipe",
763
+ type: TranslatePipe,
764
+ name: "translate"
765
+ }
766
+ ]
767
+ });
768
+ };
769
+ i0.ɵɵngDeclareClassMetadata({
770
+ minVersion: "12.0.0",
771
+ version: "22.2.1",
772
+ ngImport: i0,
773
+ type: LoginPageComponent,
774
+ decorators: [{
775
+ type: Component,
776
+ args: [{
777
+ selector: "lib-login-page",
778
+ imports: [
779
+ CheckboxComponent,
780
+ FButtonDirective,
781
+ InputTextDirective,
782
+ PasswordComponent,
783
+ SelectComponent,
784
+ TreeSelectComponent,
785
+ UiIconComponent,
786
+ RouterLink,
787
+ FormField,
788
+ AppFloatingConfigurator,
789
+ SocialLoginButtonsComponent,
790
+ TranslatePipe
791
+ ],
792
+ template: `
793
+ <app-floating-configurator />
794
+ <div
795
+ class="bg-surface-ground flex items-center justify-center min-h-screen"
796
+ >
797
+ <div
798
+ class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4"
799
+ >
800
+ <div
801
+ class="w-full"
802
+ style="border-radius: var(--f-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)"
803
+ >
804
+ <div
805
+ class="w-full py-8 px-4 sm:py-12 sm:px-8 md:py-16 md:px-12"
806
+ style="border-radius: var(--f-content-border-radius); background: var(--f-content-background)"
807
+ >
808
+ <!-- Step Indicator (only when company feature enabled) -->
809
+ @if (companyFeatureEnabled()) {
810
+ <div class="flex items-center justify-center mb-6 gap-2">
811
+ <div
812
+ class="flex items-center justify-center w-8 h-8 rounded-full text-sm font-semibold transition-colors"
813
+ [class.bg-primary]="currentStep() === 'credentials'"
814
+ [class.text-primary-contrast]="
815
+ currentStep() === 'credentials'
816
+ "
817
+ [class.bg-surface-200]="currentStep() !== 'credentials'"
818
+ [class.dark:bg-surface-700]="currentStep() !== 'credentials'"
819
+ [class.text-muted-color]="currentStep() !== 'credentials'"
820
+ >
821
+ 1
822
+ </div>
823
+ <div class="w-12 h-0.5 bg-surface-200 dark:bg-surface-700"></div>
824
+ <div
825
+ class="flex items-center justify-center w-8 h-8 rounded-full text-sm font-semibold transition-colors"
826
+ [class.bg-primary]="currentStep() === 'company-selection'"
827
+ [class.text-primary-contrast]="
828
+ currentStep() === 'company-selection'
829
+ "
830
+ [class.bg-surface-200]="currentStep() !== 'company-selection'"
831
+ [class.dark:bg-surface-700]="currentStep() !== 'company-selection'"
832
+ [class.text-muted-color]="
833
+ currentStep() !== 'company-selection'
834
+ "
835
+ >
836
+ 2
837
+ </div>
838
+ </div>
839
+ }
840
+
841
+ <!-- Step 1: Credentials -->
842
+ @if (currentStep() === 'credentials') {
843
+ <div class="text-center mb-8">
844
+ <div
845
+ class="text-3xl font-medium mb-4 text-color truncate"
846
+ [title]="savedUserName() ? welcomeBackText() : null"
847
+ >
848
+ @if (savedUserName()) {
849
+ {{ welcomeBackText() }}
850
+ } @else {
851
+ {{ 'auth.login.welcome' | translate }}
852
+ }
853
+ </div>
854
+ <span class="text-muted-color font-medium">{{
855
+ 'auth.login.sign.in.to.continue' | translate
856
+ }}</span>
857
+ </div>
858
+
859
+ <form (submit)="onSubmit($event)">
860
+ <label
861
+ for="email"
862
+ class="block text-lg font-medium mb-2 text-color"
863
+ >
864
+ {{ 'auth.email.label' | translate }}
865
+ </label>
866
+ <input
867
+ fInputText
868
+ id="email"
869
+ type="email"
870
+ [placeholder]="'auth.email.placeholder' | translate"
871
+ class="w-full"
872
+ [formField]="loginForm.email"
873
+ autocomplete="email"
874
+ />
875
+ @if (emailValidationError()) {
876
+ <small class="text-danger block mt-1">{{
877
+ emailValidationError()
878
+ }}</small>
879
+ }
880
+ <div class="mb-4"></div>
881
+
882
+ <label
883
+ for="password"
884
+ class="block font-medium text-lg mb-2 text-color"
885
+ >
886
+ {{ 'auth.password.label' | translate }}
887
+ </label>
888
+ <div class="mb-4">
889
+ <f-password
890
+ [inputId]="'password'"
891
+ [placeholder]="'auth.password.placeholder' | translate"
892
+ class="w-full"
893
+ [formField]="loginForm.password"
894
+ autocomplete="current-password"
895
+ />
896
+ </div>
897
+
898
+ <div
899
+ class="flex flex-col sm:flex-row items-start sm:items-center justify-between mt-2 mb-8 gap-4"
900
+ >
901
+ <f-checkbox [formField]="loginForm.rememberMe" [label]="'auth.field.remember.me' | translate" />
902
+ @if (emailFeatureEnabled()) {
903
+ <span
904
+ class="font-medium no-underline cursor-pointer text-primary"
905
+ style="margin-inline-start: 0.5rem;"
906
+ routerLink="/auth/forgot-password"
907
+ >
908
+ {{ 'auth.password.forgot' | translate }}
909
+ </span>
910
+ }
911
+ </div>
912
+
913
+ <button
914
+ fButton
915
+ class="w-full"
916
+ type="submit"
917
+ [disabled]="!isLoginFormValid() || isLoading()"
918
+ >
919
+ @if (isLoading()) {
920
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
921
+ }
922
+ <span pButtonLabel>{{ 'auth.login.sign.in' | translate }}</span>
923
+ </button>
924
+
925
+ <lib-social-login-buttons [returnUrl]="returnUrl()" />
926
+
927
+ @if (signUpEnabled()) {
928
+ <div class="flex items-center justify-center mt-4 gap-2">
929
+ <span class="font-medium">{{
930
+ 'auth.login.no.account' | translate
931
+ }}</span>
932
+ <a
933
+ routerLink="/auth/register"
934
+ class="font-semibold text-primary cursor-pointer"
935
+ >
936
+ {{ 'auth.login.sign.up' | translate }}
937
+ </a>
938
+ </div>
939
+ }
940
+ </form>
941
+ }
942
+
943
+ <!-- Email Verification Required -->
944
+ @if (currentStep() === 'email-verification') {
945
+ <div class="text-center">
946
+ <div class="flex justify-center mb-6">
947
+ <ui-icon name="envelope" class="text-6xl text-warn"></ui-icon>
948
+ </div>
949
+ <div class="text-3xl font-medium mb-4 text-color">
950
+ {{ 'auth.login.email.not.verified' | translate }}
951
+ </div>
952
+ <p class="text-muted-color font-medium mb-6">
953
+ {{ 'auth.login.verify.email.message' | translate }}
954
+ </p>
955
+ <button
956
+ fButton
957
+ class="w-full mb-4"
958
+ (click)="onResendVerification()"
959
+ [disabled]="isLoading()"
960
+ >
961
+ @if (isLoading()) {
962
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
963
+ }
964
+ <span pButtonLabel>{{ 'auth.verify.resend' | translate }}</span>
965
+ </button>
966
+ <div class="mt-4">
967
+ <span
968
+ class="font-semibold text-primary cursor-pointer"
969
+ (click)="goBackToCredentials()"
970
+ >
971
+ {{ 'auth.login.back.to.sign.in' | translate }}
972
+ </span>
973
+ </div>
974
+ </div>
975
+ }
976
+
977
+ <!-- Step 2: Company Selection -->
978
+ @if (currentStep() === 'company-selection') {
979
+ <div class="text-center mb-8">
980
+ <div class="text-3xl font-medium mb-4 text-color">
981
+ {{ 'auth.login.select.company' | translate }}
982
+ </div>
983
+ <div class="text-muted-color font-medium">
984
+ {{ 'auth.login.select.company.message' | translate }}
985
+ </div>
986
+ </div>
987
+
988
+ <form (submit)="onSelectCompany($event)">
989
+ <label
990
+ for="company"
991
+ class="block text-lg font-medium mb-2 text-color"
992
+ >
993
+ {{ 'shared.company' | translate }}
994
+ </label>
995
+ <f-select
996
+ inputId="company"
997
+ class="w-full mb-4"
998
+ [options]="companies()"
999
+ optionLabel="name"
1000
+ optionValue="id"
1001
+ [formField]="selectionForm.companyId"
1002
+ [placeholder]="'auth.login.select.company.option' | translate"
1003
+ (onChange)="onCompanyChange()"
1004
+ />
1005
+
1006
+ <label
1007
+ for="branch"
1008
+ class="block text-lg font-medium mb-2 text-color"
1009
+ >
1010
+ {{ 'auth.login.branch.label' | translate }}
1011
+ </label>
1012
+ <f-treeselect
1013
+ inputId="branch"
1014
+ class="w-full mb-4"
1015
+ [options]="branchTreeNodes()"
1016
+ [formField]="selectionForm.branchId"
1017
+ [placeholder]="'auth.login.select.branch.option' | translate"
1018
+ [filter]="true"
1019
+ [filterPlaceholder]="'shared.search' | translate"
1020
+ [emptyMessage]="'shared.no.results' | translate"
1021
+ />
1022
+
1023
+ <div class="flex items-center mt-2 mb-8">
1024
+ <f-checkbox
1025
+ [formField]="selectionForm.rememberSelection"
1026
+ [label]="'auth.login.remember.selection' | translate"
1027
+ />
1028
+ </div>
1029
+
1030
+ <div class="flex flex-col sm:flex-row gap-4">
1031
+ <button
1032
+ fButton
1033
+ class="w-full"
1034
+ severity="secondary"
1035
+ [outlined]="true"
1036
+ type="button"
1037
+ (click)="goBackToCredentials()"
1038
+ >
1039
+ <span pButtonLabel>{{ 'shared.back' | translate }}</span>
1040
+ </button>
1041
+ <button
1042
+ fButton
1043
+ class="w-full"
1044
+ type="submit"
1045
+ [disabled]="!isSelectionFormValid() || isLoading()"
1046
+ >
1047
+ @if (isLoading()) {
1048
+ <ui-icon pButtonIcon name="spinner" [spin]="true"></ui-icon>
1049
+ }
1050
+ <span pButtonLabel>{{ 'shared.continue' | translate }}</span>
1051
+ </button>
1052
+ </div>
1053
+ </form>
1054
+ }
1055
+ </div>
1056
+ </div>
1057
+ </div>
1058
+ </div>
1059
+ `
1060
+ }]
1061
+ }],
1062
+ ctorParameters: () => [],
1063
+ propDecorators: { returnUrl: [{
1064
+ type: i0.Input,
1065
+ args: [{
1066
+ isSignal: true,
1067
+ alias: "returnUrl",
1068
+ required: false
1069
+ }]
1070
+ }] }
1071
+ });
1072
+ export { LoginPageComponent };
1073
+
1074
+ //# sourceMappingURL=flusys-ng-auth-login-page.component-DQl52Qau.mjs.map