diff --git a/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.html b/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.html index 60e7c49798..42b1d6ad2d 100644 --- a/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.html +++ b/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.html @@ -11,7 +11,7 @@ -
+
{{ 'Google Authenticator' | translate }} @@ -19,8 +19,8 @@
{{ 'Status' | translate }} @@ -128,7 +128,7 @@ [bindLabel]="'name'" [bindValue]="'languageCode'" [clearable]="false" - [(ngModel)]="userSettingsModel.locale" + formControlName="locale" [items]="activeLanguages"> @@ -139,7 +139,7 @@ [bindLabel]="'text'" [bindValue]="'id'" [clearable]="false" - [(ngModel)]="userSettingsModel.formats" + formControlName="formats" [items]="countries"> @@ -150,14 +150,14 @@ [bindLabel]="'name'" [bindValue]="'id'" [clearable]="false" - [(ngModel)]="userSettingsModel.timeZone" + formControlName="timeZone" [items]="timeZones.timeZoneModels">
+ formControlName="darkTheme"> {{ 'Dark theme' | translate }}
diff --git a/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.ts b/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.ts index eaa2239811..0838e5c93e 100644 --- a/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.ts +++ b/eform-client/src/app/modules/account-management/components/profile/profile-settings/profile-settings.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit, inject } from '@angular/core'; +import { FormBuilder, FormGroup, FormControl } from '@angular/forms'; import { GoogleAuthInfoModel } from 'src/app/common/models/auth'; import { UserSettingsModel } from 'src/app/common/models/settings'; import { @@ -32,6 +33,7 @@ import * as R from 'ramda'; standalone: false }) export class ProfileSettingsComponent implements OnInit { + private fb = inject(FormBuilder); authStateService = inject(AuthStateService); private authStore = inject(Store); private googleAuthService = inject(GoogleAuthService); @@ -55,6 +57,14 @@ export class ProfileSettingsComponent implements OnInit { public selectCurrentUserIsAdmin$ = this.authStore.select(selectCurrentUserIsAdmin); private selectBearerToken$ = this.authStore.select(selectBearerToken); + profileForm: FormGroup = this.fb.group({ + locale: new FormControl(''), + formats: new FormControl(''), + timeZone: new FormControl(''), + darkTheme: new FormControl(false), + isTwoFactorEnabled: new FormControl(false), + }); + ngOnInit() { this.getEnabledLanguages(); this.initializeUploaders(); @@ -94,12 +104,25 @@ export class ProfileSettingsComponent implements OnInit { }); } - isTwoFactorEnabledCheckBoxChanged(e) { - if (e.target) { - this.googleAuthInfoModel.isTwoFactorEnabled = e.target.checked; - } else { - return; - } + // isTwoFactorEnabledCheckBoxChanged(e) { + // if (e.target) { + // this.googleAuthInfoModel.isTwoFactorEnabled = e.target.checked; + // } else { + // return; + // } + // this.googleAuthService + // .updateGoogleAuthenticatorInfo(this.googleAuthInfoModel) + // .subscribe((data) => { + // if (data.success) { + // this.authStateService.logout(); + // } + // }); + // } + + toggleTwoFactor(): void { + const isEnabled = this.profileForm.get('isTwoFactorEnabled')?.value; + this.googleAuthInfoModel.isTwoFactorEnabled = isEnabled; + this.googleAuthService .updateGoogleAuthenticatorInfo(this.googleAuthInfoModel) .subscribe((data) => { @@ -117,41 +140,106 @@ export class ProfileSettingsComponent implements OnInit { }); } + // deleteGoogleAuthenticatorInfo() { + // this.googleAuthService.deleteGoogleAuthenticatorInfo().subscribe((data) => { + // if (data && data.success) { + // this.googleAuthInfoModel.psk = null; + // } + // }); + // } + + // updateUserProfileSettings() { + // + // if (this.profilePictureUploader.queue.length > 0) { + // this.profilePictureUploader.queue[0].upload(); + // } + // this.userSettingsService + // .updateUserSettings(this.userSettingsModel) + // .subscribe((data) => { + // this.userSettings.getUserSettings().subscribe((data) => { + // this.userSettingsModel = data.model; + // this.store.dispatch(updateCurrentUserLocaleAndDarkTheme({ + // darkTheme: this.userSettingsModel.darkTheme, + // locale: this.userSettingsModel.locale, + // languageId: this.userSettingsModel.languageId + // })); + // this.getUserSettings(); + // }); + // this.store.dispatch(loadAppMenu()); + // }); + // } + updateUserProfileSettings() { + const formValue = this.profileForm.value; + + this.userSettingsModel.locale = formValue.locale; + this.userSettingsModel.formats = formValue.formats; + this.userSettingsModel.timeZone = formValue.timeZone; + this.userSettingsModel.darkTheme = formValue.darkTheme; if (this.profilePictureUploader.queue.length > 0) { this.profilePictureUploader.queue[0].upload(); } - this.userSettingsService - .updateUserSettings(this.userSettingsModel) - .subscribe((data) => { - this.userSettings.getUserSettings().subscribe((data) => { - this.userSettingsModel = data.model; - this.store.dispatch(updateCurrentUserLocaleAndDarkTheme({ + + this.userSettingsService.updateUserSettings(this.userSettingsModel).subscribe((data) => { + this.userSettings.getUserSettings().subscribe((data) => { + this.userSettingsModel = data.model; + this.store.dispatch( + updateCurrentUserLocaleAndDarkTheme({ darkTheme: this.userSettingsModel.darkTheme, locale: this.userSettingsModel.locale, - languageId: this.userSettingsModel.languageId - })); - this.getUserSettings(); - }); - this.store.dispatch(loadAppMenu()); + languageId: this.userSettingsModel.languageId, + }) + ); + this.getUserSettings(); }); + this.store.dispatch(loadAppMenu()); + }); } + // getEnabledLanguages() { + // this.getLanguagesSub$ = this.appSettingsStateService.getLanguages() + // .pipe(tap(data => { + // if (data && data.success && data.model) { + // this.appLanguages = data.model; + // this.activeLanguages = this.appLanguages.languages.filter((x) => x.isActive); + // this.getTimeZones(); + // this.getGoogleAuthenticatorInfo(); + // this.getUserSettings(); + // } + // })) + // .subscribe(); + // } + getEnabledLanguages() { this.getLanguagesSub$ = this.appSettingsStateService.getLanguages() - .pipe(tap(data => { - if (data && data.success && data.model) { - this.appLanguages = data.model; - this.activeLanguages = this.appLanguages.languages.filter((x) => x.isActive); - this.getTimeZones(); - this.getGoogleAuthenticatorInfo(); - this.getUserSettings(); - } - })) + .pipe( + tap((data) => { + if (data && data.success && data.model) { + this.appLanguages = data.model; + this.activeLanguages = this.appLanguages.languages.filter((x) => x.isActive); + this.getTimeZones(); + this.getGoogleAuthenticatorInfo(); + + // Fetch user settings after languages are available + this.userSettingsService.getUserSettings().subscribe((data) => { + this.userSettingsModel = data.model; + + this.profileForm.patchValue({ + locale: this.userSettingsModel.locale, + formats: this.userSettingsModel.formats, + timeZone: this.userSettingsModel.timeZone, + darkTheme: this.userSettingsModel.darkTheme, + isTwoFactorEnabled: this.googleAuthInfoModel.isTwoFactorEnabled, + }); + }); + } + }) + ) .subscribe(); } + get languages() { return this.appLanguages.languages.filter((x) => x.isActive); } diff --git a/eform-client/src/app/modules/account-management/components/users/user-modal/user-modal.component.html b/eform-client/src/app/modules/account-management/components/users/user-modal/user-modal.component.html index f66dece436..25ea61990d 100644 --- a/eform-client/src/app/modules/account-management/components/users/user-modal/user-modal.component.html +++ b/eform-client/src/app/modules/account-management/components/users/user-modal/user-modal.component.html @@ -1,16 +1,16 @@

{{ (edit ? 'Edit User' : 'New User') | translate }}

-
+
{{ 'First name' | translate }} @@ -18,8 +18,8 @@

{{ (edit ? 'Edit User' : 'New User') | translate }}

@@ -31,9 +31,8 @@

{{ (edit ? 'Edit User' : 'New User') | translate }}

@@ -44,7 +43,7 @@

{{ (edit ? 'Edit User' : 'New User') | translate }}

matInput required type="password" - [(ngModel)]="userModel.password" + formControlName="password" [id]="edit ? 'editPassword' : 'createPassword'" [name]="edit ? 'editPassword' : 'createPassword'" > @@ -58,8 +57,7 @@

{{ (edit ? 'Edit User' : 'New User') | translate }}

bindLabel="name" bindValue="id" [clearable]="false" - [(ngModel)]="userModel.role" - [name]="edit ? 'editRole' : 'createRole'" + formControlName="role" [id]="edit ? 'editRole' : 'createRole'" id="createRole" appendTo="body" @@ -69,14 +67,13 @@

{{ (edit ? 'Edit User' : 'New User') | translate }}

]" >
- + {{ 'Group' | translate }} {{ (edit ? 'Edit User' : 'New User') | translate }}
diff --git a/eform-client/src/app/modules/account-management/components/users/user-set-password-modal/user-set-password.component.ts b/eform-client/src/app/modules/account-management/components/users/user-set-password-modal/user-set-password.component.ts index 724787d77d..6efb278542 100644 --- a/eform-client/src/app/modules/account-management/components/users/user-set-password-modal/user-set-password.component.ts +++ b/eform-client/src/app/modules/account-management/components/users/user-set-password-modal/user-set-password.component.ts @@ -1,14 +1,14 @@ -import { Component, EventEmitter, OnInit, inject } from '@angular/core'; +import {Component, EventEmitter, OnInit, inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {UserInfoModel} from 'src/app/common/models'; -import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {FormBuilder, FormGroup, Validators, AbstractControl} from '@angular/forms'; import {AuthService} from 'src/app/common/services'; import {ChangePasswordAdminModel} from 'src/app/common/models/user/change-password-admin.model'; @Component({ - selector: 'app-user-set-password', - templateUrl: './user-set-password.component.html', - standalone: false + selector: 'app-user-set-password', + templateUrl: './user-set-password.component.html', + standalone: false }) export class UserSetPasswordComponent implements OnInit { private authService = inject(AuthService); @@ -24,12 +24,17 @@ export class UserSetPasswordComponent implements OnInit { passwordStrength = 0; // Track password strength score constructor() { this.setPasswordForm = this.fb.group({ - newPassword: ['', [Validators.minLength(8)]], - confirmPassword: ['', [Validators.minLength(8)]] - }); + newPassword: ['', [Validators.minLength(8)]], + confirmPassword: ['', [Validators.minLength(8)]] + }, + {validators: this.passwordMatchValidator} + ); } ngOnInit() { + this.setPasswordForm.get('newPassword')?.valueChanges.subscribe(() => { + this.setPasswordForm.get('confirmPassword')?.updateValueAndValidity({ onlySelf: true }); + }); } hide() { @@ -40,12 +45,14 @@ export class UserSetPasswordComponent implements OnInit { this.changePasswordModel = this.setPasswordForm.getRawValue(); this.changePasswordModel.email = this.selectedUser.email; this.authService.changePasswordAdmin(this.changePasswordModel).subscribe( - (data) => {}, + (data) => { + }, ); this.userPasswordSet.emit(this.selectedUser); } - get valid(){ - if(this.setPasswordForm) { + + get valid() { + if (this.setPasswordForm) { return this.setPasswordForm.valid; } return false; @@ -61,15 +68,27 @@ export class UserSetPasswordComponent implements OnInit { onPasswordStrengthChanged(strength: number): void { this.passwordStrength = strength; - // Optionally add additional validation based on strength const passwordControl = this.setPasswordForm.get('newPassword'); - if (passwordControl && strength < 40) { - passwordControl.setErrors({ ...passwordControl.errors, weakPassword: true }); - } else if (passwordControl && passwordControl.hasError('weakPassword')) { - delete passwordControl.errors.weakPassword; - if (Object.keys(passwordControl.errors).length === 0) { - passwordControl.setErrors(null); + if (passwordControl) { + if (strength < 40) { + passwordControl.setErrors({...passwordControl.errors, weakPassword: true}); + } else if (passwordControl.hasError('weakPassword')) { + delete passwordControl.errors.weakPassword; + if (Object.keys(passwordControl.errors || {}).length === 0) { + passwordControl.setErrors(null); + } } } } + + private passwordMatchValidator(group: AbstractControl): { [key: string]: boolean } | null { + const password = group.get('newPassword')?.value; + const confirm = group.get('confirmPassword')?.value; + if (password && confirm && password !== confirm) { + group.get('confirmPassword')?.setErrors({mismatch: true}); + return {mismatch: true}; + } else { + return null; + } + } } diff --git a/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.html b/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.html index c9567852bb..6bdae733c8 100644 --- a/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.html +++ b/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.html @@ -1,18 +1,20 @@ -

{{'Plugin permissions' | translate}}

-
+

{{ 'Plugin permissions' | translate }}

+ +
-

{{getSecurityGroupName(groupPermissions.groupId)}}

+

{{ getSecurityGroupName(groupPermissions.groupId) }}

- {{permission.permissionName | translate}} + > + {{ permission.permissionName | translate }}
@@ -27,13 +29,13 @@

{{getSecurityGroupName(groupPermissions.groupId)}}

(click)="updatePluginPermissions()" id="saveBtn" > - {{'Save' | translate}} + {{ 'Save' | translate }}
diff --git a/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.ts b/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.ts index c77845f3b3..4e733a3946 100644 --- a/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.ts +++ b/eform-client/src/app/modules/plugins-management/components/installed/installed-plugin-permissions/installed-plugin-permissions.component.ts @@ -1,51 +1,85 @@ -import { Component, OnInit, inject } from '@angular/core'; +import {Component, OnInit, inject} from '@angular/core'; import { PluginGroupPermissionsListModel, PluginGroupPermissionsUpdateModel, SecurityGroupModel, } from 'src/app/common/models'; +import {FormBuilder, FormGroup, FormControl} from '@angular/forms'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {PluginPermissionsService} from 'src/app/common/services'; @Component({ - selector: 'app-installed-plugin-permissions', - templateUrl: './installed-plugin-permissions.component.html', - styleUrls: ['./installed-plugin-permissions.component.scss'], - standalone: false + selector: 'app-installed-plugin-permissions', + templateUrl: './installed-plugin-permissions.component.html', + styleUrls: ['./installed-plugin-permissions.component.scss'], + standalone: false, }) + export class InstalledPluginPermissionsComponent implements OnInit { private pluginPermissionsService = inject(PluginPermissionsService); + private fb = inject(FormBuilder); dialogRef = inject>(MatDialogRef); securityGroups: SecurityGroupModel[] = []; pluginGroupPermissions: PluginGroupPermissionsListModel[] = []; pluginId: number; + permissionsForm: FormGroup = this.fb.group({}); constructor() { const model = inject<{ - pluginPermissions: PluginGroupPermissionsUpdateModel; - securityGroups: SecurityGroupModel[]; -}>(MAT_DIALOG_DATA); + pluginPermissions: PluginGroupPermissionsUpdateModel; + securityGroups: SecurityGroupModel[]; + }>(MAT_DIALOG_DATA); this.pluginId = model.pluginPermissions.pluginId; this.pluginGroupPermissions = model.pluginPermissions.groupPermissions; this.securityGroups = model.securityGroups; } - ngOnInit() {} + ngOnInit() { + this.initializeForm(); + } + + private initializeForm() { + const controls: { [key: string]: FormControl } = {}; + + this.pluginGroupPermissions.forEach(group => { + group.permissions.forEach(permission => { + const controlName = this.getPermissionControlName(group.groupId, permission.permissionId); + controls[controlName] = new FormControl(permission.isEnabled); + }); + }); + + this.permissionsForm = this.fb.group(controls); + } + + getPermissionControlName(groupId: number, permissionId: number): string { + return `perm_${groupId}_${permissionId}`; + } hide(result = false) { this.dialogRef.close(result); } getSecurityGroupName(id: number) { - const group = this.securityGroups.find((g) => g.id === id); + const group = this.securityGroups.find(g => g.id === id); return group ? group.groupName : ''; } updatePluginPermissions() { + + this.pluginGroupPermissions.forEach(group => { + group.permissions.forEach(permission => { + const controlName = this.getPermissionControlName(group.groupId, permission.permissionId); + permission.isEnabled = this.permissionsForm.get(controlName)?.value ?? false; + }); + }); + this.pluginPermissionsService - .updatePluginGroupPermissions({pluginId: this.pluginId, groupPermissions: this.pluginGroupPermissions}) + .updatePluginGroupPermissions({ + pluginId: this.pluginId, + groupPermissions: this.pluginGroupPermissions, + }) .subscribe((data) => { if (data && data.success) { this.hide(true); diff --git a/eform-client/src/app/modules/plugins-management/plugins-management.module.ts b/eform-client/src/app/modules/plugins-management/plugins-management.module.ts index 6d4038eede..da684bd23a 100644 --- a/eform-client/src/app/modules/plugins-management/plugins-management.module.ts +++ b/eform-client/src/app/modules/plugins-management/plugins-management.module.ts @@ -1,6 +1,6 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; -import {FormsModule} from '@angular/forms'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {TranslateModule} from '@ngx-translate/core'; import {EformSharedModule} from '../../common/modules/eform-shared/eform-shared.module'; import {PluginsManagementRouting} from './plugins-management.routing'; @@ -21,21 +21,22 @@ import {MatDialogModule} from '@angular/material/dialog'; import {MatCardModule} from '@angular/material/card'; @NgModule({ - imports: [ - CommonModule, - EformSharedModule, - FormsModule, - TranslateModule, - PluginsManagementRouting, - MatCheckboxModule, - MatButtonModule, - MtxGridModule, - MatSlideToggleModule, - MatTooltipModule, - MatIconModule, - MatDialogModule, - MatCardModule, - ], + imports: [ + CommonModule, + EformSharedModule, + FormsModule, + TranslateModule, + PluginsManagementRouting, + MatCheckboxModule, + MatButtonModule, + MtxGridModule, + MatSlideToggleModule, + MatTooltipModule, + MatIconModule, + MatDialogModule, + MatCardModule, + ReactiveFormsModule, + ], declarations: [ InstalledPluginsPageComponent, InstalledPluginModalComponent,