Integrate CRUD Apis for Setup User
#15
Closed
mazdak.gibran
wants to merge 22 commits from dev-pending-09-12-2025 into mazdak/UX-1572
@ -0,0 +1,22 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
import { SetupUser } from '../../models/user';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'userFilter',
|
||||||
|
standalone: true
|
||||||
|
})
|
||||||
|
export class UserFilterPipe implements PipeTransform {
|
||||||
|
transform(users: SetupUser[], searchText: string): SetupUser[] {
|
||||||
|
if (!users || !searchText.trim()) {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = searchText.toLowerCase();
|
||||||
|
|
||||||
|
return users.filter(user =>
|
||||||
|
user.userId.toLowerCase().includes(search) ||
|
||||||
|
user.userFullname.toLowerCase().includes(search) ||
|
||||||
|
user.email.toLowerCase().includes(search)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,26 +1,88 @@
|
|||||||
import { Component, ViewChild } from '@angular/core';
|
import { Component, ViewChild, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators, AbstractControl, ValidationErrors, ReactiveFormsModule } from '@angular/forms';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
|
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
|
||||||
|
import { URIKey } from '../../utils/uri-enums';
|
||||||
|
import { HttpURIService } from '../../app.http.uri.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-reset-password',
|
selector: 'app-reset-password',
|
||||||
imports: [TranslateModule, PasswordHideShowComponent],
|
imports: [TranslateModule, PasswordHideShowComponent, CommonModule, ReactiveFormsModule],
|
||||||
templateUrl: './reset-password.component.html',
|
templateUrl: './reset-password.component.html',
|
||||||
styleUrl: './reset-password.component.scss'
|
styleUrl: './reset-password.component.scss'
|
||||||
})
|
})
|
||||||
export class ResetPasswordComponent {
|
export class ResetPasswordComponent implements OnInit{
|
||||||
passwordType1: string = 'password';
|
resetPasswordForm!: FormGroup
|
||||||
passwordType2: string = 'password';
|
passwordType1: string = 'password';
|
||||||
|
passwordType2: string = 'password';
|
||||||
|
|
||||||
|
@ViewChild('psh1') passwordHideShow1?: PasswordHideShowComponent;
|
||||||
|
@ViewChild('psh2') passwordHideShow2?: PasswordHideShowComponent;
|
||||||
|
|
||||||
@ViewChild('psh1') passwordHideShow1 ?: PasswordHideShowComponent;
|
constructor(private fb: FormBuilder, private httpURIService: HttpURIService){}
|
||||||
@ViewChild('psh2') passwordHideShow2 ?: PasswordHideShowComponent;
|
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.resetPasswordForm = this.fb.group({
|
||||||
|
userId: ['', Validators.required],
|
||||||
|
newPassword: ['', [Validators.required, Validators.minLength(6)]],
|
||||||
|
confirmPassword: ['', [Validators.required, Validators.minLength(6)]]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validators: this.passwordMatchValidator
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this.resetPasswordForm.get('newPassword')?.valueChanges.subscribe(()=>{
|
||||||
|
this.resetPasswordForm.get('confirmPassword')?.updateValueAndValidity();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
togglePasswordType1() {
|
togglePasswordType1() {
|
||||||
this.passwordType1 = this.passwordHideShow1?.showPassword ? 'password' : 'text';
|
this.passwordType1 = this.passwordHideShow1?.showPassword ? 'password' : 'text';
|
||||||
}
|
}
|
||||||
togglePasswordType2() {
|
togglePasswordType2() {
|
||||||
this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text';
|
this.passwordType2 = this.passwordHideShow2?.showPassword ? 'password' : 'text';
|
||||||
}
|
}
|
||||||
|
passwordMatchValidator(group: AbstractControl): ValidationErrors | null {
|
||||||
|
const newPassword = group.get('newPassword')?.value;
|
||||||
|
const confirmPassword = group.get('confirmPassword')?.value;
|
||||||
|
|
||||||
|
return newPassword === confirmPassword ? null : { passwordMismatch: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
get newPasswordError() {
|
||||||
|
const control = this.resetPasswordForm.get('newPassword');
|
||||||
|
if (!control || !control.touched) return null;
|
||||||
|
|
||||||
|
if (control.hasError('required')) return 'fieldRequired';
|
||||||
|
if (control.hasError('minlength')) return 'passwordTooShort';
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
get confirmPasswordError() {
|
||||||
|
const control = this.resetPasswordForm.get('confirmPassword');
|
||||||
|
if (!control || !control.touched) return null;
|
||||||
|
|
||||||
|
if (control.hasError('required')) return 'fieldRequired';
|
||||||
|
if (control.hasError('minlength')) return 'passwordTooShort';
|
||||||
|
if (this.resetPasswordForm.hasError('passwordMismatch')) return 'passwordsDoNotMatch';
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
if (this.resetPasswordForm.invalid) return;
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
userId: this.resetPasswordForm.value.userId,
|
||||||
|
newPassword: this.resetPasswordForm.value.newPassword
|
||||||
|
};
|
||||||
|
this.httpURIService.requestPOST(URIKey.RESET_PASSWORD_URI, payload)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1 +1,95 @@
|
|||||||
<p>user-permissions works!</p>
|
<div class="page-content">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row mt-2 mb-2">
|
||||||
|
<div class="col-lg-6">
|
||||||
|
<div class="col-6">
|
||||||
|
<div class="mt-0 mb-2 d-sm-flex align-items-center justify-content-between font-edit-13">
|
||||||
|
<h5 class="text-muted fw-bold mt-3">{{'permissionManagement' | translate}}</h5>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<form [formGroup]="permission">
|
||||||
|
|
||||||
|
<div class="form-group row mb-2">
|
||||||
|
<label for="userCode" class="form-label font-edit-13">{{ 'userCode' |
|
||||||
|
translate
|
||||||
|
}}</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<select class="form-select" id="userCode" formControlName="userCode" (change)="onUserChange()">
|
||||||
|
<option selected disabled value="">{{ 'choose' |
|
||||||
|
translate }}
|
||||||
|
</option>
|
||||||
|
<option *ngFor="let user of users" [value]="user.userId">
|
||||||
|
{{user.userName}}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card" *ngIf="showPermissions">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{{ 'permissions' | translate}}</h4>
|
||||||
|
<hr>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let node of permissions">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
[checked]="node.checked"
|
||||||
|
(change)="toggleNode(node, $event)">
|
||||||
|
{{node.name | translate}}
|
||||||
|
<span (click)="toggleExpand(node)">
|
||||||
|
<span *ngIf="node.children && node.children.length">
|
||||||
|
<span *ngIf="node.expanded"><i class="bx bx-chevron-down"></i></span>
|
||||||
|
<span *ngIf="!node.expanded"><i class="bx bx-chevron-right"></i></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<ul *ngIf="node.children && node.children.length > 0 && node.expanded">
|
||||||
|
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.children }"></ng-container>
|
||||||
|
</ul>
|
||||||
|
<ul *ngIf="node.buttons && node.buttons.length > 0 && node.expanded">
|
||||||
|
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.buttons }"></ng-container>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<ng-template #treeTemplate let-nodes>
|
||||||
|
<ul>
|
||||||
|
<li *ngFor="let node of nodes">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
[checked]="node.checked"
|
||||||
|
(change)="toggleNode(node, $event)">
|
||||||
|
{{node.name | translate}}
|
||||||
|
<span (click)="toggleExpand(node)">
|
||||||
|
<span *ngIf="node.children && node.children.length">
|
||||||
|
<span *ngIf="node.expanded"><i class="bx bx-chevron-down"></i></span>
|
||||||
|
<span *ngIf="!node.expanded"><i class="bx bx-chevron-right"></i></span>
|
||||||
|
</span>
|
||||||
|
<span *ngIf="node.buttons && node.buttons.length">
|
||||||
|
<span *ngIf="node.expanded"><i class="bx bx-chevron-down"></i></span>
|
||||||
|
<span *ngIf="!node.expanded"><i class="bx bx-chevron-right"></i></span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<ul *ngIf="node.children && node.children.length > 0 && node.expanded">
|
||||||
|
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.children }"></ng-container>
|
||||||
|
</ul>
|
||||||
|
<ul *ngIf="node.buttons && node.buttons.length > 0 && node.expanded">
|
||||||
|
<ng-container *ngTemplateOutlet="treeTemplate; context: { $implicit: node.buttons }"></ng-container>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</ng-template>
|
||||||
|
<div class="row float-end me-4 mb-3 mt-2 font-edit-13">
|
||||||
|
<button type="button" class="btn btn-primary font-edit-13 px-3 btn-sm" (click)="savePermissions()">{{ 'save' | translate }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
li {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 10px; /* Reduce overall left padding */
|
||||||
|
}
|
||||||
|
|
||||||
|
ul ul {
|
||||||
|
padding-left: 5px; /* Reduce space for nested items */
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 3px; /* Add slight vertical spacing between items */
|
||||||
|
}
|
||||||
@ -1,11 +1,180 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||||
|
import { PermissionNode } from '../utils/app.constants';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { CredentialService } from '../services/credential.service';
|
||||||
|
import { I18NService } from '../services/i18n.service';
|
||||||
|
import { HttpURIService } from '../app.http.uri.service';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { URIKey } from '../utils/uri-enums';
|
||||||
|
import { HttpErrorResponse, HttpParams } from '@angular/common/http';
|
||||||
|
import { FormConstants, HiddenValues, SuccessMessages } from '../utils/enums';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-permissions',
|
selector: 'app-user-permissions',
|
||||||
imports: [],
|
imports: [TranslateModule, ReactiveFormsModule, CommonModule],
|
||||||
templateUrl: './user-permissions.component.html',
|
templateUrl: './user-permissions.component.html',
|
||||||
styleUrl: './user-permissions.component.scss'
|
styleUrl: './user-permissions.component.scss'
|
||||||
})
|
})
|
||||||
export class UserPermissionsComponent {
|
export class UserPermissionsComponent {
|
||||||
|
dropdownOptions: { [key: string]: any[] } = {};
|
||||||
|
users: any[] = [];
|
||||||
|
permission: FormGroup;
|
||||||
|
showPermissions = false;
|
||||||
|
permissions: PermissionNode[] = [];
|
||||||
|
|
||||||
|
constructor(private credentialService: CredentialService, private fb: FormBuilder, private httpService: HttpURIService, private i18nService: I18NService) {
|
||||||
|
this.permission = this.fb.group({
|
||||||
|
allocation: [''],
|
||||||
|
userCode: [''],
|
||||||
|
userRole: [''],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.defaultPermissions().subscribe((data: PermissionNode[]) => {
|
||||||
|
this.permissions = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(){
|
||||||
|
this.getAllUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultPermissions(): Observable<PermissionNode[]> {
|
||||||
|
return this.httpService.requestGET<PermissionNode[]>('assets/data/sideMenu.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleNode(node: PermissionNode, event: Event): void {
|
||||||
|
node.checked = (event.target as HTMLInputElement).checked;
|
||||||
|
if (node.children) {
|
||||||
|
this.toggleChildren(node.children, node.checked);
|
||||||
|
}
|
||||||
|
if (node.buttons) {
|
||||||
|
this.toggleButtons(node.buttons, node.checked);
|
||||||
|
}
|
||||||
|
this.updateParentState(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleChildren(children: PermissionNode[], isChecked: boolean): void {
|
||||||
|
children.forEach(child => {
|
||||||
|
child.checked = isChecked;
|
||||||
|
if (child.children) {
|
||||||
|
this.toggleChildren(child.children, isChecked);
|
||||||
|
}
|
||||||
|
if (child.buttons) {
|
||||||
|
this.toggleButtons(child.buttons, child.checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleButtons(buttons: PermissionNode[], isChecked: boolean): void {
|
||||||
|
buttons.forEach(button => {
|
||||||
|
button.checked = isChecked;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateParentState(node: PermissionNode): void {
|
||||||
|
const parent = this.findParent(node, this.permissions);
|
||||||
|
if (parent) {
|
||||||
|
parent.checked = parent.children?.some(child => child.checked) || false;
|
||||||
|
this.updateParentState(parent); // Recursively update ancestors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findParent(target: PermissionNode, nodes: PermissionNode[]): PermissionNode | null {
|
||||||
|
for (let node of nodes) {
|
||||||
|
if (node.children?.includes(target)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
const parent = this.findParent(target, node.children);
|
||||||
|
if (parent) return parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleExpand(node: PermissionNode): void {
|
||||||
|
node.expanded = !node.expanded;
|
||||||
|
}
|
||||||
|
populateDropdowns(data: any) {
|
||||||
|
data.forEach((control: any) => {
|
||||||
|
this.dropdownOptions[control.controlId] = control.options;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onAllocationChange(event: Event): void {
|
||||||
|
this.showPermissions = false;
|
||||||
|
const selectedValue = (event.target as HTMLInputElement).value;
|
||||||
|
if (selectedValue === "R") {
|
||||||
|
this.permission.get('userCode')?.reset();
|
||||||
|
}
|
||||||
|
else if (selectedValue === "U") {
|
||||||
|
this.permission.get('userRole')?.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllUsers() {
|
||||||
|
this.httpService.requestGET<any[]>(URIKey.GET_ALL_USER_URI).subscribe((response) => {
|
||||||
|
if (!(response instanceof HttpErrorResponse)) {
|
||||||
|
this.users = response.map(item => ({
|
||||||
|
userName: item.userFullname,
|
||||||
|
userId: item.userId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onUserChange() {
|
||||||
|
this.showPermissions = true;
|
||||||
|
const params = new HttpParams().set('userId', this.permission.get('userCode')?.value);
|
||||||
|
this.httpService.requestGET(URIKey.USER_GET_PERMISSIONS, params).subscribe((response: any) => {
|
||||||
|
if (!(response instanceof HttpErrorResponse)) {
|
||||||
|
if (response.permissions) {
|
||||||
|
this.updatePermissions(JSON.parse(response.permissions), this.permissions);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.defaultPermissions().subscribe((data: PermissionNode[]) => {
|
||||||
|
this.permissions = data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
savePermissions() {
|
||||||
|
let payload = {
|
||||||
|
// porOrgacode: this.credentialService.getPorOrgacode(),
|
||||||
|
userId: this.permission.get('userCode')?.value,
|
||||||
|
permissions: JSON.stringify(this.permissions)
|
||||||
|
}
|
||||||
|
this.httpService.requestPUT(URIKey.USER_SAVE_PERMISSION, payload).subscribe((response: any) => {
|
||||||
|
if (!(response instanceof HttpErrorResponse)) {
|
||||||
|
this.i18nService.success(SuccessMessages.SAVED_SUCESSFULLY, []);
|
||||||
|
this.permission.reset();
|
||||||
|
this.permission.get('userCode')?.setValue("");
|
||||||
|
this.showPermissions = false;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePermissions(savedPermissions: PermissionNode[], existingPermissions: PermissionNode[]): void {
|
||||||
|
for (const existingNode of existingPermissions) {
|
||||||
|
const savedNode = savedPermissions.find(node => node.name === existingNode.name);
|
||||||
|
|
||||||
|
if (savedNode) {
|
||||||
|
// Update state from saved node
|
||||||
|
existingNode.checked = savedNode.checked;
|
||||||
|
//existingNode.expanded = savedNode.expanded;
|
||||||
|
|
||||||
|
// Recursively update children if they exist
|
||||||
|
if (existingNode.children) {
|
||||||
|
this.updatePermissions(savedNode.children || [], existingNode.children);
|
||||||
|
}
|
||||||
|
if (existingNode.buttons) {
|
||||||
|
this.updatePermissions(savedNode.buttons || [], existingNode.buttons);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue