@flusys/ng-auth 1.0.0-rc → 1.0.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.
- package/README.md +3 -4
- package/fesm2022/flusys-ng-auth.mjs +622 -1272
- package/fesm2022/flusys-ng-auth.mjs.map +1 -1
- package/package.json +12 -12
- package/types/flusys-ng-auth.d.ts +70 -667
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import * as i1 from '@angular/common/http';
|
|
2
1
|
import { HttpClient } from '@angular/common/http';
|
|
3
2
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, Injectable, signal, computed, effect, PLATFORM_ID, ChangeDetectionStrategy, Component, DestroyRef, input, output, makeStateKey, TransferState } from '@angular/core';
|
|
3
|
+
import { inject, Injectable, signal, computed, effect, PLATFORM_ID, ChangeDetectionStrategy, Component, untracked, DestroyRef, input, output, makeStateKey, TransferState } from '@angular/core';
|
|
5
4
|
import { APP_CONFIG, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, getPermissionMode } from '@flusys/ng-core';
|
|
6
5
|
import * as i2$1 from '@angular/router';
|
|
7
6
|
import { Router, RouterOutlet, RouterLink, RouterLinkActive, ActivatedRoute } from '@angular/router';
|
|
8
7
|
import { of, timeout, switchMap, map, tap, catchError, shareReplay, finalize, BehaviorSubject, throwError, filter, take, firstValueFrom } from 'rxjs';
|
|
9
|
-
import { ApiResourceService, CookieService, USER_PROVIDER, COMPANY_API_PROVIDER, USER_PERMISSION_PROVIDER, AUTH_STATE_PROVIDER, AngularModule, PrimeModule,
|
|
8
|
+
import { PlatformService, ApiResourceService, CookieService, permissionGuard, USER_PERMISSIONS, COMPANY_PERMISSIONS, BRANCH_PERMISSIONS, USER_PROVIDER, COMPANY_API_PROVIDER, USER_PERMISSION_PROVIDER, AUTH_STATE_PROVIDER, PermissionValidatorService, AngularModule, PrimeModule, HasPermissionDirective, FileUrlService, PROFILE_UPLOAD_PROVIDER, PROFILE_PERMISSION_PROVIDER, FileUploaderComponent } from '@flusys/ng-shared';
|
|
10
9
|
import { map as map$1, catchError as catchError$1, tap as tap$1 } from 'rxjs/operators';
|
|
11
10
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
12
11
|
import { MessageService, ConfirmationService } from 'primeng/api';
|
|
13
12
|
import { LAYOUT_AUTH_STATE, LAYOUT_AUTH_API, AppFloatingConfigurator } from '@flusys/ng-layout';
|
|
14
13
|
import { toSignal, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
15
14
|
import { form, required, FormField, email, minLength } from '@angular/forms/signals';
|
|
16
|
-
import * as i1
|
|
15
|
+
import * as i1 from '@angular/forms';
|
|
17
16
|
import * as i2 from 'primeng/button';
|
|
18
17
|
import * as i3 from 'primeng/card';
|
|
19
18
|
import * as i4 from 'primeng/inputtext';
|
|
@@ -31,42 +30,16 @@ import * as i5$2 from 'primeng/progressspinner';
|
|
|
31
30
|
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
|
32
31
|
import * as i3$2 from 'primeng/checkbox';
|
|
33
32
|
|
|
34
|
-
// AUTH INTERFACES - Matching NestJS Auth Package DTOs
|
|
35
|
-
/**
|
|
36
|
-
* Type guard to check if response requires email verification
|
|
37
|
-
*/
|
|
38
33
|
function isEmailVerificationRequired(response) {
|
|
39
34
|
return 'requiresEmailVerification' in response && response.requiresEmailVerification === true;
|
|
40
35
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Type guard to check if response requires company selection
|
|
43
|
-
*/
|
|
44
36
|
function isCompanySelectionResponse(response) {
|
|
45
37
|
return 'requiresSelection' in response && response.requiresSelection === true;
|
|
46
38
|
}
|
|
47
|
-
/**
|
|
48
|
-
* Type guard to check if response is a successful login
|
|
49
|
-
*/
|
|
50
39
|
function isLoginResponse(response) {
|
|
51
40
|
return 'accessToken' in response && !('requiresSelection' in response) && !('requiresEmailVerification' in response);
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
// =============================================================================
|
|
55
|
-
// BRANCH INTERFACES - Matching NestJS Auth Package DTOs
|
|
56
|
-
// =============================================================================
|
|
57
|
-
|
|
58
|
-
// =============================================================================
|
|
59
|
-
// COMPANY INTERFACES - Matching NestJS Auth Package DTOs
|
|
60
|
-
// =============================================================================
|
|
61
|
-
|
|
62
|
-
// =============================================================================
|
|
63
|
-
// USER PERMISSION INTERFACES - Matching NestJS Auth Package DTOs
|
|
64
|
-
// =============================================================================
|
|
65
|
-
|
|
66
|
-
// =============================================================================
|
|
67
|
-
// USER INTERFACES - Matching NestJS Auth Package DTOs
|
|
68
|
-
// =============================================================================
|
|
69
|
-
|
|
70
43
|
/**
|
|
71
44
|
* Auth API Service
|
|
72
45
|
* Handles all authentication-related API calls matching NestJS auth endpoints
|
|
@@ -170,10 +143,10 @@ class AuthApiService {
|
|
|
170
143
|
token,
|
|
171
144
|
});
|
|
172
145
|
}
|
|
173
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
174
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
146
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
147
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthApiService, providedIn: 'root' });
|
|
175
148
|
}
|
|
176
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
149
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthApiService, decorators: [{
|
|
177
150
|
type: Injectable,
|
|
178
151
|
args: [{ providedIn: 'root' }]
|
|
179
152
|
}], ctorParameters: () => [] });
|
|
@@ -196,6 +169,7 @@ class AuthStateService {
|
|
|
196
169
|
// DI Injection
|
|
197
170
|
appConfig = inject(APP_CONFIG);
|
|
198
171
|
router = inject(Router);
|
|
172
|
+
platform = inject(PlatformService);
|
|
199
173
|
// Core State (private writable, public readonly)
|
|
200
174
|
_user = signal(null, ...(ngDevMode ? [{ debugName: "_user" }] : []));
|
|
201
175
|
_accessToken = signal('', ...(ngDevMode ? [{ debugName: "_accessToken" }] : [])); // Memory-only, not persisted
|
|
@@ -213,7 +187,6 @@ class AuthStateService {
|
|
|
213
187
|
_selectionExpiresAt = signal(null, ...(ngDevMode ? [{ debugName: "_selectionExpiresAt" }] : []));
|
|
214
188
|
selectionSessionId = this._selectionSessionId.asReadonly();
|
|
215
189
|
availableCompanies = this._availableCompanies.asReadonly();
|
|
216
|
-
selectionExpiresAt = this._selectionExpiresAt.asReadonly();
|
|
217
190
|
// Computed States
|
|
218
191
|
isAuthenticated = computed(() => !!this._accessToken() && !!this._user(), ...(ngDevMode ? [{ debugName: "isAuthenticated" }] : []));
|
|
219
192
|
requiresCompanySelection = computed(() => !!this._selectionSessionId() && this._availableCompanies().length > 0, ...(ngDevMode ? [{ debugName: "requiresCompanySelection" }] : []));
|
|
@@ -231,15 +204,6 @@ class AuthStateService {
|
|
|
231
204
|
// Consider expired 30 seconds before actual expiry
|
|
232
205
|
return Date.now() >= expiresAt - 30000;
|
|
233
206
|
}, ...(ngDevMode ? [{ debugName: "isTokenExpired" }] : []));
|
|
234
|
-
/**
|
|
235
|
-
* Check if we need to refresh on app load (token expired but refresh cookie may exist)
|
|
236
|
-
*/
|
|
237
|
-
needsRefreshOnLoad = computed(() => {
|
|
238
|
-
const expiresAt = this._tokenExpiresAt();
|
|
239
|
-
const hasToken = !!this._accessToken();
|
|
240
|
-
// Need refresh if: we have stored expiry (was logged in) but no current token
|
|
241
|
-
return expiresAt !== null && !hasToken;
|
|
242
|
-
}, ...(ngDevMode ? [{ debugName: "needsRefreshOnLoad" }] : []));
|
|
243
207
|
// Config-derived states
|
|
244
208
|
tenantId = computed(() => this.appConfig.multiTenant.tenantId ?? null, ...(ngDevMode ? [{ debugName: "tenantId" }] : []));
|
|
245
209
|
constructor() {
|
|
@@ -342,7 +306,7 @@ class AuthStateService {
|
|
|
342
306
|
}
|
|
343
307
|
// Storage helpers
|
|
344
308
|
loadFromStorage() {
|
|
345
|
-
if (
|
|
309
|
+
if (this.platform.isServer)
|
|
346
310
|
return;
|
|
347
311
|
// Load token expiry (NOT the token - it's memory-only for security)
|
|
348
312
|
const expiryStr = this.get(STORAGE.TOKEN_EXPIRY);
|
|
@@ -372,20 +336,20 @@ class AuthStateService {
|
|
|
372
336
|
}
|
|
373
337
|
}
|
|
374
338
|
store(key, value) {
|
|
375
|
-
if (
|
|
339
|
+
if (!this.platform.isServer)
|
|
376
340
|
localStorage.setItem(key, value);
|
|
377
341
|
}
|
|
378
342
|
get(key) {
|
|
379
|
-
return
|
|
343
|
+
return !this.platform.isServer ? localStorage.getItem(key) : null;
|
|
380
344
|
}
|
|
381
345
|
remove(key) {
|
|
382
|
-
if (
|
|
346
|
+
if (!this.platform.isServer)
|
|
383
347
|
localStorage.removeItem(key);
|
|
384
348
|
}
|
|
385
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
386
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
349
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
350
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthStateService, providedIn: 'root' });
|
|
387
351
|
}
|
|
388
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
352
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthStateService, decorators: [{
|
|
389
353
|
type: Injectable,
|
|
390
354
|
args: [{ providedIn: 'root' }]
|
|
391
355
|
}], ctorParameters: () => [] });
|
|
@@ -492,10 +456,10 @@ class AuthInitService {
|
|
|
492
456
|
}));
|
|
493
457
|
return this.initializationCache$;
|
|
494
458
|
}
|
|
495
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
496
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
459
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthInitService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
460
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthInitService, providedIn: 'root' });
|
|
497
461
|
}
|
|
498
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
462
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthInitService, decorators: [{
|
|
499
463
|
type: Injectable,
|
|
500
464
|
args: [{ providedIn: 'root' }]
|
|
501
465
|
}] });
|
|
@@ -503,158 +467,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
503
467
|
/**
|
|
504
468
|
* Branch API Service
|
|
505
469
|
* Signal-based service for branch CRUD operations
|
|
506
|
-
* Branches default to current company context
|
|
507
|
-
*
|
|
508
|
-
* @example
|
|
509
|
-
* ```typescript
|
|
510
|
-
* branchService = inject(BranchApiService);
|
|
511
|
-
*
|
|
512
|
-
* // Set current company context
|
|
513
|
-
* branchService.setCurrentCompanyId('company-uuid');
|
|
514
|
-
*
|
|
515
|
-
* // Access data via signals
|
|
516
|
-
* branches = this.branchService.data;
|
|
517
|
-
* isLoading = this.branchService.isLoading;
|
|
518
|
-
*
|
|
519
|
-
* // Fetch branches (will filter by current company)
|
|
520
|
-
* this.branchService.fetchList('search');
|
|
521
|
-
*
|
|
522
|
-
* // Create branch (companyId will be auto-set if not provided)
|
|
523
|
-
* await this.branchService.insertBranch({ name: 'New Branch', slug: 'new-branch' });
|
|
524
|
-
* ```
|
|
525
470
|
*/
|
|
526
471
|
class BranchApiService extends ApiResourceService {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
companyBranches = computed(() => {
|
|
531
|
-
const companyId = this.currentCompanyId();
|
|
532
|
-
if (!companyId)
|
|
533
|
-
return this.data();
|
|
534
|
-
return this.data().filter((branch) => branch.companyId === companyId);
|
|
535
|
-
}, ...(ngDevMode ? [{ debugName: "companyBranches" }] : []));
|
|
536
|
-
constructor(http) {
|
|
537
|
-
super('administration/branch', http);
|
|
472
|
+
_currentCompanyId = signal(null, ...(ngDevMode ? [{ debugName: "_currentCompanyId" }] : []));
|
|
473
|
+
constructor() {
|
|
474
|
+
super('branch', inject(HttpClient), 'administration');
|
|
538
475
|
}
|
|
539
|
-
/**
|
|
540
|
-
* Set the current company context for branch operations
|
|
541
|
-
*/
|
|
542
476
|
setCurrentCompanyId(companyId) {
|
|
543
|
-
this.
|
|
477
|
+
this._currentCompanyId.set(companyId);
|
|
544
478
|
}
|
|
545
|
-
/**
|
|
546
|
-
* Insert a new branch, auto-setting companyId if not provided
|
|
547
|
-
*/
|
|
548
|
-
async insertBranch(data) {
|
|
549
|
-
const companyId = data.companyId ?? this.currentCompanyId();
|
|
550
|
-
if (!companyId) {
|
|
551
|
-
throw new Error('Company ID is required. Set currentCompanyId or provide companyId in data.');
|
|
552
|
-
}
|
|
553
|
-
return this.insertAsync({ ...data, companyId });
|
|
554
|
-
}
|
|
555
|
-
/**
|
|
556
|
-
* Fetch branches for a specific company
|
|
557
|
-
*/
|
|
558
479
|
fetchByCompany(companyId, search = '') {
|
|
559
480
|
this.setCurrentCompanyId(companyId);
|
|
560
|
-
this.fetchList(search, {
|
|
561
|
-
filter: { companyId },
|
|
562
|
-
});
|
|
481
|
+
this.fetchList(search, { filter: { companyId } });
|
|
563
482
|
}
|
|
564
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
565
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
483
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
484
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchApiService, providedIn: 'root' });
|
|
566
485
|
}
|
|
567
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
486
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchApiService, decorators: [{
|
|
568
487
|
type: Injectable,
|
|
569
488
|
args: [{ providedIn: 'root' }]
|
|
570
|
-
}], ctorParameters: () => [
|
|
489
|
+
}], ctorParameters: () => [] });
|
|
571
490
|
|
|
572
491
|
/**
|
|
573
492
|
* Company API Service
|
|
574
493
|
* Signal-based service for company CRUD operations
|
|
575
|
-
*
|
|
576
|
-
* @example
|
|
577
|
-
* ```typescript
|
|
578
|
-
* companyService = inject(CompanyApiService);
|
|
579
|
-
*
|
|
580
|
-
* // Access data via signals
|
|
581
|
-
* companies = this.companyService.data;
|
|
582
|
-
* isLoading = this.companyService.isLoading;
|
|
583
|
-
*
|
|
584
|
-
* // Fetch companies
|
|
585
|
-
* this.companyService.fetchList('search', { pagination: { currentPage: 0, pageSize: 10 } });
|
|
586
|
-
*
|
|
587
|
-
* // Create company
|
|
588
|
-
* await this.companyService.insertAsync({ name: 'New Company', slug: 'new-company' });
|
|
589
|
-
* ```
|
|
590
494
|
*/
|
|
591
495
|
class CompanyApiService extends ApiResourceService {
|
|
592
|
-
constructor(
|
|
593
|
-
super('
|
|
496
|
+
constructor() {
|
|
497
|
+
super('company', inject(HttpClient), 'administration');
|
|
594
498
|
}
|
|
595
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
596
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
499
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
500
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyApiService, providedIn: 'root' });
|
|
597
501
|
}
|
|
598
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
502
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyApiService, decorators: [{
|
|
599
503
|
type: Injectable,
|
|
600
504
|
args: [{ providedIn: 'root' }]
|
|
601
|
-
}], ctorParameters: () => [
|
|
505
|
+
}], ctorParameters: () => [] });
|
|
602
506
|
|
|
603
507
|
/**
|
|
604
508
|
* Token Refresh State Service
|
|
605
|
-
*
|
|
606
|
-
*
|
|
607
|
-
* Replaces module-level state that would be shared across requests in SSR.
|
|
608
|
-
*
|
|
609
|
-
* NOTE: Uses BehaviorSubject intentionally for RxJS interoperability.
|
|
610
|
-
* The HTTP interceptor needs to queue concurrent requests and have them
|
|
611
|
-
* wait for the new token via Observable subscription. This is a valid
|
|
612
|
-
* pattern that signals cannot replicate directly (no multi-subscriber
|
|
613
|
-
* async coordination). Signal is used for the boolean flag only.
|
|
614
|
-
*
|
|
615
|
-
* @example
|
|
616
|
-
* ```typescript
|
|
617
|
-
* const refreshState = inject(TokenRefreshStateService);
|
|
618
|
-
*
|
|
619
|
-
* if (refreshState.isRefreshing()) {
|
|
620
|
-
* // Queue request via refreshTokenSubject$
|
|
621
|
-
* } else {
|
|
622
|
-
* refreshState.startRefresh();
|
|
623
|
-
* // Perform refresh
|
|
624
|
-
* }
|
|
625
|
-
* ```
|
|
509
|
+
* Manages token refresh state for SSR-safe request queuing.
|
|
510
|
+
* Uses BehaviorSubject for RxJS interop (multi-subscriber async coordination).
|
|
626
511
|
*/
|
|
627
512
|
class TokenRefreshStateService {
|
|
628
|
-
/** Flag to track if token refresh is in progress */
|
|
629
513
|
_isRefreshing = signal(false, ...(ngDevMode ? [{ debugName: "_isRefreshing" }] : []));
|
|
630
514
|
isRefreshing = this._isRefreshing.asReadonly();
|
|
631
|
-
/** Subject to queue requests while refreshing */
|
|
632
515
|
_refreshTokenSubject = new BehaviorSubject(null);
|
|
633
516
|
refreshTokenSubject$ = this._refreshTokenSubject.asObservable();
|
|
634
|
-
/**
|
|
635
|
-
* Mark refresh as started
|
|
636
|
-
*/
|
|
637
517
|
startRefresh() {
|
|
638
518
|
this._isRefreshing.set(true);
|
|
639
519
|
this._refreshTokenSubject.next(null);
|
|
640
520
|
}
|
|
641
|
-
/**
|
|
642
|
-
* Mark refresh as completed and broadcast new token
|
|
643
|
-
*/
|
|
644
521
|
completeRefresh(newToken) {
|
|
645
522
|
this._isRefreshing.set(false);
|
|
646
523
|
this._refreshTokenSubject.next(newToken);
|
|
647
524
|
}
|
|
648
|
-
/**
|
|
649
|
-
* Mark refresh as failed
|
|
650
|
-
*/
|
|
651
525
|
failRefresh() {
|
|
652
526
|
this._isRefreshing.set(false);
|
|
653
527
|
}
|
|
654
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
655
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
528
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TokenRefreshStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
529
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TokenRefreshStateService, providedIn: 'root' });
|
|
656
530
|
}
|
|
657
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
531
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: TokenRefreshStateService, decorators: [{
|
|
658
532
|
type: Injectable,
|
|
659
533
|
args: [{ providedIn: 'root' }]
|
|
660
534
|
}] });
|
|
@@ -662,372 +536,140 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
662
536
|
/**
|
|
663
537
|
* User API Service
|
|
664
538
|
* Signal-based service for user CRUD operations
|
|
665
|
-
*
|
|
666
|
-
* @example
|
|
667
|
-
* ```typescript
|
|
668
|
-
* userService = inject(UserApiService);
|
|
669
|
-
*
|
|
670
|
-
* // Access data via signals
|
|
671
|
-
* users = this.userService.data;
|
|
672
|
-
* isLoading = this.userService.isLoading;
|
|
673
|
-
*
|
|
674
|
-
* // Fetch users
|
|
675
|
-
* this.userService.fetchList('search', { pagination: { currentPage: 0, pageSize: 10 } });
|
|
676
|
-
*
|
|
677
|
-
* // Create user
|
|
678
|
-
* await this.userService.insertAsync({
|
|
679
|
-
* name: 'John Doe',
|
|
680
|
-
* email: 'john@example.com',
|
|
681
|
-
* password: 'password123'
|
|
682
|
-
* });
|
|
683
|
-
* ```
|
|
684
539
|
*/
|
|
685
540
|
class UserApiService extends ApiResourceService {
|
|
686
|
-
constructor(
|
|
687
|
-
super('
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* Verify user's email
|
|
691
|
-
*/
|
|
692
|
-
verifyEmail(userId) {
|
|
693
|
-
return this.http.post(`${this.baseUrl}/${userId}/verify-email`, {});
|
|
694
|
-
}
|
|
695
|
-
/**
|
|
696
|
-
* Verify user's phone
|
|
697
|
-
*/
|
|
698
|
-
verifyPhone(userId) {
|
|
699
|
-
return this.http.post(`${this.baseUrl}/${userId}/verify-phone`, {});
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Update user status (active/inactive)
|
|
703
|
-
*/
|
|
704
|
-
updateStatus(userId, isActive) {
|
|
705
|
-
return this.http.post(`${this.baseUrl}/${userId}/status`, { isActive });
|
|
541
|
+
constructor() {
|
|
542
|
+
super('users', inject(HttpClient), 'administration');
|
|
706
543
|
}
|
|
707
|
-
/**
|
|
708
|
-
* Update current user's profile
|
|
709
|
-
*/
|
|
710
544
|
updateProfile(data) {
|
|
711
545
|
return this.http.post(`${this.baseUrl}/profile`, data);
|
|
712
546
|
}
|
|
713
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
714
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
547
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
548
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserApiService, providedIn: 'root' });
|
|
715
549
|
}
|
|
716
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
550
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserApiService, decorators: [{
|
|
717
551
|
type: Injectable,
|
|
718
552
|
args: [{ providedIn: 'root' }]
|
|
719
|
-
}], ctorParameters: () => [
|
|
553
|
+
}], ctorParameters: () => [] });
|
|
720
554
|
|
|
721
555
|
/**
|
|
722
556
|
* User Permission API Service
|
|
723
|
-
*
|
|
724
557
|
* Handles user-company and user-branch permission assignments.
|
|
725
|
-
*
|
|
726
|
-
*
|
|
727
|
-
* Backend Endpoints (POST-only RPC):
|
|
728
|
-
* - POST /administration/permissions/user-company/assign - Batch assign/revoke company permissions
|
|
729
|
-
* - POST /administration/permissions/get-user-company - Get user's companies
|
|
730
|
-
* - POST /administration/permissions/user-branch/assign - Batch assign/revoke branch permissions
|
|
731
|
-
* - POST /administration/permissions/get-user-branch - Get user's branches
|
|
558
|
+
* Endpoint: POST /administration/permissions/*
|
|
732
559
|
*/
|
|
733
560
|
class UserPermissionApiService {
|
|
734
561
|
appConfig = inject(APP_CONFIG);
|
|
735
562
|
http = inject(HttpClient);
|
|
736
|
-
baseUrl
|
|
737
|
-
|
|
738
|
-
this.baseUrl = this.appConfig.apiBaseUrl + '/administration/permissions';
|
|
739
|
-
}
|
|
740
|
-
// ============================================
|
|
741
|
-
// Company Permission Endpoints
|
|
742
|
-
// ============================================
|
|
743
|
-
/**
|
|
744
|
-
* Batch assign/revoke user-company permissions
|
|
745
|
-
*
|
|
746
|
-
* @example
|
|
747
|
-
* // Add company permissions
|
|
748
|
-
* service.assignUserCompanies({
|
|
749
|
-
* userId: 'user-id',
|
|
750
|
-
* items: [
|
|
751
|
-
* { targetId: 'company-1', isAdd: true },
|
|
752
|
-
* { targetId: 'company-2', isAdd: true }
|
|
753
|
-
* ]
|
|
754
|
-
* });
|
|
755
|
-
*
|
|
756
|
-
* @example
|
|
757
|
-
* // Remove company permissions
|
|
758
|
-
* service.assignUserCompanies({
|
|
759
|
-
* userId: 'user-id',
|
|
760
|
-
* items: [
|
|
761
|
-
* { targetId: 'company-1', isAdd: false }
|
|
762
|
-
* ]
|
|
763
|
-
* });
|
|
764
|
-
*/
|
|
563
|
+
baseUrl = `${getServiceUrl(this.appConfig, 'administration')}/permissions`;
|
|
564
|
+
// Company Permissions
|
|
765
565
|
assignUserCompanies(data) {
|
|
766
566
|
return this.http.post(`${this.baseUrl}/user-company/assign`, data);
|
|
767
567
|
}
|
|
768
|
-
/**
|
|
769
|
-
* Assign single company to user (convenience method)
|
|
770
|
-
*/
|
|
771
568
|
assignUserToCompany(userId, companyId) {
|
|
772
|
-
return this.assignUserCompanies({
|
|
773
|
-
userId,
|
|
774
|
-
items: [{ targetId: companyId, isAdd: true }],
|
|
775
|
-
});
|
|
569
|
+
return this.assignUserCompanies({ userId, items: [{ targetId: companyId, isAdd: true }] });
|
|
776
570
|
}
|
|
777
|
-
/**
|
|
778
|
-
* Revoke single company from user (convenience method)
|
|
779
|
-
*/
|
|
780
571
|
revokeUserFromCompany(userId, companyId) {
|
|
781
|
-
return this.assignUserCompanies({
|
|
782
|
-
userId,
|
|
783
|
-
items: [{ targetId: companyId, isAdd: false }],
|
|
784
|
-
});
|
|
572
|
+
return this.assignUserCompanies({ userId, items: [{ targetId: companyId, isAdd: false }] });
|
|
785
573
|
}
|
|
786
|
-
/**
|
|
787
|
-
* Get user's company permissions
|
|
788
|
-
*
|
|
789
|
-
* @param userId - User ID to get companies for
|
|
790
|
-
* @returns Observable with user's company permissions
|
|
791
|
-
*/
|
|
792
574
|
getUserCompanies(userId) {
|
|
793
575
|
return this.http.post(`${this.baseUrl}/get-user-company`, { userId });
|
|
794
576
|
}
|
|
795
|
-
//
|
|
796
|
-
// Branch Permission Endpoints
|
|
797
|
-
// ============================================
|
|
798
|
-
/**
|
|
799
|
-
* Batch assign/revoke user-branch permissions
|
|
800
|
-
*
|
|
801
|
-
* @example
|
|
802
|
-
* // Add branch permissions
|
|
803
|
-
* service.assignUserBranches({
|
|
804
|
-
* userId: 'user-id',
|
|
805
|
-
* items: [
|
|
806
|
-
* { targetId: 'branch-1', isAdd: true },
|
|
807
|
-
* { targetId: 'branch-2', isAdd: true }
|
|
808
|
-
* ]
|
|
809
|
-
* });
|
|
810
|
-
*
|
|
811
|
-
* @example
|
|
812
|
-
* // Remove branch permissions
|
|
813
|
-
* service.assignUserBranches({
|
|
814
|
-
* userId: 'user-id',
|
|
815
|
-
* items: [
|
|
816
|
-
* { targetId: 'branch-1', isAdd: false }
|
|
817
|
-
* ]
|
|
818
|
-
* });
|
|
819
|
-
*/
|
|
577
|
+
// Branch Permissions
|
|
820
578
|
assignUserBranches(data) {
|
|
821
579
|
return this.http.post(`${this.baseUrl}/user-branch/assign`, data);
|
|
822
580
|
}
|
|
823
|
-
/**
|
|
824
|
-
* Assign single branch to user (convenience method)
|
|
825
|
-
*/
|
|
826
581
|
assignUserToBranch(userId, branchId) {
|
|
827
|
-
return this.assignUserBranches({
|
|
828
|
-
userId,
|
|
829
|
-
items: [{ targetId: branchId, isAdd: true }],
|
|
830
|
-
});
|
|
582
|
+
return this.assignUserBranches({ userId, items: [{ targetId: branchId, isAdd: true }] });
|
|
831
583
|
}
|
|
832
|
-
/**
|
|
833
|
-
* Revoke single branch from user (convenience method)
|
|
834
|
-
*/
|
|
835
584
|
revokeUserFromBranch(userId, branchId) {
|
|
836
|
-
return this.assignUserBranches({
|
|
585
|
+
return this.assignUserBranches({ userId, items: [{ targetId: branchId, isAdd: false }] });
|
|
586
|
+
}
|
|
587
|
+
getUserBranches(userId, companyId) {
|
|
588
|
+
return this.http.post(`${this.baseUrl}/get-user-branch`, {
|
|
837
589
|
userId,
|
|
838
|
-
|
|
590
|
+
...(companyId && { companyId }),
|
|
839
591
|
});
|
|
840
592
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
*
|
|
844
|
-
* @param userId - User ID to get branches for
|
|
845
|
-
* @returns Observable with user's branch permissions
|
|
846
|
-
*/
|
|
847
|
-
getUserBranches(userId) {
|
|
848
|
-
return this.http.post(`${this.baseUrl}/get-user-branch`, { userId });
|
|
849
|
-
}
|
|
850
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserPermissionApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
851
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserPermissionApiService, providedIn: 'root' });
|
|
593
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserPermissionApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
594
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserPermissionApiService, providedIn: 'root' });
|
|
852
595
|
}
|
|
853
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
596
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserPermissionApiService, decorators: [{
|
|
854
597
|
type: Injectable,
|
|
855
598
|
args: [{ providedIn: 'root' }]
|
|
856
|
-
}]
|
|
599
|
+
}] });
|
|
857
600
|
|
|
858
601
|
/**
|
|
859
602
|
* Authentication API Endpoints
|
|
860
603
|
*
|
|
861
604
|
* Centralized endpoint definitions to prevent inconsistencies across interceptors
|
|
862
605
|
* and services. All auth-related endpoints should be referenced from here.
|
|
863
|
-
*
|
|
864
|
-
* @example
|
|
865
|
-
* ```typescript
|
|
866
|
-
* if (isAuthEndpoint(req.url)) {
|
|
867
|
-
* // Skip interceptor logic
|
|
868
|
-
* }
|
|
869
|
-
* ```
|
|
870
606
|
*/
|
|
871
607
|
const AUTH_ENDPOINTS = {
|
|
872
608
|
LOGIN: '/auth/login',
|
|
873
609
|
LOGOUT: '/auth/logout',
|
|
874
610
|
REFRESH: '/auth/refresh',
|
|
875
611
|
REGISTER: '/auth/register',
|
|
612
|
+
ME: '/auth/me',
|
|
876
613
|
SELECT: '/auth/select',
|
|
877
614
|
SWITCH_COMPANY: '/auth/switch-company',
|
|
615
|
+
CHANGE_PASSWORD: '/auth/change-password',
|
|
878
616
|
VERIFY_EMAIL: '/auth/verify-email',
|
|
879
617
|
FORGOT_PASSWORD: '/auth/forgot-password',
|
|
880
618
|
RESET_PASSWORD: '/auth/reset-password',
|
|
881
619
|
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
882
620
|
};
|
|
883
|
-
/**
|
|
884
|
-
* Check if a URL is an auth-related endpoint
|
|
885
|
-
*/
|
|
886
|
-
function isAuthEndpoint(url) {
|
|
887
|
-
return Object.values(AUTH_ENDPOINTS).some((endpoint) => url.includes(endpoint));
|
|
888
|
-
}
|
|
889
|
-
/**
|
|
890
|
-
* Check if a URL is a specific auth endpoint
|
|
891
|
-
*/
|
|
621
|
+
/** Check if a URL matches a specific endpoint */
|
|
892
622
|
function isEndpoint(url, endpoint) {
|
|
893
623
|
return url.includes(endpoint);
|
|
894
624
|
}
|
|
895
625
|
|
|
896
|
-
/**
|
|
897
|
-
* Validates a return URL to prevent open redirect attacks.
|
|
898
|
-
* Only allows relative URLs that start with '/' and don't contain protocol.
|
|
899
|
-
*/
|
|
900
626
|
function isValidReturnUrl(url) {
|
|
901
627
|
if (typeof url !== 'string' || !url)
|
|
902
628
|
return false;
|
|
903
|
-
// Must start with '/' and not contain protocol indicators
|
|
904
629
|
return url.startsWith('/') && !url.includes('://') && !url.startsWith('//');
|
|
905
630
|
}
|
|
906
|
-
/**
|
|
907
|
-
* Auth Guard
|
|
908
|
-
* Protects routes that require authentication.
|
|
909
|
-
*
|
|
910
|
-
* Session restoration is handled by guestGuard. This guard assumes
|
|
911
|
-
* the session has already been restored if user accessed login page first.
|
|
912
|
-
*
|
|
913
|
-
* Handles company feature:
|
|
914
|
-
* - If company feature enabled and no company selected, redirects to login
|
|
915
|
-
* (login page handles company selection as step 2)
|
|
916
|
-
* - If not authenticated, redirects to login
|
|
917
|
-
*
|
|
918
|
-
* @example
|
|
919
|
-
* ```typescript
|
|
920
|
-
* // In routes configuration
|
|
921
|
-
* {
|
|
922
|
-
* path: 'dashboard',
|
|
923
|
-
* loadComponent: () => import('./dashboard.component'),
|
|
924
|
-
* canActivate: [authGuard]
|
|
925
|
-
* }
|
|
926
|
-
* ```
|
|
927
|
-
*/
|
|
631
|
+
/** Protects routes requiring authentication */
|
|
928
632
|
const authGuard = (route, state) => {
|
|
929
633
|
const authState = inject(AuthStateService);
|
|
930
634
|
const router = inject(Router);
|
|
931
635
|
const appConfig = inject(APP_CONFIG);
|
|
932
|
-
// Check if user is authenticated
|
|
933
636
|
if (!authState.isAuthenticated()) {
|
|
934
|
-
|
|
935
|
-
return router.createUrlTree([AUTH_ENDPOINTS.LOGIN], {
|
|
936
|
-
queryParams: { returnUrl: state.url },
|
|
937
|
-
});
|
|
637
|
+
return router.createUrlTree([AUTH_ENDPOINTS.LOGIN], { queryParams: { returnUrl: state.url } });
|
|
938
638
|
}
|
|
939
|
-
// Check if company feature is enabled and company selection is required
|
|
940
639
|
if (appConfig.enableCompanyFeature) {
|
|
941
|
-
// If user needs to select company or no company selected, redirect to login
|
|
942
|
-
// Login page handles company selection as step 2
|
|
943
640
|
if (authState.requiresCompanySelection() || !authState.company()) {
|
|
944
|
-
return router.createUrlTree([AUTH_ENDPOINTS.LOGIN], {
|
|
945
|
-
queryParams: { returnUrl: state.url },
|
|
946
|
-
});
|
|
641
|
+
return router.createUrlTree([AUTH_ENDPOINTS.LOGIN], { queryParams: { returnUrl: state.url } });
|
|
947
642
|
}
|
|
948
643
|
}
|
|
949
644
|
return true;
|
|
950
645
|
};
|
|
951
|
-
/**
|
|
952
|
-
* Guest Guard
|
|
953
|
-
* Protects auth routes (login, register) from authenticated users.
|
|
954
|
-
* Redirects authenticated users to home page.
|
|
955
|
-
*
|
|
956
|
-
* CRITICAL: Unlike old implementation, this guard RESTORES the session first
|
|
957
|
-
* before checking authentication. This handles the case when users directly
|
|
958
|
-
* navigate to /auth/login via browser URL while having valid refresh token.
|
|
959
|
-
*
|
|
960
|
-
* Flow:
|
|
961
|
-
* 1. Restore session (if needed) via AuthInitService.initialize()
|
|
962
|
-
* 2. Check if user is authenticated
|
|
963
|
-
* 3. If authenticated -> redirect to dashboard
|
|
964
|
-
* 4. If not authenticated -> allow access to login page
|
|
965
|
-
*
|
|
966
|
-
* @example
|
|
967
|
-
* ```typescript
|
|
968
|
-
* // In routes configuration
|
|
969
|
-
* {
|
|
970
|
-
* path: 'auth/login',
|
|
971
|
-
* loadComponent: () => import('./login.component'),
|
|
972
|
-
* canActivate: [guestGuard]
|
|
973
|
-
* }
|
|
974
|
-
* ```
|
|
975
|
-
*/
|
|
646
|
+
/** Protects auth routes from authenticated users, restores session first */
|
|
976
647
|
const guestGuard = (route) => {
|
|
977
648
|
const authState = inject(AuthStateService);
|
|
978
649
|
const authInit = inject(AuthInitService);
|
|
979
650
|
const router = inject(Router);
|
|
980
651
|
const appConfig = inject(APP_CONFIG);
|
|
981
|
-
// Helper to check and redirect authenticated users
|
|
982
652
|
const checkAndRedirect = () => {
|
|
983
|
-
|
|
984
|
-
if (authState.requiresCompanySelection()) {
|
|
653
|
+
if (authState.requiresCompanySelection())
|
|
985
654
|
return true;
|
|
986
|
-
}
|
|
987
|
-
// If authenticated, check if company selection is needed
|
|
988
655
|
if (authState.isAuthenticated()) {
|
|
989
|
-
|
|
990
|
-
if (appConfig.enableCompanyFeature && !authState.company()) {
|
|
656
|
+
if (appConfig.enableCompanyFeature && !authState.company())
|
|
991
657
|
return true;
|
|
992
|
-
}
|
|
993
|
-
// Redirect to home or returnUrl (with open redirect protection)
|
|
994
658
|
const returnUrl = route.queryParams['returnUrl'];
|
|
995
|
-
|
|
996
|
-
return router.createUrlTree([safeUrl]);
|
|
659
|
+
return router.createUrlTree([isValidReturnUrl(returnUrl) ? returnUrl : '/']);
|
|
997
660
|
}
|
|
998
661
|
return true;
|
|
999
662
|
};
|
|
1000
|
-
|
|
1001
|
-
if (authInit.initialized()) {
|
|
663
|
+
if (authInit.initialized())
|
|
1002
664
|
return checkAndRedirect();
|
|
1003
|
-
}
|
|
1004
|
-
// Otherwise, restore session first
|
|
1005
665
|
return authInit.initialize().pipe(map$1(() => checkAndRedirect()), catchError$1(() => of(true)));
|
|
1006
666
|
};
|
|
1007
|
-
/**
|
|
1008
|
-
* Company Feature Guard
|
|
1009
|
-
* Protects routes that require company feature to be enabled.
|
|
1010
|
-
* Redirects to home if company feature is disabled.
|
|
1011
|
-
*
|
|
1012
|
-
* Use this guard on company/branch management routes.
|
|
1013
|
-
*
|
|
1014
|
-
* @example
|
|
1015
|
-
* ```typescript
|
|
1016
|
-
* // In routes configuration
|
|
1017
|
-
* {
|
|
1018
|
-
* path: 'administration/company',
|
|
1019
|
-
* loadComponent: () => import('./company-list.component'),
|
|
1020
|
-
* canActivate: [authGuard, companyFeatureGuard]
|
|
1021
|
-
* }
|
|
1022
|
-
* ```
|
|
1023
|
-
*/
|
|
667
|
+
/** Protects routes requiring company feature to be enabled */
|
|
1024
668
|
const companyFeatureGuard = () => {
|
|
1025
669
|
const router = inject(Router);
|
|
1026
670
|
const appConfig = inject(APP_CONFIG);
|
|
1027
|
-
|
|
1028
|
-
if (!appConfig.enableCompanyFeature) {
|
|
671
|
+
if (!appConfig.enableCompanyFeature)
|
|
1029
672
|
return router.createUrlTree(['/']);
|
|
1030
|
-
}
|
|
1031
673
|
return true;
|
|
1032
674
|
};
|
|
1033
675
|
|
|
@@ -1073,34 +715,7 @@ function authInterceptor(req, next) {
|
|
|
1073
715
|
return next(req);
|
|
1074
716
|
}
|
|
1075
717
|
|
|
1076
|
-
/**
|
|
1077
|
-
* Token Refresh Interceptor
|
|
1078
|
-
* Handles token refresh both proactively and reactively.
|
|
1079
|
-
*
|
|
1080
|
-
* PROACTIVE REFRESH:
|
|
1081
|
-
* - Checks token expiry BEFORE making requests
|
|
1082
|
-
* - Refreshes token if expired or expiring soon (within 30 seconds)
|
|
1083
|
-
* - Prevents unnecessary 401 failures
|
|
1084
|
-
*
|
|
1085
|
-
* REACTIVE REFRESH:
|
|
1086
|
-
* - Still handles 401 errors as fallback
|
|
1087
|
-
* - Queues concurrent requests during refresh
|
|
1088
|
-
* - Redirects to login on refresh failure
|
|
1089
|
-
*
|
|
1090
|
-
* SECURITY:
|
|
1091
|
-
* - Token kept in memory only (not localStorage)
|
|
1092
|
-
* - Uses HTTP-only refresh token cookie
|
|
1093
|
-
* - SSR-safe: Uses injectable service
|
|
1094
|
-
*
|
|
1095
|
-
* @example
|
|
1096
|
-
* ```typescript
|
|
1097
|
-
* // In app.config.ts
|
|
1098
|
-
* provideHttpClient(
|
|
1099
|
-
* withFetch(),
|
|
1100
|
-
* withInterceptors([authInterceptor, tokenRefreshInterceptor])
|
|
1101
|
-
* )
|
|
1102
|
-
* ```
|
|
1103
|
-
*/
|
|
718
|
+
/** Token Refresh Interceptor - Proactive and reactive token refresh with request queuing */
|
|
1104
719
|
function tokenRefreshInterceptor(req, next) {
|
|
1105
720
|
const authState = inject(AuthStateService);
|
|
1106
721
|
const authApi = inject(AuthApiService);
|
|
@@ -1132,48 +747,25 @@ function tokenRefreshInterceptor(req, next) {
|
|
|
1132
747
|
return handleTokenRefresh(req, next, authState, authApi, messageService, isBrowser, refreshState);
|
|
1133
748
|
}));
|
|
1134
749
|
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Handle token refresh - either proactively or after 401
|
|
1137
|
-
*/
|
|
1138
750
|
function handleTokenRefresh(req, next, authState, authApi, messageService, isBrowser, refreshState) {
|
|
1139
|
-
// If already refreshing, queue this request
|
|
1140
751
|
if (refreshState.isRefreshing()) {
|
|
1141
|
-
return refreshState.refreshTokenSubject$.pipe(filter((token) => token !== null), take(1), switchMap((token) => {
|
|
1142
|
-
return next(req.clone({
|
|
1143
|
-
setHeaders: { Authorization: `Bearer ${token}` },
|
|
1144
|
-
}));
|
|
1145
|
-
}));
|
|
752
|
+
return refreshState.refreshTokenSubject$.pipe(filter((token) => token !== null), take(1), switchMap((token) => next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }))));
|
|
1146
753
|
}
|
|
1147
|
-
// Start refresh process
|
|
1148
754
|
refreshState.startRefresh();
|
|
1149
755
|
return authApi.refresh().pipe(switchMap((response) => {
|
|
1150
756
|
const newToken = response.accessToken;
|
|
1151
|
-
// Update auth state (memory-only token + decoded expiry)
|
|
1152
757
|
authState.setAccessToken(newToken);
|
|
1153
|
-
// Complete refresh and notify queued requests
|
|
1154
758
|
refreshState.completeRefresh(newToken);
|
|
1155
|
-
|
|
1156
|
-
return next(req.clone({
|
|
1157
|
-
setHeaders: { Authorization: `Bearer ${newToken}` },
|
|
1158
|
-
}));
|
|
759
|
+
return next(req.clone({ setHeaders: { Authorization: `Bearer ${newToken}` } }));
|
|
1159
760
|
}), catchError((refreshError) => {
|
|
1160
761
|
refreshState.failRefresh();
|
|
1161
762
|
return handleRefreshFailure(authState, messageService, isBrowser, refreshError);
|
|
1162
763
|
}));
|
|
1163
764
|
}
|
|
1164
|
-
/**
|
|
1165
|
-
* Handle refresh token failure - logout and redirect
|
|
1166
|
-
*/
|
|
1167
765
|
function handleRefreshFailure(authState, messageService, isBrowser, error) {
|
|
1168
|
-
// Show message
|
|
1169
766
|
if (isBrowser) {
|
|
1170
|
-
messageService.add({
|
|
1171
|
-
severity: 'error',
|
|
1172
|
-
summary: 'Session Expired',
|
|
1173
|
-
detail: 'Please log in again.',
|
|
1174
|
-
});
|
|
767
|
+
messageService.add({ severity: 'error', summary: 'Session Expired', detail: 'Please log in again.' });
|
|
1175
768
|
}
|
|
1176
|
-
// Reset auth state and redirect
|
|
1177
769
|
authState.resetState();
|
|
1178
770
|
authState.navigateToLogin();
|
|
1179
771
|
return throwError(() => error);
|
|
@@ -1229,13 +821,13 @@ const AUTH_ROUTES = [
|
|
|
1229
821
|
const ADMINISTRATION_ROUTES = [
|
|
1230
822
|
{
|
|
1231
823
|
path: '',
|
|
1232
|
-
// canActivate: [authGuard, permissionGuard('admin-check')],
|
|
1233
824
|
canActivate: [authGuard],
|
|
1234
825
|
loadComponent: () => Promise.resolve().then(function () { return administrationPage_component; }).then((m) => m.AdministrationPageComponent),
|
|
1235
826
|
children: [
|
|
1236
|
-
// Users tab
|
|
827
|
+
// Users tab
|
|
1237
828
|
{
|
|
1238
829
|
path: 'users',
|
|
830
|
+
canActivate: [permissionGuard(USER_PERMISSIONS.READ)],
|
|
1239
831
|
children: [
|
|
1240
832
|
{
|
|
1241
833
|
path: '',
|
|
@@ -1254,7 +846,7 @@ const ADMINISTRATION_ROUTES = [
|
|
|
1254
846
|
// Company tab (requires company feature)
|
|
1255
847
|
{
|
|
1256
848
|
path: 'company',
|
|
1257
|
-
canActivate: [companyFeatureGuard],
|
|
849
|
+
canActivate: [companyFeatureGuard, permissionGuard(COMPANY_PERMISSIONS.READ)],
|
|
1258
850
|
children: [
|
|
1259
851
|
{
|
|
1260
852
|
path: '',
|
|
@@ -1273,7 +865,7 @@ const ADMINISTRATION_ROUTES = [
|
|
|
1273
865
|
// Branch tab (requires company feature)
|
|
1274
866
|
{
|
|
1275
867
|
path: 'branch',
|
|
1276
|
-
canActivate: [companyFeatureGuard],
|
|
868
|
+
canActivate: [companyFeatureGuard, permissionGuard(BRANCH_PERMISSIONS.READ)],
|
|
1277
869
|
children: [
|
|
1278
870
|
{
|
|
1279
871
|
path: '',
|
|
@@ -1311,16 +903,21 @@ const PROFILE_ROUTES = [
|
|
|
1311
903
|
];
|
|
1312
904
|
|
|
1313
905
|
/**
|
|
1314
|
-
* Auth Layout
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
906
|
+
* Auth Layout Provider Adapters
|
|
907
|
+
*
|
|
908
|
+
* Implements provider interfaces from ng-layout using ng-auth services.
|
|
909
|
+
* Bridges auth state and API to layout components.
|
|
910
|
+
*
|
|
911
|
+
* @example
|
|
912
|
+
* // In app.config.ts
|
|
913
|
+
* import { provideAuthLayoutIntegration } from '@flusys/ng-auth';
|
|
914
|
+
* providers: [...provideAuthLayoutIntegration()]
|
|
1317
915
|
*/
|
|
916
|
+
// =============================================================================
|
|
917
|
+
// Layout State Adapter
|
|
918
|
+
// =============================================================================
|
|
1318
919
|
class AuthLayoutStateAdapter {
|
|
1319
920
|
authState = inject(AuthStateService);
|
|
1320
|
-
/**
|
|
1321
|
-
* Maps company info to company info for layout.
|
|
1322
|
-
* Falls back to app name from config when no company is selected.
|
|
1323
|
-
*/
|
|
1324
921
|
currentCompanyInfo = computed(() => {
|
|
1325
922
|
const company = this.authState.company();
|
|
1326
923
|
if (company) {
|
|
@@ -1333,10 +930,6 @@ class AuthLayoutStateAdapter {
|
|
|
1333
930
|
}
|
|
1334
931
|
return null;
|
|
1335
932
|
}, ...(ngDevMode ? [{ debugName: "currentCompanyInfo" }] : []));
|
|
1336
|
-
/**
|
|
1337
|
-
* Maps branch info to branch info for layout.
|
|
1338
|
-
* Returns null when no branch is selected.
|
|
1339
|
-
*/
|
|
1340
933
|
currentBranchInfo = computed(() => {
|
|
1341
934
|
const branch = this.authState.branch();
|
|
1342
935
|
if (branch) {
|
|
@@ -1349,10 +942,6 @@ class AuthLayoutStateAdapter {
|
|
|
1349
942
|
}
|
|
1350
943
|
return null;
|
|
1351
944
|
}, ...(ngDevMode ? [{ debugName: "currentBranchInfo" }] : []));
|
|
1352
|
-
/**
|
|
1353
|
-
* Maps user info from auth state to layout user info.
|
|
1354
|
-
* Profile picture URL is managed separately by AuthLayoutSyncService via FileUrlService.
|
|
1355
|
-
*/
|
|
1356
945
|
loginUserData = computed(() => {
|
|
1357
946
|
const user = this.authState.user();
|
|
1358
947
|
if (user) {
|
|
@@ -1364,37 +953,26 @@ class AuthLayoutStateAdapter {
|
|
|
1364
953
|
}
|
|
1365
954
|
return null;
|
|
1366
955
|
}, ...(ngDevMode ? [{ debugName: "loginUserData" }] : []));
|
|
1367
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1368
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
956
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthLayoutStateAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
957
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthLayoutStateAdapter, providedIn: 'root' });
|
|
1369
958
|
}
|
|
1370
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
959
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthLayoutStateAdapter, decorators: [{
|
|
1371
960
|
type: Injectable,
|
|
1372
961
|
args: [{ providedIn: 'root' }]
|
|
1373
962
|
}] });
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
* Handles logout and navigation for layout components.
|
|
1378
|
-
*/
|
|
963
|
+
// =============================================================================
|
|
964
|
+
// Layout API Adapter
|
|
965
|
+
// =============================================================================
|
|
1379
966
|
class AuthLayoutApiAdapter {
|
|
1380
967
|
authApi = inject(AuthApiService);
|
|
1381
968
|
authState = inject(AuthStateService);
|
|
1382
969
|
permissionApi = inject(UserPermissionApiService);
|
|
1383
970
|
router = inject(Router);
|
|
1384
|
-
/**
|
|
1385
|
-
* Logout the current user.
|
|
1386
|
-
* Calls backend logout endpoint and resets local auth state.
|
|
1387
|
-
* Auth state reset handles clearing all localStorage including company/branch.
|
|
1388
|
-
*/
|
|
1389
971
|
logOut() {
|
|
1390
972
|
return this.authApi.logout().pipe(tap$1(() => {
|
|
1391
|
-
this.authState.resetState();
|
|
973
|
+
this.authState.resetState();
|
|
1392
974
|
}));
|
|
1393
975
|
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Navigate to login page.
|
|
1396
|
-
* Optionally preserves current URL as return URL.
|
|
1397
|
-
*/
|
|
1398
976
|
navigateLogin(withUrl) {
|
|
1399
977
|
if (withUrl) {
|
|
1400
978
|
const currentUrl = this.router.url;
|
|
@@ -1404,83 +982,60 @@ class AuthLayoutApiAdapter {
|
|
|
1404
982
|
this.authState.navigateToLogin();
|
|
1405
983
|
}
|
|
1406
984
|
}
|
|
1407
|
-
/**
|
|
1408
|
-
* Switch to a different company and branch.
|
|
1409
|
-
* Calls backend switch endpoint, then triggers full page reload to
|
|
1410
|
-
* refresh permissions, reset component state, and re-filter all data.
|
|
1411
|
-
*/
|
|
1412
985
|
switchCompany(companyId, branchId) {
|
|
1413
986
|
return this.authApi.switchCompany({ companyId, branchId }).pipe(tap$1(() => {
|
|
1414
|
-
// Reload page to refresh permissions and all component state
|
|
1415
|
-
// This ensures all data is filtered by new company/branch context
|
|
1416
987
|
window.location.reload();
|
|
1417
988
|
}));
|
|
1418
989
|
}
|
|
1419
|
-
/**
|
|
1420
|
-
* Get list of companies the logged-in user has permissions for.
|
|
1421
|
-
* Uses permission API to filter by user's actual company-level permissions.
|
|
1422
|
-
*/
|
|
1423
990
|
getUserCompanies() {
|
|
1424
991
|
const userId = this.authState.user()?.id;
|
|
1425
992
|
if (!userId) {
|
|
1426
993
|
return of([]);
|
|
1427
994
|
}
|
|
1428
|
-
return this.permissionApi.getUserCompanies(userId).pipe(map$1((response) => {
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
}));
|
|
1435
|
-
}));
|
|
995
|
+
return this.permissionApi.getUserCompanies(userId).pipe(map$1((response) => (response.data || []).map((permission) => ({
|
|
996
|
+
id: permission.companyId,
|
|
997
|
+
name: permission.companyName,
|
|
998
|
+
slug: permission.companySlug ?? '',
|
|
999
|
+
imageId: '',
|
|
1000
|
+
}))));
|
|
1436
1001
|
}
|
|
1437
|
-
/**
|
|
1438
|
-
* Get branches for a specific company that the logged-in user has access to.
|
|
1439
|
-
* Fetches all user branches and filters by companyId.
|
|
1440
|
-
*/
|
|
1441
1002
|
getCompanyBranches(companyId) {
|
|
1442
1003
|
const userId = this.authState.user()?.id;
|
|
1443
1004
|
if (!userId) {
|
|
1444
1005
|
return of([]);
|
|
1445
1006
|
}
|
|
1446
|
-
return this.permissionApi.getUserBranches(userId).pipe(map$1((response) => {
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
}));
|
|
1455
|
-
}
|
|
1456
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthLayoutApiAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1457
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthLayoutApiAdapter, providedIn: 'root' });
|
|
1007
|
+
return this.permissionApi.getUserBranches(userId, companyId).pipe(map$1((response) => (response.data || []).map((permission) => ({
|
|
1008
|
+
id: permission.branchId,
|
|
1009
|
+
name: permission.branchName,
|
|
1010
|
+
slug: permission.branchSlug ?? '',
|
|
1011
|
+
}))));
|
|
1012
|
+
}
|
|
1013
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthLayoutApiAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1014
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthLayoutApiAdapter, providedIn: 'root' });
|
|
1458
1015
|
}
|
|
1459
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1016
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthLayoutApiAdapter, decorators: [{
|
|
1460
1017
|
type: Injectable,
|
|
1461
1018
|
args: [{ providedIn: 'root' }]
|
|
1462
1019
|
}] });
|
|
1463
1020
|
|
|
1464
1021
|
/**
|
|
1465
|
-
* Auth
|
|
1022
|
+
* Auth Shared Provider Adapters
|
|
1466
1023
|
*
|
|
1467
|
-
* Implements
|
|
1468
|
-
*
|
|
1024
|
+
* Implements provider interfaces from ng-shared using ng-auth services.
|
|
1025
|
+
* Allows IAM, Storage, and Form-Builder packages to access auth data
|
|
1026
|
+
* without depending on ng-auth directly.
|
|
1469
1027
|
*
|
|
1470
1028
|
* @example
|
|
1471
1029
|
* // In app.config.ts
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
* ]
|
|
1475
|
-
*
|
|
1476
|
-
* // In IAM component
|
|
1477
|
-
* private readonly userProvider = inject(USER_PROVIDER);
|
|
1478
|
-
* this.userProvider.getUsers({ page: 0, pageSize: 50 }).subscribe(...);
|
|
1030
|
+
* import { provideAuthProviders } from '@flusys/ng-auth';
|
|
1031
|
+
* providers: [...provideAuthProviders()]
|
|
1479
1032
|
*/
|
|
1033
|
+
// =============================================================================
|
|
1034
|
+
// User Provider Adapter
|
|
1035
|
+
// =============================================================================
|
|
1480
1036
|
class AuthUserProviderAdapter {
|
|
1481
1037
|
userApi = inject(UserApiService);
|
|
1482
1038
|
getUsers(filter) {
|
|
1483
|
-
// Convert filter to API format
|
|
1484
1039
|
const apiFilter = {
|
|
1485
1040
|
pagination: {
|
|
1486
1041
|
currentPage: filter?.page ?? 0,
|
|
@@ -1504,25 +1059,15 @@ class AuthUserProviderAdapter {
|
|
|
1504
1059
|
meta: response.meta,
|
|
1505
1060
|
})));
|
|
1506
1061
|
}
|
|
1507
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1508
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
1062
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthUserProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1063
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthUserProviderAdapter });
|
|
1509
1064
|
}
|
|
1510
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1065
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthUserProviderAdapter, decorators: [{
|
|
1511
1066
|
type: Injectable
|
|
1512
1067
|
}] });
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
*
|
|
1517
|
-
* Implements ICompanyApiProvider interface from ng-shared using CompanyApiService.
|
|
1518
|
-
* Allows IAM package to access company data without depending on ng-auth directly.
|
|
1519
|
-
*
|
|
1520
|
-
* @example
|
|
1521
|
-
* // In app.config.ts
|
|
1522
|
-
* providers: [
|
|
1523
|
-
* { provide: COMPANY_API_PROVIDER, useClass: AuthCompanyApiProviderAdapter }
|
|
1524
|
-
* ]
|
|
1525
|
-
*/
|
|
1068
|
+
// =============================================================================
|
|
1069
|
+
// Company API Provider Adapter
|
|
1070
|
+
// =============================================================================
|
|
1526
1071
|
class AuthCompanyApiProviderAdapter {
|
|
1527
1072
|
companyApi = inject(CompanyApiService);
|
|
1528
1073
|
getCompanies(filter) {
|
|
@@ -1544,94 +1089,46 @@ class AuthCompanyApiProviderAdapter {
|
|
|
1544
1089
|
meta: response.meta,
|
|
1545
1090
|
})));
|
|
1546
1091
|
}
|
|
1547
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1548
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
1092
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthCompanyApiProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1093
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthCompanyApiProviderAdapter });
|
|
1549
1094
|
}
|
|
1550
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1095
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthCompanyApiProviderAdapter, decorators: [{
|
|
1551
1096
|
type: Injectable
|
|
1552
1097
|
}] });
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
*
|
|
1557
|
-
* Implements IUserPermissionProvider interface from ng-shared using UserPermissionApiService.
|
|
1558
|
-
* Allows IAM package to query user permissions without depending on ng-auth directly.
|
|
1559
|
-
*
|
|
1560
|
-
* @example
|
|
1561
|
-
* // In app.config.ts
|
|
1562
|
-
* providers: [
|
|
1563
|
-
* { provide: USER_PERMISSION_PROVIDER, useClass: AuthUserPermissionProviderAdapter }
|
|
1564
|
-
* ]
|
|
1565
|
-
*/
|
|
1098
|
+
// =============================================================================
|
|
1099
|
+
// User Permission Provider Adapter
|
|
1100
|
+
// =============================================================================
|
|
1566
1101
|
class AuthUserPermissionProviderAdapter {
|
|
1567
1102
|
userPermissionApi = inject(UserPermissionApiService);
|
|
1568
|
-
getUserBranchPermissions(userId) {
|
|
1569
|
-
return this.userPermissionApi.getUserBranches(userId);
|
|
1103
|
+
getUserBranchPermissions(userId, companyId) {
|
|
1104
|
+
return this.userPermissionApi.getUserBranches(userId, companyId);
|
|
1570
1105
|
}
|
|
1571
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1572
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
1106
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthUserPermissionProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1107
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthUserPermissionProviderAdapter });
|
|
1573
1108
|
}
|
|
1574
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1109
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthUserPermissionProviderAdapter, decorators: [{
|
|
1575
1110
|
type: Injectable
|
|
1576
1111
|
}] });
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
*
|
|
1581
|
-
* Implements IAuthStateProvider interface from ng-shared using auth services from ng-auth.
|
|
1582
|
-
* This allows feature packages (form-builder, etc.) to check auth state without
|
|
1583
|
-
* depending on ng-auth directly.
|
|
1584
|
-
*
|
|
1585
|
-
* @example
|
|
1586
|
-
* // In app.config.ts
|
|
1587
|
-
* providers: [
|
|
1588
|
-
* { provide: AUTH_STATE_PROVIDER, useClass: AuthStateProviderAdapter }
|
|
1589
|
-
* ]
|
|
1590
|
-
*
|
|
1591
|
-
* // In form-builder component
|
|
1592
|
-
* private readonly authStateProvider = inject(AUTH_STATE_PROVIDER);
|
|
1593
|
-
* const isLoggedIn = this.authStateProvider.isAuthenticated();
|
|
1594
|
-
*/
|
|
1112
|
+
// =============================================================================
|
|
1113
|
+
// Auth State Provider Adapter
|
|
1114
|
+
// =============================================================================
|
|
1595
1115
|
class AuthStateProviderAdapter {
|
|
1596
1116
|
authState = inject(AuthStateService);
|
|
1597
1117
|
authInit = inject(AuthInitService);
|
|
1598
|
-
/**
|
|
1599
|
-
* Signal indicating if user is currently authenticated
|
|
1600
|
-
*/
|
|
1601
1118
|
get isAuthenticated() {
|
|
1602
1119
|
return this.authState.isAuthenticated;
|
|
1603
1120
|
}
|
|
1604
|
-
/**
|
|
1605
|
-
* Initialize auth state (restore session from cookies/storage)
|
|
1606
|
-
* Returns Observable<void> that completes when initialization is done
|
|
1607
|
-
*/
|
|
1608
1121
|
initialize() {
|
|
1609
1122
|
return this.authInit.initialize().pipe(map(() => undefined));
|
|
1610
1123
|
}
|
|
1611
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1612
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.
|
|
1124
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthStateProviderAdapter, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1125
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthStateProviderAdapter });
|
|
1613
1126
|
}
|
|
1614
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AuthStateProviderAdapter, decorators: [{
|
|
1615
1128
|
type: Injectable
|
|
1616
1129
|
}] });
|
|
1617
1130
|
|
|
1618
|
-
/**
|
|
1619
|
-
* Provides auth layout integration for ng-layout.
|
|
1620
|
-
* Call this in your app.config.ts to integrate ng-auth with ng-layout.
|
|
1621
|
-
*
|
|
1622
|
-
* Also registers LAYOUT_AUTH_STATE since AuthLayoutStateAdapter
|
|
1623
|
-
* implements both ILayoutAuthState and ICompanyContextProvider.
|
|
1624
|
-
*
|
|
1625
|
-
* @example
|
|
1626
|
-
* ```typescript
|
|
1627
|
-
* // In app.config.ts
|
|
1628
|
-
* export const appConfig: ApplicationConfig = {
|
|
1629
|
-
* providers: [
|
|
1630
|
-
* ...provideAuthLayoutIntegration(),
|
|
1631
|
-
* ],
|
|
1632
|
-
* };
|
|
1633
|
-
* ```
|
|
1634
|
-
*/
|
|
1131
|
+
/** Provides auth integration for ng-layout */
|
|
1635
1132
|
function provideAuthLayoutIntegration() {
|
|
1636
1133
|
return [
|
|
1637
1134
|
{ provide: LAYOUT_AUTH_STATE, useClass: AuthLayoutStateAdapter },
|
|
@@ -1639,34 +1136,12 @@ function provideAuthLayoutIntegration() {
|
|
|
1639
1136
|
];
|
|
1640
1137
|
}
|
|
1641
1138
|
|
|
1642
|
-
/**
|
|
1643
|
-
* Provide Auth Provider Adapters
|
|
1644
|
-
*
|
|
1645
|
-
* Registers auth implementations for provider interfaces from ng-shared.
|
|
1646
|
-
* This allows IAM and Storage packages to use auth services without direct dependencies.
|
|
1647
|
-
*
|
|
1648
|
-
* @example
|
|
1649
|
-
* // In app.config.ts
|
|
1650
|
-
* import { provideAuthProviders } from '@flusys/ng-auth';
|
|
1651
|
-
*
|
|
1652
|
-
* export const appConfig: ApplicationConfig = {
|
|
1653
|
-
* providers: [
|
|
1654
|
-
* provideAuthProviders(),
|
|
1655
|
-
* // ... other providers
|
|
1656
|
-
* ]
|
|
1657
|
-
* };
|
|
1658
|
-
*
|
|
1659
|
-
* @returns Array of Angular providers
|
|
1660
|
-
*/
|
|
1139
|
+
/** Provides auth implementations for ng-shared provider interfaces */
|
|
1661
1140
|
function provideAuthProviders() {
|
|
1662
1141
|
return [
|
|
1663
|
-
// User data provider (for IAM user selection)
|
|
1664
1142
|
{ provide: USER_PROVIDER, useClass: AuthUserProviderAdapter },
|
|
1665
|
-
// Company API provider (for IAM company selection)
|
|
1666
1143
|
{ provide: COMPANY_API_PROVIDER, useClass: AuthCompanyApiProviderAdapter },
|
|
1667
|
-
// User permission provider (for IAM permission assignment)
|
|
1668
1144
|
{ provide: USER_PERMISSION_PROVIDER, useClass: AuthUserPermissionProviderAdapter },
|
|
1669
|
-
// Auth state provider (for form-builder and other packages)
|
|
1670
1145
|
{ provide: AUTH_STATE_PROVIDER, useClass: AuthStateProviderAdapter },
|
|
1671
1146
|
];
|
|
1672
1147
|
}
|
|
@@ -1680,6 +1155,7 @@ function provideAuthProviders() {
|
|
|
1680
1155
|
*/
|
|
1681
1156
|
class AdministrationPageComponent {
|
|
1682
1157
|
appConfig = inject(APP_CONFIG);
|
|
1158
|
+
permissionValidator = inject(PermissionValidatorService);
|
|
1683
1159
|
/** All available tabs */
|
|
1684
1160
|
allTabs = [
|
|
1685
1161
|
{
|
|
@@ -1687,12 +1163,14 @@ class AdministrationPageComponent {
|
|
|
1687
1163
|
label: 'Users',
|
|
1688
1164
|
icon: 'pi pi-users',
|
|
1689
1165
|
route: 'users',
|
|
1166
|
+
permission: USER_PERMISSIONS.READ,
|
|
1690
1167
|
},
|
|
1691
1168
|
{
|
|
1692
1169
|
id: 'companies',
|
|
1693
1170
|
label: 'Companies',
|
|
1694
1171
|
icon: 'pi pi-building',
|
|
1695
1172
|
route: 'company',
|
|
1173
|
+
permission: COMPANY_PERMISSIONS.READ,
|
|
1696
1174
|
requiresCompanyFeature: true,
|
|
1697
1175
|
},
|
|
1698
1176
|
{
|
|
@@ -1700,23 +1178,29 @@ class AdministrationPageComponent {
|
|
|
1700
1178
|
label: 'Branches',
|
|
1701
1179
|
icon: 'pi pi-sitemap',
|
|
1702
1180
|
route: 'branch',
|
|
1181
|
+
permission: BRANCH_PERMISSIONS.READ,
|
|
1703
1182
|
requiresCompanyFeature: true,
|
|
1704
1183
|
},
|
|
1705
1184
|
];
|
|
1706
1185
|
/** Check if company feature is enabled */
|
|
1707
1186
|
companyFeatureEnabled = computed(() => isCompanyFeatureEnabled(this.appConfig), ...(ngDevMode ? [{ debugName: "companyFeatureEnabled" }] : []));
|
|
1708
|
-
/** Visible tabs based on config */
|
|
1187
|
+
/** Visible tabs based on config and permissions */
|
|
1709
1188
|
visibleTabs = computed(() => {
|
|
1710
|
-
return this.allTabs.filter((tab) =>
|
|
1189
|
+
return this.allTabs.filter((tab) => {
|
|
1190
|
+
const hasCompanyFeature = !tab.requiresCompanyFeature || this.companyFeatureEnabled();
|
|
1191
|
+
const hasPermission = this.permissionValidator.hasPermission(tab.permission);
|
|
1192
|
+
return hasCompanyFeature && hasPermission;
|
|
1193
|
+
});
|
|
1711
1194
|
}, ...(ngDevMode ? [{ debugName: "visibleTabs" }] : []));
|
|
1712
|
-
/** Dynamic subtitle based on visible
|
|
1195
|
+
/** Dynamic subtitle based on visible tabs */
|
|
1713
1196
|
subtitle = computed(() => {
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1197
|
+
const tabs = this.visibleTabs();
|
|
1198
|
+
if (tabs.length === 0)
|
|
1199
|
+
return 'No permissions available';
|
|
1200
|
+
return `Manage ${tabs.map((t) => t.label.toLowerCase()).join(', ')}`;
|
|
1717
1201
|
}, ...(ngDevMode ? [{ debugName: "subtitle" }] : []));
|
|
1718
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
1719
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
1202
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AdministrationPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1203
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: AdministrationPageComponent, isStandalone: true, selector: "lib-administration-page", ngImport: i0, template: `
|
|
1720
1204
|
<div class="p-4">
|
|
1721
1205
|
<div class="mb-4">
|
|
1722
1206
|
<h1 class="text-2xl font-bold m-0">Administration</h1>
|
|
@@ -1741,9 +1225,9 @@ class AdministrationPageComponent {
|
|
|
1741
1225
|
</div>
|
|
1742
1226
|
`, isInline: true, styles: [".scrollbar-hide{-ms-overflow-style:none;scrollbar-width:none}.scrollbar-hide::-webkit-scrollbar{display:none}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterOutlet, selector: "router-outlet", inputs: ["name", "routerOutletData"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1743
1227
|
}
|
|
1744
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1228
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: AdministrationPageComponent, decorators: [{
|
|
1745
1229
|
type: Component,
|
|
1746
|
-
args: [{ selector: 'lib-administration-page',
|
|
1230
|
+
args: [{ selector: 'lib-administration-page', changeDetection: ChangeDetectionStrategy.OnPush, imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive], template: `
|
|
1747
1231
|
<div class="p-4">
|
|
1748
1232
|
<div class="mb-4">
|
|
1749
1233
|
<h1 class="text-2xl font-bold m-0">Administration</h1>
|
|
@@ -1855,25 +1339,27 @@ class BranchFormComponent {
|
|
|
1855
1339
|
const response = await this.branchService.findByIdAsync(id);
|
|
1856
1340
|
if (response.success && response.data) {
|
|
1857
1341
|
this.existingBranch.set(response.data);
|
|
1858
|
-
this.formModel.set(
|
|
1859
|
-
companyId: response.data.companyId,
|
|
1860
|
-
name: response.data.name,
|
|
1861
|
-
slug: response.data.slug,
|
|
1862
|
-
email: response.data.email || '',
|
|
1863
|
-
phone: response.data.phone || '',
|
|
1864
|
-
address: response.data.address || '',
|
|
1865
|
-
isActive: response.data.isActive,
|
|
1866
|
-
});
|
|
1342
|
+
this.formModel.set(this.toFormModel(response.data));
|
|
1867
1343
|
}
|
|
1868
1344
|
}
|
|
1869
1345
|
catch {
|
|
1870
|
-
// Error toast handled by global interceptor
|
|
1871
1346
|
this.router.navigate(['/administration/branch']);
|
|
1872
1347
|
}
|
|
1873
1348
|
finally {
|
|
1874
1349
|
this.isLoading.set(false);
|
|
1875
1350
|
}
|
|
1876
1351
|
}
|
|
1352
|
+
toFormModel(branch) {
|
|
1353
|
+
return {
|
|
1354
|
+
companyId: branch.companyId,
|
|
1355
|
+
name: branch.name,
|
|
1356
|
+
slug: branch.slug,
|
|
1357
|
+
email: branch.email || '',
|
|
1358
|
+
phone: branch.phone || '',
|
|
1359
|
+
address: branch.address || '',
|
|
1360
|
+
isActive: branch.isActive,
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1877
1363
|
generateSlug() {
|
|
1878
1364
|
const model = this.formModel();
|
|
1879
1365
|
if (!model.slug && model.name) {
|
|
@@ -1895,65 +1381,68 @@ class BranchFormComponent {
|
|
|
1895
1381
|
}
|
|
1896
1382
|
this.isLoading.set(true);
|
|
1897
1383
|
try {
|
|
1898
|
-
const model = this.formModel();
|
|
1899
1384
|
if (this.isEditMode() && this.existingBranch()) {
|
|
1900
|
-
|
|
1901
|
-
const updateData = {
|
|
1902
|
-
id: this.existingBranch().id,
|
|
1903
|
-
name: model.name,
|
|
1904
|
-
slug: model.slug,
|
|
1905
|
-
email: model.email || undefined,
|
|
1906
|
-
phone: model.phone || undefined,
|
|
1907
|
-
address: model.address || undefined,
|
|
1908
|
-
isActive: model.isActive,
|
|
1909
|
-
};
|
|
1910
|
-
await this.branchService.updateAsync(updateData);
|
|
1911
|
-
this.messageService.add({
|
|
1912
|
-
severity: 'success',
|
|
1913
|
-
summary: 'Success',
|
|
1914
|
-
detail: 'Branch updated successfully.',
|
|
1915
|
-
});
|
|
1385
|
+
await this.updateBranch();
|
|
1916
1386
|
}
|
|
1917
1387
|
else {
|
|
1918
|
-
|
|
1919
|
-
const createData = {
|
|
1920
|
-
companyId: model.companyId,
|
|
1921
|
-
name: model.name,
|
|
1922
|
-
slug: model.slug,
|
|
1923
|
-
email: model.email || undefined,
|
|
1924
|
-
phone: model.phone || undefined,
|
|
1925
|
-
address: model.address || undefined,
|
|
1926
|
-
isActive: model.isActive,
|
|
1927
|
-
};
|
|
1928
|
-
await this.branchService.insertAsync(createData);
|
|
1929
|
-
this.messageService.add({
|
|
1930
|
-
severity: 'success',
|
|
1931
|
-
summary: 'Success',
|
|
1932
|
-
detail: 'Branch created successfully.',
|
|
1933
|
-
});
|
|
1388
|
+
await this.createBranch();
|
|
1934
1389
|
}
|
|
1935
|
-
|
|
1936
|
-
const companyId = this.existingBranch()?.companyId || model.companyId;
|
|
1937
|
-
this.router.navigate(['/administration/branch'], {
|
|
1938
|
-
queryParams: { companyId },
|
|
1939
|
-
});
|
|
1390
|
+
this.navigateToList();
|
|
1940
1391
|
}
|
|
1941
1392
|
catch {
|
|
1942
|
-
// Error
|
|
1393
|
+
// Error handled by global interceptor
|
|
1943
1394
|
}
|
|
1944
1395
|
finally {
|
|
1945
1396
|
this.isLoading.set(false);
|
|
1946
1397
|
}
|
|
1947
1398
|
}
|
|
1948
|
-
|
|
1949
|
-
const model = this.formModel();
|
|
1950
|
-
const
|
|
1399
|
+
async updateBranch() {
|
|
1400
|
+
const model = this.formModel();
|
|
1401
|
+
const updateData = {
|
|
1402
|
+
id: this.existingBranch().id,
|
|
1403
|
+
name: model.name,
|
|
1404
|
+
slug: model.slug,
|
|
1405
|
+
...this.buildOptionalFields(model),
|
|
1406
|
+
};
|
|
1407
|
+
await this.branchService.updateAsync(updateData);
|
|
1408
|
+
this.onSaveSuccess('Branch updated successfully.');
|
|
1409
|
+
}
|
|
1410
|
+
async createBranch() {
|
|
1411
|
+
const model = this.formModel();
|
|
1412
|
+
const createData = {
|
|
1413
|
+
companyId: model.companyId,
|
|
1414
|
+
name: model.name,
|
|
1415
|
+
slug: model.slug,
|
|
1416
|
+
...this.buildOptionalFields(model),
|
|
1417
|
+
};
|
|
1418
|
+
await this.branchService.insertAsync(createData);
|
|
1419
|
+
this.onSaveSuccess('Branch created successfully.');
|
|
1420
|
+
}
|
|
1421
|
+
buildOptionalFields(model) {
|
|
1422
|
+
return {
|
|
1423
|
+
email: model.email || undefined,
|
|
1424
|
+
phone: model.phone || undefined,
|
|
1425
|
+
address: model.address || undefined,
|
|
1426
|
+
isActive: model.isActive,
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
onSaveSuccess(message) {
|
|
1430
|
+
this.messageService.add({ severity: 'success', summary: 'Success', detail: message });
|
|
1431
|
+
}
|
|
1432
|
+
getTargetCompanyId() {
|
|
1433
|
+
return this.existingBranch()?.companyId || this.formModel().companyId || null;
|
|
1434
|
+
}
|
|
1435
|
+
navigateToList() {
|
|
1436
|
+
const companyId = this.getTargetCompanyId();
|
|
1951
1437
|
this.router.navigate(['/administration/branch'], {
|
|
1952
1438
|
queryParams: companyId ? { companyId } : {},
|
|
1953
1439
|
});
|
|
1954
1440
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
1441
|
+
onCancel() {
|
|
1442
|
+
this.navigateToList();
|
|
1443
|
+
}
|
|
1444
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1445
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: BranchFormComponent, isStandalone: true, selector: "lib-branch-form", ngImport: i0, template: `
|
|
1957
1446
|
<p-card>
|
|
1958
1447
|
<ng-template #header>
|
|
1959
1448
|
<div class="flex justify-between items-center p-4">
|
|
@@ -2064,13 +1553,12 @@ class BranchFormComponent {
|
|
|
2064
1553
|
</div>
|
|
2065
1554
|
</form>
|
|
2066
1555
|
</p-card>
|
|
2067
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
1556
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2068
1557
|
}
|
|
2069
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1558
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchFormComponent, decorators: [{
|
|
2070
1559
|
type: Component,
|
|
2071
1560
|
args: [{
|
|
2072
1561
|
selector: 'lib-branch-form',
|
|
2073
|
-
standalone: true,
|
|
2074
1562
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2075
1563
|
imports: [AngularModule, PrimeModule, FormField],
|
|
2076
1564
|
template: `
|
|
@@ -2226,15 +1714,18 @@ class BranchListComponent {
|
|
|
2226
1714
|
if (this.initialized)
|
|
2227
1715
|
return;
|
|
2228
1716
|
this.initialized = true;
|
|
2229
|
-
//
|
|
2230
|
-
|
|
2231
|
-
|
|
1717
|
+
// Use untracked to avoid nested effect error when initializing resources
|
|
1718
|
+
untracked(() => {
|
|
1719
|
+
// Load companies for dropdown
|
|
1720
|
+
this.companyService.fetchList('', {
|
|
1721
|
+
pagination: { currentPage: 0, pageSize: 100 },
|
|
1722
|
+
});
|
|
1723
|
+
// Check for company ID from query params or use current company
|
|
1724
|
+
// p-table's onLazyLoad will handle the initial data fetch
|
|
1725
|
+
const queryCompanyId = params?.get('companyId') || null;
|
|
1726
|
+
const currentCompanyId = this.authState.company()?.id;
|
|
1727
|
+
this.selectedCompanyId.set(queryCompanyId || currentCompanyId || null);
|
|
2232
1728
|
});
|
|
2233
|
-
// Check for company ID from query params or use current company
|
|
2234
|
-
// p-table's onLazyLoad will handle the initial data fetch
|
|
2235
|
-
const queryCompanyId = params?.get('companyId') || null;
|
|
2236
|
-
const currentCompanyId = this.authState.company()?.id;
|
|
2237
|
-
this.selectedCompanyId.set(queryCompanyId || currentCompanyId || null);
|
|
2238
1729
|
});
|
|
2239
1730
|
}
|
|
2240
1731
|
loadBranches() {
|
|
@@ -2292,8 +1783,8 @@ class BranchListComponent {
|
|
|
2292
1783
|
},
|
|
2293
1784
|
});
|
|
2294
1785
|
}
|
|
2295
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
2296
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
1786
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1787
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: BranchListComponent, isStandalone: true, selector: "lib-branch-list", ngImport: i0, template: `
|
|
2297
1788
|
<p-card>
|
|
2298
1789
|
<ng-template #header>
|
|
2299
1790
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
|
|
@@ -2390,13 +1881,12 @@ class BranchListComponent {
|
|
|
2390
1881
|
</p-table>
|
|
2391
1882
|
</div>
|
|
2392
1883
|
</p-card>
|
|
2393
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
1884
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: i4$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: i3$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: TagModule }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2394
1885
|
}
|
|
2395
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
1886
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BranchListComponent, decorators: [{
|
|
2396
1887
|
type: Component,
|
|
2397
1888
|
args: [{
|
|
2398
1889
|
selector: 'lib-branch-list',
|
|
2399
|
-
standalone: true,
|
|
2400
1890
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2401
1891
|
imports: [AngularModule, PrimeModule, TagModule, HasPermissionDirective],
|
|
2402
1892
|
template: `
|
|
@@ -2603,44 +2093,22 @@ class CompanyFormComponent {
|
|
|
2603
2093
|
}
|
|
2604
2094
|
this.isLoading.set(true);
|
|
2605
2095
|
try {
|
|
2606
|
-
const
|
|
2607
|
-
if (
|
|
2608
|
-
// Update existing company
|
|
2096
|
+
const isEdit = this.isEditMode() && this.existingCompany();
|
|
2097
|
+
if (isEdit) {
|
|
2609
2098
|
const updateData = {
|
|
2610
2099
|
id: this.existingCompany().id,
|
|
2611
|
-
|
|
2612
|
-
slug: model.slug,
|
|
2613
|
-
email: model.email || undefined,
|
|
2614
|
-
phone: model.phone || undefined,
|
|
2615
|
-
website: model.website || undefined,
|
|
2616
|
-
address: model.address || undefined,
|
|
2617
|
-
isActive: model.isActive,
|
|
2100
|
+
...this.buildCompanyData(),
|
|
2618
2101
|
};
|
|
2619
2102
|
await this.companyService.updateAsync(updateData);
|
|
2620
|
-
this.messageService.add({
|
|
2621
|
-
severity: 'success',
|
|
2622
|
-
summary: 'Success',
|
|
2623
|
-
detail: 'Company updated successfully.',
|
|
2624
|
-
});
|
|
2625
2103
|
}
|
|
2626
2104
|
else {
|
|
2627
|
-
|
|
2628
|
-
const createData = {
|
|
2629
|
-
name: model.name,
|
|
2630
|
-
slug: model.slug,
|
|
2631
|
-
email: model.email || undefined,
|
|
2632
|
-
phone: model.phone || undefined,
|
|
2633
|
-
website: model.website || undefined,
|
|
2634
|
-
address: model.address || undefined,
|
|
2635
|
-
isActive: model.isActive,
|
|
2636
|
-
};
|
|
2637
|
-
await this.companyService.insertAsync(createData);
|
|
2638
|
-
this.messageService.add({
|
|
2639
|
-
severity: 'success',
|
|
2640
|
-
summary: 'Success',
|
|
2641
|
-
detail: 'Company created successfully.',
|
|
2642
|
-
});
|
|
2105
|
+
await this.companyService.insertAsync(this.buildCompanyData());
|
|
2643
2106
|
}
|
|
2107
|
+
this.messageService.add({
|
|
2108
|
+
severity: 'success',
|
|
2109
|
+
summary: 'Success',
|
|
2110
|
+
detail: `Company ${isEdit ? 'updated' : 'created'} successfully.`,
|
|
2111
|
+
});
|
|
2644
2112
|
this.router.navigate(['/administration/company']);
|
|
2645
2113
|
}
|
|
2646
2114
|
catch {
|
|
@@ -2653,8 +2121,21 @@ class CompanyFormComponent {
|
|
|
2653
2121
|
onCancel() {
|
|
2654
2122
|
this.router.navigate(['/administration/company']);
|
|
2655
2123
|
}
|
|
2656
|
-
|
|
2657
|
-
|
|
2124
|
+
/** Build company data from form model */
|
|
2125
|
+
buildCompanyData() {
|
|
2126
|
+
const model = this.formModel();
|
|
2127
|
+
return {
|
|
2128
|
+
name: model.name,
|
|
2129
|
+
slug: model.slug,
|
|
2130
|
+
email: model.email || undefined,
|
|
2131
|
+
phone: model.phone || undefined,
|
|
2132
|
+
website: model.website || undefined,
|
|
2133
|
+
address: model.address || undefined,
|
|
2134
|
+
isActive: model.isActive,
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2138
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: CompanyFormComponent, isStandalone: true, selector: "lib-company-form", ngImport: i0, template: `
|
|
2658
2139
|
<p-card>
|
|
2659
2140
|
<ng-template #header>
|
|
2660
2141
|
<div class="flex justify-between items-center p-4">
|
|
@@ -2760,13 +2241,12 @@ class CompanyFormComponent {
|
|
|
2760
2241
|
</div>
|
|
2761
2242
|
</form>
|
|
2762
2243
|
</p-card>
|
|
2763
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
2244
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2764
2245
|
}
|
|
2765
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
2246
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyFormComponent, decorators: [{
|
|
2766
2247
|
type: Component,
|
|
2767
2248
|
args: [{
|
|
2768
2249
|
selector: 'lib-company-form',
|
|
2769
|
-
standalone: true,
|
|
2770
2250
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
2771
2251
|
imports: [AngularModule, PrimeModule, FormField],
|
|
2772
2252
|
template: `
|
|
@@ -2903,11 +2383,6 @@ class CompanyListComponent {
|
|
|
2903
2383
|
isLoading = this.companyService.isLoading;
|
|
2904
2384
|
/** Total count from service */
|
|
2905
2385
|
total = this.companyService.total;
|
|
2906
|
-
loadCompanies() {
|
|
2907
|
-
this.companyService.fetchList('', {
|
|
2908
|
-
pagination: { currentPage: 0, pageSize: 10 },
|
|
2909
|
-
});
|
|
2910
|
-
}
|
|
2911
2386
|
onLazyLoad(event) {
|
|
2912
2387
|
const first = event.first ?? 0;
|
|
2913
2388
|
const rows = event.rows ?? 10;
|
|
@@ -2949,8 +2424,8 @@ class CompanyListComponent {
|
|
|
2949
2424
|
},
|
|
2950
2425
|
});
|
|
2951
2426
|
}
|
|
2952
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
2953
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
2427
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2428
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: CompanyListComponent, isStandalone: true, selector: "lib-company-list", ngImport: i0, template: `
|
|
2954
2429
|
<p-card>
|
|
2955
2430
|
<ng-template #header>
|
|
2956
2431
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
|
|
@@ -3040,11 +2515,10 @@ class CompanyListComponent {
|
|
|
3040
2515
|
</p-card>
|
|
3041
2516
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: i3$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: TagModule }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3042
2517
|
}
|
|
3043
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
2518
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: CompanyListComponent, decorators: [{
|
|
3044
2519
|
type: Component,
|
|
3045
2520
|
args: [{
|
|
3046
2521
|
selector: 'lib-company-list',
|
|
3047
|
-
standalone: true,
|
|
3048
2522
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3049
2523
|
imports: [AngularModule, PrimeModule, TagModule, HasPermissionDirective],
|
|
3050
2524
|
template: `
|
|
@@ -3168,29 +2642,23 @@ class ForgotPasswordPageComponent {
|
|
|
3168
2642
|
}
|
|
3169
2643
|
this.isLoading.set(true);
|
|
3170
2644
|
const email = this.formModel().email;
|
|
2645
|
+
// Always show success (security: don't reveal if email exists)
|
|
3171
2646
|
this.authApi
|
|
3172
2647
|
.forgotPassword(email)
|
|
3173
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3174
|
-
.subscribe(
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
},
|
|
3180
|
-
error: () => {
|
|
3181
|
-
this.isLoading.set(false);
|
|
3182
|
-
this.submittedEmail.set(email);
|
|
3183
|
-
this.emailSent.set(true);
|
|
3184
|
-
},
|
|
3185
|
-
});
|
|
2648
|
+
.pipe(takeUntilDestroyed(this.destroyRef), tap({ next: () => this.showEmailSent(email), error: () => this.showEmailSent(email) }), finalize(() => this.isLoading.set(false)))
|
|
2649
|
+
.subscribe();
|
|
2650
|
+
}
|
|
2651
|
+
showEmailSent(email) {
|
|
2652
|
+
this.submittedEmail.set(email);
|
|
2653
|
+
this.emailSent.set(true);
|
|
3186
2654
|
}
|
|
3187
2655
|
resetForm() {
|
|
3188
2656
|
this.emailSent.set(false);
|
|
3189
2657
|
this.submittedEmail.set('');
|
|
3190
2658
|
this.formModel.set({ email: '' });
|
|
3191
2659
|
}
|
|
3192
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
3193
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
2660
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ForgotPasswordPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2661
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: ForgotPasswordPageComponent, isStandalone: true, selector: "lib-forgot-password-page", ngImport: i0, template: `
|
|
3194
2662
|
<app-floating-configurator />
|
|
3195
2663
|
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
3196
2664
|
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
@@ -3267,13 +2735,12 @@ class ForgotPasswordPageComponent {
|
|
|
3267
2735
|
</div>
|
|
3268
2736
|
</div>
|
|
3269
2737
|
</div>
|
|
3270
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
2738
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3271
2739
|
}
|
|
3272
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
2740
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ForgotPasswordPageComponent, decorators: [{
|
|
3273
2741
|
type: Component,
|
|
3274
2742
|
args: [{
|
|
3275
2743
|
selector: 'lib-forgot-password-page',
|
|
3276
|
-
standalone: true,
|
|
3277
2744
|
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
3278
2745
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3279
2746
|
template: `
|
|
@@ -3362,12 +2829,6 @@ var forgotPasswordPage_component = /*#__PURE__*/Object.freeze({
|
|
|
3362
2829
|
ForgotPasswordPageComponent: ForgotPasswordPageComponent
|
|
3363
2830
|
});
|
|
3364
2831
|
|
|
3365
|
-
/**
|
|
3366
|
-
* Login Page Component
|
|
3367
|
-
* Multi-step authentication flow:
|
|
3368
|
-
* - Step 1: Email/Password credentials
|
|
3369
|
-
* - Step 2: Company/Branch selection (when company feature enabled and user has multiple)
|
|
3370
|
-
*/
|
|
3371
2832
|
class LoginPageComponent {
|
|
3372
2833
|
destroyRef = inject(DestroyRef);
|
|
3373
2834
|
appConfig = inject(APP_CONFIG);
|
|
@@ -3378,52 +2839,32 @@ class LoginPageComponent {
|
|
|
3378
2839
|
currentStep = signal('credentials', ...(ngDevMode ? [{ debugName: "currentStep" }] : []));
|
|
3379
2840
|
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
3380
2841
|
unverifiedEmail = signal('', ...(ngDevMode ? [{ debugName: "unverifiedEmail" }] : []));
|
|
3381
|
-
// ============================================
|
|
3382
|
-
// Login Form (Signal Forms)
|
|
3383
|
-
// ============================================
|
|
3384
|
-
/** Login form model */
|
|
3385
2842
|
loginFormModel = signal({
|
|
3386
2843
|
email: '',
|
|
3387
2844
|
password: '',
|
|
3388
2845
|
rememberMe: true,
|
|
3389
2846
|
}, ...(ngDevMode ? [{ debugName: "loginFormModel" }] : []));
|
|
3390
|
-
/** Login form with validation schema */
|
|
3391
2847
|
loginForm = form(this.loginFormModel, (f) => {
|
|
3392
2848
|
required(f.email, { message: 'Email is required' });
|
|
3393
2849
|
email(f.email, { message: 'Please enter a valid email address' });
|
|
3394
2850
|
required(f.password, { message: 'Password is required' });
|
|
3395
|
-
minLength(f.password,
|
|
2851
|
+
minLength(f.password, 8, { message: 'Password must be at least 8 characters' });
|
|
3396
2852
|
});
|
|
3397
|
-
/** Check if login form is valid */
|
|
3398
2853
|
isLoginFormValid = computed(() => this.loginForm().valid(), ...(ngDevMode ? [{ debugName: "isLoginFormValid" }] : []));
|
|
3399
|
-
// ============================================
|
|
3400
|
-
// Selection Form (Signal Forms)
|
|
3401
|
-
// ============================================
|
|
3402
|
-
/** Selection form model */
|
|
3403
2854
|
selectionFormModel = signal({
|
|
3404
2855
|
companyId: '',
|
|
3405
2856
|
branchId: '',
|
|
3406
2857
|
rememberSelection: true,
|
|
3407
2858
|
}, ...(ngDevMode ? [{ debugName: "selectionFormModel" }] : []));
|
|
3408
|
-
/** Selection form with validation schema */
|
|
3409
2859
|
selectionForm = form(this.selectionFormModel, (f) => {
|
|
3410
2860
|
required(f.companyId, { message: 'Please select a company' });
|
|
3411
2861
|
required(f.branchId, { message: 'Please select a branch' });
|
|
3412
2862
|
});
|
|
3413
|
-
/** Check if selection form is valid */
|
|
3414
2863
|
isSelectionFormValid = computed(() => this.selectionForm().valid(), ...(ngDevMode ? [{ debugName: "isSelectionFormValid" }] : []));
|
|
3415
|
-
/** Whether company feature is enabled */
|
|
3416
2864
|
companyFeatureEnabled = computed(() => isCompanyFeatureEnabled(this.appConfig), ...(ngDevMode ? [{ debugName: "companyFeatureEnabled" }] : []));
|
|
3417
|
-
// ============================================
|
|
3418
|
-
// Company Selection State (Step 2)
|
|
3419
|
-
// ============================================
|
|
3420
|
-
/** Session ID for company selection */
|
|
3421
2865
|
sessionId = null;
|
|
3422
|
-
/** Available companies */
|
|
3423
2866
|
companies = signal([], ...(ngDevMode ? [{ debugName: "companies" }] : []));
|
|
3424
|
-
/** User's name */
|
|
3425
2867
|
userName = signal('', ...(ngDevMode ? [{ debugName: "userName" }] : []));
|
|
3426
|
-
/** Branches for selected company */
|
|
3427
2868
|
branches = computed(() => {
|
|
3428
2869
|
const companyId = this.selectionFormModel().companyId;
|
|
3429
2870
|
if (!companyId)
|
|
@@ -3432,17 +2873,12 @@ class LoginPageComponent {
|
|
|
3432
2873
|
return company?.branches ?? [];
|
|
3433
2874
|
}, ...(ngDevMode ? [{ debugName: "branches" }] : []));
|
|
3434
2875
|
constructor() {
|
|
3435
|
-
// Effect to restore pending company selection state (e.g., on page refresh during Step 2)
|
|
3436
2876
|
effect(() => {
|
|
3437
2877
|
if (this.authState.requiresCompanySelection()) {
|
|
3438
2878
|
this.restorePendingSelectionState();
|
|
3439
2879
|
}
|
|
3440
2880
|
});
|
|
3441
2881
|
}
|
|
3442
|
-
/**
|
|
3443
|
-
* Restores pending company selection state from auth service.
|
|
3444
|
-
* Called when user refreshes page during Step 2 of login flow.
|
|
3445
|
-
*/
|
|
3446
2882
|
restorePendingSelectionState() {
|
|
3447
2883
|
const availableCompanies = this.authState.availableCompanies();
|
|
3448
2884
|
const sessionId = this.authState.selectionSessionId();
|
|
@@ -3455,9 +2891,6 @@ class LoginPageComponent {
|
|
|
3455
2891
|
this.autoSelectIfSingle();
|
|
3456
2892
|
}
|
|
3457
2893
|
}
|
|
3458
|
-
// ============================================
|
|
3459
|
-
// Step 1: Login
|
|
3460
|
-
// ============================================
|
|
3461
2894
|
onSubmit() {
|
|
3462
2895
|
if (!this.isLoginFormValid()) {
|
|
3463
2896
|
this.messageService.add({
|
|
@@ -3502,23 +2935,22 @@ class LoginPageComponent {
|
|
|
3502
2935
|
this.authState.navigateAfterLogin(this.returnUrl());
|
|
3503
2936
|
}
|
|
3504
2937
|
},
|
|
3505
|
-
error: () => {
|
|
2938
|
+
error: (error) => {
|
|
3506
2939
|
this.isLoading.set(false);
|
|
3507
|
-
|
|
2940
|
+
const message = error?.error?.message || 'Login failed. Please try again.';
|
|
2941
|
+
this.messageService.add({
|
|
2942
|
+
severity: 'error',
|
|
2943
|
+
summary: 'Login Failed',
|
|
2944
|
+
detail: message,
|
|
2945
|
+
});
|
|
3508
2946
|
},
|
|
3509
2947
|
});
|
|
3510
2948
|
}
|
|
3511
|
-
// ============================================
|
|
3512
|
-
// Step 2: Company Selection
|
|
3513
|
-
// ============================================
|
|
3514
|
-
/** Handle company selection change */
|
|
3515
2949
|
onCompanyChange() {
|
|
3516
|
-
// Reset branch when company changes
|
|
3517
2950
|
this.selectionFormModel.update((model) => ({
|
|
3518
2951
|
...model,
|
|
3519
2952
|
branchId: '',
|
|
3520
2953
|
}));
|
|
3521
|
-
// Auto-select if only one branch
|
|
3522
2954
|
const branches = this.branches();
|
|
3523
2955
|
if (branches.length === 1) {
|
|
3524
2956
|
this.selectionFormModel.update((model) => ({
|
|
@@ -3558,7 +2990,6 @@ class LoginPageComponent {
|
|
|
3558
2990
|
.subscribe({
|
|
3559
2991
|
next: (response) => {
|
|
3560
2992
|
this.isLoading.set(false);
|
|
3561
|
-
// Response is unwrapped - LoginResponse directly
|
|
3562
2993
|
this.authState.setLoginState(response);
|
|
3563
2994
|
this.messageService.add({
|
|
3564
2995
|
severity: 'success',
|
|
@@ -3569,7 +3000,6 @@ class LoginPageComponent {
|
|
|
3569
3000
|
},
|
|
3570
3001
|
error: () => {
|
|
3571
3002
|
this.isLoading.set(false);
|
|
3572
|
-
// Error toast handled by global interceptor
|
|
3573
3003
|
},
|
|
3574
3004
|
});
|
|
3575
3005
|
}
|
|
@@ -3604,15 +3034,10 @@ class LoginPageComponent {
|
|
|
3604
3034
|
},
|
|
3605
3035
|
error: () => {
|
|
3606
3036
|
this.isLoading.set(false);
|
|
3607
|
-
// Error toast handled by global interceptor
|
|
3608
3037
|
},
|
|
3609
3038
|
});
|
|
3610
3039
|
}
|
|
3611
|
-
// ============================================
|
|
3612
|
-
// Helpers
|
|
3613
|
-
// ============================================
|
|
3614
3040
|
autoSelectIfSingle() {
|
|
3615
|
-
// Auto-select if only one company
|
|
3616
3041
|
if (this.companies().length === 1) {
|
|
3617
3042
|
this.selectionFormModel.update((model) => ({
|
|
3618
3043
|
...model,
|
|
@@ -3620,7 +3045,6 @@ class LoginPageComponent {
|
|
|
3620
3045
|
}));
|
|
3621
3046
|
this.onCompanyChange();
|
|
3622
3047
|
}
|
|
3623
|
-
// Try to restore from storage
|
|
3624
3048
|
const storedCompanyId = this.authState.getStoredCompanyId();
|
|
3625
3049
|
const storedBranchId = this.authState.getStoredBranchId();
|
|
3626
3050
|
if (storedCompanyId) {
|
|
@@ -3642,8 +3066,8 @@ class LoginPageComponent {
|
|
|
3642
3066
|
}
|
|
3643
3067
|
}
|
|
3644
3068
|
}
|
|
3645
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
3646
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
3069
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LoginPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3070
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: LoginPageComponent, isStandalone: true, selector: "lib-login-page", inputs: { returnUrl: { classPropertyName: "returnUrl", publicName: "returnUrl", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
3647
3071
|
<app-floating-configurator />
|
|
3648
3072
|
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
3649
3073
|
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
@@ -3839,13 +3263,12 @@ class LoginPageComponent {
|
|
|
3839
3263
|
</div>
|
|
3840
3264
|
</div>
|
|
3841
3265
|
</div>
|
|
3842
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
3266
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3843
3267
|
}
|
|
3844
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
3268
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: LoginPageComponent, decorators: [{
|
|
3845
3269
|
type: Component,
|
|
3846
3270
|
args: [{
|
|
3847
3271
|
selector: 'lib-login-page',
|
|
3848
|
-
standalone: true,
|
|
3849
3272
|
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
3850
3273
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
3851
3274
|
template: `
|
|
@@ -4125,6 +3548,10 @@ class ProfilePageComponent {
|
|
|
4125
3548
|
// Show bottom row only if at least one section is visible
|
|
4126
3549
|
showBottomRow = computed(() => this.showCompanySection() || this.showPermissionsSection(), ...(ngDevMode ? [{ debugName: "showBottomRow" }] : []));
|
|
4127
3550
|
// Form
|
|
3551
|
+
originalFormModel = signal({
|
|
3552
|
+
name: '',
|
|
3553
|
+
phone: '',
|
|
3554
|
+
}, ...(ngDevMode ? [{ debugName: "originalFormModel" }] : []));
|
|
4128
3555
|
formModel = signal({
|
|
4129
3556
|
name: '',
|
|
4130
3557
|
phone: '',
|
|
@@ -4136,6 +3563,11 @@ class ProfilePageComponent {
|
|
|
4136
3563
|
const model = this.formModel();
|
|
4137
3564
|
return model.name.trim().length > 0;
|
|
4138
3565
|
}, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
3566
|
+
isFormDirty = computed(() => {
|
|
3567
|
+
const current = this.formModel();
|
|
3568
|
+
const original = this.originalFormModel();
|
|
3569
|
+
return current.name !== original.name || current.phone !== original.phone;
|
|
3570
|
+
}, ...(ngDevMode ? [{ debugName: "isFormDirty" }] : []));
|
|
4139
3571
|
// Password form
|
|
4140
3572
|
passwordModel = signal({
|
|
4141
3573
|
currentPassword: '',
|
|
@@ -4196,10 +3628,12 @@ class ProfilePageComponent {
|
|
|
4196
3628
|
loadUserData() {
|
|
4197
3629
|
const user = this.authState.user();
|
|
4198
3630
|
if (user) {
|
|
4199
|
-
|
|
3631
|
+
const initialValues = {
|
|
4200
3632
|
name: user.name ?? '',
|
|
4201
3633
|
phone: user.phone ?? '',
|
|
4202
|
-
}
|
|
3634
|
+
};
|
|
3635
|
+
this.originalFormModel.set(initialValues);
|
|
3636
|
+
this.formModel.set(initialValues);
|
|
4203
3637
|
// Load profile picture URL (only if storage is enabled)
|
|
4204
3638
|
if (user.profilePictureId && isFeatureEnabled(this.appConfig, 'storage')) {
|
|
4205
3639
|
this.fileUrlService
|
|
@@ -4316,7 +3750,7 @@ class ProfilePageComponent {
|
|
|
4316
3750
|
};
|
|
4317
3751
|
this.userService
|
|
4318
3752
|
.updateProfile(updateData)
|
|
4319
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3753
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoading.set(false)))
|
|
4320
3754
|
.subscribe({
|
|
4321
3755
|
next: (response) => {
|
|
4322
3756
|
if (response.success && response.data) {
|
|
@@ -4326,17 +3760,20 @@ class ProfilePageComponent {
|
|
|
4326
3760
|
name: response.data.name,
|
|
4327
3761
|
phone: response.data.phone ?? null,
|
|
4328
3762
|
});
|
|
3763
|
+
// Update original form model to reset dirty state
|
|
3764
|
+
this.originalFormModel.set({
|
|
3765
|
+
name: response.data.name,
|
|
3766
|
+
phone: response.data.phone ?? '',
|
|
3767
|
+
});
|
|
4329
3768
|
this.messageService.add({
|
|
4330
3769
|
severity: 'success',
|
|
4331
3770
|
summary: 'Success',
|
|
4332
3771
|
detail: 'Profile updated successfully',
|
|
4333
3772
|
});
|
|
4334
3773
|
}
|
|
4335
|
-
this.isLoading.set(false);
|
|
4336
3774
|
},
|
|
4337
3775
|
error: () => {
|
|
4338
3776
|
// Error toast handled by global interceptor
|
|
4339
|
-
this.isLoading.set(false);
|
|
4340
3777
|
},
|
|
4341
3778
|
});
|
|
4342
3779
|
}
|
|
@@ -4353,7 +3790,7 @@ class ProfilePageComponent {
|
|
|
4353
3790
|
currentPassword: model.currentPassword,
|
|
4354
3791
|
newPassword: model.newPassword,
|
|
4355
3792
|
})
|
|
4356
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3793
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isPasswordLoading.set(false)))
|
|
4357
3794
|
.subscribe({
|
|
4358
3795
|
next: () => {
|
|
4359
3796
|
this.messageService.add({
|
|
@@ -4367,16 +3804,14 @@ class ProfilePageComponent {
|
|
|
4367
3804
|
newPassword: '',
|
|
4368
3805
|
confirmPassword: '',
|
|
4369
3806
|
});
|
|
4370
|
-
this.isPasswordLoading.set(false);
|
|
4371
3807
|
},
|
|
4372
3808
|
error: () => {
|
|
4373
3809
|
// Error toast handled by global interceptor
|
|
4374
|
-
this.isPasswordLoading.set(false);
|
|
4375
3810
|
},
|
|
4376
3811
|
});
|
|
4377
3812
|
}
|
|
4378
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
4379
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
3813
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfilePageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3814
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: ProfilePageComponent, isStandalone: true, selector: "lib-profile-page", ngImport: i0, template: `
|
|
4380
3815
|
<!-- Page Header -->
|
|
4381
3816
|
<div class="mb-4 md:mb-6">
|
|
4382
3817
|
<h1 class="text-2xl font-bold text-color mb-2">My Profile</h1>
|
|
@@ -4463,7 +3898,7 @@ class ProfilePageComponent {
|
|
|
4463
3898
|
icon="pi pi-check"
|
|
4464
3899
|
type="submit"
|
|
4465
3900
|
[loading]="isLoading()"
|
|
4466
|
-
[disabled]="!isFormValid() || isLoading()" />
|
|
3901
|
+
[disabled]="!isFormValid() || !isFormDirty() || isLoading()" />
|
|
4467
3902
|
</div>
|
|
4468
3903
|
</form>
|
|
4469
3904
|
</div>
|
|
@@ -4627,13 +4062,12 @@ class ProfilePageComponent {
|
|
|
4627
4062
|
}
|
|
4628
4063
|
</div>
|
|
4629
4064
|
}
|
|
4630
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
4065
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2$2.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "component", type: i5$1.Password, selector: "p-password", inputs: ["ariaLabel", "ariaLabelledBy", "label", "promptLabel", "mediumRegex", "strongRegex", "weakLabel", "mediumLabel", "maxLength", "strongLabel", "inputId", "feedback", "toggleMask", "inputStyleClass", "styleClass", "inputStyle", "showTransitionOptions", "hideTransitionOptions", "autocomplete", "placeholder", "showClear", "autofocus", "tabindex", "appendTo", "motionOptions", "overlayOptions"], outputs: ["onFocus", "onBlur", "onClear"] }, { kind: "component", type: i6.Skeleton, selector: "p-skeleton", inputs: ["styleClass", "shape", "animation", "borderRadius", "size", "width", "height"] }, { kind: "component", type: i4$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: FileUploaderComponent, selector: "lib-file-uploader", inputs: ["uploadFile", "acceptTypes", "multiple", "maxFiles", "maxSizeMb", "uploadOptions", "disabled", "showPreview", "autoUpload"], outputs: ["fileUploaded", "filesUploaded", "onError", "fileSelected"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4631
4066
|
}
|
|
4632
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
4067
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ProfilePageComponent, decorators: [{
|
|
4633
4068
|
type: Component,
|
|
4634
4069
|
args: [{
|
|
4635
4070
|
selector: 'lib-profile-page',
|
|
4636
|
-
standalone: true,
|
|
4637
4071
|
imports: [AngularModule, PrimeModule, FormField, FileUploaderComponent],
|
|
4638
4072
|
template: `
|
|
4639
4073
|
<!-- Page Header -->
|
|
@@ -4722,7 +4156,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4722
4156
|
icon="pi pi-check"
|
|
4723
4157
|
type="submit"
|
|
4724
4158
|
[loading]="isLoading()"
|
|
4725
|
-
[disabled]="!isFormValid() || isLoading()" />
|
|
4159
|
+
[disabled]="!isFormValid() || !isFormDirty() || isLoading()" />
|
|
4726
4160
|
</div>
|
|
4727
4161
|
</form>
|
|
4728
4162
|
</div>
|
|
@@ -4922,7 +4356,9 @@ class RegisterPageComponent {
|
|
|
4922
4356
|
required(f.email, { message: 'Email is required' });
|
|
4923
4357
|
email(f.email, { message: 'Please enter a valid email address' });
|
|
4924
4358
|
required(f.password, { message: 'Password is required' });
|
|
4925
|
-
minLength(f.password,
|
|
4359
|
+
minLength(f.password, 8, {
|
|
4360
|
+
message: 'Password must be at least 8 characters',
|
|
4361
|
+
});
|
|
4926
4362
|
required(f.confirmPassword, { message: 'Please confirm your password' });
|
|
4927
4363
|
});
|
|
4928
4364
|
companyFormModel = signal({
|
|
@@ -4942,7 +4378,7 @@ class RegisterPageComponent {
|
|
|
4942
4378
|
const userModel = this.userFormModel();
|
|
4943
4379
|
const userValid = userModel.name.trim().length >= 2 &&
|
|
4944
4380
|
userModel.email.includes('@') &&
|
|
4945
|
-
userModel.password.length >=
|
|
4381
|
+
userModel.password.length >= 8 &&
|
|
4946
4382
|
userModel.confirmPassword.length > 0 &&
|
|
4947
4383
|
this.passwordsMatch();
|
|
4948
4384
|
if (!isCompanyFeatureEnabled(this.appConfig))
|
|
@@ -5040,7 +4476,6 @@ class RegisterPageComponent {
|
|
|
5040
4476
|
user: response.user,
|
|
5041
4477
|
company: response.company,
|
|
5042
4478
|
branch: response.branch,
|
|
5043
|
-
companyFeatureEnabled: response.companyFeatureEnabled,
|
|
5044
4479
|
});
|
|
5045
4480
|
this.messageService.add({
|
|
5046
4481
|
severity: 'success',
|
|
@@ -5052,12 +4487,11 @@ class RegisterPageComponent {
|
|
|
5052
4487
|
},
|
|
5053
4488
|
error: () => {
|
|
5054
4489
|
this.isLoading.set(false);
|
|
5055
|
-
// Error toast handled by global interceptor
|
|
5056
4490
|
},
|
|
5057
4491
|
});
|
|
5058
4492
|
}
|
|
5059
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
5060
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
4493
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RegisterPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4494
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: RegisterPageComponent, isStandalone: true, selector: "lib-register-page", inputs: { returnUrl: { classPropertyName: "returnUrl", publicName: "returnUrl", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
5061
4495
|
<app-floating-configurator />
|
|
5062
4496
|
<div
|
|
5063
4497
|
class="bg-surface-ground flex items-center justify-center min-h-screen py-8"
|
|
@@ -5068,7 +4502,8 @@ class RegisterPageComponent {
|
|
|
5068
4502
|
>
|
|
5069
4503
|
<div
|
|
5070
4504
|
class="py-8 px-4 sm:py-10 sm:px-8 md:py-12 md:px-12 w-full sm:w-[650px] max-w-[95vw] sm:max-w-[90vw]"
|
|
5071
|
-
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background)"
|
|
4505
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background)"
|
|
4506
|
+
>
|
|
5072
4507
|
<div class="text-center mb-6">
|
|
5073
4508
|
<div class="text-3xl font-medium mb-4 text-color">
|
|
5074
4509
|
Create Account
|
|
@@ -5134,7 +4569,7 @@ class RegisterPageComponent {
|
|
|
5134
4569
|
id="password"
|
|
5135
4570
|
type="password"
|
|
5136
4571
|
[formField]="userForm.password"
|
|
5137
|
-
placeholder="Min.
|
|
4572
|
+
placeholder="Min. 8 characters"
|
|
5138
4573
|
class="w-full"
|
|
5139
4574
|
autocomplete="new-password"
|
|
5140
4575
|
/>
|
|
@@ -5154,110 +4589,115 @@ class RegisterPageComponent {
|
|
|
5154
4589
|
autocomplete="new-password"
|
|
5155
4590
|
/>
|
|
5156
4591
|
@if (!passwordsMatch() && userFormModel().confirmPassword) {
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
4592
|
+
<small class="text-danger block mt-2"
|
|
4593
|
+
>Passwords do not match</small
|
|
4594
|
+
>
|
|
5160
4595
|
}
|
|
5161
4596
|
</div>
|
|
5162
4597
|
</div>
|
|
5163
4598
|
|
|
5164
4599
|
@if (companyFeatureEnabled()) {
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
>
|
|
5173
|
-
</div>
|
|
5174
|
-
|
|
5175
|
-
<div class="mb-4">
|
|
5176
|
-
<label class="p-checkbox-native" (click)="onCreateCompanyToggle()">
|
|
5177
|
-
<input
|
|
5178
|
-
type="checkbox"
|
|
5179
|
-
[formField]="companyForm.createNewCompany"
|
|
5180
|
-
/>
|
|
5181
|
-
<span>Create new company</span>
|
|
5182
|
-
</label>
|
|
5183
|
-
</div>
|
|
5184
|
-
|
|
5185
|
-
@if (!companyFormModel().createNewCompany) {
|
|
5186
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
5187
|
-
<div>
|
|
5188
|
-
<label for="companySlug" class="block font-medium mb-2"
|
|
5189
|
-
>Company Code</label
|
|
5190
|
-
>
|
|
5191
|
-
<input
|
|
5192
|
-
pInputText
|
|
5193
|
-
id="companySlug"
|
|
5194
|
-
[formField]="companyForm.companySlug"
|
|
5195
|
-
placeholder="Company code/slug"
|
|
5196
|
-
class="w-full"
|
|
5197
|
-
/>
|
|
5198
|
-
<small class="text-muted-color"
|
|
5199
|
-
>Enter company code to join</small
|
|
4600
|
+
<div class="border-t border-surface pt-6 mt-6">
|
|
4601
|
+
<div class="text-center mb-6">
|
|
4602
|
+
<div class="text-2xl font-medium mb-2 text-color">
|
|
4603
|
+
Company Details
|
|
4604
|
+
</div>
|
|
4605
|
+
<span class="text-muted-color font-medium"
|
|
4606
|
+
>Join existing or create new company</span
|
|
5200
4607
|
>
|
|
5201
4608
|
</div>
|
|
5202
4609
|
|
|
5203
|
-
<div>
|
|
5204
|
-
<label
|
|
5205
|
-
|
|
4610
|
+
<div class="mb-4">
|
|
4611
|
+
<label
|
|
4612
|
+
class="p-checkbox-native"
|
|
4613
|
+
(click)="onCreateCompanyToggle()"
|
|
5206
4614
|
>
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
/>
|
|
4615
|
+
<input
|
|
4616
|
+
type="checkbox"
|
|
4617
|
+
[formField]="companyForm.createNewCompany"
|
|
4618
|
+
/>
|
|
4619
|
+
<span>Create new company</span>
|
|
4620
|
+
</label>
|
|
5214
4621
|
</div>
|
|
5215
|
-
</div>
|
|
5216
|
-
} @else {
|
|
5217
|
-
<div class="mb-4">
|
|
5218
|
-
<label for="newCompanyName" class="block font-medium mb-2"
|
|
5219
|
-
>Company Name *</label
|
|
5220
|
-
>
|
|
5221
|
-
<input
|
|
5222
|
-
pInputText
|
|
5223
|
-
id="newCompanyName"
|
|
5224
|
-
[formField]="companyForm.newCompanyName"
|
|
5225
|
-
placeholder="Your company name"
|
|
5226
|
-
class="w-full"
|
|
5227
|
-
/>
|
|
5228
|
-
</div>
|
|
5229
4622
|
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
4623
|
+
@if (!companyFormModel().createNewCompany) {
|
|
4624
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
4625
|
+
<div>
|
|
4626
|
+
<label for="companySlug" class="block font-medium mb-2"
|
|
4627
|
+
>Company Code</label
|
|
4628
|
+
>
|
|
4629
|
+
<input
|
|
4630
|
+
pInputText
|
|
4631
|
+
id="companySlug"
|
|
4632
|
+
[formField]="companyForm.companySlug"
|
|
4633
|
+
placeholder="Company code/slug"
|
|
4634
|
+
class="w-full"
|
|
4635
|
+
/>
|
|
4636
|
+
<small class="text-muted-color"
|
|
4637
|
+
>Enter company code to join</small
|
|
4638
|
+
>
|
|
4639
|
+
</div>
|
|
4640
|
+
|
|
4641
|
+
<div>
|
|
4642
|
+
<label for="branchSlug" class="block font-medium mb-2"
|
|
4643
|
+
>Branch Code</label
|
|
4644
|
+
>
|
|
4645
|
+
<input
|
|
4646
|
+
pInputText
|
|
4647
|
+
id="branchSlug"
|
|
4648
|
+
[formField]="companyForm.branchSlug"
|
|
4649
|
+
placeholder="Branch code (optional)"
|
|
4650
|
+
class="w-full"
|
|
4651
|
+
/>
|
|
4652
|
+
</div>
|
|
4653
|
+
</div>
|
|
4654
|
+
} @else {
|
|
4655
|
+
<div class="mb-4">
|
|
4656
|
+
<label for="newCompanyName" class="block font-medium mb-2"
|
|
4657
|
+
>Company Name *</label
|
|
4658
|
+
>
|
|
4659
|
+
<input
|
|
4660
|
+
pInputText
|
|
4661
|
+
id="newCompanyName"
|
|
4662
|
+
[formField]="companyForm.newCompanyName"
|
|
4663
|
+
placeholder="Your company name"
|
|
4664
|
+
class="w-full"
|
|
4665
|
+
/>
|
|
4666
|
+
</div>
|
|
5243
4667
|
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
4668
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
4669
|
+
<div>
|
|
4670
|
+
<label
|
|
4671
|
+
for="newCompanyPhone"
|
|
4672
|
+
class="block font-medium mb-2"
|
|
4673
|
+
>Company Phone</label
|
|
4674
|
+
>
|
|
4675
|
+
<input
|
|
4676
|
+
pInputText
|
|
4677
|
+
id="newCompanyPhone"
|
|
4678
|
+
[formField]="companyForm.newCompanyPhone"
|
|
4679
|
+
placeholder="Company phone"
|
|
4680
|
+
class="w-full"
|
|
4681
|
+
/>
|
|
4682
|
+
</div>
|
|
4683
|
+
|
|
4684
|
+
<div>
|
|
4685
|
+
<label
|
|
4686
|
+
for="newCompanyAddress"
|
|
4687
|
+
class="block font-medium mb-2"
|
|
4688
|
+
>Address</label
|
|
4689
|
+
>
|
|
4690
|
+
<input
|
|
4691
|
+
pInputText
|
|
4692
|
+
id="newCompanyAddress"
|
|
4693
|
+
[formField]="companyForm.newCompanyAddress"
|
|
4694
|
+
placeholder="Company address"
|
|
4695
|
+
class="w-full"
|
|
4696
|
+
/>
|
|
4697
|
+
</div>
|
|
4698
|
+
</div>
|
|
4699
|
+
}
|
|
5258
4700
|
</div>
|
|
5259
|
-
}
|
|
5260
|
-
</div>
|
|
5261
4701
|
}
|
|
5262
4702
|
|
|
5263
4703
|
<p-button
|
|
@@ -5282,13 +4722,12 @@ class RegisterPageComponent {
|
|
|
5282
4722
|
</div>
|
|
5283
4723
|
</div>
|
|
5284
4724
|
</div>
|
|
5285
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
4725
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5286
4726
|
}
|
|
5287
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
4727
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: RegisterPageComponent, decorators: [{
|
|
5288
4728
|
type: Component,
|
|
5289
4729
|
args: [{
|
|
5290
4730
|
selector: 'lib-register-page',
|
|
5291
|
-
standalone: true,
|
|
5292
4731
|
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
5293
4732
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
5294
4733
|
template: `
|
|
@@ -5302,7 +4741,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
5302
4741
|
>
|
|
5303
4742
|
<div
|
|
5304
4743
|
class="py-8 px-4 sm:py-10 sm:px-8 md:py-12 md:px-12 w-full sm:w-[650px] max-w-[95vw] sm:max-w-[90vw]"
|
|
5305
|
-
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background)"
|
|
4744
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background)"
|
|
4745
|
+
>
|
|
5306
4746
|
<div class="text-center mb-6">
|
|
5307
4747
|
<div class="text-3xl font-medium mb-4 text-color">
|
|
5308
4748
|
Create Account
|
|
@@ -5368,7 +4808,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
5368
4808
|
id="password"
|
|
5369
4809
|
type="password"
|
|
5370
4810
|
[formField]="userForm.password"
|
|
5371
|
-
placeholder="Min.
|
|
4811
|
+
placeholder="Min. 8 characters"
|
|
5372
4812
|
class="w-full"
|
|
5373
4813
|
autocomplete="new-password"
|
|
5374
4814
|
/>
|
|
@@ -5388,110 +4828,115 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
5388
4828
|
autocomplete="new-password"
|
|
5389
4829
|
/>
|
|
5390
4830
|
@if (!passwordsMatch() && userFormModel().confirmPassword) {
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
4831
|
+
<small class="text-danger block mt-2"
|
|
4832
|
+
>Passwords do not match</small
|
|
4833
|
+
>
|
|
5394
4834
|
}
|
|
5395
4835
|
</div>
|
|
5396
4836
|
</div>
|
|
5397
4837
|
|
|
5398
4838
|
@if (companyFeatureEnabled()) {
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
>
|
|
5407
|
-
</div>
|
|
5408
|
-
|
|
5409
|
-
<div class="mb-4">
|
|
5410
|
-
<label class="p-checkbox-native" (click)="onCreateCompanyToggle()">
|
|
5411
|
-
<input
|
|
5412
|
-
type="checkbox"
|
|
5413
|
-
[formField]="companyForm.createNewCompany"
|
|
5414
|
-
/>
|
|
5415
|
-
<span>Create new company</span>
|
|
5416
|
-
</label>
|
|
5417
|
-
</div>
|
|
5418
|
-
|
|
5419
|
-
@if (!companyFormModel().createNewCompany) {
|
|
5420
|
-
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
5421
|
-
<div>
|
|
5422
|
-
<label for="companySlug" class="block font-medium mb-2"
|
|
5423
|
-
>Company Code</label
|
|
5424
|
-
>
|
|
5425
|
-
<input
|
|
5426
|
-
pInputText
|
|
5427
|
-
id="companySlug"
|
|
5428
|
-
[formField]="companyForm.companySlug"
|
|
5429
|
-
placeholder="Company code/slug"
|
|
5430
|
-
class="w-full"
|
|
5431
|
-
/>
|
|
5432
|
-
<small class="text-muted-color"
|
|
5433
|
-
>Enter company code to join</small
|
|
4839
|
+
<div class="border-t border-surface pt-6 mt-6">
|
|
4840
|
+
<div class="text-center mb-6">
|
|
4841
|
+
<div class="text-2xl font-medium mb-2 text-color">
|
|
4842
|
+
Company Details
|
|
4843
|
+
</div>
|
|
4844
|
+
<span class="text-muted-color font-medium"
|
|
4845
|
+
>Join existing or create new company</span
|
|
5434
4846
|
>
|
|
5435
4847
|
</div>
|
|
5436
4848
|
|
|
5437
|
-
<div>
|
|
5438
|
-
<label
|
|
5439
|
-
|
|
4849
|
+
<div class="mb-4">
|
|
4850
|
+
<label
|
|
4851
|
+
class="p-checkbox-native"
|
|
4852
|
+
(click)="onCreateCompanyToggle()"
|
|
5440
4853
|
>
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
/>
|
|
4854
|
+
<input
|
|
4855
|
+
type="checkbox"
|
|
4856
|
+
[formField]="companyForm.createNewCompany"
|
|
4857
|
+
/>
|
|
4858
|
+
<span>Create new company</span>
|
|
4859
|
+
</label>
|
|
5448
4860
|
</div>
|
|
5449
|
-
</div>
|
|
5450
|
-
} @else {
|
|
5451
|
-
<div class="mb-4">
|
|
5452
|
-
<label for="newCompanyName" class="block font-medium mb-2"
|
|
5453
|
-
>Company Name *</label
|
|
5454
|
-
>
|
|
5455
|
-
<input
|
|
5456
|
-
pInputText
|
|
5457
|
-
id="newCompanyName"
|
|
5458
|
-
[formField]="companyForm.newCompanyName"
|
|
5459
|
-
placeholder="Your company name"
|
|
5460
|
-
class="w-full"
|
|
5461
|
-
/>
|
|
5462
|
-
</div>
|
|
5463
4861
|
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
4862
|
+
@if (!companyFormModel().createNewCompany) {
|
|
4863
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
4864
|
+
<div>
|
|
4865
|
+
<label for="companySlug" class="block font-medium mb-2"
|
|
4866
|
+
>Company Code</label
|
|
4867
|
+
>
|
|
4868
|
+
<input
|
|
4869
|
+
pInputText
|
|
4870
|
+
id="companySlug"
|
|
4871
|
+
[formField]="companyForm.companySlug"
|
|
4872
|
+
placeholder="Company code/slug"
|
|
4873
|
+
class="w-full"
|
|
4874
|
+
/>
|
|
4875
|
+
<small class="text-muted-color"
|
|
4876
|
+
>Enter company code to join</small
|
|
4877
|
+
>
|
|
4878
|
+
</div>
|
|
4879
|
+
|
|
4880
|
+
<div>
|
|
4881
|
+
<label for="branchSlug" class="block font-medium mb-2"
|
|
4882
|
+
>Branch Code</label
|
|
4883
|
+
>
|
|
4884
|
+
<input
|
|
4885
|
+
pInputText
|
|
4886
|
+
id="branchSlug"
|
|
4887
|
+
[formField]="companyForm.branchSlug"
|
|
4888
|
+
placeholder="Branch code (optional)"
|
|
4889
|
+
class="w-full"
|
|
4890
|
+
/>
|
|
4891
|
+
</div>
|
|
4892
|
+
</div>
|
|
4893
|
+
} @else {
|
|
4894
|
+
<div class="mb-4">
|
|
4895
|
+
<label for="newCompanyName" class="block font-medium mb-2"
|
|
4896
|
+
>Company Name *</label
|
|
4897
|
+
>
|
|
4898
|
+
<input
|
|
4899
|
+
pInputText
|
|
4900
|
+
id="newCompanyName"
|
|
4901
|
+
[formField]="companyForm.newCompanyName"
|
|
4902
|
+
placeholder="Your company name"
|
|
4903
|
+
class="w-full"
|
|
4904
|
+
/>
|
|
4905
|
+
</div>
|
|
5477
4906
|
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
4907
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
4908
|
+
<div>
|
|
4909
|
+
<label
|
|
4910
|
+
for="newCompanyPhone"
|
|
4911
|
+
class="block font-medium mb-2"
|
|
4912
|
+
>Company Phone</label
|
|
4913
|
+
>
|
|
4914
|
+
<input
|
|
4915
|
+
pInputText
|
|
4916
|
+
id="newCompanyPhone"
|
|
4917
|
+
[formField]="companyForm.newCompanyPhone"
|
|
4918
|
+
placeholder="Company phone"
|
|
4919
|
+
class="w-full"
|
|
4920
|
+
/>
|
|
4921
|
+
</div>
|
|
4922
|
+
|
|
4923
|
+
<div>
|
|
4924
|
+
<label
|
|
4925
|
+
for="newCompanyAddress"
|
|
4926
|
+
class="block font-medium mb-2"
|
|
4927
|
+
>Address</label
|
|
4928
|
+
>
|
|
4929
|
+
<input
|
|
4930
|
+
pInputText
|
|
4931
|
+
id="newCompanyAddress"
|
|
4932
|
+
[formField]="companyForm.newCompanyAddress"
|
|
4933
|
+
placeholder="Company address"
|
|
4934
|
+
class="w-full"
|
|
4935
|
+
/>
|
|
4936
|
+
</div>
|
|
4937
|
+
</div>
|
|
4938
|
+
}
|
|
5492
4939
|
</div>
|
|
5493
|
-
}
|
|
5494
|
-
</div>
|
|
5495
4940
|
}
|
|
5496
4941
|
|
|
5497
4942
|
<p-button
|
|
@@ -5538,7 +4983,7 @@ class ResetPasswordPageComponent {
|
|
|
5538
4983
|
formModel = signal({ newPassword: '', confirmPassword: '' }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
5539
4984
|
resetForm = form(this.formModel, (f) => {
|
|
5540
4985
|
required(f.newPassword, { message: 'New password is required' });
|
|
5541
|
-
minLength(f.newPassword,
|
|
4986
|
+
minLength(f.newPassword, 8, { message: 'Password must be at least 8 characters' });
|
|
5542
4987
|
required(f.confirmPassword, { message: 'Please confirm your password' });
|
|
5543
4988
|
});
|
|
5544
4989
|
passwordMismatch = computed(() => {
|
|
@@ -5571,10 +5016,9 @@ class ResetPasswordPageComponent {
|
|
|
5571
5016
|
const newPassword = this.formModel().newPassword;
|
|
5572
5017
|
this.authApi
|
|
5573
5018
|
.resetPassword(this.token, newPassword)
|
|
5574
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5019
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoading.set(false)))
|
|
5575
5020
|
.subscribe({
|
|
5576
5021
|
next: () => {
|
|
5577
|
-
this.isLoading.set(false);
|
|
5578
5022
|
this.messageService.add({
|
|
5579
5023
|
severity: 'success',
|
|
5580
5024
|
summary: 'Success',
|
|
@@ -5583,7 +5027,6 @@ class ResetPasswordPageComponent {
|
|
|
5583
5027
|
this.pageState.set('success');
|
|
5584
5028
|
},
|
|
5585
5029
|
error: (error) => {
|
|
5586
|
-
this.isLoading.set(false);
|
|
5587
5030
|
const message = error.error?.message || '';
|
|
5588
5031
|
// Show invalid-token state for expired/invalid token errors
|
|
5589
5032
|
if (message.toLowerCase().includes('expired') || message.toLowerCase().includes('invalid')) {
|
|
@@ -5593,8 +5036,8 @@ class ResetPasswordPageComponent {
|
|
|
5593
5036
|
},
|
|
5594
5037
|
});
|
|
5595
5038
|
}
|
|
5596
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
5597
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
5039
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ResetPasswordPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5040
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: ResetPasswordPageComponent, isStandalone: true, selector: "lib-reset-password-page", ngImport: i0, template: `
|
|
5598
5041
|
<app-floating-configurator />
|
|
5599
5042
|
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
5600
5043
|
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
@@ -5705,13 +5148,12 @@ class ResetPasswordPageComponent {
|
|
|
5705
5148
|
</div>
|
|
5706
5149
|
</div>
|
|
5707
5150
|
</div>
|
|
5708
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
5151
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5709
5152
|
}
|
|
5710
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
5153
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: ResetPasswordPageComponent, decorators: [{
|
|
5711
5154
|
type: Component,
|
|
5712
5155
|
args: [{
|
|
5713
5156
|
selector: 'lib-reset-password-page',
|
|
5714
|
-
standalone: true,
|
|
5715
5157
|
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
5716
5158
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
5717
5159
|
template: `
|
|
@@ -5888,8 +5330,7 @@ class UserFormComponent {
|
|
|
5888
5330
|
// Basic validation
|
|
5889
5331
|
const nameValid = model.name.trim().length > 0;
|
|
5890
5332
|
const emailValid = model.email.includes('@');
|
|
5891
|
-
|
|
5892
|
-
const passwordValid = isEdit ? true : model.password.length >= 6;
|
|
5333
|
+
const passwordValid = isEdit ? true : model.password.length >= 8;
|
|
5893
5334
|
return nameValid && emailValid && passwordValid;
|
|
5894
5335
|
}, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
5895
5336
|
constructor() {
|
|
@@ -5907,29 +5348,27 @@ class UserFormComponent {
|
|
|
5907
5348
|
loadUser(id) {
|
|
5908
5349
|
this.isLoading.set(true);
|
|
5909
5350
|
this.userService.findById(id)
|
|
5910
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5351
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoading.set(false)))
|
|
5911
5352
|
.subscribe({
|
|
5912
5353
|
next: (response) => {
|
|
5913
5354
|
if (response.success && response.data) {
|
|
5914
5355
|
this.existingUser.set(response.data);
|
|
5915
|
-
this.formModel.set(
|
|
5916
|
-
name: response.data.name,
|
|
5917
|
-
email: response.data.email,
|
|
5918
|
-
password: '',
|
|
5919
|
-
phone: response.data.phone || '',
|
|
5920
|
-
isActive: response.data.isActive,
|
|
5921
|
-
emailVerified: response.data.emailVerified,
|
|
5922
|
-
});
|
|
5356
|
+
this.formModel.set(this.toFormModel(response.data));
|
|
5923
5357
|
}
|
|
5924
|
-
this.isLoading.set(false);
|
|
5925
|
-
},
|
|
5926
|
-
error: () => {
|
|
5927
|
-
// Error toast handled by global interceptor
|
|
5928
|
-
this.router.navigate(['/administration/users']);
|
|
5929
|
-
this.isLoading.set(false);
|
|
5930
5358
|
},
|
|
5359
|
+
error: () => this.router.navigate(['/administration/users']),
|
|
5931
5360
|
});
|
|
5932
5361
|
}
|
|
5362
|
+
toFormModel(user) {
|
|
5363
|
+
return {
|
|
5364
|
+
name: user.name,
|
|
5365
|
+
email: user.email,
|
|
5366
|
+
password: '',
|
|
5367
|
+
phone: user.phone || '',
|
|
5368
|
+
isActive: user.isActive,
|
|
5369
|
+
emailVerified: user.emailVerified,
|
|
5370
|
+
};
|
|
5371
|
+
}
|
|
5933
5372
|
onSubmit() {
|
|
5934
5373
|
if (!this.isFormValid()) {
|
|
5935
5374
|
this.messageService.add({
|
|
@@ -5940,72 +5379,56 @@ class UserFormComponent {
|
|
|
5940
5379
|
return;
|
|
5941
5380
|
}
|
|
5942
5381
|
this.isLoading.set(true);
|
|
5943
|
-
const model = this.formModel();
|
|
5944
5382
|
if (this.isEditMode() && this.existingUser()) {
|
|
5945
|
-
|
|
5946
|
-
const updateData = {
|
|
5947
|
-
id: this.existingUser().id,
|
|
5948
|
-
name: model.name,
|
|
5949
|
-
email: model.email,
|
|
5950
|
-
phone: model.phone || undefined,
|
|
5951
|
-
isActive: model.isActive,
|
|
5952
|
-
emailVerified: model.emailVerified,
|
|
5953
|
-
};
|
|
5954
|
-
// Only include password if provided
|
|
5955
|
-
if (model.password) {
|
|
5956
|
-
updateData.password = model.password;
|
|
5957
|
-
}
|
|
5958
|
-
this.userService.update(updateData)
|
|
5959
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5960
|
-
.subscribe({
|
|
5961
|
-
next: () => {
|
|
5962
|
-
this.messageService.add({
|
|
5963
|
-
severity: 'success',
|
|
5964
|
-
summary: 'Success',
|
|
5965
|
-
detail: 'User updated successfully.',
|
|
5966
|
-
});
|
|
5967
|
-
this.router.navigate(['/administration/users']);
|
|
5968
|
-
this.isLoading.set(false);
|
|
5969
|
-
},
|
|
5970
|
-
error: () => {
|
|
5971
|
-
// Error toast handled by global interceptor
|
|
5972
|
-
this.isLoading.set(false);
|
|
5973
|
-
},
|
|
5974
|
-
});
|
|
5383
|
+
this.updateUser();
|
|
5975
5384
|
}
|
|
5976
5385
|
else {
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
5981
|
-
|
|
5982
|
-
|
|
5983
|
-
|
|
5984
|
-
|
|
5985
|
-
|
|
5986
|
-
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
const userId = createResponse.data.id;
|
|
5993
|
-
this.assignUserToCompanyAndBranch(userId);
|
|
5994
|
-
}
|
|
5995
|
-
this.messageService.add({
|
|
5996
|
-
severity: 'success',
|
|
5997
|
-
summary: 'Success',
|
|
5998
|
-
detail: 'User created successfully.',
|
|
5999
|
-
});
|
|
6000
|
-
this.router.navigate(['/administration/users']);
|
|
6001
|
-
this.isLoading.set(false);
|
|
6002
|
-
},
|
|
6003
|
-
error: () => {
|
|
6004
|
-
// Error toast handled by global interceptor
|
|
6005
|
-
this.isLoading.set(false);
|
|
6006
|
-
},
|
|
6007
|
-
});
|
|
5386
|
+
this.createUser();
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
updateUser() {
|
|
5390
|
+
const model = this.formModel();
|
|
5391
|
+
const updateData = {
|
|
5392
|
+
id: this.existingUser().id,
|
|
5393
|
+
name: model.name,
|
|
5394
|
+
email: model.email,
|
|
5395
|
+
phone: model.phone || undefined,
|
|
5396
|
+
isActive: model.isActive,
|
|
5397
|
+
emailVerified: model.emailVerified,
|
|
5398
|
+
};
|
|
5399
|
+
if (model.password) {
|
|
5400
|
+
updateData.password = model.password;
|
|
6008
5401
|
}
|
|
5402
|
+
this.userService.update(updateData)
|
|
5403
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoading.set(false)))
|
|
5404
|
+
.subscribe({
|
|
5405
|
+
next: () => this.onSaveSuccess('User updated successfully.'),
|
|
5406
|
+
});
|
|
5407
|
+
}
|
|
5408
|
+
createUser() {
|
|
5409
|
+
const model = this.formModel();
|
|
5410
|
+
const createData = {
|
|
5411
|
+
name: model.name,
|
|
5412
|
+
email: model.email,
|
|
5413
|
+
password: model.password,
|
|
5414
|
+
phone: model.phone || undefined,
|
|
5415
|
+
isActive: model.isActive,
|
|
5416
|
+
emailVerified: model.emailVerified,
|
|
5417
|
+
};
|
|
5418
|
+
this.userService.insert(createData)
|
|
5419
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.isLoading.set(false)))
|
|
5420
|
+
.subscribe({
|
|
5421
|
+
next: (response) => {
|
|
5422
|
+
if (this.companyFeatureEnabled() && response.success && response.data) {
|
|
5423
|
+
this.assignUserToCompanyAndBranch(response.data.id);
|
|
5424
|
+
}
|
|
5425
|
+
this.onSaveSuccess('User created successfully.');
|
|
5426
|
+
},
|
|
5427
|
+
});
|
|
5428
|
+
}
|
|
5429
|
+
onSaveSuccess(message) {
|
|
5430
|
+
this.messageService.add({ severity: 'success', summary: 'Success', detail: message });
|
|
5431
|
+
this.router.navigate(['/administration/users']);
|
|
6009
5432
|
}
|
|
6010
5433
|
/**
|
|
6011
5434
|
* Assign user to current company and branch
|
|
@@ -6035,8 +5458,8 @@ class UserFormComponent {
|
|
|
6035
5458
|
onCancel() {
|
|
6036
5459
|
this.router.navigate(['/administration/users']);
|
|
6037
5460
|
}
|
|
6038
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
6039
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
5461
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5462
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: UserFormComponent, isStandalone: true, selector: "lib-user-form", ngImport: i0, template: `
|
|
6040
5463
|
<p-card>
|
|
6041
5464
|
<ng-template #header>
|
|
6042
5465
|
<div class="flex justify-between items-center p-4">
|
|
@@ -6138,13 +5561,12 @@ class UserFormComponent {
|
|
|
6138
5561
|
</div>
|
|
6139
5562
|
</form>
|
|
6140
5563
|
</p-card>
|
|
6141
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
5564
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6142
5565
|
}
|
|
6143
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
5566
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserFormComponent, decorators: [{
|
|
6144
5567
|
type: Component,
|
|
6145
5568
|
args: [{
|
|
6146
5569
|
selector: 'lib-user-form',
|
|
6147
|
-
standalone: true,
|
|
6148
5570
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
6149
5571
|
imports: [AngularModule, PrimeModule, FormField],
|
|
6150
5572
|
template: `
|
|
@@ -6258,34 +5680,19 @@ var userForm_component = /*#__PURE__*/Object.freeze({
|
|
|
6258
5680
|
UserFormComponent: UserFormComponent
|
|
6259
5681
|
});
|
|
6260
5682
|
|
|
6261
|
-
/**
|
|
6262
|
-
* User Company Permission Dialog
|
|
6263
|
-
* Shows all companies with checkboxes
|
|
6264
|
-
* Initially assigned companies are checked
|
|
6265
|
-
* Check to assign, uncheck to revoke
|
|
6266
|
-
*/
|
|
6267
5683
|
class UserCompanyPermissionDialogComponent {
|
|
6268
5684
|
companyApi = inject(CompanyApiService);
|
|
6269
5685
|
permissionApi = inject(UserPermissionApiService);
|
|
6270
5686
|
messageService = inject(MessageService);
|
|
6271
|
-
/** Dialog visibility */
|
|
6272
5687
|
visible = input.required(...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
6273
|
-
/** User to manage permissions for */
|
|
6274
5688
|
user = input.required(...(ngDevMode ? [{ debugName: "user" }] : []));
|
|
6275
|
-
/** Close event */
|
|
6276
5689
|
closed = output();
|
|
6277
|
-
/** Refresh event after changes */
|
|
6278
5690
|
permissionsChanged = output();
|
|
6279
|
-
/** Loading state */
|
|
6280
5691
|
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
6281
|
-
/** Saving state */
|
|
6282
5692
|
isSaving = signal(false, ...(ngDevMode ? [{ debugName: "isSaving" }] : []));
|
|
6283
|
-
/** Companies with assignment status */
|
|
6284
5693
|
companies = signal([], ...(ngDevMode ? [{ debugName: "companies" }] : []));
|
|
6285
|
-
/** Currently assigned company IDs */
|
|
6286
5694
|
assignedCompanyIds = new Set();
|
|
6287
5695
|
constructor() {
|
|
6288
|
-
// Load companies and permissions when dialog becomes visible
|
|
6289
5696
|
effect(() => {
|
|
6290
5697
|
if (this.visible() && this.user()) {
|
|
6291
5698
|
this.loadData();
|
|
@@ -6298,23 +5705,19 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6298
5705
|
return;
|
|
6299
5706
|
this.isLoading.set(true);
|
|
6300
5707
|
try {
|
|
6301
|
-
// Load all companies
|
|
6302
5708
|
const companiesResponse = await firstValueFrom(this.companyApi.getAll('', {
|
|
6303
5709
|
pagination: { currentPage: 0, pageSize: 1000 },
|
|
6304
5710
|
filter: {},
|
|
6305
5711
|
select: [],
|
|
6306
5712
|
sort: {},
|
|
6307
5713
|
}));
|
|
6308
|
-
// Load user's assigned companies
|
|
6309
5714
|
const assignedResponse = await firstValueFrom(this.permissionApi.getUserCompanies(currentUser.id));
|
|
6310
|
-
// Build set of assigned company IDs
|
|
6311
5715
|
this.assignedCompanyIds.clear();
|
|
6312
5716
|
if (assignedResponse.data) {
|
|
6313
5717
|
assignedResponse.data.forEach((permission) => {
|
|
6314
5718
|
this.assignedCompanyIds.add(permission.companyId);
|
|
6315
5719
|
});
|
|
6316
5720
|
}
|
|
6317
|
-
// Map companies with assignment status
|
|
6318
5721
|
const companiesWithStatus = (companiesResponse.data || []).map((company) => ({
|
|
6319
5722
|
...company,
|
|
6320
5723
|
isAssigned: this.assignedCompanyIds.has(company.id),
|
|
@@ -6322,7 +5725,6 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6322
5725
|
this.companies.set(companiesWithStatus);
|
|
6323
5726
|
}
|
|
6324
5727
|
catch {
|
|
6325
|
-
// Error toast handled by global interceptor
|
|
6326
5728
|
}
|
|
6327
5729
|
finally {
|
|
6328
5730
|
this.isLoading.set(false);
|
|
@@ -6335,7 +5737,6 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6335
5737
|
this.isSaving.set(true);
|
|
6336
5738
|
try {
|
|
6337
5739
|
if (company.isAssigned) {
|
|
6338
|
-
// Assign company
|
|
6339
5740
|
const response = await firstValueFrom(this.permissionApi.assignUserToCompany(currentUser.id, company.id));
|
|
6340
5741
|
if (response.success) {
|
|
6341
5742
|
this.assignedCompanyIds.add(company.id);
|
|
@@ -6348,7 +5749,6 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6348
5749
|
}
|
|
6349
5750
|
}
|
|
6350
5751
|
else {
|
|
6351
|
-
// Revoke company
|
|
6352
5752
|
const response = await firstValueFrom(this.permissionApi.revokeUserFromCompany(currentUser.id, company.id));
|
|
6353
5753
|
if (response.success) {
|
|
6354
5754
|
this.assignedCompanyIds.delete(company.id);
|
|
@@ -6362,9 +5762,7 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6362
5762
|
}
|
|
6363
5763
|
}
|
|
6364
5764
|
catch {
|
|
6365
|
-
// Revert checkbox on error
|
|
6366
5765
|
company.isAssigned = !company.isAssigned;
|
|
6367
|
-
// Error toast handled by global interceptor
|
|
6368
5766
|
}
|
|
6369
5767
|
finally {
|
|
6370
5768
|
this.isSaving.set(false);
|
|
@@ -6373,8 +5771,8 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6373
5771
|
onClose() {
|
|
6374
5772
|
this.closed.emit();
|
|
6375
5773
|
}
|
|
6376
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
6377
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
5774
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserCompanyPermissionDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5775
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: UserCompanyPermissionDialogComponent, isStandalone: true, selector: "lib-user-company-permission-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed", permissionsChanged: "permissionsChanged" }, ngImport: i0, template: `
|
|
6378
5776
|
<p-dialog
|
|
6379
5777
|
[visible]="visible()"
|
|
6380
5778
|
[modal]="true"
|
|
@@ -6425,13 +5823,12 @@ class UserCompanyPermissionDialogComponent {
|
|
|
6425
5823
|
[disabled]="isSaving()" />
|
|
6426
5824
|
</ng-template>
|
|
6427
5825
|
</p-dialog>
|
|
6428
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
5826
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: ProgressSpinnerModule }, { kind: "component", type: i5$2.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6429
5827
|
}
|
|
6430
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
5828
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserCompanyPermissionDialogComponent, decorators: [{
|
|
6431
5829
|
type: Component,
|
|
6432
5830
|
args: [{
|
|
6433
5831
|
selector: 'lib-user-company-permission-dialog',
|
|
6434
|
-
standalone: true,
|
|
6435
5832
|
imports: [AngularModule, PrimeModule, DialogModule, ProgressSpinnerModule],
|
|
6436
5833
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
6437
5834
|
template: `
|
|
@@ -6489,76 +5886,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
6489
5886
|
}]
|
|
6490
5887
|
}], ctorParameters: () => [], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: true }] }], user: [{ type: i0.Input, args: [{ isSignal: true, alias: "user", required: true }] }], closed: [{ type: i0.Output, args: ["closed"] }], permissionsChanged: [{ type: i0.Output, args: ["permissionsChanged"] }] } });
|
|
6491
5888
|
|
|
6492
|
-
/**
|
|
6493
|
-
* User Branch Permission Dialog
|
|
6494
|
-
*
|
|
6495
|
-
* Workflow:
|
|
6496
|
-
* 1. Dialog opens → Shows company dropdown with user's permitted companies
|
|
6497
|
-
* 2. User selects a company → Branches load for that company
|
|
6498
|
-
* 3. Branches show with checkboxes (checked = already assigned)
|
|
6499
|
-
* 4. Check to assign, uncheck to revoke
|
|
6500
|
-
*
|
|
6501
|
-
* Company dropdown only shows companies the user has company-level permissions for.
|
|
6502
|
-
* Branch permissions can only be assigned within user's permitted companies.
|
|
6503
|
-
*/
|
|
6504
5889
|
class UserBranchPermissionDialogComponent {
|
|
6505
5890
|
branchApi = inject(BranchApiService);
|
|
6506
5891
|
permissionApi = inject(UserPermissionApiService);
|
|
6507
|
-
authState = inject(AuthStateService);
|
|
6508
5892
|
messageService = inject(MessageService);
|
|
6509
|
-
/** Dialog visibility */
|
|
6510
5893
|
visible = input.required(...(ngDevMode ? [{ debugName: "visible" }] : []));
|
|
6511
|
-
/** User to manage permissions for */
|
|
6512
5894
|
user = input.required(...(ngDevMode ? [{ debugName: "user" }] : []));
|
|
6513
|
-
/** Close event */
|
|
6514
5895
|
closed = output();
|
|
6515
|
-
/** Refresh event after changes */
|
|
6516
5896
|
permissionsChanged = output();
|
|
6517
|
-
/** Loading state */
|
|
6518
5897
|
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
6519
|
-
/** Saving state */
|
|
6520
5898
|
isSaving = signal(false, ...(ngDevMode ? [{ debugName: "isSaving" }] : []));
|
|
6521
|
-
/** Branches with assignment status */
|
|
6522
5899
|
branches = signal([], ...(ngDevMode ? [{ debugName: "branches" }] : []));
|
|
6523
|
-
/** Available companies (filtered by user's company permissions) */
|
|
6524
5900
|
companies = signal([], ...(ngDevMode ? [{ debugName: "companies" }] : []));
|
|
6525
|
-
/** Selected company ID */
|
|
6526
5901
|
selectedCompanyId = null;
|
|
6527
|
-
/** Current company from auth state */
|
|
6528
|
-
currentCompany = computed(() => this.authState.company(), ...(ngDevMode ? [{ debugName: "currentCompany" }] : []));
|
|
6529
|
-
/** Currently assigned branch IDs */
|
|
6530
5902
|
assignedBranchIds = new Set();
|
|
6531
5903
|
constructor() {
|
|
6532
|
-
// Load user's companies and branches when dialog becomes visible
|
|
6533
5904
|
effect(() => {
|
|
6534
5905
|
if (this.visible() && this.user()) {
|
|
6535
5906
|
this.loadUserCompaniesAndData();
|
|
6536
5907
|
}
|
|
6537
5908
|
});
|
|
6538
5909
|
}
|
|
6539
|
-
/**
|
|
6540
|
-
* Load user's assigned companies (does not auto-select or load branches)
|
|
6541
|
-
*/
|
|
6542
5910
|
async loadUserCompaniesAndData() {
|
|
6543
5911
|
const currentUser = this.user();
|
|
6544
5912
|
if (!currentUser)
|
|
6545
5913
|
return;
|
|
6546
5914
|
try {
|
|
6547
|
-
// Load user's company permissions
|
|
6548
5915
|
const companiesResponse = await firstValueFrom(this.permissionApi.getUserCompanies(currentUser.id));
|
|
6549
|
-
// Map to company dropdown options
|
|
6550
5916
|
const userCompanies = (companiesResponse.data || []).map((permission) => ({
|
|
6551
5917
|
id: permission.companyId,
|
|
6552
5918
|
name: permission.companyName,
|
|
6553
5919
|
}));
|
|
6554
5920
|
this.companies.set(userCompanies);
|
|
6555
|
-
// Clear selection and branches when dialog reopens
|
|
6556
5921
|
this.selectedCompanyId = null;
|
|
6557
5922
|
this.branches.set([]);
|
|
6558
5923
|
}
|
|
6559
|
-
catch {
|
|
6560
|
-
// Error toast handled by global interceptor
|
|
6561
|
-
}
|
|
5924
|
+
catch { }
|
|
6562
5925
|
}
|
|
6563
5926
|
async loadData() {
|
|
6564
5927
|
const currentUser = this.user();
|
|
@@ -6567,26 +5930,21 @@ class UserBranchPermissionDialogComponent {
|
|
|
6567
5930
|
return;
|
|
6568
5931
|
this.isLoading.set(true);
|
|
6569
5932
|
try {
|
|
6570
|
-
// Load all branches for selected company
|
|
6571
5933
|
const branchesResponse = await firstValueFrom(this.branchApi.getAll('', {
|
|
6572
5934
|
pagination: { currentPage: 0, pageSize: 1000 },
|
|
6573
5935
|
filter: { companyId },
|
|
6574
5936
|
select: [],
|
|
6575
5937
|
sort: {},
|
|
6576
5938
|
}));
|
|
6577
|
-
// Load user's assigned branches
|
|
6578
5939
|
const assignedResponse = await firstValueFrom(this.permissionApi.getUserBranches(currentUser.id));
|
|
6579
|
-
// Build set of assigned branch IDs (filter by selected company)
|
|
6580
5940
|
this.assignedBranchIds.clear();
|
|
6581
5941
|
if (assignedResponse.data) {
|
|
6582
5942
|
assignedResponse.data.forEach((permission) => {
|
|
6583
|
-
// Filter by selected company
|
|
6584
5943
|
if (permission.companyId === companyId) {
|
|
6585
5944
|
this.assignedBranchIds.add(permission.branchId);
|
|
6586
5945
|
}
|
|
6587
5946
|
});
|
|
6588
5947
|
}
|
|
6589
|
-
// Map branches with assignment status
|
|
6590
5948
|
const branchesWithStatus = (branchesResponse.data || []).map((branch) => ({
|
|
6591
5949
|
...branch,
|
|
6592
5950
|
isAssigned: this.assignedBranchIds.has(branch.id),
|
|
@@ -6594,7 +5952,6 @@ class UserBranchPermissionDialogComponent {
|
|
|
6594
5952
|
this.branches.set(branchesWithStatus);
|
|
6595
5953
|
}
|
|
6596
5954
|
catch {
|
|
6597
|
-
// Error toast handled by global interceptor
|
|
6598
5955
|
}
|
|
6599
5956
|
finally {
|
|
6600
5957
|
this.isLoading.set(false);
|
|
@@ -6612,7 +5969,6 @@ class UserBranchPermissionDialogComponent {
|
|
|
6612
5969
|
this.isSaving.set(true);
|
|
6613
5970
|
try {
|
|
6614
5971
|
if (branch.isAssigned) {
|
|
6615
|
-
// Assign branch
|
|
6616
5972
|
const response = await firstValueFrom(this.permissionApi.assignUserToBranch(currentUser.id, branch.id));
|
|
6617
5973
|
if (response.success) {
|
|
6618
5974
|
this.assignedBranchIds.add(branch.id);
|
|
@@ -6625,7 +5981,6 @@ class UserBranchPermissionDialogComponent {
|
|
|
6625
5981
|
}
|
|
6626
5982
|
}
|
|
6627
5983
|
else {
|
|
6628
|
-
// Revoke branch
|
|
6629
5984
|
const response = await firstValueFrom(this.permissionApi.revokeUserFromBranch(currentUser.id, branch.id));
|
|
6630
5985
|
if (response.success) {
|
|
6631
5986
|
this.assignedBranchIds.delete(branch.id);
|
|
@@ -6639,9 +5994,7 @@ class UserBranchPermissionDialogComponent {
|
|
|
6639
5994
|
}
|
|
6640
5995
|
}
|
|
6641
5996
|
catch {
|
|
6642
|
-
// Revert checkbox on error
|
|
6643
5997
|
branch.isAssigned = !branch.isAssigned;
|
|
6644
|
-
// Error toast handled by global interceptor
|
|
6645
5998
|
}
|
|
6646
5999
|
finally {
|
|
6647
6000
|
this.isSaving.set(false);
|
|
@@ -6650,8 +6003,8 @@ class UserBranchPermissionDialogComponent {
|
|
|
6650
6003
|
onClose() {
|
|
6651
6004
|
this.closed.emit();
|
|
6652
6005
|
}
|
|
6653
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
6654
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
6006
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserBranchPermissionDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6007
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: UserBranchPermissionDialogComponent, isStandalone: true, selector: "lib-user-branch-permission-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null }, user: { classPropertyName: "user", publicName: "user", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { closed: "closed", permissionsChanged: "permissionsChanged" }, ngImport: i0, template: `
|
|
6655
6008
|
<p-dialog
|
|
6656
6009
|
[visible]="visible()"
|
|
6657
6010
|
[modal]="true"
|
|
@@ -6721,13 +6074,12 @@ class UserBranchPermissionDialogComponent {
|
|
|
6721
6074
|
[disabled]="isSaving()" />
|
|
6722
6075
|
</ng-template>
|
|
6723
6076
|
</p-dialog>
|
|
6724
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
6077
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3$2.Checkbox, selector: "p-checkbox, p-checkBox, p-check-box", inputs: ["hostName", "value", "binary", "ariaLabelledBy", "ariaLabel", "tabindex", "inputId", "inputStyle", "styleClass", "inputClass", "indeterminate", "formControl", "checkboxIcon", "readonly", "autofocus", "trueValue", "falseValue", "variant", "size"], outputs: ["onChange", "onFocus", "onBlur"] }, { kind: "component", type: i4$3.Dialog, selector: "p-dialog", inputs: ["hostName", "header", "draggable", "resizable", "contentStyle", "contentStyleClass", "modal", "closeOnEscape", "dismissableMask", "rtl", "closable", "breakpoints", "styleClass", "maskStyleClass", "maskStyle", "showHeader", "blockScroll", "autoZIndex", "baseZIndex", "minX", "minY", "focusOnShow", "maximizable", "keepInViewport", "focusTrap", "transitionOptions", "maskMotionOptions", "motionOptions", "closeIcon", "closeAriaLabel", "closeTabindex", "minimizeIcon", "maximizeIcon", "closeButtonProps", "maximizeButtonProps", "visible", "style", "position", "role", "appendTo", "content", "contentTemplate", "footerTemplate", "closeIconTemplate", "maximizeIconTemplate", "minimizeIconTemplate", "headlessTemplate"], outputs: ["onShow", "onHide", "visibleChange", "onResizeInit", "onResizeEnd", "onDragEnd", "onMaximize"] }, { kind: "component", type: i4$1.Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "ngmodule", type: DialogModule }, { kind: "ngmodule", type: ProgressSpinnerModule }, { kind: "component", type: i5$2.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6725
6078
|
}
|
|
6726
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
6079
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserBranchPermissionDialogComponent, decorators: [{
|
|
6727
6080
|
type: Component,
|
|
6728
6081
|
args: [{
|
|
6729
6082
|
selector: 'lib-user-branch-permission-dialog',
|
|
6730
|
-
standalone: true,
|
|
6731
6083
|
imports: [AngularModule, PrimeModule, DialogModule, ProgressSpinnerModule],
|
|
6732
6084
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
6733
6085
|
template: `
|
|
@@ -6849,12 +6201,14 @@ class UserListComponent {
|
|
|
6849
6201
|
this.router.navigate(['/administration/users', user.id]);
|
|
6850
6202
|
}
|
|
6851
6203
|
onManageCompanyPermissions(user) {
|
|
6852
|
-
this.
|
|
6853
|
-
this.showCompanyDialog.set(true);
|
|
6204
|
+
this.openPermissionDialog(user, this.showCompanyDialog);
|
|
6854
6205
|
}
|
|
6855
6206
|
onManageBranchPermissions(user) {
|
|
6207
|
+
this.openPermissionDialog(user, this.showBranchDialog);
|
|
6208
|
+
}
|
|
6209
|
+
openPermissionDialog(user, dialogSignal) {
|
|
6856
6210
|
this.selectedUser.set(user);
|
|
6857
|
-
|
|
6211
|
+
dialogSignal.set(true);
|
|
6858
6212
|
}
|
|
6859
6213
|
onDelete(user) {
|
|
6860
6214
|
this.confirmationService.confirm({
|
|
@@ -6878,8 +6232,8 @@ class UserListComponent {
|
|
|
6878
6232
|
},
|
|
6879
6233
|
});
|
|
6880
6234
|
}
|
|
6881
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
6882
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
6235
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6236
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: UserListComponent, isStandalone: true, selector: "lib-user-list", ngImport: i0, template: `
|
|
6883
6237
|
<p-card>
|
|
6884
6238
|
<ng-template #header>
|
|
6885
6239
|
<div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3 p-4">
|
|
@@ -6992,11 +6346,10 @@ class UserListComponent {
|
|
|
6992
6346
|
}
|
|
6993
6347
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: i3.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: i3$1.Table, selector: "p-table", inputs: ["frozenColumns", "frozenValue", "styleClass", "tableStyle", "tableStyleClass", "paginator", "pageLinks", "rowsPerPageOptions", "alwaysShowPaginator", "paginatorPosition", "paginatorStyleClass", "paginatorDropdownAppendTo", "paginatorDropdownScrollHeight", "currentPageReportTemplate", "showCurrentPageReport", "showJumpToPageDropdown", "showJumpToPageInput", "showFirstLastIcon", "showPageLinks", "defaultSortOrder", "sortMode", "resetPageOnSort", "selectionMode", "selectionPageOnly", "contextMenuSelection", "contextMenuSelectionMode", "dataKey", "metaKeySelection", "rowSelectable", "rowTrackBy", "lazy", "lazyLoadOnInit", "compareSelectionBy", "csvSeparator", "exportFilename", "filters", "globalFilterFields", "filterDelay", "filterLocale", "expandedRowKeys", "editingRowKeys", "rowExpandMode", "scrollable", "rowGroupMode", "scrollHeight", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "virtualScrollDelay", "frozenWidth", "contextMenu", "resizableColumns", "columnResizeMode", "reorderableColumns", "loading", "loadingIcon", "showLoader", "rowHover", "customSort", "showInitialSortBadge", "exportFunction", "exportHeader", "stateKey", "stateStorage", "editMode", "groupRowsBy", "size", "showGridlines", "stripedRows", "groupRowsByOrder", "responsiveLayout", "breakpoint", "paginatorLocale", "value", "columns", "first", "rows", "totalRecords", "sortField", "sortOrder", "multiSortMeta", "selection", "selectAll"], outputs: ["contextMenuSelectionChange", "selectAllChange", "selectionChange", "onRowSelect", "onRowUnselect", "onPage", "onSort", "onFilter", "onLazyLoad", "onRowExpand", "onRowCollapse", "onContextMenuSelect", "onColResize", "onColReorder", "onRowReorder", "onEditInit", "onEditComplete", "onEditCancel", "onHeaderCheckboxToggle", "sortFunction", "firstChange", "rowsChange", "onStateSave", "onStateRestore"] }, { kind: "component", type: i4$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "directive", type: i5.Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "ngmodule", type: TagModule }, { kind: "directive", type: HasPermissionDirective, selector: "[hasPermission]", inputs: ["hasPermission"] }, { kind: "component", type: UserCompanyPermissionDialogComponent, selector: "lib-user-company-permission-dialog", inputs: ["visible", "user"], outputs: ["closed", "permissionsChanged"] }, { kind: "component", type: UserBranchPermissionDialogComponent, selector: "lib-user-branch-permission-dialog", inputs: ["visible", "user"], outputs: ["closed", "permissionsChanged"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6994
6348
|
}
|
|
6995
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
6349
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: UserListComponent, decorators: [{
|
|
6996
6350
|
type: Component,
|
|
6997
6351
|
args: [{
|
|
6998
6352
|
selector: 'lib-user-list',
|
|
6999
|
-
standalone: true,
|
|
7000
6353
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7001
6354
|
imports: [
|
|
7002
6355
|
AngularModule,
|
|
@@ -7217,13 +6570,11 @@ class VerifyEmailPageComponent {
|
|
|
7217
6570
|
}
|
|
7218
6571
|
this.isLoading.set(true);
|
|
7219
6572
|
const email = this.resendFormModel().email;
|
|
6573
|
+
// Always show success (security: don't reveal if email exists)
|
|
7220
6574
|
this.authApi
|
|
7221
6575
|
.resendVerification(email)
|
|
7222
|
-
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7223
|
-
.subscribe(
|
|
7224
|
-
next: () => this.handleResendComplete(email),
|
|
7225
|
-
error: () => this.handleResendComplete(email),
|
|
7226
|
-
});
|
|
6576
|
+
.pipe(takeUntilDestroyed(this.destroyRef), finalize(() => this.handleResendComplete(email)))
|
|
6577
|
+
.subscribe();
|
|
7227
6578
|
}
|
|
7228
6579
|
handleResendComplete(email) {
|
|
7229
6580
|
this.isLoading.set(false);
|
|
@@ -7235,8 +6586,8 @@ class VerifyEmailPageComponent {
|
|
|
7235
6586
|
this.submittedEmail.set(email);
|
|
7236
6587
|
this.resendSuccess.set(true);
|
|
7237
6588
|
}
|
|
7238
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.
|
|
7239
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.
|
|
6589
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: VerifyEmailPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6590
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.5", type: VerifyEmailPageComponent, isStandalone: true, selector: "lib-verify-email-page", ngImport: i0, template: `
|
|
7240
6591
|
<app-floating-configurator />
|
|
7241
6592
|
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
7242
6593
|
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
@@ -7360,13 +6711,12 @@ class VerifyEmailPageComponent {
|
|
|
7360
6711
|
</div>
|
|
7361
6712
|
</div>
|
|
7362
6713
|
</div>
|
|
7363
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1
|
|
6714
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2$1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i2.Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: i4.InputText, selector: "[pInputText]", inputs: ["hostName", "ptInputText", "pInputTextPT", "pInputTextUnstyled", "pSize", "variant", "fluid", "invalid"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"] }, { kind: "component", type: AppFloatingConfigurator, selector: "app-floating-configurator" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7364
6715
|
}
|
|
7365
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.
|
|
6716
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: VerifyEmailPageComponent, decorators: [{
|
|
7366
6717
|
type: Component,
|
|
7367
6718
|
args: [{
|
|
7368
6719
|
selector: 'lib-verify-email-page',
|
|
7369
|
-
standalone: true,
|
|
7370
6720
|
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
7371
6721
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
7372
6722
|
template: `
|
|
@@ -7511,5 +6861,5 @@ var verifyEmailPage_component = /*#__PURE__*/Object.freeze({
|
|
|
7511
6861
|
* Generated bundle index. Do not edit.
|
|
7512
6862
|
*/
|
|
7513
6863
|
|
|
7514
|
-
export { ADMINISTRATION_ROUTES, AUTH_ENDPOINTS, AUTH_ROUTES, AdministrationPageComponent, AuthApiService, AuthCompanyApiProviderAdapter, AuthInitService, AuthLayoutApiAdapter, AuthLayoutStateAdapter, AuthStateProviderAdapter, AuthStateService, AuthUserPermissionProviderAdapter, AuthUserProviderAdapter, BranchApiService, BranchFormComponent, BranchListComponent, CompanyApiService, CompanyFormComponent, CompanyListComponent, ForgotPasswordPageComponent, LoginPageComponent, PROFILE_ROUTES, ProfilePageComponent, RegisterPageComponent, ResetPasswordPageComponent, TokenRefreshStateService, UserApiService, UserBranchPermissionDialogComponent, UserCompanyPermissionDialogComponent, UserFormComponent, UserListComponent, UserPermissionApiService, VerifyEmailPageComponent, authGuard, authInterceptor, companyFeatureGuard, guestGuard,
|
|
6864
|
+
export { ADMINISTRATION_ROUTES, AUTH_ENDPOINTS, AUTH_ROUTES, AdministrationPageComponent, AuthApiService, AuthCompanyApiProviderAdapter, AuthInitService, AuthLayoutApiAdapter, AuthLayoutStateAdapter, AuthStateProviderAdapter, AuthStateService, AuthUserPermissionProviderAdapter, AuthUserProviderAdapter, BranchApiService, BranchFormComponent, BranchListComponent, CompanyApiService, CompanyFormComponent, CompanyListComponent, ForgotPasswordPageComponent, LoginPageComponent, PROFILE_ROUTES, ProfilePageComponent, RegisterPageComponent, ResetPasswordPageComponent, TokenRefreshStateService, UserApiService, UserBranchPermissionDialogComponent, UserCompanyPermissionDialogComponent, UserFormComponent, UserListComponent, UserPermissionApiService, VerifyEmailPageComponent, authGuard, authInterceptor, companyFeatureGuard, guestGuard, isCompanySelectionResponse, isEmailVerificationRequired, isEndpoint, isLoginResponse, provideAuthLayoutIntegration, provideAuthProviders, tokenRefreshInterceptor };
|
|
7515
6865
|
//# sourceMappingURL=flusys-ng-auth.mjs.map
|