@flusys/ng-auth 1.0.0-beta → 1.1.1-beta
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 +45 -4
- package/fesm2022/flusys-ng-auth.mjs +2055 -247
- package/fesm2022/flusys-ng-auth.mjs.map +1 -1
- package/package.json +4 -4
- package/types/flusys-ng-auth.d.ts +199 -43
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as i1 from '@angular/common/http';
|
|
2
2
|
import { HttpClient } from '@angular/common/http';
|
|
3
3
|
import * as i0 from '@angular/core';
|
|
4
|
-
import { inject, Injectable, signal, computed, effect, PLATFORM_ID, Component, DestroyRef, input, output } from '@angular/core';
|
|
5
|
-
import { APP_CONFIG, getServiceUrl, isCompanyFeatureEnabled } from '@flusys/ng-core';
|
|
6
|
-
import * as i2
|
|
4
|
+
import { inject, Injectable, signal, computed, effect, PLATFORM_ID, Component, DestroyRef, input, ChangeDetectionStrategy, output, makeStateKey, TransferState } from '@angular/core';
|
|
5
|
+
import { APP_CONFIG, getServiceUrl, isCompanyFeatureEnabled, isFeatureEnabled, getPermissionMode } from '@flusys/ng-core';
|
|
6
|
+
import * as i2 from '@angular/router';
|
|
7
7
|
import { Router, RouterOutlet, RouterLink, RouterLinkActive, ActivatedRoute } from '@angular/router';
|
|
8
8
|
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 } from '@flusys/ng-shared';
|
|
9
|
+
import { ApiResourceService, CookieService, USER_PROVIDER, COMPANY_API_PROVIDER, USER_PERMISSION_PROVIDER, AUTH_STATE_PROVIDER, AngularModule, PrimeModule, FileUrlService, PROFILE_UPLOAD_PROVIDER, PROFILE_PERMISSION_PROVIDER, FileUploaderComponent } from '@flusys/ng-shared';
|
|
10
10
|
import { map as map$1, catchError as catchError$1, tap as tap$1 } from 'rxjs/operators';
|
|
11
11
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
12
12
|
import * as i2$2 from 'primeng/api';
|
|
@@ -14,24 +14,31 @@ import { MessageService, ConfirmationService } from 'primeng/api';
|
|
|
14
14
|
import { LAYOUT_AUTH_STATE, LAYOUT_AUTH_API, AppFloatingConfigurator } from '@flusys/ng-layout';
|
|
15
15
|
import { form, required, FormField, email, minLength } from '@angular/forms/signals';
|
|
16
16
|
import * as i1$1 from '@angular/forms';
|
|
17
|
-
import * as i2 from 'primeng/inputtext';
|
|
18
17
|
import * as i3 from 'primeng/button';
|
|
19
|
-
import * as
|
|
20
|
-
import * as
|
|
18
|
+
import * as i3$1 from 'primeng/card';
|
|
19
|
+
import * as i4 from 'primeng/inputtext';
|
|
20
|
+
import * as i4$2 from 'primeng/tag';
|
|
21
21
|
import { TagModule } from 'primeng/tag';
|
|
22
|
-
import * as
|
|
23
|
-
import * as
|
|
24
|
-
import * as
|
|
22
|
+
import * as i4$1 from 'primeng/select';
|
|
23
|
+
import * as i3$2 from 'primeng/table';
|
|
24
|
+
import * as i5 from 'primeng/tooltip';
|
|
25
25
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
26
|
-
import * as
|
|
26
|
+
import * as i2$1 from 'primeng/avatar';
|
|
27
|
+
import * as i5$1 from 'primeng/password';
|
|
28
|
+
import * as i6 from 'primeng/skeleton';
|
|
29
|
+
import * as i5$2 from 'primeng/dialog';
|
|
27
30
|
import { DialogModule } from 'primeng/dialog';
|
|
28
|
-
import * as i6 from 'primeng/progressspinner';
|
|
31
|
+
import * as i6$1 from 'primeng/progressspinner';
|
|
29
32
|
import { ProgressSpinnerModule } from 'primeng/progressspinner';
|
|
30
|
-
import * as i4$
|
|
33
|
+
import * as i4$3 from 'primeng/checkbox';
|
|
31
34
|
|
|
32
|
-
// =============================================================================
|
|
33
35
|
// AUTH INTERFACES - Matching NestJS Auth Package DTOs
|
|
34
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Type guard to check if response requires email verification
|
|
38
|
+
*/
|
|
39
|
+
function isEmailVerificationRequired(response) {
|
|
40
|
+
return 'requiresEmailVerification' in response && response.requiresEmailVerification === true;
|
|
41
|
+
}
|
|
35
42
|
/**
|
|
36
43
|
* Type guard to check if response requires company selection
|
|
37
44
|
*/
|
|
@@ -42,7 +49,7 @@ function isCompanySelectionResponse(response) {
|
|
|
42
49
|
* Type guard to check if response is a successful login
|
|
43
50
|
*/
|
|
44
51
|
function isLoginResponse(response) {
|
|
45
|
-
return 'accessToken' in response && !('requiresSelection' in response);
|
|
52
|
+
return 'accessToken' in response && !('requiresSelection' in response) && !('requiresEmailVerification' in response);
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
// =============================================================================
|
|
@@ -148,6 +155,22 @@ class AuthApiService {
|
|
|
148
155
|
newPassword,
|
|
149
156
|
});
|
|
150
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Resend verification email (unauthenticated)
|
|
160
|
+
*/
|
|
161
|
+
resendVerification(email) {
|
|
162
|
+
return this.http.post(`${this.baseUrl}/resend-verification`, { email });
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Verify email with token
|
|
166
|
+
* Returns IMessageResponse for existing user verification
|
|
167
|
+
* Returns IRegistrationResponse for pending registration completion
|
|
168
|
+
*/
|
|
169
|
+
verifyEmail(token) {
|
|
170
|
+
return this.http.post(`${this.baseUrl}/verify-email`, {
|
|
171
|
+
token,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
151
174
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthApiService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
152
175
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: AuthApiService, providedIn: 'root' });
|
|
153
176
|
}
|
|
@@ -764,6 +787,10 @@ const AUTH_ENDPOINTS = {
|
|
|
764
787
|
REGISTER: '/auth/register',
|
|
765
788
|
SELECT: '/auth/select',
|
|
766
789
|
SWITCH_COMPANY: '/auth/switch-company',
|
|
790
|
+
VERIFY_EMAIL: '/auth/verify-email',
|
|
791
|
+
FORGOT_PASSWORD: '/auth/forgot-password',
|
|
792
|
+
RESET_PASSWORD: '/auth/reset-password',
|
|
793
|
+
RESEND_VERIFICATION: '/auth/resend-verification',
|
|
767
794
|
};
|
|
768
795
|
/**
|
|
769
796
|
* Check if a URL is an auth-related endpoint
|
|
@@ -905,22 +932,18 @@ const companyFeatureGuard = () => {
|
|
|
905
932
|
return true;
|
|
906
933
|
};
|
|
907
934
|
|
|
935
|
+
/** Endpoints that need withCredentials for cookie handling */
|
|
936
|
+
const COOKIE_ENDPOINTS = [
|
|
937
|
+
AUTH_ENDPOINTS.LOGIN,
|
|
938
|
+
AUTH_ENDPOINTS.LOGOUT,
|
|
939
|
+
AUTH_ENDPOINTS.REFRESH,
|
|
940
|
+
AUTH_ENDPOINTS.REGISTER,
|
|
941
|
+
AUTH_ENDPOINTS.SELECT,
|
|
942
|
+
AUTH_ENDPOINTS.SWITCH_COMPANY,
|
|
943
|
+
AUTH_ENDPOINTS.VERIFY_EMAIL,
|
|
944
|
+
];
|
|
908
945
|
/**
|
|
909
|
-
* Auth Interceptor
|
|
910
|
-
* Adds Authorization header and tenant header to HTTP requests.
|
|
911
|
-
*
|
|
912
|
-
* - Adds Bearer token for authenticated requests (including /auth/me)
|
|
913
|
-
* - Adds tenant header for multi-tenant mode
|
|
914
|
-
* - Adds withCredentials for auth endpoints (cookies)
|
|
915
|
-
*
|
|
916
|
-
* @example
|
|
917
|
-
* ```typescript
|
|
918
|
-
* // In app.config.ts
|
|
919
|
-
* provideHttpClient(
|
|
920
|
-
* withFetch(),
|
|
921
|
-
* withInterceptors([authInterceptor, tokenRefreshInterceptor])
|
|
922
|
-
* )
|
|
923
|
-
* ```
|
|
946
|
+
* Auth Interceptor - Adds auth headers and cookie credentials to requests
|
|
924
947
|
*/
|
|
925
948
|
function authInterceptor(req, next) {
|
|
926
949
|
const authState = inject(AuthStateService);
|
|
@@ -930,33 +953,21 @@ function authInterceptor(req, next) {
|
|
|
930
953
|
// Add tenant header if multi-tenant mode
|
|
931
954
|
if (appConfig.multiTenant.enabled) {
|
|
932
955
|
const tenantId = authState.tenantId();
|
|
933
|
-
const tenantHeader = appConfig.multiTenant.tenantHeader;
|
|
934
956
|
if (tenantId) {
|
|
935
|
-
headers[tenantHeader] = tenantId;
|
|
957
|
+
headers[appConfig.multiTenant.tenantHeader] = tenantId;
|
|
936
958
|
}
|
|
937
959
|
}
|
|
938
|
-
// Add Bearer token
|
|
960
|
+
// Add Bearer token if available
|
|
939
961
|
const token = authState.accessToken();
|
|
940
962
|
if (token) {
|
|
941
963
|
headers['Authorization'] = `Bearer ${token}`;
|
|
942
964
|
}
|
|
943
|
-
// Check if this
|
|
944
|
-
const needsCredentials = isEndpoint(req.url,
|
|
945
|
-
isEndpoint(req.url, AUTH_ENDPOINTS.LOGOUT) ||
|
|
946
|
-
isEndpoint(req.url, AUTH_ENDPOINTS.LOGIN) ||
|
|
947
|
-
isEndpoint(req.url, AUTH_ENDPOINTS.REGISTER) ||
|
|
948
|
-
isEndpoint(req.url, AUTH_ENDPOINTS.SELECT) ||
|
|
949
|
-
isEndpoint(req.url, AUTH_ENDPOINTS.SWITCH_COMPANY);
|
|
965
|
+
// Check if this endpoint needs withCredentials for cookies
|
|
966
|
+
const needsCredentials = COOKIE_ENDPOINTS.some((ep) => isEndpoint(req.url, ep));
|
|
950
967
|
if (needsCredentials) {
|
|
951
|
-
|
|
952
|
-
return next(req.clone({
|
|
953
|
-
withCredentials: true,
|
|
954
|
-
setHeaders: {
|
|
955
|
-
cookie: cookieService.get(),
|
|
956
|
-
},
|
|
957
|
-
}));
|
|
968
|
+
headers['cookie'] = cookieService.get();
|
|
969
|
+
return next(req.clone({ withCredentials: true, setHeaders: headers }));
|
|
958
970
|
}
|
|
959
|
-
// Other requests just need headers (Bearer token + tenant)
|
|
960
971
|
if (Object.keys(headers).length > 0) {
|
|
961
972
|
return next(req.clone({ setHeaders: headers }));
|
|
962
973
|
}
|
|
@@ -1072,6 +1083,20 @@ const AUTH_ROUTES = [
|
|
|
1072
1083
|
loadComponent: () => Promise.resolve().then(function () { return registerPage_component; }).then((m) => m.RegisterPageComponent),
|
|
1073
1084
|
canActivate: [guestGuard],
|
|
1074
1085
|
},
|
|
1086
|
+
{
|
|
1087
|
+
path: 'forgot-password',
|
|
1088
|
+
loadComponent: () => Promise.resolve().then(function () { return forgotPasswordPage_component; }).then((m) => m.ForgotPasswordPageComponent),
|
|
1089
|
+
canActivate: [guestGuard],
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
path: 'reset-password',
|
|
1093
|
+
loadComponent: () => Promise.resolve().then(function () { return resetPasswordPage_component; }).then((m) => m.ResetPasswordPageComponent),
|
|
1094
|
+
canActivate: [guestGuard],
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
path: 'verify-email',
|
|
1098
|
+
loadComponent: () => Promise.resolve().then(function () { return verifyEmailPage_component; }).then((m) => m.VerifyEmailPageComponent),
|
|
1099
|
+
},
|
|
1075
1100
|
{
|
|
1076
1101
|
path: '',
|
|
1077
1102
|
redirectTo: 'login',
|
|
@@ -1157,6 +1182,17 @@ const ADMINISTRATION_ROUTES = [
|
|
|
1157
1182
|
],
|
|
1158
1183
|
},
|
|
1159
1184
|
];
|
|
1185
|
+
/**
|
|
1186
|
+
* Profile Routes Configuration
|
|
1187
|
+
* Protected route for user profile management
|
|
1188
|
+
*/
|
|
1189
|
+
const PROFILE_ROUTES = [
|
|
1190
|
+
{
|
|
1191
|
+
path: '',
|
|
1192
|
+
canActivate: [authGuard],
|
|
1193
|
+
loadComponent: () => Promise.resolve().then(function () { return profilePage_component; }).then((m) => m.ProfilePageComponent),
|
|
1194
|
+
},
|
|
1195
|
+
];
|
|
1160
1196
|
|
|
1161
1197
|
/**
|
|
1162
1198
|
* Auth Layout State Adapter
|
|
@@ -1568,10 +1604,10 @@ class AdministrationPageComponent {
|
|
|
1568
1604
|
<div class="administration-page p-4">
|
|
1569
1605
|
<div class="administration-header mb-4">
|
|
1570
1606
|
<h1 class="text-2xl font-bold m-0">Administration</h1>
|
|
1571
|
-
<p class="text-
|
|
1607
|
+
<p class="text-muted-color mt-1">{{ subtitle() }}</p>
|
|
1572
1608
|
</div>
|
|
1573
1609
|
|
|
1574
|
-
<div class="administration-tabs flex gap-1 mb-4 border-b border-
|
|
1610
|
+
<div class="administration-tabs flex gap-1 mb-4 border-b border-surface-200">
|
|
1575
1611
|
@for (tab of visibleTabs(); track tab.id) {
|
|
1576
1612
|
<a
|
|
1577
1613
|
[routerLink]="tab.route"
|
|
@@ -1595,10 +1631,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
1595
1631
|
<div class="administration-page p-4">
|
|
1596
1632
|
<div class="administration-header mb-4">
|
|
1597
1633
|
<h1 class="text-2xl font-bold m-0">Administration</h1>
|
|
1598
|
-
<p class="text-
|
|
1634
|
+
<p class="text-muted-color mt-1">{{ subtitle() }}</p>
|
|
1599
1635
|
</div>
|
|
1600
1636
|
|
|
1601
|
-
<div class="administration-tabs flex gap-1 mb-4 border-b border-
|
|
1637
|
+
<div class="administration-tabs flex gap-1 mb-4 border-b border-surface-200">
|
|
1602
1638
|
@for (tab of visibleTabs(); track tab.id) {
|
|
1603
1639
|
<a
|
|
1604
1640
|
[routerLink]="tab.route"
|
|
@@ -1915,7 +1951,7 @@ class BranchFormComponent {
|
|
|
1915
1951
|
</div>
|
|
1916
1952
|
</form>
|
|
1917
1953
|
</p-card>
|
|
1918
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "
|
|
1954
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.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: i3.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$1.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"] }] });
|
|
1919
1955
|
}
|
|
1920
1956
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BranchFormComponent, decorators: [{
|
|
1921
1957
|
type: Component,
|
|
@@ -2232,7 +2268,7 @@ class BranchListComponent {
|
|
|
2232
2268
|
</ng-template>
|
|
2233
2269
|
</p-table>
|
|
2234
2270
|
</p-card>
|
|
2235
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type:
|
|
2271
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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$1.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$2.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 }] });
|
|
2236
2272
|
}
|
|
2237
2273
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: BranchListComponent, decorators: [{
|
|
2238
2274
|
type: Component,
|
|
@@ -2599,7 +2635,7 @@ class CompanyFormComponent {
|
|
|
2599
2635
|
</div>
|
|
2600
2636
|
</form>
|
|
2601
2637
|
</p-card>
|
|
2602
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "
|
|
2638
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.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: i3.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$1.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"] }] });
|
|
2603
2639
|
}
|
|
2604
2640
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CompanyFormComponent, decorators: [{
|
|
2605
2641
|
type: Component,
|
|
@@ -2876,7 +2912,7 @@ class CompanyListComponent {
|
|
|
2876
2912
|
</ng-template>
|
|
2877
2913
|
</p-table>
|
|
2878
2914
|
</p-card>
|
|
2879
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type:
|
|
2915
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: i3$2.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 }] });
|
|
2880
2916
|
}
|
|
2881
2917
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: CompanyListComponent, decorators: [{
|
|
2882
2918
|
type: Component,
|
|
@@ -2974,6 +3010,223 @@ var companyList_component = /*#__PURE__*/Object.freeze({
|
|
|
2974
3010
|
CompanyListComponent: CompanyListComponent
|
|
2975
3011
|
});
|
|
2976
3012
|
|
|
3013
|
+
class ForgotPasswordPageComponent {
|
|
3014
|
+
destroyRef = inject(DestroyRef);
|
|
3015
|
+
messageService = inject(MessageService);
|
|
3016
|
+
authApi = inject(AuthApiService);
|
|
3017
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
3018
|
+
emailSent = signal(false, ...(ngDevMode ? [{ debugName: "emailSent" }] : []));
|
|
3019
|
+
submittedEmail = signal('', ...(ngDevMode ? [{ debugName: "submittedEmail" }] : []));
|
|
3020
|
+
formModel = signal({ email: '' }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
3021
|
+
forgotForm = form(this.formModel, (f) => {
|
|
3022
|
+
required(f.email, { message: 'Email is required' });
|
|
3023
|
+
email(f.email, { message: 'Please enter a valid email address' });
|
|
3024
|
+
});
|
|
3025
|
+
isFormValid = computed(() => this.forgotForm().valid(), ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
3026
|
+
onSubmit() {
|
|
3027
|
+
if (!this.isFormValid()) {
|
|
3028
|
+
this.messageService.add({
|
|
3029
|
+
severity: 'error',
|
|
3030
|
+
summary: 'Validation Error',
|
|
3031
|
+
detail: 'Please enter a valid email address.',
|
|
3032
|
+
});
|
|
3033
|
+
return;
|
|
3034
|
+
}
|
|
3035
|
+
this.isLoading.set(true);
|
|
3036
|
+
const email = this.formModel().email;
|
|
3037
|
+
this.authApi
|
|
3038
|
+
.forgotPassword(email)
|
|
3039
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3040
|
+
.subscribe({
|
|
3041
|
+
next: () => {
|
|
3042
|
+
this.isLoading.set(false);
|
|
3043
|
+
this.submittedEmail.set(email);
|
|
3044
|
+
this.emailSent.set(true);
|
|
3045
|
+
},
|
|
3046
|
+
error: () => {
|
|
3047
|
+
this.isLoading.set(false);
|
|
3048
|
+
this.submittedEmail.set(email);
|
|
3049
|
+
this.emailSent.set(true);
|
|
3050
|
+
},
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
resetForm() {
|
|
3054
|
+
this.emailSent.set(false);
|
|
3055
|
+
this.submittedEmail.set('');
|
|
3056
|
+
this.formModel.set({ email: '' });
|
|
3057
|
+
}
|
|
3058
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ForgotPasswordPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3059
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ForgotPasswordPageComponent, isStandalone: true, selector: "lib-forgot-password-page", ngImport: i0, template: `
|
|
3060
|
+
<app-floating-configurator />
|
|
3061
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
3062
|
+
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
3063
|
+
<div
|
|
3064
|
+
class="w-full"
|
|
3065
|
+
style="border-radius: var(--p-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
|
3066
|
+
<div
|
|
3067
|
+
class="w-full py-16 px-12"
|
|
3068
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background);">
|
|
3069
|
+
|
|
3070
|
+
@if (!emailSent()) {
|
|
3071
|
+
<div class="text-center mb-8">
|
|
3072
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
3073
|
+
Forgot Password
|
|
3074
|
+
</div>
|
|
3075
|
+
<span class="text-muted-color font-medium">
|
|
3076
|
+
Enter your email address and we'll send you a link to reset your password.
|
|
3077
|
+
</span>
|
|
3078
|
+
</div>
|
|
3079
|
+
|
|
3080
|
+
<form (ngSubmit)="onSubmit()">
|
|
3081
|
+
<label for="email" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
3082
|
+
Email
|
|
3083
|
+
</label>
|
|
3084
|
+
<input
|
|
3085
|
+
pInputText
|
|
3086
|
+
id="email"
|
|
3087
|
+
type="email"
|
|
3088
|
+
placeholder="Enter your email address"
|
|
3089
|
+
class="w-full md:w-[25rem] mb-6"
|
|
3090
|
+
[formField]="forgotForm.email"
|
|
3091
|
+
autocomplete="email" />
|
|
3092
|
+
|
|
3093
|
+
<p-button
|
|
3094
|
+
label="Send Reset Link"
|
|
3095
|
+
styleClass="w-full mb-4"
|
|
3096
|
+
type="submit"
|
|
3097
|
+
[loading]="isLoading()"
|
|
3098
|
+
[disabled]="!isFormValid() || isLoading()" />
|
|
3099
|
+
|
|
3100
|
+
<div class="flex items-center justify-center gap-2">
|
|
3101
|
+
<span class="font-medium">Remember your password?</span>
|
|
3102
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
3103
|
+
Sign In
|
|
3104
|
+
</a>
|
|
3105
|
+
</div>
|
|
3106
|
+
</form>
|
|
3107
|
+
} @else {
|
|
3108
|
+
<div class="text-center">
|
|
3109
|
+
<div class="flex justify-center mb-6">
|
|
3110
|
+
<i class="pi pi-envelope text-6xl text-primary"></i>
|
|
3111
|
+
</div>
|
|
3112
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
3113
|
+
Check Your Email
|
|
3114
|
+
</div>
|
|
3115
|
+
<p class="text-muted-color font-medium mb-6">
|
|
3116
|
+
We've sent a password reset link to <strong>{{ submittedEmail() }}</strong>.
|
|
3117
|
+
Please check your inbox and follow the instructions.
|
|
3118
|
+
</p>
|
|
3119
|
+
<p class="text-sm text-muted-color mb-6">
|
|
3120
|
+
Didn't receive the email? Check your spam folder or
|
|
3121
|
+
<span
|
|
3122
|
+
class="text-primary cursor-pointer font-semibold"
|
|
3123
|
+
(click)="resetForm()">
|
|
3124
|
+
try again
|
|
3125
|
+
</span>.
|
|
3126
|
+
</p>
|
|
3127
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
3128
|
+
Back to Sign In
|
|
3129
|
+
</a>
|
|
3130
|
+
</div>
|
|
3131
|
+
}
|
|
3132
|
+
</div>
|
|
3133
|
+
</div>
|
|
3134
|
+
</div>
|
|
3135
|
+
</div>
|
|
3136
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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" }] });
|
|
3137
|
+
}
|
|
3138
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ForgotPasswordPageComponent, decorators: [{
|
|
3139
|
+
type: Component,
|
|
3140
|
+
args: [{
|
|
3141
|
+
selector: 'lib-forgot-password-page',
|
|
3142
|
+
standalone: true,
|
|
3143
|
+
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
3144
|
+
template: `
|
|
3145
|
+
<app-floating-configurator />
|
|
3146
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
3147
|
+
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
3148
|
+
<div
|
|
3149
|
+
class="w-full"
|
|
3150
|
+
style="border-radius: var(--p-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
|
3151
|
+
<div
|
|
3152
|
+
class="w-full py-16 px-12"
|
|
3153
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background);">
|
|
3154
|
+
|
|
3155
|
+
@if (!emailSent()) {
|
|
3156
|
+
<div class="text-center mb-8">
|
|
3157
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
3158
|
+
Forgot Password
|
|
3159
|
+
</div>
|
|
3160
|
+
<span class="text-muted-color font-medium">
|
|
3161
|
+
Enter your email address and we'll send you a link to reset your password.
|
|
3162
|
+
</span>
|
|
3163
|
+
</div>
|
|
3164
|
+
|
|
3165
|
+
<form (ngSubmit)="onSubmit()">
|
|
3166
|
+
<label for="email" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
3167
|
+
Email
|
|
3168
|
+
</label>
|
|
3169
|
+
<input
|
|
3170
|
+
pInputText
|
|
3171
|
+
id="email"
|
|
3172
|
+
type="email"
|
|
3173
|
+
placeholder="Enter your email address"
|
|
3174
|
+
class="w-full md:w-[25rem] mb-6"
|
|
3175
|
+
[formField]="forgotForm.email"
|
|
3176
|
+
autocomplete="email" />
|
|
3177
|
+
|
|
3178
|
+
<p-button
|
|
3179
|
+
label="Send Reset Link"
|
|
3180
|
+
styleClass="w-full mb-4"
|
|
3181
|
+
type="submit"
|
|
3182
|
+
[loading]="isLoading()"
|
|
3183
|
+
[disabled]="!isFormValid() || isLoading()" />
|
|
3184
|
+
|
|
3185
|
+
<div class="flex items-center justify-center gap-2">
|
|
3186
|
+
<span class="font-medium">Remember your password?</span>
|
|
3187
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
3188
|
+
Sign In
|
|
3189
|
+
</a>
|
|
3190
|
+
</div>
|
|
3191
|
+
</form>
|
|
3192
|
+
} @else {
|
|
3193
|
+
<div class="text-center">
|
|
3194
|
+
<div class="flex justify-center mb-6">
|
|
3195
|
+
<i class="pi pi-envelope text-6xl text-primary"></i>
|
|
3196
|
+
</div>
|
|
3197
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
3198
|
+
Check Your Email
|
|
3199
|
+
</div>
|
|
3200
|
+
<p class="text-muted-color font-medium mb-6">
|
|
3201
|
+
We've sent a password reset link to <strong>{{ submittedEmail() }}</strong>.
|
|
3202
|
+
Please check your inbox and follow the instructions.
|
|
3203
|
+
</p>
|
|
3204
|
+
<p class="text-sm text-muted-color mb-6">
|
|
3205
|
+
Didn't receive the email? Check your spam folder or
|
|
3206
|
+
<span
|
|
3207
|
+
class="text-primary cursor-pointer font-semibold"
|
|
3208
|
+
(click)="resetForm()">
|
|
3209
|
+
try again
|
|
3210
|
+
</span>.
|
|
3211
|
+
</p>
|
|
3212
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
3213
|
+
Back to Sign In
|
|
3214
|
+
</a>
|
|
3215
|
+
</div>
|
|
3216
|
+
}
|
|
3217
|
+
</div>
|
|
3218
|
+
</div>
|
|
3219
|
+
</div>
|
|
3220
|
+
</div>
|
|
3221
|
+
`,
|
|
3222
|
+
}]
|
|
3223
|
+
}] });
|
|
3224
|
+
|
|
3225
|
+
var forgotPasswordPage_component = /*#__PURE__*/Object.freeze({
|
|
3226
|
+
__proto__: null,
|
|
3227
|
+
ForgotPasswordPageComponent: ForgotPasswordPageComponent
|
|
3228
|
+
});
|
|
3229
|
+
|
|
2977
3230
|
/**
|
|
2978
3231
|
* Login Page Component
|
|
2979
3232
|
* Multi-step authentication flow:
|
|
@@ -2986,12 +3239,10 @@ class LoginPageComponent {
|
|
|
2986
3239
|
messageService = inject(MessageService);
|
|
2987
3240
|
authApi = inject(AuthApiService);
|
|
2988
3241
|
authState = inject(AuthStateService);
|
|
2989
|
-
/** Return URL after successful login */
|
|
2990
3242
|
returnUrl = input('', ...(ngDevMode ? [{ debugName: "returnUrl" }] : []));
|
|
2991
|
-
/** Current step in login flow */
|
|
2992
3243
|
currentStep = signal('credentials', ...(ngDevMode ? [{ debugName: "currentStep" }] : []));
|
|
2993
|
-
/** Loading state */
|
|
2994
3244
|
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
3245
|
+
unverifiedEmail = signal('', ...(ngDevMode ? [{ debugName: "unverifiedEmail" }] : []));
|
|
2995
3246
|
// ============================================
|
|
2996
3247
|
// Login Form (Signal Forms)
|
|
2997
3248
|
// ============================================
|
|
@@ -3080,23 +3331,24 @@ class LoginPageComponent {
|
|
|
3080
3331
|
};
|
|
3081
3332
|
this.authApi
|
|
3082
3333
|
.login(loginData)
|
|
3083
|
-
.pipe(
|
|
3334
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3084
3335
|
.subscribe({
|
|
3085
3336
|
next: (response) => {
|
|
3086
3337
|
this.isLoading.set(false);
|
|
3087
|
-
|
|
3338
|
+
if (isEmailVerificationRequired(response)) {
|
|
3339
|
+
this.unverifiedEmail.set(response.email);
|
|
3340
|
+
this.currentStep.set('email-verification');
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3088
3343
|
if (isCompanySelectionResponse(response)) {
|
|
3089
|
-
// Move to step 2 (company selection)
|
|
3090
3344
|
this.sessionId = response.sessionId;
|
|
3091
3345
|
this.companies.set(response.companies);
|
|
3092
3346
|
this.userName.set(response.user.name ?? response.user.email);
|
|
3093
|
-
// Store selection state for potential page refresh
|
|
3094
3347
|
this.authState.setSelectionState(response);
|
|
3095
3348
|
this.currentStep.set('company-selection');
|
|
3096
3349
|
this.autoSelectIfSingle();
|
|
3097
3350
|
}
|
|
3098
3351
|
else {
|
|
3099
|
-
// Direct login success
|
|
3100
3352
|
this.authState.setLoginState(response);
|
|
3101
3353
|
this.messageService.add({
|
|
3102
3354
|
severity: 'success',
|
|
@@ -3162,7 +3414,7 @@ class LoginPageComponent {
|
|
|
3162
3414
|
};
|
|
3163
3415
|
this.authApi
|
|
3164
3416
|
.select(selectData)
|
|
3165
|
-
.pipe(
|
|
3417
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3166
3418
|
.subscribe({
|
|
3167
3419
|
next: (response) => {
|
|
3168
3420
|
this.isLoading.set(false);
|
|
@@ -3186,9 +3438,9 @@ class LoginPageComponent {
|
|
|
3186
3438
|
});
|
|
3187
3439
|
}
|
|
3188
3440
|
goBackToCredentials() {
|
|
3189
|
-
// Reset selection state
|
|
3190
3441
|
this.sessionId = null;
|
|
3191
3442
|
this.companies.set([]);
|
|
3443
|
+
this.unverifiedEmail.set('');
|
|
3192
3444
|
this.selectionFormModel.set({
|
|
3193
3445
|
companyId: '',
|
|
3194
3446
|
branchId: '',
|
|
@@ -3197,6 +3449,33 @@ class LoginPageComponent {
|
|
|
3197
3449
|
this.authState.resetState();
|
|
3198
3450
|
this.currentStep.set('credentials');
|
|
3199
3451
|
}
|
|
3452
|
+
onResendVerification() {
|
|
3453
|
+
const email = this.unverifiedEmail();
|
|
3454
|
+
if (!email)
|
|
3455
|
+
return;
|
|
3456
|
+
this.isLoading.set(true);
|
|
3457
|
+
this.authApi
|
|
3458
|
+
.resendVerification(email)
|
|
3459
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3460
|
+
.subscribe({
|
|
3461
|
+
next: (response) => {
|
|
3462
|
+
this.isLoading.set(false);
|
|
3463
|
+
this.messageService.add({
|
|
3464
|
+
severity: 'success',
|
|
3465
|
+
summary: 'Email Sent',
|
|
3466
|
+
detail: response.message || 'Verification email has been sent.',
|
|
3467
|
+
});
|
|
3468
|
+
},
|
|
3469
|
+
error: (error) => {
|
|
3470
|
+
this.isLoading.set(false);
|
|
3471
|
+
this.messageService.add({
|
|
3472
|
+
severity: 'error',
|
|
3473
|
+
summary: 'Failed to Send',
|
|
3474
|
+
detail: error.error?.message || 'Unable to send verification email.',
|
|
3475
|
+
});
|
|
3476
|
+
},
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3200
3479
|
// ============================================
|
|
3201
3480
|
// Helpers
|
|
3202
3481
|
// ============================================
|
|
@@ -3332,6 +3611,33 @@ class LoginPageComponent {
|
|
|
3332
3611
|
</form>
|
|
3333
3612
|
}
|
|
3334
3613
|
|
|
3614
|
+
<!-- Email Verification Required -->
|
|
3615
|
+
@if (currentStep() === 'email-verification') {
|
|
3616
|
+
<div class="text-center">
|
|
3617
|
+
<div class="flex justify-center mb-6">
|
|
3618
|
+
<i class="pi pi-envelope text-6xl text-warn"></i>
|
|
3619
|
+
</div>
|
|
3620
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
3621
|
+
Email Not Verified
|
|
3622
|
+
</div>
|
|
3623
|
+
<p class="text-muted-color font-medium mb-6">
|
|
3624
|
+
Please verify your email address <strong>{{ unverifiedEmail() }}</strong> before logging in.
|
|
3625
|
+
</p>
|
|
3626
|
+
<p-button
|
|
3627
|
+
label="Resend Verification Email"
|
|
3628
|
+
styleClass="w-full mb-4"
|
|
3629
|
+
[loading]="isLoading()"
|
|
3630
|
+
(onClick)="onResendVerification()" />
|
|
3631
|
+
<div class="mt-4">
|
|
3632
|
+
<span
|
|
3633
|
+
class="font-semibold text-primary cursor-pointer"
|
|
3634
|
+
(click)="goBackToCredentials()">
|
|
3635
|
+
Back to Sign In
|
|
3636
|
+
</span>
|
|
3637
|
+
</div>
|
|
3638
|
+
</div>
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3335
3641
|
<!-- Step 2: Company Selection -->
|
|
3336
3642
|
@if (currentStep() === 'company-selection') {
|
|
3337
3643
|
<div class="text-center mb-8">
|
|
@@ -3401,7 +3707,7 @@ class LoginPageComponent {
|
|
|
3401
3707
|
</div>
|
|
3402
3708
|
</div>
|
|
3403
3709
|
</div>
|
|
3404
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2
|
|
3710
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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" }] });
|
|
3405
3711
|
}
|
|
3406
3712
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: LoginPageComponent, decorators: [{
|
|
3407
3713
|
type: Component,
|
|
@@ -3509,6 +3815,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
3509
3815
|
</form>
|
|
3510
3816
|
}
|
|
3511
3817
|
|
|
3818
|
+
<!-- Email Verification Required -->
|
|
3819
|
+
@if (currentStep() === 'email-verification') {
|
|
3820
|
+
<div class="text-center">
|
|
3821
|
+
<div class="flex justify-center mb-6">
|
|
3822
|
+
<i class="pi pi-envelope text-6xl text-warn"></i>
|
|
3823
|
+
</div>
|
|
3824
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
3825
|
+
Email Not Verified
|
|
3826
|
+
</div>
|
|
3827
|
+
<p class="text-muted-color font-medium mb-6">
|
|
3828
|
+
Please verify your email address <strong>{{ unverifiedEmail() }}</strong> before logging in.
|
|
3829
|
+
</p>
|
|
3830
|
+
<p-button
|
|
3831
|
+
label="Resend Verification Email"
|
|
3832
|
+
styleClass="w-full mb-4"
|
|
3833
|
+
[loading]="isLoading()"
|
|
3834
|
+
(onClick)="onResendVerification()" />
|
|
3835
|
+
<div class="mt-4">
|
|
3836
|
+
<span
|
|
3837
|
+
class="font-semibold text-primary cursor-pointer"
|
|
3838
|
+
(click)="goBackToCredentials()">
|
|
3839
|
+
Back to Sign In
|
|
3840
|
+
</span>
|
|
3841
|
+
</div>
|
|
3842
|
+
</div>
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3512
3845
|
<!-- Step 2: Company Selection -->
|
|
3513
3846
|
@if (currentStep() === 'company-selection') {
|
|
3514
3847
|
<div class="text-center mb-8">
|
|
@@ -3588,109 +3921,922 @@ var loginPage_component = /*#__PURE__*/Object.freeze({
|
|
|
3588
3921
|
});
|
|
3589
3922
|
|
|
3590
3923
|
/**
|
|
3591
|
-
*
|
|
3592
|
-
*
|
|
3593
|
-
*
|
|
3924
|
+
* Profile Page Component
|
|
3925
|
+
*
|
|
3926
|
+
* Allows users to view and update their profile information.
|
|
3927
|
+
* Features:
|
|
3928
|
+
* - Update name, phone, profile picture (if storage enabled)
|
|
3929
|
+
* - View company/branch info (read-only, if company feature enabled)
|
|
3930
|
+
* - View roles/actions (read-only, if IAM enabled via optional injection)
|
|
3931
|
+
*
|
|
3932
|
+
* Package Independence:
|
|
3933
|
+
* - Uses PROFILE_UPLOAD_PROVIDER (optional) for storage functionality
|
|
3934
|
+
* - Uses PROFILE_PERMISSION_PROVIDER (optional) for IAM functionality
|
|
3935
|
+
* - No direct imports from ng-storage or ng-iam
|
|
3594
3936
|
*/
|
|
3595
|
-
class
|
|
3937
|
+
class ProfilePageComponent {
|
|
3596
3938
|
destroyRef = inject(DestroyRef);
|
|
3597
3939
|
appConfig = inject(APP_CONFIG);
|
|
3598
3940
|
messageService = inject(MessageService);
|
|
3599
|
-
authApi = inject(AuthApiService);
|
|
3600
3941
|
authState = inject(AuthStateService);
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3942
|
+
userService = inject(UserApiService);
|
|
3943
|
+
authApiService = inject(AuthApiService);
|
|
3944
|
+
fileUrlService = inject(FileUrlService);
|
|
3945
|
+
// Optional injection - null if storage not configured
|
|
3946
|
+
uploadProvider = inject(PROFILE_UPLOAD_PROVIDER, {
|
|
3947
|
+
optional: true,
|
|
3948
|
+
});
|
|
3949
|
+
// Optional injection - null if IAM not configured
|
|
3950
|
+
permissionProvider = inject(PROFILE_PERMISSION_PROVIDER, {
|
|
3951
|
+
optional: true,
|
|
3952
|
+
});
|
|
3953
|
+
// State
|
|
3605
3954
|
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
//
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3955
|
+
isLoadingPermissions = signal(false, ...(ngDevMode ? [{ debugName: "isLoadingPermissions" }] : []));
|
|
3956
|
+
_profilePictureUrl = signal(null, ...(ngDevMode ? [{ debugName: "_profilePictureUrl" }] : []));
|
|
3957
|
+
profilePictureUrl = this._profilePictureUrl.asReadonly();
|
|
3958
|
+
// Permission data
|
|
3959
|
+
_userRoles = signal([], ...(ngDevMode ? [{ debugName: "_userRoles" }] : []));
|
|
3960
|
+
userRoles = this._userRoles.asReadonly();
|
|
3961
|
+
_userActions = signal([], ...(ngDevMode ? [{ debugName: "_userActions" }] : []));
|
|
3962
|
+
userActions = this._userActions.asReadonly();
|
|
3963
|
+
// Computed from auth state
|
|
3964
|
+
userEmail = computed(() => this.authState.user()?.email ?? '', ...(ngDevMode ? [{ debugName: "userEmail" }] : []));
|
|
3965
|
+
userName = computed(() => this.authState.user()?.name ?? 'User', ...(ngDevMode ? [{ debugName: "userName" }] : []));
|
|
3966
|
+
userInitials = computed(() => {
|
|
3967
|
+
const name = this.authState.user()?.name ?? '';
|
|
3968
|
+
return name
|
|
3969
|
+
.split(' ')
|
|
3970
|
+
.map((n) => n[0])
|
|
3971
|
+
.join('')
|
|
3972
|
+
.toUpperCase()
|
|
3973
|
+
.slice(0, 2);
|
|
3974
|
+
}, ...(ngDevMode ? [{ debugName: "userInitials" }] : []));
|
|
3975
|
+
companyName = computed(() => this.authState.companyName(), ...(ngDevMode ? [{ debugName: "companyName" }] : []));
|
|
3976
|
+
branchName = computed(() => this.authState.branchName(), ...(ngDevMode ? [{ debugName: "branchName" }] : []));
|
|
3977
|
+
// Feature flags - Storage feature check
|
|
3978
|
+
showUploadSection = computed(() => isFeatureEnabled(this.appConfig, 'storage') &&
|
|
3979
|
+
this.uploadProvider !== null, ...(ngDevMode ? [{ debugName: "showUploadSection" }] : []));
|
|
3980
|
+
showCompanySection = computed(() => isCompanyFeatureEnabled(this.appConfig), ...(ngDevMode ? [{ debugName: "showCompanySection" }] : []));
|
|
3981
|
+
showPermissionsSection = computed(() => isFeatureEnabled(this.appConfig, 'iam') &&
|
|
3982
|
+
this.permissionProvider !== null, ...(ngDevMode ? [{ debugName: "showPermissionsSection" }] : []));
|
|
3983
|
+
permissionMode = computed(() => getPermissionMode(this.appConfig), ...(ngDevMode ? [{ debugName: "permissionMode" }] : []));
|
|
3984
|
+
showRoles = computed(() => {
|
|
3985
|
+
const mode = this.permissionMode();
|
|
3986
|
+
return mode === 'rbac' || mode === 'full';
|
|
3987
|
+
}, ...(ngDevMode ? [{ debugName: "showRoles" }] : []));
|
|
3988
|
+
showActions = computed(() => {
|
|
3989
|
+
const mode = this.permissionMode();
|
|
3990
|
+
return mode === 'direct' || mode === 'full';
|
|
3991
|
+
}, ...(ngDevMode ? [{ debugName: "showActions" }] : []));
|
|
3992
|
+
// Show bottom row only if at least one section is visible
|
|
3993
|
+
showBottomRow = computed(() => this.showCompanySection() || this.showPermissionsSection(), ...(ngDevMode ? [{ debugName: "showBottomRow" }] : []));
|
|
3994
|
+
// Form
|
|
3995
|
+
formModel = signal({
|
|
3613
3996
|
name: '',
|
|
3614
|
-
email: '',
|
|
3615
|
-
password: '',
|
|
3616
|
-
confirmPassword: '',
|
|
3617
3997
|
phone: '',
|
|
3618
|
-
}, ...(ngDevMode ? [{ debugName: "
|
|
3619
|
-
|
|
3620
|
-
userForm = form(this.userFormModel, (f) => {
|
|
3998
|
+
}, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
3999
|
+
formTree = form(this.formModel, (f) => {
|
|
3621
4000
|
required(f.name, { message: 'Name is required' });
|
|
3622
|
-
minLength(f.name, 2, { message: 'Name must be at least 2 characters' });
|
|
3623
|
-
required(f.email, { message: 'Email is required' });
|
|
3624
|
-
email(f.email, { message: 'Please enter a valid email address' });
|
|
3625
|
-
required(f.password, { message: 'Password is required' });
|
|
3626
|
-
minLength(f.password, 6, { message: 'Password must be at least 6 characters' });
|
|
3627
|
-
required(f.confirmPassword, { message: 'Please confirm your password' });
|
|
3628
|
-
});
|
|
3629
|
-
// ============================================
|
|
3630
|
-
// Company Form (Signal Forms)
|
|
3631
|
-
// ============================================
|
|
3632
|
-
/** Company form model */
|
|
3633
|
-
companyFormModel = signal({
|
|
3634
|
-
createNewCompany: false,
|
|
3635
|
-
companySlug: '',
|
|
3636
|
-
branchSlug: '',
|
|
3637
|
-
newCompanyName: '',
|
|
3638
|
-
newCompanyPhone: '',
|
|
3639
|
-
newCompanyAddress: '',
|
|
3640
|
-
}, ...(ngDevMode ? [{ debugName: "companyFormModel" }] : []));
|
|
3641
|
-
/** Company form with conditional validation schema */
|
|
3642
|
-
companyForm = form(this.companyFormModel, (f) => {
|
|
3643
|
-
// Validation is conditional based on createNewCompany flag
|
|
3644
|
-
// We'll handle this in isFormValid() computed
|
|
3645
4001
|
});
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
4002
|
+
isFormValid = computed(() => {
|
|
4003
|
+
const model = this.formModel();
|
|
4004
|
+
return model.name.trim().length > 0;
|
|
4005
|
+
}, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
4006
|
+
// Password form
|
|
4007
|
+
passwordModel = signal({
|
|
4008
|
+
currentPassword: '',
|
|
4009
|
+
newPassword: '',
|
|
4010
|
+
confirmPassword: '',
|
|
4011
|
+
}, ...(ngDevMode ? [{ debugName: "passwordModel" }] : []));
|
|
4012
|
+
isPasswordLoading = signal(false, ...(ngDevMode ? [{ debugName: "isPasswordLoading" }] : []));
|
|
4013
|
+
isPasswordFormValid = computed(() => {
|
|
4014
|
+
const model = this.passwordModel();
|
|
4015
|
+
return (model.currentPassword.length > 0 &&
|
|
4016
|
+
model.newPassword.length >= 6 &&
|
|
4017
|
+
model.newPassword === model.confirmPassword);
|
|
4018
|
+
}, ...(ngDevMode ? [{ debugName: "isPasswordFormValid" }] : []));
|
|
4019
|
+
passwordsMatch = computed(() => {
|
|
4020
|
+
const model = this.passwordModel();
|
|
4021
|
+
return model.confirmPassword.length === 0 || model.newPassword === model.confirmPassword;
|
|
4022
|
+
}, ...(ngDevMode ? [{ debugName: "passwordsMatch" }] : []));
|
|
4023
|
+
// Upload function for FileUploader - uses provider if available
|
|
4024
|
+
uploadFileFn = (file, options) => {
|
|
4025
|
+
if (!this.uploadProvider) {
|
|
4026
|
+
throw new Error('Upload provider not available');
|
|
4027
|
+
}
|
|
4028
|
+
return this.uploadProvider
|
|
4029
|
+
.uploadProfilePicture(file, {
|
|
4030
|
+
folderPath: options?.folderPath ?? 'profile-pictures',
|
|
4031
|
+
compress: options?.compress ?? true,
|
|
4032
|
+
maxWidth: options?.maxWidth ?? 400,
|
|
4033
|
+
maxHeight: options?.maxHeight ?? 400,
|
|
4034
|
+
})
|
|
4035
|
+
.pipe(map((response) => ({
|
|
4036
|
+
success: response.success,
|
|
4037
|
+
message: response.message,
|
|
4038
|
+
data: response.data
|
|
4039
|
+
? this.mapUploadResultToUploadedFile(response.data)
|
|
4040
|
+
: undefined,
|
|
4041
|
+
})));
|
|
4042
|
+
};
|
|
4043
|
+
/** Map upload result to IUploadedFile format */
|
|
4044
|
+
mapUploadResultToUploadedFile(result) {
|
|
4045
|
+
return {
|
|
4046
|
+
id: result.id, // UUID for storing in profilePictureId
|
|
4047
|
+
name: result.name,
|
|
4048
|
+
key: result.key,
|
|
4049
|
+
size: result.size,
|
|
4050
|
+
contentType: result.contentType,
|
|
4051
|
+
};
|
|
4052
|
+
}
|
|
4053
|
+
ngOnInit() {
|
|
4054
|
+
this.loadUserData();
|
|
4055
|
+
if (this.showPermissionsSection()) {
|
|
4056
|
+
this.loadPermissions();
|
|
3657
4057
|
}
|
|
3658
4058
|
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
4059
|
+
loadUserData() {
|
|
4060
|
+
const user = this.authState.user();
|
|
4061
|
+
if (user) {
|
|
4062
|
+
this.formModel.set({
|
|
4063
|
+
name: user.name ?? '',
|
|
4064
|
+
phone: user.phone ?? '',
|
|
4065
|
+
});
|
|
4066
|
+
// Load profile picture URL (only if storage is enabled)
|
|
4067
|
+
if (user.profilePictureId && isFeatureEnabled(this.appConfig, 'storage')) {
|
|
4068
|
+
this.fileUrlService
|
|
4069
|
+
.fetchSingleFileUrl(user.profilePictureId)
|
|
4070
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4071
|
+
.subscribe((file) => {
|
|
4072
|
+
this._profilePictureUrl.set(file?.url ?? null);
|
|
4073
|
+
});
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
}
|
|
4077
|
+
loadPermissions() {
|
|
4078
|
+
const user = this.authState.user();
|
|
4079
|
+
const branch = this.authState.branch();
|
|
4080
|
+
if (!user || !this.permissionProvider)
|
|
4081
|
+
return;
|
|
4082
|
+
const loadRoles = this.showRoles();
|
|
4083
|
+
const loadActions = this.showActions();
|
|
4084
|
+
if (!loadRoles && !loadActions)
|
|
4085
|
+
return;
|
|
4086
|
+
this.isLoadingPermissions.set(true);
|
|
4087
|
+
let rolesLoaded = !loadRoles;
|
|
4088
|
+
let actionsLoaded = !loadActions;
|
|
4089
|
+
const checkComplete = () => {
|
|
4090
|
+
if (rolesLoaded && actionsLoaded) {
|
|
4091
|
+
this.isLoadingPermissions.set(false);
|
|
4092
|
+
}
|
|
4093
|
+
};
|
|
4094
|
+
// Load roles (if applicable)
|
|
4095
|
+
if (loadRoles) {
|
|
4096
|
+
this.permissionProvider
|
|
4097
|
+
.getUserRoles(user.id, branch?.id)
|
|
4098
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4099
|
+
.subscribe({
|
|
4100
|
+
next: (response) => {
|
|
4101
|
+
this._userRoles.set(response.data ?? []);
|
|
4102
|
+
rolesLoaded = true;
|
|
4103
|
+
checkComplete();
|
|
4104
|
+
},
|
|
4105
|
+
error: () => {
|
|
4106
|
+
this._userRoles.set([]);
|
|
4107
|
+
rolesLoaded = true;
|
|
4108
|
+
checkComplete();
|
|
4109
|
+
},
|
|
4110
|
+
});
|
|
4111
|
+
}
|
|
4112
|
+
// Load actions (if applicable)
|
|
4113
|
+
if (loadActions) {
|
|
4114
|
+
this.permissionProvider
|
|
4115
|
+
.getUserActions(user.id, branch?.id)
|
|
4116
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4117
|
+
.subscribe({
|
|
4118
|
+
next: (response) => {
|
|
4119
|
+
this._userActions.set(response.data ?? []);
|
|
4120
|
+
actionsLoaded = true;
|
|
4121
|
+
checkComplete();
|
|
4122
|
+
},
|
|
4123
|
+
error: () => {
|
|
4124
|
+
this._userActions.set([]);
|
|
4125
|
+
actionsLoaded = true;
|
|
4126
|
+
checkComplete();
|
|
4127
|
+
},
|
|
4128
|
+
});
|
|
4129
|
+
}
|
|
4130
|
+
}
|
|
4131
|
+
onProfilePictureUploaded(file) {
|
|
4132
|
+
// Update user with new profile picture ID (file.id is the FileManager UUID)
|
|
4133
|
+
const user = this.authState.user();
|
|
4134
|
+
if (!user || !file.id)
|
|
4135
|
+
return;
|
|
4136
|
+
const fileManagerId = file.id;
|
|
4137
|
+
this.userService
|
|
4138
|
+
.updateProfile({ id: user.id, profilePictureId: fileManagerId })
|
|
4139
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4140
|
+
.subscribe({
|
|
4141
|
+
next: (response) => {
|
|
4142
|
+
if (response.success && response.data) {
|
|
4143
|
+
// Update auth state with new profile picture ID
|
|
4144
|
+
this.authState.user.set({
|
|
4145
|
+
...user,
|
|
4146
|
+
profilePictureId: fileManagerId,
|
|
4147
|
+
});
|
|
4148
|
+
// Fetch new profile picture URL (force refresh to get fresh URL)
|
|
4149
|
+
this.fileUrlService
|
|
4150
|
+
.fetchSingleFileUrl(fileManagerId, true)
|
|
4151
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4152
|
+
.subscribe((fileInfo) => {
|
|
4153
|
+
this._profilePictureUrl.set(fileInfo?.url ?? null);
|
|
4154
|
+
});
|
|
4155
|
+
this.messageService.add({
|
|
4156
|
+
severity: 'success',
|
|
4157
|
+
summary: 'Success',
|
|
4158
|
+
detail: 'Profile picture updated',
|
|
4159
|
+
});
|
|
4160
|
+
}
|
|
4161
|
+
},
|
|
4162
|
+
error: () => {
|
|
4163
|
+
this.messageService.add({
|
|
4164
|
+
severity: 'error',
|
|
4165
|
+
summary: 'Error',
|
|
4166
|
+
detail: 'Failed to update profile picture',
|
|
4167
|
+
});
|
|
4168
|
+
},
|
|
4169
|
+
});
|
|
4170
|
+
}
|
|
4171
|
+
onSubmit() {
|
|
4172
|
+
if (!this.isFormValid())
|
|
4173
|
+
return;
|
|
4174
|
+
const user = this.authState.user();
|
|
4175
|
+
if (!user)
|
|
4176
|
+
return;
|
|
4177
|
+
this.isLoading.set(true);
|
|
4178
|
+
const model = this.formModel();
|
|
4179
|
+
const updateData = {
|
|
4180
|
+
id: user.id,
|
|
4181
|
+
name: model.name,
|
|
4182
|
+
phone: model.phone || undefined,
|
|
4183
|
+
};
|
|
4184
|
+
this.userService
|
|
4185
|
+
.updateProfile(updateData)
|
|
4186
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4187
|
+
.subscribe({
|
|
4188
|
+
next: (response) => {
|
|
4189
|
+
if (response.success && response.data) {
|
|
4190
|
+
// Update auth state with new user data (name and phone)
|
|
4191
|
+
this.authState.user.set({
|
|
4192
|
+
...user,
|
|
4193
|
+
name: response.data.name,
|
|
4194
|
+
phone: response.data.phone ?? null,
|
|
4195
|
+
});
|
|
4196
|
+
this.messageService.add({
|
|
4197
|
+
severity: 'success',
|
|
4198
|
+
summary: 'Success',
|
|
4199
|
+
detail: 'Profile updated successfully',
|
|
4200
|
+
});
|
|
4201
|
+
}
|
|
4202
|
+
this.isLoading.set(false);
|
|
4203
|
+
},
|
|
4204
|
+
error: (error) => {
|
|
4205
|
+
const err = error;
|
|
4206
|
+
this.messageService.add({
|
|
4207
|
+
severity: 'error',
|
|
4208
|
+
summary: 'Error',
|
|
4209
|
+
detail: err.error?.message || 'Failed to update profile',
|
|
4210
|
+
});
|
|
4211
|
+
this.isLoading.set(false);
|
|
4212
|
+
},
|
|
4213
|
+
});
|
|
4214
|
+
}
|
|
4215
|
+
updatePasswordField(field, value) {
|
|
4216
|
+
this.passwordModel.update((model) => ({ ...model, [field]: value }));
|
|
4217
|
+
}
|
|
4218
|
+
onChangePassword() {
|
|
4219
|
+
if (!this.isPasswordFormValid())
|
|
4220
|
+
return;
|
|
4221
|
+
this.isPasswordLoading.set(true);
|
|
4222
|
+
const model = this.passwordModel();
|
|
4223
|
+
this.authApiService
|
|
4224
|
+
.changePassword({
|
|
4225
|
+
currentPassword: model.currentPassword,
|
|
4226
|
+
newPassword: model.newPassword,
|
|
4227
|
+
})
|
|
4228
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
4229
|
+
.subscribe({
|
|
4230
|
+
next: () => {
|
|
4231
|
+
this.messageService.add({
|
|
4232
|
+
severity: 'success',
|
|
4233
|
+
summary: 'Success',
|
|
4234
|
+
detail: 'Password changed successfully',
|
|
4235
|
+
});
|
|
4236
|
+
// Reset password form
|
|
4237
|
+
this.passwordModel.set({
|
|
4238
|
+
currentPassword: '',
|
|
4239
|
+
newPassword: '',
|
|
4240
|
+
confirmPassword: '',
|
|
4241
|
+
});
|
|
4242
|
+
this.isPasswordLoading.set(false);
|
|
4243
|
+
},
|
|
4244
|
+
error: (error) => {
|
|
4245
|
+
const err = error;
|
|
4246
|
+
this.messageService.add({
|
|
4247
|
+
severity: 'error',
|
|
4248
|
+
summary: 'Error',
|
|
4249
|
+
detail: err.error?.message || 'Failed to change password',
|
|
4250
|
+
});
|
|
4251
|
+
this.isPasswordLoading.set(false);
|
|
4252
|
+
},
|
|
4253
|
+
});
|
|
4254
|
+
}
|
|
4255
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfilePageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4256
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ProfilePageComponent, isStandalone: true, selector: "lib-profile-page", ngImport: i0, template: `
|
|
4257
|
+
<!-- Page Header -->
|
|
4258
|
+
<div class="mb-4 md:mb-6">
|
|
4259
|
+
<h1 class="text-2xl font-bold text-color mb-2">My Profile</h1>
|
|
4260
|
+
<p class="text-muted-color">Manage your account settings and preferences.</p>
|
|
4261
|
+
</div>
|
|
4262
|
+
|
|
4263
|
+
<!-- Profile Form Card (full width) -->
|
|
4264
|
+
<div class="card mb-2 md:mb-4">
|
|
4265
|
+
<div class="flex flex-col md:flex-row gap-4 md:gap-6">
|
|
4266
|
+
<!-- Avatar Section -->
|
|
4267
|
+
<div class="flex flex-col items-center gap-4 md:w-72 lg:w-80 md:border-r md:border-surface-200 md:pr-6">
|
|
4268
|
+
@if (profilePictureUrl()) {
|
|
4269
|
+
<p-avatar
|
|
4270
|
+
[image]="profilePictureUrl()!"
|
|
4271
|
+
size="xlarge"
|
|
4272
|
+
shape="circle"
|
|
4273
|
+
styleClass="w-28 h-28" />
|
|
4274
|
+
} @else {
|
|
4275
|
+
<p-avatar
|
|
4276
|
+
[label]="userInitials()"
|
|
4277
|
+
size="xlarge"
|
|
4278
|
+
shape="circle"
|
|
4279
|
+
styleClass="w-28 h-28 text-3xl bg-primary text-primary-contrast" />
|
|
4280
|
+
}
|
|
4281
|
+
<div class="text-center">
|
|
4282
|
+
<div class="font-semibold text-color">{{ userName() }}</div>
|
|
4283
|
+
<div class="text-muted-color text-sm">{{ userEmail() }}</div>
|
|
4284
|
+
</div>
|
|
4285
|
+
<!-- Upload section - only show if storage is enabled and provider is available -->
|
|
4286
|
+
@if (showUploadSection()) {
|
|
4287
|
+
<lib-file-uploader
|
|
4288
|
+
[uploadFile]="uploadFileFn"
|
|
4289
|
+
[acceptTypes]="['image/*']"
|
|
4290
|
+
[multiple]="false"
|
|
4291
|
+
[maxSizeMb]="5"
|
|
4292
|
+
[showPreview]="false"
|
|
4293
|
+
(fileUploaded)="onProfilePictureUploaded($event)"
|
|
4294
|
+
class="w-full" />
|
|
4295
|
+
} @else {
|
|
4296
|
+
<div class="w-full p-3 bg-surface-100 rounded-lg text-center">
|
|
4297
|
+
<i class="pi pi-info-circle text-muted-color mr-2"></i>
|
|
4298
|
+
<span class="text-sm text-muted-color">Storage feature not enabled</span>
|
|
4299
|
+
</div>
|
|
4300
|
+
}
|
|
4301
|
+
</div>
|
|
4302
|
+
|
|
4303
|
+
<!-- Form Section -->
|
|
4304
|
+
<div class="flex-1">
|
|
4305
|
+
<form (ngSubmit)="onSubmit()" class="flex flex-col gap-4">
|
|
4306
|
+
<!-- Email (read-only) -->
|
|
4307
|
+
<div class="flex flex-col gap-2">
|
|
4308
|
+
<label class="font-medium text-color">Email</label>
|
|
4309
|
+
<input
|
|
4310
|
+
pInputText
|
|
4311
|
+
[value]="userEmail()"
|
|
4312
|
+
disabled
|
|
4313
|
+
class="bg-surface-50" />
|
|
4314
|
+
</div>
|
|
4315
|
+
|
|
4316
|
+
<!-- Name -->
|
|
4317
|
+
<div class="flex flex-col gap-2">
|
|
4318
|
+
<label for="name" class="font-medium text-color">Name *</label>
|
|
4319
|
+
<input
|
|
4320
|
+
pInputText
|
|
4321
|
+
id="name"
|
|
4322
|
+
[formField]="formTree.name"
|
|
4323
|
+
placeholder="Your full name" />
|
|
4324
|
+
</div>
|
|
4325
|
+
|
|
4326
|
+
<!-- Phone -->
|
|
4327
|
+
<div class="flex flex-col gap-2">
|
|
4328
|
+
<label for="phone" class="font-medium text-color">Phone</label>
|
|
4329
|
+
<input
|
|
4330
|
+
pInputText
|
|
4331
|
+
id="phone"
|
|
4332
|
+
[formField]="formTree.phone"
|
|
4333
|
+
placeholder="+1 234 567 8900" />
|
|
4334
|
+
</div>
|
|
4335
|
+
|
|
4336
|
+
<!-- Submit -->
|
|
4337
|
+
<div class="flex justify-end pt-4">
|
|
4338
|
+
<p-button
|
|
4339
|
+
label="Save Changes"
|
|
4340
|
+
icon="pi pi-check"
|
|
4341
|
+
type="submit"
|
|
4342
|
+
[loading]="isLoading()"
|
|
4343
|
+
[disabled]="!isFormValid() || isLoading()" />
|
|
4344
|
+
</div>
|
|
4345
|
+
</form>
|
|
4346
|
+
</div>
|
|
4347
|
+
</div>
|
|
4348
|
+
</div>
|
|
4349
|
+
|
|
4350
|
+
<!-- Security Card -->
|
|
4351
|
+
<div class="card mb-2 md:mb-4">
|
|
4352
|
+
<div class="flex items-center gap-2 mb-4">
|
|
4353
|
+
<i class="pi pi-lock text-primary"></i>
|
|
4354
|
+
<span class="font-semibold text-color">Security</span>
|
|
4355
|
+
</div>
|
|
4356
|
+
<form (ngSubmit)="onChangePassword()" class="flex flex-col gap-4">
|
|
4357
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
4358
|
+
<!-- Current Password -->
|
|
4359
|
+
<div class="flex flex-col gap-2">
|
|
4360
|
+
<label for="currentPassword" class="font-medium text-color">Current Password *</label>
|
|
4361
|
+
<p-password
|
|
4362
|
+
id="currentPassword"
|
|
4363
|
+
name="currentPassword"
|
|
4364
|
+
[ngModel]="passwordModel().currentPassword"
|
|
4365
|
+
(ngModelChange)="updatePasswordField('currentPassword', $event)"
|
|
4366
|
+
[feedback]="false"
|
|
4367
|
+
[toggleMask]="true"
|
|
4368
|
+
placeholder="Enter current password"
|
|
4369
|
+
styleClass="w-full"
|
|
4370
|
+
inputStyleClass="w-full" />
|
|
4371
|
+
</div>
|
|
4372
|
+
|
|
4373
|
+
<!-- New Password -->
|
|
4374
|
+
<div class="flex flex-col gap-2">
|
|
4375
|
+
<label for="newPassword" class="font-medium text-color">New Password *</label>
|
|
4376
|
+
<p-password
|
|
4377
|
+
id="newPassword"
|
|
4378
|
+
name="newPassword"
|
|
4379
|
+
[ngModel]="passwordModel().newPassword"
|
|
4380
|
+
(ngModelChange)="updatePasswordField('newPassword', $event)"
|
|
4381
|
+
[feedback]="true"
|
|
4382
|
+
[toggleMask]="true"
|
|
4383
|
+
placeholder="Min 6 characters"
|
|
4384
|
+
styleClass="w-full"
|
|
4385
|
+
inputStyleClass="w-full" />
|
|
4386
|
+
</div>
|
|
4387
|
+
|
|
4388
|
+
<!-- Confirm Password -->
|
|
4389
|
+
<div class="flex flex-col gap-2">
|
|
4390
|
+
<label for="confirmPassword" class="font-medium text-color">Confirm Password *</label>
|
|
4391
|
+
<p-password
|
|
4392
|
+
id="confirmPassword"
|
|
4393
|
+
name="confirmPassword"
|
|
4394
|
+
[ngModel]="passwordModel().confirmPassword"
|
|
4395
|
+
(ngModelChange)="updatePasswordField('confirmPassword', $event)"
|
|
4396
|
+
[feedback]="false"
|
|
4397
|
+
[toggleMask]="true"
|
|
4398
|
+
placeholder="Confirm new password"
|
|
4399
|
+
styleClass="w-full"
|
|
4400
|
+
inputStyleClass="w-full" />
|
|
4401
|
+
@if (!passwordsMatch()) {
|
|
4402
|
+
<small class="text-danger">Passwords do not match</small>
|
|
4403
|
+
}
|
|
4404
|
+
</div>
|
|
4405
|
+
</div>
|
|
4406
|
+
|
|
4407
|
+
<!-- Submit -->
|
|
4408
|
+
<div class="flex justify-end">
|
|
4409
|
+
<p-button
|
|
4410
|
+
label="Change Password"
|
|
4411
|
+
icon="pi pi-key"
|
|
4412
|
+
type="submit"
|
|
4413
|
+
[loading]="isPasswordLoading()"
|
|
4414
|
+
[disabled]="!isPasswordFormValid() || isPasswordLoading()" />
|
|
4415
|
+
</div>
|
|
4416
|
+
</form>
|
|
4417
|
+
</div>
|
|
4418
|
+
|
|
4419
|
+
<!-- Company & Permissions Row (50/50 on desktop, stacked on mobile) -->
|
|
4420
|
+
@if (showBottomRow()) {
|
|
4421
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-4 flex-1">
|
|
4422
|
+
<!-- Company/Branch Info -->
|
|
4423
|
+
@if (showCompanySection()) {
|
|
4424
|
+
<div class="card h-48 md:h-56 flex flex-col">
|
|
4425
|
+
<div class="flex items-center gap-2 mb-4">
|
|
4426
|
+
<i class="pi pi-building text-primary"></i>
|
|
4427
|
+
<span class="font-semibold text-color">Company & Branch</span>
|
|
4428
|
+
</div>
|
|
4429
|
+
<div class="flex flex-col gap-4 flex-1 overflow-auto">
|
|
4430
|
+
<div class="flex items-center gap-3">
|
|
4431
|
+
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0">
|
|
4432
|
+
<i class="pi pi-briefcase text-primary"></i>
|
|
4433
|
+
</div>
|
|
4434
|
+
<div>
|
|
4435
|
+
<div class="text-sm text-muted-color">Company</div>
|
|
4436
|
+
<div class="font-medium text-color">{{ companyName() || 'Not assigned' }}</div>
|
|
4437
|
+
</div>
|
|
4438
|
+
</div>
|
|
4439
|
+
<div class="flex items-center gap-3">
|
|
4440
|
+
<div class="w-10 h-10 rounded-lg bg-success/10 flex items-center justify-center flex-shrink-0">
|
|
4441
|
+
<i class="pi pi-map-marker text-success"></i>
|
|
4442
|
+
</div>
|
|
4443
|
+
<div>
|
|
4444
|
+
<div class="text-sm text-muted-color">Branch</div>
|
|
4445
|
+
<div class="font-medium text-color">{{ branchName() || 'Not assigned' }}</div>
|
|
4446
|
+
</div>
|
|
4447
|
+
</div>
|
|
4448
|
+
</div>
|
|
4449
|
+
</div>
|
|
4450
|
+
}
|
|
4451
|
+
|
|
4452
|
+
<!-- Permissions -->
|
|
4453
|
+
@if (showPermissionsSection()) {
|
|
4454
|
+
<div class="card h-48 md:h-56 flex flex-col">
|
|
4455
|
+
<div class="flex items-center justify-between mb-4">
|
|
4456
|
+
<div class="flex items-center gap-2">
|
|
4457
|
+
<i class="pi pi-shield text-primary"></i>
|
|
4458
|
+
<span class="font-semibold text-color">My Permissions</span>
|
|
4459
|
+
</div>
|
|
4460
|
+
<p-tag value="Read-only" severity="secondary" />
|
|
4461
|
+
</div>
|
|
4462
|
+
<div class="flex flex-col gap-4 flex-1 overflow-auto">
|
|
4463
|
+
<!-- Roles -->
|
|
4464
|
+
@if (showRoles()) {
|
|
4465
|
+
<div>
|
|
4466
|
+
<div class="text-sm text-muted-color mb-2 flex items-center gap-2">
|
|
4467
|
+
<i class="pi pi-users"></i> Roles
|
|
4468
|
+
</div>
|
|
4469
|
+
@if (isLoadingPermissions()) {
|
|
4470
|
+
<p-skeleton height="1.5rem" styleClass="mb-2" />
|
|
4471
|
+
} @else if (userRoles().length === 0) {
|
|
4472
|
+
<span class="text-muted-color text-sm">No roles assigned</span>
|
|
4473
|
+
} @else {
|
|
4474
|
+
<div class="flex flex-wrap gap-2">
|
|
4475
|
+
@for (role of userRoles(); track role.id) {
|
|
4476
|
+
<p-tag [value]="role.name" severity="info" />
|
|
4477
|
+
}
|
|
4478
|
+
</div>
|
|
4479
|
+
}
|
|
4480
|
+
</div>
|
|
4481
|
+
}
|
|
4482
|
+
|
|
4483
|
+
<!-- Actions -->
|
|
4484
|
+
@if (showActions()) {
|
|
4485
|
+
<div>
|
|
4486
|
+
<div class="text-sm text-muted-color mb-2 flex items-center gap-2">
|
|
4487
|
+
<i class="pi pi-bolt"></i> Direct Actions
|
|
4488
|
+
</div>
|
|
4489
|
+
@if (isLoadingPermissions()) {
|
|
4490
|
+
<p-skeleton height="1.5rem" styleClass="mb-2" />
|
|
4491
|
+
} @else if (userActions().length === 0) {
|
|
4492
|
+
<span class="text-muted-color text-sm">No actions assigned</span>
|
|
4493
|
+
} @else {
|
|
4494
|
+
<div class="flex flex-wrap gap-2">
|
|
4495
|
+
@for (action of userActions(); track action.id) {
|
|
4496
|
+
<p-tag [value]="action.name" severity="success" />
|
|
4497
|
+
}
|
|
4498
|
+
</div>
|
|
4499
|
+
}
|
|
4500
|
+
</div>
|
|
4501
|
+
}
|
|
4502
|
+
</div>
|
|
4503
|
+
</div>
|
|
4504
|
+
}
|
|
4505
|
+
</div>
|
|
4506
|
+
}
|
|
4507
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1$1.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$1.Avatar, selector: "p-avatar", inputs: ["label", "icon", "image", "size", "shape", "styleClass", "ariaLabel", "ariaLabelledBy"], outputs: ["onImageError"] }, { kind: "component", type: i3.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 });
|
|
4508
|
+
}
|
|
4509
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ProfilePageComponent, decorators: [{
|
|
4510
|
+
type: Component,
|
|
4511
|
+
args: [{
|
|
4512
|
+
selector: 'lib-profile-page',
|
|
4513
|
+
standalone: true,
|
|
4514
|
+
imports: [AngularModule, PrimeModule, FormField, FileUploaderComponent],
|
|
4515
|
+
template: `
|
|
4516
|
+
<!-- Page Header -->
|
|
4517
|
+
<div class="mb-4 md:mb-6">
|
|
4518
|
+
<h1 class="text-2xl font-bold text-color mb-2">My Profile</h1>
|
|
4519
|
+
<p class="text-muted-color">Manage your account settings and preferences.</p>
|
|
4520
|
+
</div>
|
|
4521
|
+
|
|
4522
|
+
<!-- Profile Form Card (full width) -->
|
|
4523
|
+
<div class="card mb-2 md:mb-4">
|
|
4524
|
+
<div class="flex flex-col md:flex-row gap-4 md:gap-6">
|
|
4525
|
+
<!-- Avatar Section -->
|
|
4526
|
+
<div class="flex flex-col items-center gap-4 md:w-72 lg:w-80 md:border-r md:border-surface-200 md:pr-6">
|
|
4527
|
+
@if (profilePictureUrl()) {
|
|
4528
|
+
<p-avatar
|
|
4529
|
+
[image]="profilePictureUrl()!"
|
|
4530
|
+
size="xlarge"
|
|
4531
|
+
shape="circle"
|
|
4532
|
+
styleClass="w-28 h-28" />
|
|
4533
|
+
} @else {
|
|
4534
|
+
<p-avatar
|
|
4535
|
+
[label]="userInitials()"
|
|
4536
|
+
size="xlarge"
|
|
4537
|
+
shape="circle"
|
|
4538
|
+
styleClass="w-28 h-28 text-3xl bg-primary text-primary-contrast" />
|
|
4539
|
+
}
|
|
4540
|
+
<div class="text-center">
|
|
4541
|
+
<div class="font-semibold text-color">{{ userName() }}</div>
|
|
4542
|
+
<div class="text-muted-color text-sm">{{ userEmail() }}</div>
|
|
4543
|
+
</div>
|
|
4544
|
+
<!-- Upload section - only show if storage is enabled and provider is available -->
|
|
4545
|
+
@if (showUploadSection()) {
|
|
4546
|
+
<lib-file-uploader
|
|
4547
|
+
[uploadFile]="uploadFileFn"
|
|
4548
|
+
[acceptTypes]="['image/*']"
|
|
4549
|
+
[multiple]="false"
|
|
4550
|
+
[maxSizeMb]="5"
|
|
4551
|
+
[showPreview]="false"
|
|
4552
|
+
(fileUploaded)="onProfilePictureUploaded($event)"
|
|
4553
|
+
class="w-full" />
|
|
4554
|
+
} @else {
|
|
4555
|
+
<div class="w-full p-3 bg-surface-100 rounded-lg text-center">
|
|
4556
|
+
<i class="pi pi-info-circle text-muted-color mr-2"></i>
|
|
4557
|
+
<span class="text-sm text-muted-color">Storage feature not enabled</span>
|
|
4558
|
+
</div>
|
|
4559
|
+
}
|
|
4560
|
+
</div>
|
|
4561
|
+
|
|
4562
|
+
<!-- Form Section -->
|
|
4563
|
+
<div class="flex-1">
|
|
4564
|
+
<form (ngSubmit)="onSubmit()" class="flex flex-col gap-4">
|
|
4565
|
+
<!-- Email (read-only) -->
|
|
4566
|
+
<div class="flex flex-col gap-2">
|
|
4567
|
+
<label class="font-medium text-color">Email</label>
|
|
4568
|
+
<input
|
|
4569
|
+
pInputText
|
|
4570
|
+
[value]="userEmail()"
|
|
4571
|
+
disabled
|
|
4572
|
+
class="bg-surface-50" />
|
|
4573
|
+
</div>
|
|
4574
|
+
|
|
4575
|
+
<!-- Name -->
|
|
4576
|
+
<div class="flex flex-col gap-2">
|
|
4577
|
+
<label for="name" class="font-medium text-color">Name *</label>
|
|
4578
|
+
<input
|
|
4579
|
+
pInputText
|
|
4580
|
+
id="name"
|
|
4581
|
+
[formField]="formTree.name"
|
|
4582
|
+
placeholder="Your full name" />
|
|
4583
|
+
</div>
|
|
4584
|
+
|
|
4585
|
+
<!-- Phone -->
|
|
4586
|
+
<div class="flex flex-col gap-2">
|
|
4587
|
+
<label for="phone" class="font-medium text-color">Phone</label>
|
|
4588
|
+
<input
|
|
4589
|
+
pInputText
|
|
4590
|
+
id="phone"
|
|
4591
|
+
[formField]="formTree.phone"
|
|
4592
|
+
placeholder="+1 234 567 8900" />
|
|
4593
|
+
</div>
|
|
4594
|
+
|
|
4595
|
+
<!-- Submit -->
|
|
4596
|
+
<div class="flex justify-end pt-4">
|
|
4597
|
+
<p-button
|
|
4598
|
+
label="Save Changes"
|
|
4599
|
+
icon="pi pi-check"
|
|
4600
|
+
type="submit"
|
|
4601
|
+
[loading]="isLoading()"
|
|
4602
|
+
[disabled]="!isFormValid() || isLoading()" />
|
|
4603
|
+
</div>
|
|
4604
|
+
</form>
|
|
4605
|
+
</div>
|
|
4606
|
+
</div>
|
|
4607
|
+
</div>
|
|
4608
|
+
|
|
4609
|
+
<!-- Security Card -->
|
|
4610
|
+
<div class="card mb-2 md:mb-4">
|
|
4611
|
+
<div class="flex items-center gap-2 mb-4">
|
|
4612
|
+
<i class="pi pi-lock text-primary"></i>
|
|
4613
|
+
<span class="font-semibold text-color">Security</span>
|
|
4614
|
+
</div>
|
|
4615
|
+
<form (ngSubmit)="onChangePassword()" class="flex flex-col gap-4">
|
|
4616
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
4617
|
+
<!-- Current Password -->
|
|
4618
|
+
<div class="flex flex-col gap-2">
|
|
4619
|
+
<label for="currentPassword" class="font-medium text-color">Current Password *</label>
|
|
4620
|
+
<p-password
|
|
4621
|
+
id="currentPassword"
|
|
4622
|
+
name="currentPassword"
|
|
4623
|
+
[ngModel]="passwordModel().currentPassword"
|
|
4624
|
+
(ngModelChange)="updatePasswordField('currentPassword', $event)"
|
|
4625
|
+
[feedback]="false"
|
|
4626
|
+
[toggleMask]="true"
|
|
4627
|
+
placeholder="Enter current password"
|
|
4628
|
+
styleClass="w-full"
|
|
4629
|
+
inputStyleClass="w-full" />
|
|
4630
|
+
</div>
|
|
4631
|
+
|
|
4632
|
+
<!-- New Password -->
|
|
4633
|
+
<div class="flex flex-col gap-2">
|
|
4634
|
+
<label for="newPassword" class="font-medium text-color">New Password *</label>
|
|
4635
|
+
<p-password
|
|
4636
|
+
id="newPassword"
|
|
4637
|
+
name="newPassword"
|
|
4638
|
+
[ngModel]="passwordModel().newPassword"
|
|
4639
|
+
(ngModelChange)="updatePasswordField('newPassword', $event)"
|
|
4640
|
+
[feedback]="true"
|
|
4641
|
+
[toggleMask]="true"
|
|
4642
|
+
placeholder="Min 6 characters"
|
|
4643
|
+
styleClass="w-full"
|
|
4644
|
+
inputStyleClass="w-full" />
|
|
4645
|
+
</div>
|
|
4646
|
+
|
|
4647
|
+
<!-- Confirm Password -->
|
|
4648
|
+
<div class="flex flex-col gap-2">
|
|
4649
|
+
<label for="confirmPassword" class="font-medium text-color">Confirm Password *</label>
|
|
4650
|
+
<p-password
|
|
4651
|
+
id="confirmPassword"
|
|
4652
|
+
name="confirmPassword"
|
|
4653
|
+
[ngModel]="passwordModel().confirmPassword"
|
|
4654
|
+
(ngModelChange)="updatePasswordField('confirmPassword', $event)"
|
|
4655
|
+
[feedback]="false"
|
|
4656
|
+
[toggleMask]="true"
|
|
4657
|
+
placeholder="Confirm new password"
|
|
4658
|
+
styleClass="w-full"
|
|
4659
|
+
inputStyleClass="w-full" />
|
|
4660
|
+
@if (!passwordsMatch()) {
|
|
4661
|
+
<small class="text-danger">Passwords do not match</small>
|
|
4662
|
+
}
|
|
4663
|
+
</div>
|
|
4664
|
+
</div>
|
|
4665
|
+
|
|
4666
|
+
<!-- Submit -->
|
|
4667
|
+
<div class="flex justify-end">
|
|
4668
|
+
<p-button
|
|
4669
|
+
label="Change Password"
|
|
4670
|
+
icon="pi pi-key"
|
|
4671
|
+
type="submit"
|
|
4672
|
+
[loading]="isPasswordLoading()"
|
|
4673
|
+
[disabled]="!isPasswordFormValid() || isPasswordLoading()" />
|
|
4674
|
+
</div>
|
|
4675
|
+
</form>
|
|
4676
|
+
</div>
|
|
4677
|
+
|
|
4678
|
+
<!-- Company & Permissions Row (50/50 on desktop, stacked on mobile) -->
|
|
4679
|
+
@if (showBottomRow()) {
|
|
4680
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-2 md:gap-4 flex-1">
|
|
4681
|
+
<!-- Company/Branch Info -->
|
|
4682
|
+
@if (showCompanySection()) {
|
|
4683
|
+
<div class="card h-48 md:h-56 flex flex-col">
|
|
4684
|
+
<div class="flex items-center gap-2 mb-4">
|
|
4685
|
+
<i class="pi pi-building text-primary"></i>
|
|
4686
|
+
<span class="font-semibold text-color">Company & Branch</span>
|
|
4687
|
+
</div>
|
|
4688
|
+
<div class="flex flex-col gap-4 flex-1 overflow-auto">
|
|
4689
|
+
<div class="flex items-center gap-3">
|
|
4690
|
+
<div class="w-10 h-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0">
|
|
4691
|
+
<i class="pi pi-briefcase text-primary"></i>
|
|
4692
|
+
</div>
|
|
4693
|
+
<div>
|
|
4694
|
+
<div class="text-sm text-muted-color">Company</div>
|
|
4695
|
+
<div class="font-medium text-color">{{ companyName() || 'Not assigned' }}</div>
|
|
4696
|
+
</div>
|
|
4697
|
+
</div>
|
|
4698
|
+
<div class="flex items-center gap-3">
|
|
4699
|
+
<div class="w-10 h-10 rounded-lg bg-success/10 flex items-center justify-center flex-shrink-0">
|
|
4700
|
+
<i class="pi pi-map-marker text-success"></i>
|
|
4701
|
+
</div>
|
|
4702
|
+
<div>
|
|
4703
|
+
<div class="text-sm text-muted-color">Branch</div>
|
|
4704
|
+
<div class="font-medium text-color">{{ branchName() || 'Not assigned' }}</div>
|
|
4705
|
+
</div>
|
|
4706
|
+
</div>
|
|
4707
|
+
</div>
|
|
4708
|
+
</div>
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
<!-- Permissions -->
|
|
4712
|
+
@if (showPermissionsSection()) {
|
|
4713
|
+
<div class="card h-48 md:h-56 flex flex-col">
|
|
4714
|
+
<div class="flex items-center justify-between mb-4">
|
|
4715
|
+
<div class="flex items-center gap-2">
|
|
4716
|
+
<i class="pi pi-shield text-primary"></i>
|
|
4717
|
+
<span class="font-semibold text-color">My Permissions</span>
|
|
4718
|
+
</div>
|
|
4719
|
+
<p-tag value="Read-only" severity="secondary" />
|
|
4720
|
+
</div>
|
|
4721
|
+
<div class="flex flex-col gap-4 flex-1 overflow-auto">
|
|
4722
|
+
<!-- Roles -->
|
|
4723
|
+
@if (showRoles()) {
|
|
4724
|
+
<div>
|
|
4725
|
+
<div class="text-sm text-muted-color mb-2 flex items-center gap-2">
|
|
4726
|
+
<i class="pi pi-users"></i> Roles
|
|
4727
|
+
</div>
|
|
4728
|
+
@if (isLoadingPermissions()) {
|
|
4729
|
+
<p-skeleton height="1.5rem" styleClass="mb-2" />
|
|
4730
|
+
} @else if (userRoles().length === 0) {
|
|
4731
|
+
<span class="text-muted-color text-sm">No roles assigned</span>
|
|
4732
|
+
} @else {
|
|
4733
|
+
<div class="flex flex-wrap gap-2">
|
|
4734
|
+
@for (role of userRoles(); track role.id) {
|
|
4735
|
+
<p-tag [value]="role.name" severity="info" />
|
|
4736
|
+
}
|
|
4737
|
+
</div>
|
|
4738
|
+
}
|
|
4739
|
+
</div>
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4742
|
+
<!-- Actions -->
|
|
4743
|
+
@if (showActions()) {
|
|
4744
|
+
<div>
|
|
4745
|
+
<div class="text-sm text-muted-color mb-2 flex items-center gap-2">
|
|
4746
|
+
<i class="pi pi-bolt"></i> Direct Actions
|
|
4747
|
+
</div>
|
|
4748
|
+
@if (isLoadingPermissions()) {
|
|
4749
|
+
<p-skeleton height="1.5rem" styleClass="mb-2" />
|
|
4750
|
+
} @else if (userActions().length === 0) {
|
|
4751
|
+
<span class="text-muted-color text-sm">No actions assigned</span>
|
|
4752
|
+
} @else {
|
|
4753
|
+
<div class="flex flex-wrap gap-2">
|
|
4754
|
+
@for (action of userActions(); track action.id) {
|
|
4755
|
+
<p-tag [value]="action.name" severity="success" />
|
|
4756
|
+
}
|
|
4757
|
+
</div>
|
|
4758
|
+
}
|
|
4759
|
+
</div>
|
|
4760
|
+
}
|
|
4761
|
+
</div>
|
|
4762
|
+
</div>
|
|
4763
|
+
}
|
|
4764
|
+
</div>
|
|
4765
|
+
}
|
|
4766
|
+
`,
|
|
4767
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
4768
|
+
}]
|
|
4769
|
+
}] });
|
|
4770
|
+
|
|
4771
|
+
var profilePage_component = /*#__PURE__*/Object.freeze({
|
|
4772
|
+
__proto__: null,
|
|
4773
|
+
ProfilePageComponent: ProfilePageComponent
|
|
4774
|
+
});
|
|
4775
|
+
|
|
4776
|
+
class RegisterPageComponent {
|
|
4777
|
+
destroyRef = inject(DestroyRef);
|
|
4778
|
+
appConfig = inject(APP_CONFIG);
|
|
4779
|
+
messageService = inject(MessageService);
|
|
4780
|
+
authApi = inject(AuthApiService);
|
|
4781
|
+
authState = inject(AuthStateService);
|
|
4782
|
+
route = inject(ActivatedRoute);
|
|
4783
|
+
router = inject(Router);
|
|
4784
|
+
returnUrl = input('', ...(ngDevMode ? [{ debugName: "returnUrl" }] : []));
|
|
4785
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
4786
|
+
companyFeatureEnabled = computed(() => isCompanyFeatureEnabled(this.appConfig), ...(ngDevMode ? [{ debugName: "companyFeatureEnabled" }] : []));
|
|
4787
|
+
userFormModel = signal({
|
|
4788
|
+
name: '',
|
|
4789
|
+
email: '',
|
|
4790
|
+
password: '',
|
|
4791
|
+
confirmPassword: '',
|
|
4792
|
+
phone: '',
|
|
4793
|
+
}, ...(ngDevMode ? [{ debugName: "userFormModel" }] : []));
|
|
4794
|
+
userForm = form(this.userFormModel, (f) => {
|
|
4795
|
+
required(f.name, { message: 'Name is required' });
|
|
4796
|
+
minLength(f.name, 2, { message: 'Name must be at least 2 characters' });
|
|
4797
|
+
required(f.email, { message: 'Email is required' });
|
|
4798
|
+
email(f.email, { message: 'Please enter a valid email address' });
|
|
4799
|
+
required(f.password, { message: 'Password is required' });
|
|
4800
|
+
minLength(f.password, 6, { message: 'Password must be at least 6 characters' });
|
|
4801
|
+
required(f.confirmPassword, { message: 'Please confirm your password' });
|
|
4802
|
+
});
|
|
4803
|
+
companyFormModel = signal({
|
|
4804
|
+
createNewCompany: false,
|
|
4805
|
+
companySlug: '',
|
|
4806
|
+
branchSlug: '',
|
|
4807
|
+
newCompanyName: '',
|
|
4808
|
+
newCompanyPhone: '',
|
|
4809
|
+
newCompanyAddress: '',
|
|
4810
|
+
}, ...(ngDevMode ? [{ debugName: "companyFormModel" }] : []));
|
|
4811
|
+
companyForm = form(this.companyFormModel, (f) => { });
|
|
3663
4812
|
passwordsMatch = computed(() => {
|
|
3664
4813
|
const model = this.userFormModel();
|
|
3665
4814
|
return model.password === model.confirmPassword;
|
|
3666
4815
|
}, ...(ngDevMode ? [{ debugName: "passwordsMatch" }] : []));
|
|
3667
|
-
/** Check if form is valid */
|
|
3668
4816
|
isFormValid = computed(() => {
|
|
3669
4817
|
const userModel = this.userFormModel();
|
|
3670
|
-
// User validation
|
|
3671
4818
|
const userValid = userModel.name.trim().length >= 2 &&
|
|
3672
4819
|
userModel.email.includes('@') &&
|
|
3673
4820
|
userModel.password.length >= 6 &&
|
|
3674
4821
|
userModel.confirmPassword.length > 0 &&
|
|
3675
4822
|
this.passwordsMatch();
|
|
3676
|
-
if (!isCompanyFeatureEnabled(this.appConfig))
|
|
4823
|
+
if (!isCompanyFeatureEnabled(this.appConfig))
|
|
3677
4824
|
return userValid;
|
|
3678
|
-
}
|
|
3679
4825
|
const companyModel = this.companyFormModel();
|
|
3680
|
-
// Company validation
|
|
3681
4826
|
if (companyModel.createNewCompany) {
|
|
3682
4827
|
return userValid && companyModel.newCompanyName.trim().length > 0;
|
|
3683
4828
|
}
|
|
3684
4829
|
return userValid;
|
|
3685
4830
|
}, ...(ngDevMode ? [{ debugName: "isFormValid" }] : []));
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
4831
|
+
ngOnInit() {
|
|
4832
|
+
const companySlug = this.route.snapshot.queryParamMap.get('companySlug');
|
|
4833
|
+
if (companySlug) {
|
|
4834
|
+
this.companyFormModel.update((model) => ({ ...model, companySlug }));
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
3689
4837
|
onCreateCompanyToggle() {
|
|
3690
4838
|
const currentValue = this.companyFormModel().createNewCompany;
|
|
3691
|
-
// Reset fields when toggling
|
|
3692
4839
|
if (!currentValue) {
|
|
3693
|
-
// Toggling to create new company
|
|
3694
4840
|
this.companyFormModel.update((model) => ({
|
|
3695
4841
|
...model,
|
|
3696
4842
|
createNewCompany: true,
|
|
@@ -3699,7 +4845,6 @@ class RegisterPageComponent {
|
|
|
3699
4845
|
}));
|
|
3700
4846
|
}
|
|
3701
4847
|
else {
|
|
3702
|
-
// Toggling to join existing company
|
|
3703
4848
|
this.companyFormModel.update((model) => ({
|
|
3704
4849
|
...model,
|
|
3705
4850
|
createNewCompany: false,
|
|
@@ -3727,7 +4872,6 @@ class RegisterPageComponent {
|
|
|
3727
4872
|
password: userModel.password,
|
|
3728
4873
|
phone: userModel.phone || undefined,
|
|
3729
4874
|
};
|
|
3730
|
-
// Add company fields if company feature enabled
|
|
3731
4875
|
if (isCompanyFeatureEnabled(this.appConfig)) {
|
|
3732
4876
|
if (companyModel.createNewCompany) {
|
|
3733
4877
|
registrationData.newCompanyName =
|
|
@@ -3744,13 +4888,21 @@ class RegisterPageComponent {
|
|
|
3744
4888
|
}
|
|
3745
4889
|
this.authApi
|
|
3746
4890
|
.register(registrationData)
|
|
3747
|
-
.pipe(
|
|
4891
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
3748
4892
|
.subscribe({
|
|
3749
4893
|
next: (response) => {
|
|
3750
4894
|
this.isLoading.set(false);
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
4895
|
+
if (response.requiresEmailVerification) {
|
|
4896
|
+
this.messageService.add({
|
|
4897
|
+
severity: 'success',
|
|
4898
|
+
summary: 'Account Created',
|
|
4899
|
+
detail: 'Please check your email to verify your account.',
|
|
4900
|
+
life: 5000,
|
|
4901
|
+
});
|
|
4902
|
+
this.router.navigate(['/auth/login']);
|
|
4903
|
+
return;
|
|
4904
|
+
}
|
|
4905
|
+
if (response.accessToken) {
|
|
3754
4906
|
this.authState.setLoginState({
|
|
3755
4907
|
accessToken: response.accessToken,
|
|
3756
4908
|
refreshToken: response.refreshToken,
|
|
@@ -3766,21 +4918,13 @@ class RegisterPageComponent {
|
|
|
3766
4918
|
});
|
|
3767
4919
|
this.authState.navigateAfterLogin(this.returnUrl());
|
|
3768
4920
|
}
|
|
3769
|
-
else {
|
|
3770
|
-
this.messageService.add({
|
|
3771
|
-
severity: 'error',
|
|
3772
|
-
summary: 'Registration Failed',
|
|
3773
|
-
detail: 'Unable to create account. Please try again.',
|
|
3774
|
-
});
|
|
3775
|
-
}
|
|
3776
4921
|
},
|
|
3777
4922
|
error: (error) => {
|
|
3778
4923
|
this.isLoading.set(false);
|
|
3779
4924
|
this.messageService.add({
|
|
3780
4925
|
severity: 'error',
|
|
3781
4926
|
summary: 'Registration Failed',
|
|
3782
|
-
detail: error.error?.message ||
|
|
3783
|
-
'Unable to create account. Please try again.',
|
|
4927
|
+
detail: error.error?.message || 'Unable to create account. Please try again.',
|
|
3784
4928
|
});
|
|
3785
4929
|
},
|
|
3786
4930
|
});
|
|
@@ -3812,7 +4956,6 @@ class RegisterPageComponent {
|
|
|
3812
4956
|
</div>
|
|
3813
4957
|
|
|
3814
4958
|
<form (ngSubmit)="onSubmit()">
|
|
3815
|
-
<!-- User Information -->
|
|
3816
4959
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
3817
4960
|
<div>
|
|
3818
4961
|
<label for="name" class="block font-medium mb-2"
|
|
@@ -3888,14 +5031,13 @@ class RegisterPageComponent {
|
|
|
3888
5031
|
autocomplete="new-password"
|
|
3889
5032
|
/>
|
|
3890
5033
|
@if (!passwordsMatch() && userFormModel().confirmPassword) {
|
|
3891
|
-
<small class="text-
|
|
5034
|
+
<small class="text-danger block mt-2"
|
|
3892
5035
|
>Passwords do not match</small
|
|
3893
5036
|
>
|
|
3894
5037
|
}
|
|
3895
5038
|
</div>
|
|
3896
5039
|
</div>
|
|
3897
5040
|
|
|
3898
|
-
<!-- Company Feature Section -->
|
|
3899
5041
|
@if (companyFeatureEnabled()) {
|
|
3900
5042
|
<div
|
|
3901
5043
|
class="border-t pt-6 mt-6"
|
|
@@ -3923,9 +5065,7 @@ class RegisterPageComponent {
|
|
|
3923
5065
|
</label>
|
|
3924
5066
|
</div>
|
|
3925
5067
|
|
|
3926
|
-
<!-- Fixed height container to prevent height changes -->
|
|
3927
5068
|
@if (!companyFormModel().createNewCompany) {
|
|
3928
|
-
<!-- Join Existing Company -->
|
|
3929
5069
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
3930
5070
|
<div>
|
|
3931
5071
|
<label for="companySlug" class="block font-medium mb-2"
|
|
@@ -3957,7 +5097,6 @@ class RegisterPageComponent {
|
|
|
3957
5097
|
</div>
|
|
3958
5098
|
</div>
|
|
3959
5099
|
} @else {
|
|
3960
|
-
<!-- Create New Company -->
|
|
3961
5100
|
<div class="mb-4">
|
|
3962
5101
|
<label for="newCompanyName" class="block font-medium mb-2"
|
|
3963
5102
|
>Company Name *</label
|
|
@@ -4004,7 +5143,6 @@ class RegisterPageComponent {
|
|
|
4004
5143
|
</div>
|
|
4005
5144
|
}
|
|
4006
5145
|
|
|
4007
|
-
<!-- Submit -->
|
|
4008
5146
|
<p-button
|
|
4009
5147
|
label="Create Account"
|
|
4010
5148
|
styleClass="w-full mt-4"
|
|
@@ -4027,7 +5165,7 @@ class RegisterPageComponent {
|
|
|
4027
5165
|
</div>
|
|
4028
5166
|
</div>
|
|
4029
5167
|
</div>
|
|
4030
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2
|
|
5168
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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" }] });
|
|
4031
5169
|
}
|
|
4032
5170
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: RegisterPageComponent, decorators: [{
|
|
4033
5171
|
type: Component,
|
|
@@ -4061,7 +5199,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4061
5199
|
</div>
|
|
4062
5200
|
|
|
4063
5201
|
<form (ngSubmit)="onSubmit()">
|
|
4064
|
-
<!-- User Information -->
|
|
4065
5202
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
4066
5203
|
<div>
|
|
4067
5204
|
<label for="name" class="block font-medium mb-2"
|
|
@@ -4137,14 +5274,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4137
5274
|
autocomplete="new-password"
|
|
4138
5275
|
/>
|
|
4139
5276
|
@if (!passwordsMatch() && userFormModel().confirmPassword) {
|
|
4140
|
-
<small class="text-
|
|
5277
|
+
<small class="text-danger block mt-2"
|
|
4141
5278
|
>Passwords do not match</small
|
|
4142
5279
|
>
|
|
4143
5280
|
}
|
|
4144
5281
|
</div>
|
|
4145
5282
|
</div>
|
|
4146
5283
|
|
|
4147
|
-
<!-- Company Feature Section -->
|
|
4148
5284
|
@if (companyFeatureEnabled()) {
|
|
4149
5285
|
<div
|
|
4150
5286
|
class="border-t pt-6 mt-6"
|
|
@@ -4172,9 +5308,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4172
5308
|
</label>
|
|
4173
5309
|
</div>
|
|
4174
5310
|
|
|
4175
|
-
<!-- Fixed height container to prevent height changes -->
|
|
4176
5311
|
@if (!companyFormModel().createNewCompany) {
|
|
4177
|
-
<!-- Join Existing Company -->
|
|
4178
5312
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
4179
5313
|
<div>
|
|
4180
5314
|
<label for="companySlug" class="block font-medium mb-2"
|
|
@@ -4192,97 +5326,402 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4192
5326
|
>
|
|
4193
5327
|
</div>
|
|
4194
5328
|
|
|
4195
|
-
<div>
|
|
4196
|
-
<label for="branchSlug" class="block font-medium mb-2"
|
|
4197
|
-
>Branch Code</label
|
|
4198
|
-
>
|
|
4199
|
-
<input
|
|
4200
|
-
pInputText
|
|
4201
|
-
id="branchSlug"
|
|
4202
|
-
[formField]="companyForm.branchSlug"
|
|
4203
|
-
placeholder="Branch code (optional)"
|
|
4204
|
-
class="w-full"
|
|
4205
|
-
/>
|
|
5329
|
+
<div>
|
|
5330
|
+
<label for="branchSlug" class="block font-medium mb-2"
|
|
5331
|
+
>Branch Code</label
|
|
5332
|
+
>
|
|
5333
|
+
<input
|
|
5334
|
+
pInputText
|
|
5335
|
+
id="branchSlug"
|
|
5336
|
+
[formField]="companyForm.branchSlug"
|
|
5337
|
+
placeholder="Branch code (optional)"
|
|
5338
|
+
class="w-full"
|
|
5339
|
+
/>
|
|
5340
|
+
</div>
|
|
5341
|
+
</div>
|
|
5342
|
+
} @else {
|
|
5343
|
+
<div class="mb-4">
|
|
5344
|
+
<label for="newCompanyName" class="block font-medium mb-2"
|
|
5345
|
+
>Company Name *</label
|
|
5346
|
+
>
|
|
5347
|
+
<input
|
|
5348
|
+
pInputText
|
|
5349
|
+
id="newCompanyName"
|
|
5350
|
+
[formField]="companyForm.newCompanyName"
|
|
5351
|
+
placeholder="Your company name"
|
|
5352
|
+
class="w-full"
|
|
5353
|
+
/>
|
|
5354
|
+
</div>
|
|
5355
|
+
|
|
5356
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
|
5357
|
+
<div>
|
|
5358
|
+
<label for="newCompanyPhone" class="block font-medium mb-2"
|
|
5359
|
+
>Company Phone</label
|
|
5360
|
+
>
|
|
5361
|
+
<input
|
|
5362
|
+
pInputText
|
|
5363
|
+
id="newCompanyPhone"
|
|
5364
|
+
[formField]="companyForm.newCompanyPhone"
|
|
5365
|
+
placeholder="Company phone"
|
|
5366
|
+
class="w-full"
|
|
5367
|
+
/>
|
|
5368
|
+
</div>
|
|
5369
|
+
|
|
5370
|
+
<div>
|
|
5371
|
+
<label
|
|
5372
|
+
for="newCompanyAddress"
|
|
5373
|
+
class="block font-medium mb-2"
|
|
5374
|
+
>Address</label
|
|
5375
|
+
>
|
|
5376
|
+
<input
|
|
5377
|
+
pInputText
|
|
5378
|
+
id="newCompanyAddress"
|
|
5379
|
+
[formField]="companyForm.newCompanyAddress"
|
|
5380
|
+
placeholder="Company address"
|
|
5381
|
+
class="w-full"
|
|
5382
|
+
/>
|
|
5383
|
+
</div>
|
|
5384
|
+
</div>
|
|
5385
|
+
}
|
|
5386
|
+
</div>
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5389
|
+
<p-button
|
|
5390
|
+
label="Create Account"
|
|
5391
|
+
styleClass="w-full mt-4"
|
|
5392
|
+
type="submit"
|
|
5393
|
+
[loading]="isLoading()"
|
|
5394
|
+
[disabled]="!isFormValid() || isLoading()"
|
|
5395
|
+
/>
|
|
5396
|
+
|
|
5397
|
+
<div class="flex items-center justify-center mt-4 gap-2">
|
|
5398
|
+
<span class="font-medium">Already have an account?</span>
|
|
5399
|
+
<a
|
|
5400
|
+
routerLink="/auth/login"
|
|
5401
|
+
class="font-semibold text-primary cursor-pointer"
|
|
5402
|
+
>
|
|
5403
|
+
Sign In
|
|
5404
|
+
</a>
|
|
5405
|
+
</div>
|
|
5406
|
+
</form>
|
|
5407
|
+
</div>
|
|
5408
|
+
</div>
|
|
5409
|
+
</div>
|
|
5410
|
+
</div>
|
|
5411
|
+
`,
|
|
5412
|
+
}]
|
|
5413
|
+
}], propDecorators: { returnUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "returnUrl", required: false }] }] } });
|
|
5414
|
+
|
|
5415
|
+
var registerPage_component = /*#__PURE__*/Object.freeze({
|
|
5416
|
+
__proto__: null,
|
|
5417
|
+
RegisterPageComponent: RegisterPageComponent
|
|
5418
|
+
});
|
|
5419
|
+
|
|
5420
|
+
class ResetPasswordPageComponent {
|
|
5421
|
+
destroyRef = inject(DestroyRef);
|
|
5422
|
+
route = inject(ActivatedRoute);
|
|
5423
|
+
messageService = inject(MessageService);
|
|
5424
|
+
authApi = inject(AuthApiService);
|
|
5425
|
+
token = '';
|
|
5426
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
5427
|
+
pageState = signal('form', ...(ngDevMode ? [{ debugName: "pageState" }] : []));
|
|
5428
|
+
formModel = signal({ newPassword: '', confirmPassword: '' }, ...(ngDevMode ? [{ debugName: "formModel" }] : []));
|
|
5429
|
+
resetForm = form(this.formModel, (f) => {
|
|
5430
|
+
required(f.newPassword, { message: 'New password is required' });
|
|
5431
|
+
minLength(f.newPassword, 6, { message: 'Password must be at least 6 characters' });
|
|
5432
|
+
required(f.confirmPassword, { message: 'Please confirm your password' });
|
|
5433
|
+
});
|
|
5434
|
+
passwordMismatch = computed(() => {
|
|
5435
|
+
const model = this.formModel();
|
|
5436
|
+
return model.confirmPassword.length > 0 && model.newPassword !== model.confirmPassword;
|
|
5437
|
+
}, ...(ngDevMode ? [{ debugName: "passwordMismatch" }] : []));
|
|
5438
|
+
canSubmit = computed(() => this.resetForm().valid() && !this.passwordMismatch(), ...(ngDevMode ? [{ debugName: "canSubmit" }] : []));
|
|
5439
|
+
ngOnInit() {
|
|
5440
|
+
this.route.queryParams.pipe(take(1)).subscribe((params) => {
|
|
5441
|
+
this.token = params['token'] || '';
|
|
5442
|
+
if (!this.token) {
|
|
5443
|
+
this.pageState.set('invalid-token');
|
|
5444
|
+
}
|
|
5445
|
+
});
|
|
5446
|
+
}
|
|
5447
|
+
onSubmit() {
|
|
5448
|
+
if (!this.canSubmit()) {
|
|
5449
|
+
this.messageService.add({
|
|
5450
|
+
severity: 'error',
|
|
5451
|
+
summary: 'Validation Error',
|
|
5452
|
+
detail: 'Please fill in all fields correctly.',
|
|
5453
|
+
});
|
|
5454
|
+
return;
|
|
5455
|
+
}
|
|
5456
|
+
this.isLoading.set(true);
|
|
5457
|
+
const newPassword = this.formModel().newPassword;
|
|
5458
|
+
this.authApi
|
|
5459
|
+
.resetPassword(this.token, newPassword)
|
|
5460
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
5461
|
+
.subscribe({
|
|
5462
|
+
next: () => {
|
|
5463
|
+
this.isLoading.set(false);
|
|
5464
|
+
this.messageService.add({
|
|
5465
|
+
severity: 'success',
|
|
5466
|
+
summary: 'Success',
|
|
5467
|
+
detail: 'Your password has been reset successfully.',
|
|
5468
|
+
});
|
|
5469
|
+
this.pageState.set('success');
|
|
5470
|
+
},
|
|
5471
|
+
error: (error) => {
|
|
5472
|
+
this.isLoading.set(false);
|
|
5473
|
+
const message = error.error?.message || 'Failed to reset password';
|
|
5474
|
+
if (message.toLowerCase().includes('expired') || message.toLowerCase().includes('invalid')) {
|
|
5475
|
+
this.pageState.set('invalid-token');
|
|
5476
|
+
}
|
|
5477
|
+
else {
|
|
5478
|
+
this.messageService.add({
|
|
5479
|
+
severity: 'error',
|
|
5480
|
+
summary: 'Error',
|
|
5481
|
+
detail: message,
|
|
5482
|
+
});
|
|
5483
|
+
}
|
|
5484
|
+
},
|
|
5485
|
+
});
|
|
5486
|
+
}
|
|
5487
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ResetPasswordPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5488
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: ResetPasswordPageComponent, isStandalone: true, selector: "lib-reset-password-page", ngImport: i0, template: `
|
|
5489
|
+
<app-floating-configurator />
|
|
5490
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
5491
|
+
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
5492
|
+
<div
|
|
5493
|
+
class="w-full"
|
|
5494
|
+
style="border-radius: var(--p-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
|
5495
|
+
<div
|
|
5496
|
+
class="w-full py-16 px-12"
|
|
5497
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background);">
|
|
5498
|
+
|
|
5499
|
+
@switch (pageState()) {
|
|
5500
|
+
@case ('form') {
|
|
5501
|
+
<div class="text-center mb-8">
|
|
5502
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
5503
|
+
Reset Password
|
|
5504
|
+
</div>
|
|
5505
|
+
<span class="text-muted-color font-medium">
|
|
5506
|
+
Enter your new password below.
|
|
5507
|
+
</span>
|
|
5508
|
+
</div>
|
|
5509
|
+
|
|
5510
|
+
<form (ngSubmit)="onSubmit()">
|
|
5511
|
+
<label for="newPassword" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
5512
|
+
New Password
|
|
5513
|
+
</label>
|
|
5514
|
+
<input
|
|
5515
|
+
pInputText
|
|
5516
|
+
id="newPassword"
|
|
5517
|
+
type="password"
|
|
5518
|
+
placeholder="Enter new password"
|
|
5519
|
+
class="w-full md:w-[25rem] mb-4"
|
|
5520
|
+
[formField]="resetForm.newPassword"
|
|
5521
|
+
autocomplete="new-password" />
|
|
5522
|
+
|
|
5523
|
+
<label for="confirmPassword" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
5524
|
+
Confirm Password
|
|
5525
|
+
</label>
|
|
5526
|
+
<input
|
|
5527
|
+
pInputText
|
|
5528
|
+
id="confirmPassword"
|
|
5529
|
+
type="password"
|
|
5530
|
+
placeholder="Confirm new password"
|
|
5531
|
+
class="w-full md:w-[25rem] mb-2"
|
|
5532
|
+
[formField]="resetForm.confirmPassword"
|
|
5533
|
+
autocomplete="new-password" />
|
|
5534
|
+
|
|
5535
|
+
@if (passwordMismatch()) {
|
|
5536
|
+
<small class="text-danger block mb-4">Passwords do not match</small>
|
|
5537
|
+
}
|
|
5538
|
+
|
|
5539
|
+
<p-button
|
|
5540
|
+
label="Reset Password"
|
|
5541
|
+
styleClass="w-full mt-4 mb-4"
|
|
5542
|
+
type="submit"
|
|
5543
|
+
[loading]="isLoading()"
|
|
5544
|
+
[disabled]="!canSubmit() || isLoading()" />
|
|
5545
|
+
|
|
5546
|
+
<div class="flex items-center justify-center gap-2">
|
|
5547
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
5548
|
+
Back to Sign In
|
|
5549
|
+
</a>
|
|
5550
|
+
</div>
|
|
5551
|
+
</form>
|
|
5552
|
+
}
|
|
5553
|
+
|
|
5554
|
+
@case ('success') {
|
|
5555
|
+
<div class="text-center">
|
|
5556
|
+
<div class="flex justify-center mb-6">
|
|
5557
|
+
<i class="pi pi-check-circle text-6xl text-success"></i>
|
|
5558
|
+
</div>
|
|
5559
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
5560
|
+
Password Reset Successful
|
|
5561
|
+
</div>
|
|
5562
|
+
<p class="text-muted-color font-medium mb-6">
|
|
5563
|
+
Your password has been reset successfully.
|
|
5564
|
+
You can now sign in with your new password.
|
|
5565
|
+
</p>
|
|
5566
|
+
<a routerLink="/auth/login">
|
|
5567
|
+
<p-button label="Sign In" styleClass="w-full" />
|
|
5568
|
+
</a>
|
|
5569
|
+
</div>
|
|
5570
|
+
}
|
|
5571
|
+
|
|
5572
|
+
@case ('invalid-token') {
|
|
5573
|
+
<div class="text-center">
|
|
5574
|
+
<div class="flex justify-center mb-6">
|
|
5575
|
+
<i class="pi pi-times-circle text-6xl text-danger"></i>
|
|
5576
|
+
</div>
|
|
5577
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
5578
|
+
Invalid or Expired Link
|
|
5579
|
+
</div>
|
|
5580
|
+
<p class="text-muted-color font-medium mb-6">
|
|
5581
|
+
This password reset link is invalid or has expired.
|
|
5582
|
+
Please request a new one.
|
|
5583
|
+
</p>
|
|
5584
|
+
<a routerLink="/auth/forgot-password">
|
|
5585
|
+
<p-button label="Request New Link" styleClass="w-full mb-4" />
|
|
5586
|
+
</a>
|
|
5587
|
+
<div class="mt-4">
|
|
5588
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
5589
|
+
Back to Sign In
|
|
5590
|
+
</a>
|
|
5591
|
+
</div>
|
|
5592
|
+
</div>
|
|
5593
|
+
}
|
|
5594
|
+
}
|
|
5595
|
+
</div>
|
|
5596
|
+
</div>
|
|
5597
|
+
</div>
|
|
5598
|
+
</div>
|
|
5599
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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" }] });
|
|
5600
|
+
}
|
|
5601
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: ResetPasswordPageComponent, decorators: [{
|
|
5602
|
+
type: Component,
|
|
5603
|
+
args: [{
|
|
5604
|
+
selector: 'lib-reset-password-page',
|
|
5605
|
+
standalone: true,
|
|
5606
|
+
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
5607
|
+
template: `
|
|
5608
|
+
<app-floating-configurator />
|
|
5609
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
5610
|
+
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
5611
|
+
<div
|
|
5612
|
+
class="w-full"
|
|
5613
|
+
style="border-radius: var(--p-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
|
5614
|
+
<div
|
|
5615
|
+
class="w-full py-16 px-12"
|
|
5616
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background);">
|
|
5617
|
+
|
|
5618
|
+
@switch (pageState()) {
|
|
5619
|
+
@case ('form') {
|
|
5620
|
+
<div class="text-center mb-8">
|
|
5621
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
5622
|
+
Reset Password
|
|
4206
5623
|
</div>
|
|
5624
|
+
<span class="text-muted-color font-medium">
|
|
5625
|
+
Enter your new password below.
|
|
5626
|
+
</span>
|
|
4207
5627
|
</div>
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
>
|
|
5628
|
+
|
|
5629
|
+
<form (ngSubmit)="onSubmit()">
|
|
5630
|
+
<label for="newPassword" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
5631
|
+
New Password
|
|
5632
|
+
</label>
|
|
4214
5633
|
<input
|
|
4215
5634
|
pInputText
|
|
4216
|
-
id="
|
|
4217
|
-
|
|
4218
|
-
placeholder="
|
|
4219
|
-
class="w-full"
|
|
4220
|
-
|
|
4221
|
-
|
|
5635
|
+
id="newPassword"
|
|
5636
|
+
type="password"
|
|
5637
|
+
placeholder="Enter new password"
|
|
5638
|
+
class="w-full md:w-[25rem] mb-4"
|
|
5639
|
+
[formField]="resetForm.newPassword"
|
|
5640
|
+
autocomplete="new-password" />
|
|
4222
5641
|
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
5642
|
+
<label for="confirmPassword" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
5643
|
+
Confirm Password
|
|
5644
|
+
</label>
|
|
5645
|
+
<input
|
|
5646
|
+
pInputText
|
|
5647
|
+
id="confirmPassword"
|
|
5648
|
+
type="password"
|
|
5649
|
+
placeholder="Confirm new password"
|
|
5650
|
+
class="w-full md:w-[25rem] mb-2"
|
|
5651
|
+
[formField]="resetForm.confirmPassword"
|
|
5652
|
+
autocomplete="new-password" />
|
|
5653
|
+
|
|
5654
|
+
@if (passwordMismatch()) {
|
|
5655
|
+
<small class="text-danger block mb-4">Passwords do not match</small>
|
|
5656
|
+
}
|
|
5657
|
+
|
|
5658
|
+
<p-button
|
|
5659
|
+
label="Reset Password"
|
|
5660
|
+
styleClass="w-full mt-4 mb-4"
|
|
5661
|
+
type="submit"
|
|
5662
|
+
[loading]="isLoading()"
|
|
5663
|
+
[disabled]="!canSubmit() || isLoading()" />
|
|
5664
|
+
|
|
5665
|
+
<div class="flex items-center justify-center gap-2">
|
|
5666
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
5667
|
+
Back to Sign In
|
|
5668
|
+
</a>
|
|
4235
5669
|
</div>
|
|
5670
|
+
</form>
|
|
5671
|
+
}
|
|
4236
5672
|
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
>Address</label
|
|
4242
|
-
>
|
|
4243
|
-
<input
|
|
4244
|
-
pInputText
|
|
4245
|
-
id="newCompanyAddress"
|
|
4246
|
-
[formField]="companyForm.newCompanyAddress"
|
|
4247
|
-
placeholder="Company address"
|
|
4248
|
-
class="w-full"
|
|
4249
|
-
/>
|
|
5673
|
+
@case ('success') {
|
|
5674
|
+
<div class="text-center">
|
|
5675
|
+
<div class="flex justify-center mb-6">
|
|
5676
|
+
<i class="pi pi-check-circle text-6xl text-success"></i>
|
|
4250
5677
|
</div>
|
|
5678
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
5679
|
+
Password Reset Successful
|
|
5680
|
+
</div>
|
|
5681
|
+
<p class="text-muted-color font-medium mb-6">
|
|
5682
|
+
Your password has been reset successfully.
|
|
5683
|
+
You can now sign in with your new password.
|
|
5684
|
+
</p>
|
|
5685
|
+
<a routerLink="/auth/login">
|
|
5686
|
+
<p-button label="Sign In" styleClass="w-full" />
|
|
5687
|
+
</a>
|
|
4251
5688
|
</div>
|
|
4252
|
-
}
|
|
4253
|
-
</div>
|
|
4254
5689
|
}
|
|
4255
5690
|
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
routerLink="/auth/
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
5691
|
+
@case ('invalid-token') {
|
|
5692
|
+
<div class="text-center">
|
|
5693
|
+
<div class="flex justify-center mb-6">
|
|
5694
|
+
<i class="pi pi-times-circle text-6xl text-danger"></i>
|
|
5695
|
+
</div>
|
|
5696
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
5697
|
+
Invalid or Expired Link
|
|
5698
|
+
</div>
|
|
5699
|
+
<p class="text-muted-color font-medium mb-6">
|
|
5700
|
+
This password reset link is invalid or has expired.
|
|
5701
|
+
Please request a new one.
|
|
5702
|
+
</p>
|
|
5703
|
+
<a routerLink="/auth/forgot-password">
|
|
5704
|
+
<p-button label="Request New Link" styleClass="w-full mb-4" />
|
|
5705
|
+
</a>
|
|
5706
|
+
<div class="mt-4">
|
|
5707
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
5708
|
+
Back to Sign In
|
|
5709
|
+
</a>
|
|
5710
|
+
</div>
|
|
5711
|
+
</div>
|
|
5712
|
+
}
|
|
5713
|
+
}
|
|
4275
5714
|
</div>
|
|
4276
5715
|
</div>
|
|
4277
5716
|
</div>
|
|
4278
5717
|
</div>
|
|
4279
5718
|
`,
|
|
4280
5719
|
}]
|
|
4281
|
-
}]
|
|
5720
|
+
}] });
|
|
4282
5721
|
|
|
4283
|
-
var
|
|
5722
|
+
var resetPasswordPage_component = /*#__PURE__*/Object.freeze({
|
|
4284
5723
|
__proto__: null,
|
|
4285
|
-
|
|
5724
|
+
ResetPasswordPageComponent: ResetPasswordPageComponent
|
|
4286
5725
|
});
|
|
4287
5726
|
|
|
4288
5727
|
/**
|
|
@@ -4567,8 +6006,8 @@ class UserFormComponent {
|
|
|
4567
6006
|
|
|
4568
6007
|
<!-- Company Info (when company feature enabled) -->
|
|
4569
6008
|
@if (companyFeatureEnabled() && !isEditMode()) {
|
|
4570
|
-
<div class="md:col-span-2 p-3 bg-
|
|
4571
|
-
<div class="flex items-center gap-2 text-
|
|
6009
|
+
<div class="md:col-span-2 p-3 bg-primary/10 rounded-lg border border-primary/20">
|
|
6010
|
+
<div class="flex items-center gap-2 text-primary">
|
|
4572
6011
|
<i class="pi pi-info-circle"></i>
|
|
4573
6012
|
<span class="text-sm">
|
|
4574
6013
|
User will be automatically assigned to
|
|
@@ -4596,7 +6035,7 @@ class UserFormComponent {
|
|
|
4596
6035
|
</div>
|
|
4597
6036
|
</form>
|
|
4598
6037
|
</p-card>
|
|
4599
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "
|
|
6038
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.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: i3.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$1.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"] }] });
|
|
4600
6039
|
}
|
|
4601
6040
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserFormComponent, decorators: [{
|
|
4602
6041
|
type: Component,
|
|
@@ -4677,8 +6116,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImpor
|
|
|
4677
6116
|
|
|
4678
6117
|
<!-- Company Info (when company feature enabled) -->
|
|
4679
6118
|
@if (companyFeatureEnabled() && !isEditMode()) {
|
|
4680
|
-
<div class="md:col-span-2 p-3 bg-
|
|
4681
|
-
<div class="flex items-center gap-2 text-
|
|
6119
|
+
<div class="md:col-span-2 p-3 bg-primary/10 rounded-lg border border-primary/20">
|
|
6120
|
+
<div class="flex items-center gap-2 text-primary">
|
|
4682
6121
|
<i class="pi pi-info-circle"></i>
|
|
4683
6122
|
<span class="text-sm">
|
|
4684
6123
|
User will be automatically assigned to
|
|
@@ -4889,7 +6328,7 @@ class UserCompanyPermissionDialogComponent {
|
|
|
4889
6328
|
[disabled]="isSaving()" />
|
|
4890
6329
|
</ng-template>
|
|
4891
6330
|
</p-dialog>
|
|
4892
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i2$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i3.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: i4$
|
|
6331
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i2$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i3.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: i4$3.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: i5$2.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: i6$1.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }] });
|
|
4893
6332
|
}
|
|
4894
6333
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserCompanyPermissionDialogComponent, decorators: [{
|
|
4895
6334
|
type: Component,
|
|
@@ -5194,7 +6633,7 @@ class UserBranchPermissionDialogComponent {
|
|
|
5194
6633
|
[disabled]="isSaving()" />
|
|
5195
6634
|
</ng-template>
|
|
5196
6635
|
</p-dialog>
|
|
5197
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i2$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i3.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: i4$
|
|
6636
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "directive", type: i2$2.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "component", type: i3.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: i4$3.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: i5$2.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: i6$1.ProgressSpinner, selector: "p-progressSpinner, p-progress-spinner, p-progressspinner", inputs: ["styleClass", "strokeWidth", "fill", "animationDuration", "ariaLabel"] }] });
|
|
5198
6637
|
}
|
|
5199
6638
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserBranchPermissionDialogComponent, decorators: [{
|
|
5200
6639
|
type: Component,
|
|
@@ -5460,7 +6899,7 @@ class UserListComponent {
|
|
|
5460
6899
|
(closed)="showBranchDialog.set(false)"
|
|
5461
6900
|
(permissionsChanged)="loadUsers()" />
|
|
5462
6901
|
}
|
|
5463
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type:
|
|
6902
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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$1.Card, selector: "p-card", inputs: ["header", "subheader", "style", "styleClass"] }, { kind: "component", type: i3$2.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: "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"] }] });
|
|
5464
6903
|
}
|
|
5465
6904
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: UserListComponent, decorators: [{
|
|
5466
6905
|
type: Component,
|
|
@@ -5587,6 +7026,375 @@ var userList_component = /*#__PURE__*/Object.freeze({
|
|
|
5587
7026
|
UserListComponent: UserListComponent
|
|
5588
7027
|
});
|
|
5589
7028
|
|
|
7029
|
+
const VERIFY_EMAIL_KEY = makeStateKey('verify-email-result');
|
|
7030
|
+
class VerifyEmailPageComponent {
|
|
7031
|
+
platformId = inject(PLATFORM_ID);
|
|
7032
|
+
transferState = inject(TransferState);
|
|
7033
|
+
destroyRef = inject(DestroyRef);
|
|
7034
|
+
route = inject(ActivatedRoute);
|
|
7035
|
+
messageService = inject(MessageService);
|
|
7036
|
+
authApi = inject(AuthApiService);
|
|
7037
|
+
isLoading = signal(false, ...(ngDevMode ? [{ debugName: "isLoading" }] : []));
|
|
7038
|
+
pageState = signal('verifying', ...(ngDevMode ? [{ debugName: "pageState" }] : []));
|
|
7039
|
+
resendSuccess = signal(false, ...(ngDevMode ? [{ debugName: "resendSuccess" }] : []));
|
|
7040
|
+
submittedEmail = signal('', ...(ngDevMode ? [{ debugName: "submittedEmail" }] : []));
|
|
7041
|
+
resendFormModel = signal({ email: '' }, ...(ngDevMode ? [{ debugName: "resendFormModel" }] : []));
|
|
7042
|
+
resendForm = form(this.resendFormModel, (f) => {
|
|
7043
|
+
required(f.email, { message: 'Email is required' });
|
|
7044
|
+
email(f.email, { message: 'Please enter a valid email address' });
|
|
7045
|
+
});
|
|
7046
|
+
isResendFormValid = computed(() => this.resendForm().valid(), ...(ngDevMode ? [{ debugName: "isResendFormValid" }] : []));
|
|
7047
|
+
ngOnInit() {
|
|
7048
|
+
this.route.queryParams.pipe(take(1)).subscribe((params) => {
|
|
7049
|
+
const token = params['token'] || '';
|
|
7050
|
+
if (!token) {
|
|
7051
|
+
this.pageState.set('resend');
|
|
7052
|
+
return;
|
|
7053
|
+
}
|
|
7054
|
+
// Check if we already have result from SSR (avoid duplicate API call)
|
|
7055
|
+
const cachedResult = this.transferState.get(VERIFY_EMAIL_KEY, null);
|
|
7056
|
+
if (cachedResult) {
|
|
7057
|
+
this.transferState.remove(VERIFY_EMAIL_KEY);
|
|
7058
|
+
this.handleVerificationResult(cachedResult === 'success');
|
|
7059
|
+
return;
|
|
7060
|
+
}
|
|
7061
|
+
this.verifyEmail(token);
|
|
7062
|
+
});
|
|
7063
|
+
}
|
|
7064
|
+
verifyEmail(token) {
|
|
7065
|
+
this.authApi
|
|
7066
|
+
.verifyEmail(token)
|
|
7067
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7068
|
+
.subscribe({
|
|
7069
|
+
next: () => {
|
|
7070
|
+
// Store result for browser hydration (SSR only)
|
|
7071
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
7072
|
+
this.transferState.set(VERIFY_EMAIL_KEY, 'success');
|
|
7073
|
+
}
|
|
7074
|
+
this.handleVerificationResult(true);
|
|
7075
|
+
},
|
|
7076
|
+
error: () => {
|
|
7077
|
+
if (!isPlatformBrowser(this.platformId)) {
|
|
7078
|
+
this.transferState.set(VERIFY_EMAIL_KEY, 'error');
|
|
7079
|
+
}
|
|
7080
|
+
this.handleVerificationResult(false);
|
|
7081
|
+
},
|
|
7082
|
+
});
|
|
7083
|
+
}
|
|
7084
|
+
handleVerificationResult(success) {
|
|
7085
|
+
if (success) {
|
|
7086
|
+
this.pageState.set('success');
|
|
7087
|
+
if (isPlatformBrowser(this.platformId)) {
|
|
7088
|
+
this.messageService.add({
|
|
7089
|
+
severity: 'success',
|
|
7090
|
+
summary: 'Success',
|
|
7091
|
+
detail: 'Your email has been verified successfully.',
|
|
7092
|
+
});
|
|
7093
|
+
}
|
|
7094
|
+
}
|
|
7095
|
+
else {
|
|
7096
|
+
this.pageState.set('invalid-token');
|
|
7097
|
+
}
|
|
7098
|
+
}
|
|
7099
|
+
showResendForm() {
|
|
7100
|
+
this.pageState.set('resend');
|
|
7101
|
+
this.resendSuccess.set(false);
|
|
7102
|
+
}
|
|
7103
|
+
onResend() {
|
|
7104
|
+
if (!this.isResendFormValid()) {
|
|
7105
|
+
this.messageService.add({
|
|
7106
|
+
severity: 'error',
|
|
7107
|
+
summary: 'Validation Error',
|
|
7108
|
+
detail: 'Please enter a valid email address.',
|
|
7109
|
+
});
|
|
7110
|
+
return;
|
|
7111
|
+
}
|
|
7112
|
+
this.isLoading.set(true);
|
|
7113
|
+
const email = this.resendFormModel().email;
|
|
7114
|
+
this.authApi
|
|
7115
|
+
.resendVerification(email)
|
|
7116
|
+
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
7117
|
+
.subscribe({
|
|
7118
|
+
next: () => this.handleResendComplete(email),
|
|
7119
|
+
error: () => this.handleResendComplete(email),
|
|
7120
|
+
});
|
|
7121
|
+
}
|
|
7122
|
+
handleResendComplete(email) {
|
|
7123
|
+
this.isLoading.set(false);
|
|
7124
|
+
this.messageService.add({
|
|
7125
|
+
severity: 'success',
|
|
7126
|
+
summary: 'Email Sent',
|
|
7127
|
+
detail: 'If the email exists, a verification link has been sent.',
|
|
7128
|
+
});
|
|
7129
|
+
this.submittedEmail.set(email);
|
|
7130
|
+
this.resendSuccess.set(true);
|
|
7131
|
+
}
|
|
7132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: VerifyEmailPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.3", type: VerifyEmailPageComponent, isStandalone: true, selector: "lib-verify-email-page", ngImport: i0, template: `
|
|
7134
|
+
<app-floating-configurator />
|
|
7135
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
7136
|
+
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
7137
|
+
<div
|
|
7138
|
+
class="w-full"
|
|
7139
|
+
style="border-radius: var(--p-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
|
7140
|
+
<div
|
|
7141
|
+
class="w-full py-16 px-12"
|
|
7142
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background);">
|
|
7143
|
+
|
|
7144
|
+
@switch (pageState()) {
|
|
7145
|
+
@case ('verifying') {
|
|
7146
|
+
<div class="text-center">
|
|
7147
|
+
<div class="flex justify-center mb-6">
|
|
7148
|
+
<i class="pi pi-spin pi-spinner text-8xl text-primary"></i>
|
|
7149
|
+
</div>
|
|
7150
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7151
|
+
Verifying Email
|
|
7152
|
+
</div>
|
|
7153
|
+
<p class="text-muted-color font-medium">
|
|
7154
|
+
Please wait while we verify your email address...
|
|
7155
|
+
</p>
|
|
7156
|
+
</div>
|
|
7157
|
+
}
|
|
7158
|
+
|
|
7159
|
+
@case ('success') {
|
|
7160
|
+
<div class="text-center">
|
|
7161
|
+
<div class="flex justify-center mb-6">
|
|
7162
|
+
<i class="pi pi-check-circle text-8xl text-success"></i>
|
|
7163
|
+
</div>
|
|
7164
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7165
|
+
Email Verified
|
|
7166
|
+
</div>
|
|
7167
|
+
<p class="text-muted-color font-medium mb-6">
|
|
7168
|
+
Your email address has been verified successfully.
|
|
7169
|
+
You can now access all features of your account.
|
|
7170
|
+
</p>
|
|
7171
|
+
<a routerLink="/auth/login">
|
|
7172
|
+
<p-button label="Sign In" styleClass="w-full" />
|
|
7173
|
+
</a>
|
|
7174
|
+
</div>
|
|
7175
|
+
}
|
|
7176
|
+
|
|
7177
|
+
@case ('invalid-token') {
|
|
7178
|
+
<div class="text-center">
|
|
7179
|
+
<div class="flex justify-center mb-6">
|
|
7180
|
+
<i class="pi pi-times-circle text-8xl text-danger"></i>
|
|
7181
|
+
</div>
|
|
7182
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7183
|
+
Verification Failed
|
|
7184
|
+
</div>
|
|
7185
|
+
<p class="text-muted-color font-medium mb-6">
|
|
7186
|
+
This verification link is invalid or has expired.
|
|
7187
|
+
</p>
|
|
7188
|
+
<p-button
|
|
7189
|
+
label="Resend Verification Email"
|
|
7190
|
+
styleClass="w-full mb-4"
|
|
7191
|
+
(onClick)="showResendForm()" />
|
|
7192
|
+
<div class="mt-4">
|
|
7193
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
7194
|
+
Back to Sign In
|
|
7195
|
+
</a>
|
|
7196
|
+
</div>
|
|
7197
|
+
</div>
|
|
7198
|
+
}
|
|
7199
|
+
|
|
7200
|
+
@case ('resend') {
|
|
7201
|
+
<div class="text-center mb-8">
|
|
7202
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7203
|
+
Resend Verification
|
|
7204
|
+
</div>
|
|
7205
|
+
<span class="text-muted-color font-medium">
|
|
7206
|
+
Enter your email to receive a new verification link.
|
|
7207
|
+
</span>
|
|
7208
|
+
</div>
|
|
7209
|
+
|
|
7210
|
+
@if (!resendSuccess()) {
|
|
7211
|
+
<form (ngSubmit)="onResend()">
|
|
7212
|
+
<label for="email" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
7213
|
+
Email
|
|
7214
|
+
</label>
|
|
7215
|
+
<input
|
|
7216
|
+
pInputText
|
|
7217
|
+
id="email"
|
|
7218
|
+
type="email"
|
|
7219
|
+
placeholder="Enter your email address"
|
|
7220
|
+
class="w-full md:w-[25rem] mb-6"
|
|
7221
|
+
[formField]="resendForm.email"
|
|
7222
|
+
autocomplete="email" />
|
|
7223
|
+
|
|
7224
|
+
<p-button
|
|
7225
|
+
label="Resend Verification"
|
|
7226
|
+
styleClass="w-full mb-4"
|
|
7227
|
+
type="submit"
|
|
7228
|
+
[loading]="isLoading()"
|
|
7229
|
+
[disabled]="!isResendFormValid() || isLoading()" />
|
|
7230
|
+
|
|
7231
|
+
<div class="flex items-center justify-center gap-2">
|
|
7232
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
7233
|
+
Back to Sign In
|
|
7234
|
+
</a>
|
|
7235
|
+
</div>
|
|
7236
|
+
</form>
|
|
7237
|
+
} @else {
|
|
7238
|
+
<div class="text-center">
|
|
7239
|
+
<div class="flex justify-center mb-6">
|
|
7240
|
+
<i class="pi pi-envelope text-8xl text-primary"></i>
|
|
7241
|
+
</div>
|
|
7242
|
+
<p class="text-muted-color font-medium mb-6">
|
|
7243
|
+
We've sent a new verification link to <strong>{{ submittedEmail() }}</strong>.
|
|
7244
|
+
Please check your inbox.
|
|
7245
|
+
</p>
|
|
7246
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
7247
|
+
Back to Sign In
|
|
7248
|
+
</a>
|
|
7249
|
+
</div>
|
|
7250
|
+
}
|
|
7251
|
+
}
|
|
7252
|
+
}
|
|
7253
|
+
</div>
|
|
7254
|
+
</div>
|
|
7255
|
+
</div>
|
|
7256
|
+
</div>
|
|
7257
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: AngularModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "ngmodule", type: PrimeModule }, { kind: "component", type: i3.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" }] });
|
|
7258
|
+
}
|
|
7259
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.3", ngImport: i0, type: VerifyEmailPageComponent, decorators: [{
|
|
7260
|
+
type: Component,
|
|
7261
|
+
args: [{
|
|
7262
|
+
selector: 'lib-verify-email-page',
|
|
7263
|
+
standalone: true,
|
|
7264
|
+
imports: [AngularModule, PrimeModule, FormField, AppFloatingConfigurator],
|
|
7265
|
+
template: `
|
|
7266
|
+
<app-floating-configurator />
|
|
7267
|
+
<div class="bg-surface-ground flex items-center justify-center min-h-screen">
|
|
7268
|
+
<div class="flex flex-col items-center justify-center w-full max-w-[32rem] px-4">
|
|
7269
|
+
<div
|
|
7270
|
+
class="w-full"
|
|
7271
|
+
style="border-radius: var(--p-content-border-radius); padding: 0.3rem; background: linear-gradient(180deg, var(--primary-color) 10%, rgba(33, 150, 243, 0) 30%)">
|
|
7272
|
+
<div
|
|
7273
|
+
class="w-full py-16 px-12"
|
|
7274
|
+
style="border-radius: var(--p-content-border-radius); background: var(--p-content-background);">
|
|
7275
|
+
|
|
7276
|
+
@switch (pageState()) {
|
|
7277
|
+
@case ('verifying') {
|
|
7278
|
+
<div class="text-center">
|
|
7279
|
+
<div class="flex justify-center mb-6">
|
|
7280
|
+
<i class="pi pi-spin pi-spinner text-8xl text-primary"></i>
|
|
7281
|
+
</div>
|
|
7282
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7283
|
+
Verifying Email
|
|
7284
|
+
</div>
|
|
7285
|
+
<p class="text-muted-color font-medium">
|
|
7286
|
+
Please wait while we verify your email address...
|
|
7287
|
+
</p>
|
|
7288
|
+
</div>
|
|
7289
|
+
}
|
|
7290
|
+
|
|
7291
|
+
@case ('success') {
|
|
7292
|
+
<div class="text-center">
|
|
7293
|
+
<div class="flex justify-center mb-6">
|
|
7294
|
+
<i class="pi pi-check-circle text-8xl text-success"></i>
|
|
7295
|
+
</div>
|
|
7296
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7297
|
+
Email Verified
|
|
7298
|
+
</div>
|
|
7299
|
+
<p class="text-muted-color font-medium mb-6">
|
|
7300
|
+
Your email address has been verified successfully.
|
|
7301
|
+
You can now access all features of your account.
|
|
7302
|
+
</p>
|
|
7303
|
+
<a routerLink="/auth/login">
|
|
7304
|
+
<p-button label="Sign In" styleClass="w-full" />
|
|
7305
|
+
</a>
|
|
7306
|
+
</div>
|
|
7307
|
+
}
|
|
7308
|
+
|
|
7309
|
+
@case ('invalid-token') {
|
|
7310
|
+
<div class="text-center">
|
|
7311
|
+
<div class="flex justify-center mb-6">
|
|
7312
|
+
<i class="pi pi-times-circle text-8xl text-danger"></i>
|
|
7313
|
+
</div>
|
|
7314
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7315
|
+
Verification Failed
|
|
7316
|
+
</div>
|
|
7317
|
+
<p class="text-muted-color font-medium mb-6">
|
|
7318
|
+
This verification link is invalid or has expired.
|
|
7319
|
+
</p>
|
|
7320
|
+
<p-button
|
|
7321
|
+
label="Resend Verification Email"
|
|
7322
|
+
styleClass="w-full mb-4"
|
|
7323
|
+
(onClick)="showResendForm()" />
|
|
7324
|
+
<div class="mt-4">
|
|
7325
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
7326
|
+
Back to Sign In
|
|
7327
|
+
</a>
|
|
7328
|
+
</div>
|
|
7329
|
+
</div>
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7332
|
+
@case ('resend') {
|
|
7333
|
+
<div class="text-center mb-8">
|
|
7334
|
+
<div class="text-3xl font-medium mb-4" style="color: var(--p-content-color);">
|
|
7335
|
+
Resend Verification
|
|
7336
|
+
</div>
|
|
7337
|
+
<span class="text-muted-color font-medium">
|
|
7338
|
+
Enter your email to receive a new verification link.
|
|
7339
|
+
</span>
|
|
7340
|
+
</div>
|
|
7341
|
+
|
|
7342
|
+
@if (!resendSuccess()) {
|
|
7343
|
+
<form (ngSubmit)="onResend()">
|
|
7344
|
+
<label for="email" class="block text-lg font-medium mb-2" style="color: var(--p-content-color);">
|
|
7345
|
+
Email
|
|
7346
|
+
</label>
|
|
7347
|
+
<input
|
|
7348
|
+
pInputText
|
|
7349
|
+
id="email"
|
|
7350
|
+
type="email"
|
|
7351
|
+
placeholder="Enter your email address"
|
|
7352
|
+
class="w-full md:w-[25rem] mb-6"
|
|
7353
|
+
[formField]="resendForm.email"
|
|
7354
|
+
autocomplete="email" />
|
|
7355
|
+
|
|
7356
|
+
<p-button
|
|
7357
|
+
label="Resend Verification"
|
|
7358
|
+
styleClass="w-full mb-4"
|
|
7359
|
+
type="submit"
|
|
7360
|
+
[loading]="isLoading()"
|
|
7361
|
+
[disabled]="!isResendFormValid() || isLoading()" />
|
|
7362
|
+
|
|
7363
|
+
<div class="flex items-center justify-center gap-2">
|
|
7364
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
7365
|
+
Back to Sign In
|
|
7366
|
+
</a>
|
|
7367
|
+
</div>
|
|
7368
|
+
</form>
|
|
7369
|
+
} @else {
|
|
7370
|
+
<div class="text-center">
|
|
7371
|
+
<div class="flex justify-center mb-6">
|
|
7372
|
+
<i class="pi pi-envelope text-8xl text-primary"></i>
|
|
7373
|
+
</div>
|
|
7374
|
+
<p class="text-muted-color font-medium mb-6">
|
|
7375
|
+
We've sent a new verification link to <strong>{{ submittedEmail() }}</strong>.
|
|
7376
|
+
Please check your inbox.
|
|
7377
|
+
</p>
|
|
7378
|
+
<a routerLink="/auth/login" class="font-semibold text-primary cursor-pointer">
|
|
7379
|
+
Back to Sign In
|
|
7380
|
+
</a>
|
|
7381
|
+
</div>
|
|
7382
|
+
}
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
</div>
|
|
7386
|
+
</div>
|
|
7387
|
+
</div>
|
|
7388
|
+
</div>
|
|
7389
|
+
`,
|
|
7390
|
+
}]
|
|
7391
|
+
}] });
|
|
7392
|
+
|
|
7393
|
+
var verifyEmailPage_component = /*#__PURE__*/Object.freeze({
|
|
7394
|
+
__proto__: null,
|
|
7395
|
+
VerifyEmailPageComponent: VerifyEmailPageComponent
|
|
7396
|
+
});
|
|
7397
|
+
|
|
5590
7398
|
// =============================================================================
|
|
5591
7399
|
// @flusys/ng-auth - Authentication Package
|
|
5592
7400
|
// =============================================================================
|
|
@@ -5596,5 +7404,5 @@ var userList_component = /*#__PURE__*/Object.freeze({
|
|
|
5596
7404
|
* Generated bundle index. Do not edit.
|
|
5597
7405
|
*/
|
|
5598
7406
|
|
|
5599
|
-
export { ADMINISTRATION_ROUTES, AUTH_ENDPOINTS, AUTH_ROUTES, AdministrationPageComponent, AuthApiService, AuthCompanyApiProviderAdapter, AuthInitService, AuthLayoutApiAdapter, AuthLayoutStateAdapter, AuthStateService, AuthUserPermissionProviderAdapter, AuthUserProviderAdapter, BranchApiService, BranchFormComponent, BranchListComponent, CompanyApiService, CompanyFormComponent, CompanyListComponent, LoginPageComponent, RegisterPageComponent, TokenRefreshStateService, UserApiService, UserBranchPermissionDialogComponent, UserCompanyPermissionDialogComponent, UserFormComponent, UserListComponent, UserPermissionApiService, authGuard, authInterceptor, companyFeatureGuard, guestGuard, isAuthEndpoint, isCompanySelectionResponse, isEndpoint, isLoginResponse, provideAuthLayoutIntegration, provideAuthProviders, tokenRefreshInterceptor };
|
|
7407
|
+
export { ADMINISTRATION_ROUTES, AUTH_ENDPOINTS, AUTH_ROUTES, AdministrationPageComponent, AuthApiService, AuthCompanyApiProviderAdapter, AuthInitService, AuthLayoutApiAdapter, AuthLayoutStateAdapter, 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, isAuthEndpoint, isCompanySelectionResponse, isEmailVerificationRequired, isEndpoint, isLoginResponse, provideAuthLayoutIntegration, provideAuthProviders, tokenRefreshInterceptor };
|
|
5600
7408
|
//# sourceMappingURL=flusys-ng-auth.mjs.map
|