Merge branch 'dev-pending-09-12-2025' of https://ct.mfsys.com.pk/aConnect/aConnect-UX into mazdak/UX-1570

mazdak/UX-1570
Mazdak Gibran 1 month ago
commit c57bc708d4

@ -13,15 +13,11 @@ export class UserSetupService {
private usersSubject = new BehaviorSubject<SetupUser[]>([]); private usersSubject = new BehaviorSubject<SetupUser[]>([]);
private currentPageSubject = new BehaviorSubject<number>(1); private currentPageSubject = new BehaviorSubject<number>(1);
private totalCountSubject = new BehaviorSubject<number>(0); private totalCountSubject = new BehaviorSubject<number>(0);
private searchTextSubject = new BehaviorSubject<string>('');
private itemsPerPageSubject = new BehaviorSubject<number>(5); private itemsPerPageSubject = new BehaviorSubject<number>(5);
private paginatedUsersSubject = new BehaviorSubject<SetupUser[]>([]);
users$ = this.usersSubject.asObservable(); users$ = this.usersSubject.asObservable();
currentPage$ = this.currentPageSubject.asObservable(); currentPage$ = this.currentPageSubject.asObservable();
totalCount$ = this.totalCountSubject.asObservable(); totalCount$ = this.totalCountSubject.asObservable();
searchText$ = this.searchTextSubject.asObservable();
itemsPerPage$ = this.itemsPerPageSubject.asObservable(); itemsPerPage$ = this.itemsPerPageSubject.asObservable();
paginatedUsers$ = this.paginatedUsersSubject.asObservable();
constructor(private httpURIService: HttpURIService, private uriService: URIService) { } constructor(private httpURIService: HttpURIService, private uriService: URIService) { }
@ -35,7 +31,7 @@ loadUsers(): void {
const users = Array.isArray(res) ? res : res?.data; const users = Array.isArray(res) ? res : res?.data;
this.usersSubject.next(users ?? []); this.usersSubject.next(users ?? []);
this.totalCountSubject.next(users.length); this.totalCountSubject.next(users.length);
this.applyPagination();
}, },
error: (err) => console.error(err) error: (err) => console.error(err)
}); });
@ -43,36 +39,10 @@ loadUsers(): void {
}); });
} }
private applyPagination(): void {
const allUsers = this.usersSubject.value;
const searchText = this.searchTextSubject.value.toLowerCase();
const currentPage = this.currentPageSubject.value;
const itemsPerPage = this.itemsPerPageSubject.value;
let filtered = allUsers.filter(user =>
user.userId.toLowerCase().includes(searchText) ||
user.userFullname.toLowerCase().includes(searchText) ||
user.email.toLowerCase().includes(searchText)
);
const totalCount = filtered.length;
const startIndex = (currentPage - 1) * itemsPerPage;
const paginatedUsers = filtered.slice(startIndex, startIndex + itemsPerPage);
this.paginatedUsersSubject.next(paginatedUsers);
this.totalCountSubject.next(totalCount);
}
setSearchText(searchText: string): void {
this.searchTextSubject.next(searchText);
this.currentPageSubject.next(1);
this.applyPagination();
}
setItemsPerPage(itemsPerPage: number): void { setItemsPerPage(itemsPerPage: number): void {
this.itemsPerPageSubject.next(itemsPerPage); this.itemsPerPageSubject.next(itemsPerPage);
this.currentPageSubject.next(1); this.currentPageSubject.next(1);
this.applyPagination();
} }
nextPage(): void { nextPage(): void {
@ -80,7 +50,6 @@ loadUsers(): void {
const currentPage = this.currentPageSubject.value; const currentPage = this.currentPageSubject.value;
if (currentPage < totalPages) { if (currentPage < totalPages) {
this.currentPageSubject.next(currentPage + 1); this.currentPageSubject.next(currentPage + 1);
this.applyPagination();
} }
} }
@ -88,7 +57,7 @@ loadUsers(): void {
const currentPage = this.currentPageSubject.value; const currentPage = this.currentPageSubject.value;
if (currentPage > 1) { if (currentPage > 1) {
this.currentPageSubject.next(currentPage - 1); this.currentPageSubject.next(currentPage - 1);
this.applyPagination();
} }
} }
@ -96,7 +65,7 @@ loadUsers(): void {
const totalPages = this.getTotalPages(); const totalPages = this.getTotalPages();
if (page > 0 && page <= totalPages) { if (page > 0 && page <= totalPages) {
this.currentPageSubject.next(page); this.currentPageSubject.next(page);
this.applyPagination();
} }
} }

@ -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)
);
}
}

@ -22,7 +22,7 @@
{{'setupUser' | translate}} {{'setupUser' | translate}}
</div> </div>
<div class="card-body"> <div class="card-body">
<form> <form [formGroup]="userForm">
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<div class="col-md-6"> <div class="col-md-6">
<div class="d-flex align-items-center gap-2"> <div class="d-flex align-items-center gap-2">
@ -34,15 +34,15 @@
<div class="d-flex flex-row align-items-stretch"> <div class="d-flex flex-row align-items-stretch">
<input type="text" id="userId" <input type="text" id="userId"
class="form-control" class="form-control"
[(ngModel)]="userId" formControlName="userId"
name="userId" name="userId"
placeholder="{{ 'userID' | translate }}" appNoWhitespaces placeholder="{{ 'userID' | translate }}" appNoWhitespaces
/> />
</div>
<div class="text-danger" *ngIf="userForm.get('userId')?.touched && userForm.get('userId')?.invalid">
{{ 'fieldRequired' | translate }}
</div> </div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div> </div>
</div> </div>
</div> </div>
@ -57,16 +57,19 @@
<input id="userFullname" <input id="userFullname"
class="form-control" class="form-control"
[(ngModel)]="userFullname" formControlName="userFullname"
name="userFullname" name="userFullname"
maxlength="500" maxlength="500"
placeholder="{{ 'userName' | translate }}" appNoWhitespaces placeholder="{{ 'userName' | translate }}" appNoWhitespaces
rows="3" /> rows="3" />
<div class="text-danger" *ngIf="userForm.get('userFullname')?.touched && userForm.get('userFullname')?.invalid">
{{ 'fieldRequired' | translate }}
</div> </div>
</div> </div>
</div>
</div> </div>
</div> </div>
@ -80,19 +83,15 @@
<div class="password-wrapper position-relative w-100"> <div class="password-wrapper position-relative w-100">
<input id="defaultPassword" <input id="defaultPassword"
class="form-control" class="form-control"
[(ngModel)]="defaultPassword" formControlName="defaultPassword"
name="defaultPassword" name="defaultPassword"
placeholder="{{ 'passwordPlaceHolder' | translate }}" appNoWhitespaces/> placeholder="{{ 'passwordPlaceHolder' | translate }}" appNoWhitespaces/>
<!-- <div class="text-danger"> <div class="text-danger" *ngIf="userForm.get('defaultPassword')?.touched && userForm.get('defaultPassword')?.invalid">
<div> {{ 'fieldRequired' | translate }}
{{ 'requiredField' | translate }}
</div>
</div> </div>
<div>
{{ 'expiryBeforeRenewal' | translate }}
</div> -->
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
@ -101,8 +100,8 @@
</div> </div>
<div class="row g-3 mb-3"> <div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end"> <div class="col-md-6 ms-auto text-end">
<button type="button" class="btn btn-primary waves-effect waves-light" (click)="onSubmit()" <button type="button" class="btn btn-primary waves-effect waves-light" (click)="onSubmit()" [disabled]="userForm.invalid"
[hidden]="mode === 'view' && showForm"> >
{{ 'save' | translate }} {{ 'save' | translate }}
</button> </button>
@ -137,7 +136,7 @@
<div class="search-box"> <div class="search-box">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm"
[(ngModel)]="searchText" [(ngModel)]="searchText"
(ngModelChange)="onSearch(searchText)" (ngModelChange)="onSearch($event)"
placeholder="{{ 'search' | translate }}"> placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i> <i class="fas fa-search search-icon"></i>
</div> </div>
@ -162,7 +161,13 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let item of allItems"> <tr *ngFor="
let item of (
(users$ | async) ?? []
| userFilter: searchText
).slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage)
">
<td>{{ item.userId }}</td> <td>{{ item.userId }}</td>
<td>{{ item.userFullname }}</td> <td>{{ item.userFullname }}</td>
@ -177,9 +182,11 @@
<button class="btn btn-danger btn-sm" title="Delete" (click)="onDelete(item.userId)"> <button class="btn btn-danger btn-sm" title="Delete" (click)="onDelete(item.userId)">
<i class="fas fa-trash-alt"></i> <i class="fas fa-trash-alt"></i>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<div class="d-flex justify-content-between align-items-center mt-3"> <div class="d-flex justify-content-between align-items-center mt-3">

@ -6,16 +6,24 @@ import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../../utils/app.constants'; import { pageSizeOptions } from '../../utils/app.constants';
import { SetupUser } from '../../models/user'; import { SetupUser } from '../../models/user';
import { UserSetupService } from '../../services/user-setup.service'; import { UserSetupService } from '../../services/user-setup.service';
import { error } from 'node:console'; import { UserFilterPipe } from '../../shared/pipes/userFilterPipe';
import { FormBuilder, Validators, FormGroup } from '@angular/forms';
@Component({ @Component({
selector: 'app-setup-user', selector: 'app-setup-user',
standalone: true, standalone: true,
imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, NgSelectModule], imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, NgSelectModule, UserFilterPipe],
templateUrl: './setup-user.component.html', templateUrl: './setup-user.component.html',
styleUrl: './setup-user.component.scss' styleUrl: './setup-user.component.scss'
}) })
export class SetupUserComponent implements OnInit { export class SetupUserComponent implements OnInit {
userForm!: FormGroup;
showForm = false;
selectedUserId!: any;
showDeleteModal = false;
userIdToDelete: any = null;
allItems: SetupUser[] = []; allItems: SetupUser[] = [];
currentPage: number = 1; currentPage: number = 1;
pageSizeOptions = pageSizeOptions pageSizeOptions = pageSizeOptions
@ -23,18 +31,16 @@ export class SetupUserComponent implements OnInit {
searchText: string = ''; searchText: string = '';
renewalDataExpanded: boolean = true; renewalDataExpanded: boolean = true;
totalCount: number = 0; totalCount: number = 0;
userId!: string;
userFullname!: string;
defaultPassword!: string;
mode: 'edit' | 'view' = 'view'; mode: 'edit' | 'view' = 'view';
showForm = false;
selectedUserId!: any;
user: any;
constructor(private userService: UserSetupService){}
constructor(private userService: UserSetupService, private fb: FormBuilder){}
get users$(){
return this.userService.users$;
}
onSearch(value: string): void { onSearch(value: string): void {
this.userService.setSearchText(value); this.searchText = value;
} }
onPageSizeChange(pageSize: number): void { onPageSizeChange(pageSize: number): void {
@ -49,35 +55,29 @@ export class SetupUserComponent implements OnInit {
this.userService.previousPage(); this.userService.previousPage();
} }
totalPages() {
return 1;
}
toggleCard(arg0: string) {
throw new Error('Method not implemented.');
}
getTotalPages(): number { getTotalPages(): number {
return this.userService.getTotalPages(); return this.userService.getTotalPages();
} }
onSubmit() { onSubmit() {
if(!this.userId || !this.userFullname|| !this.defaultPassword){ if (this.userForm.invalid) {
console.warn('Form incomplete'); this.userForm.markAllAsTouched();
return return;
} }
const newUser : SetupUser = { const newUser : SetupUser = {
userId: this.userId, userId: this.userForm.value.userId,
userFullname: this.userFullname, userFullname: this.userForm.value.userFullname,
email: `${this.userId}@dummy.com` ,// temporary placeholder email: `${this.userForm.value.userId}@dummy.com`,
role: 'ADMIN', role: 'ADMIN',
defaultPassword: this.defaultPassword defaultPassword: this.userForm.value.defaultPassword
} }
this.userService.addUser(newUser).subscribe({ this.userService.addUser(newUser).subscribe({
next: () => { next: () => {
this.userService.loadUsers(); this.userService.loadUsers();
this.userId = ''; this.userService.loadUsers();
this.userFullname = ''; this.userForm.reset();
this.defaultPassword = ''; this.mode = 'edit';
}, },
error: (err: any) => console.error(err) error: (err: any) => console.error(err)
}); });
@ -90,9 +90,11 @@ export class SetupUserComponent implements OnInit {
this.showForm = true; this.showForm = true;
this.selectedUserId = userId; this.selectedUserId = userId;
this.userService.getUserById(userId).subscribe((user: any)=>{ this.userService.getUserById(userId).subscribe((user: any)=>{
this.userId = user.userId; this.userForm.patchValue({
this.userFullname = user.userFullname; userId : user.userId,
this.defaultPassword = ''; userFullname : user.userFullname,
defaultPassword : '',
})
}) })
} }
@ -100,10 +102,7 @@ export class SetupUserComponent implements OnInit {
this.userService.deleteUser(userId).subscribe({ this.userService.deleteUser(userId).subscribe({
next: (res: any) => { next: (res: any) => {
this.userService.loadUsers(); this.userService.loadUsers();
this.showForm = false; this.userForm.reset()
this.userId = '';
this.userFullname = '';
this.defaultPassword = '';
this.selectedUserId = null; this.selectedUserId = null;
}, },
error: (err:any) =>{ error: (err:any) =>{
@ -115,18 +114,23 @@ export class SetupUserComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.userForm = this.fb.group({
userId: ['', [Validators.required]],
userFullname: ['', [Validators.required, Validators.maxLength(500)]],
defaultPassword: ['', Validators.required]
});
this.userService.loadUsers(); this.userService.loadUsers();
this.userService.paginatedUsers$.subscribe((users: SetupUser[]) => this.allItems = users);
this.userService.users$.subscribe((users: SetupUser[]) => {
this.allItems = users;
});
this.userService.currentPage$.subscribe((page: number) => { this.userService.currentPage$.subscribe((page: number) => {
this.currentPage = page; this.currentPage = page;
}); });
this.userService.totalCount$.subscribe((count: number) => { this.userService.totalCount$.subscribe((count: number) => {
this.totalCount = count; this.totalCount = count;
}); });
this.userService.searchText$.subscribe((text: string) => {
this.searchText = text;
});
this.userService.itemsPerPage$.subscribe((size: number) => { this.userService.itemsPerPage$.subscribe((size: number) => {
this.itemsPerPage = size; this.itemsPerPage = size;
}); });

@ -130,8 +130,8 @@ export class UserPermissionsComponent {
const params = new HttpParams().set('userId', this.permission.get('userCode')?.value); const params = new HttpParams().set('userId', this.permission.get('userCode')?.value);
this.httpService.requestGET(URIKey.USER_GET_PERMISSIONS, params).subscribe((response: any) => { this.httpService.requestGET(URIKey.USER_GET_PERMISSIONS, params).subscribe((response: any) => {
if (!(response instanceof HttpErrorResponse)) { if (!(response instanceof HttpErrorResponse)) {
if (response.permission) { if (response.permissions) {
this.updatePermissions(JSON.parse(response.permission), this.permissions); this.updatePermissions(JSON.parse(response.permissions), this.permissions);
} }
else { else {
this.defaultPermissions().subscribe((data: PermissionNode[]) => { this.defaultPermissions().subscribe((data: PermissionNode[]) => {
@ -144,17 +144,18 @@ export class UserPermissionsComponent {
savePermissions() { savePermissions() {
let payload = { let payload = {
porOrgacode: this.credentialService.getPorOrgacode(), // porOrgacode: this.credentialService.getPorOrgacode(),
userId: this.permission.get('userCode')?.value, userId: this.permission.get('userCode')?.value,
permission: JSON.stringify(this.permissions) permissions: JSON.stringify(this.permissions)
} }
// this.httpService.requestPATCH(URIKey.USER_SAVE_PERMISSION, payload).subscribe((response: any) => { this.httpService.requestPUT(URIKey.USER_SAVE_PERMISSION, payload).subscribe((response: any) => {
// if (!(response instanceof HttpErrorResponse)) { if (!(response instanceof HttpErrorResponse)) {
// this.i18nService.success(SuccessMessages.SAVED_SUCESSFULLY, []); this.i18nService.success(SuccessMessages.SAVED_SUCESSFULLY, []);
// this.permission.reset(); this.permission.reset();
// this.showPermissions = false; this.permission.get('userCode')?.setValue("");
// } this.showPermissions = false;
// }) }
})
} }
updatePermissions(savedPermissions: PermissionNode[], existingPermissions: PermissionNode[]): void { updatePermissions(savedPermissions: PermissionNode[], existingPermissions: PermissionNode[]): void {

@ -227,6 +227,7 @@
"UNAUTHORIZED_REQUEST": "طلب غير مصرح به", "UNAUTHORIZED_REQUEST": "طلب غير مصرح به",
"edit": "يحرر", "edit": "يحرر",
"delete": "يمسح", "delete": "يمسح",
"deleteUser": "حذف حساب المستخدم",
"permissionManagement": "إدارة الأذونات", "permissionManagement": "إدارة الأذونات",
"userCode": "مستخدم", "userCode": "مستخدم",
"choose" : "يختار" "choose" : "يختار"

@ -226,6 +226,7 @@
"UNAUTHORIZED_REQUEST": "Unauthorized Request", "UNAUTHORIZED_REQUEST": "Unauthorized Request",
"edit": "Edit", "edit": "Edit",
"delete": "Delete", "delete": "Delete",
"deleteUser": "Delete User",
"permissionManagement": "Permission Managment", "permissionManagement": "Permission Managment",
"userCode": "User", "userCode": "User",
"choose" : "Choose" "choose" : "Choose"

Loading…
Cancel
Save