From 16165456002eda149da7f92bac4d0deec54461fd Mon Sep 17 00:00:00 2001 From: Mazdak Gibran <141390141+mazdakgibran@users.noreply.github.com> Date: Mon, 6 Apr 2026 15:57:26 +0500 Subject: [PATCH] Refactor menu permissions UI & endpoint handling --- src/app/menu/menu.component.html | 31 ++---- src/app/menu/menu.component.ts | 165 +++++++++++++++++++++---------- src/app/utils/uri-enums.ts | 3 +- src/assets/data/app.uri.json | 5 + src/assets/data/sideMenu.json | 4 - 5 files changed, 126 insertions(+), 82 deletions(-) diff --git a/src/app/menu/menu.component.html b/src/app/menu/menu.component.html index acda97c..70442ab 100644 --- a/src/app/menu/menu.component.html +++ b/src/app/menu/menu.component.html @@ -45,33 +45,14 @@ - - - - {{ "one" | translate }} - {{ "ACCOUNT_TO_ACCOUNT" | translate }} + + + {{ i + 1 }} + {{ item.name | translate }} + [(ngModel)]="item.checked" /> - - {{ "two" | translate }} - {{ "GL_TO_GL" | translate }} - - - - {{ "three" | translate }} - {{ "ACCOUNT_TO_GL" | translate }} - - - - - {{ "four" | translate }} - {{ "GL_TO_ACCOUNT" | translate }} - - - - - + diff --git a/src/app/menu/menu.component.ts b/src/app/menu/menu.component.ts index e9d7dce..c63dc1f 100644 --- a/src/app/menu/menu.component.ts +++ b/src/app/menu/menu.component.ts @@ -9,7 +9,7 @@ import { URIKey } from '../utils/uri-enums'; import { HttpErrorResponse, HttpParams } from '@angular/common/http'; import { CommonModule } from '@angular/common'; import { NgSelectModule } from '@ng-select/ng-select'; -import { Observable } from 'rxjs'; +import { filter, Observable, take } from 'rxjs'; import { SuccessMessages } from '../utils/enums'; import { URIService } from '../app.uri'; @@ -24,6 +24,13 @@ export class MenuComponent { permission: FormGroup; showPermissions = false; permissions: PermissionNode[] = []; + saving = false; + menuItems = [ + { name: 'accountToAccount', endpoint: URIKey.ACCOUNT_TO_ACCOUNT, checked: true }, + { name: 'glToGl', endpoint: URIKey.GL_TO_GL, checked: false }, + { name: 'accountToGl', endpoint: URIKey.ACCOUNT_TO_GL, checked: false }, + { name: 'glToAccount', endpoint: URIKey.GL_TO_ACCOUNT, checked: false } +]; constructor( private credentialService: CredentialService, @@ -32,15 +39,10 @@ export class MenuComponent { private i18nService: I18NService, private uriService: URIService ) { - this.permission = this.fb.group({ - allocation: [''], - userCode: [null], - userRole: [''], - }); - - this.defaultPermissions().subscribe((data: PermissionNode[]) => { - this.permissions = data; - }); + this.permission = this.fb.group({ userCode: [null] }); + this.defaultPermissions().subscribe((data: PermissionNode[]) => { + this.permissions = data; // needed for savePermissions + }); } ngOnInit() { @@ -52,7 +54,6 @@ export class MenuComponent { getAllUsers() { this.httpService.requestGET(URIKey.GET_ALL_USER_URI).subscribe((response) => { - console.log(URIKey.GET_ALL_USER_URI); if (!(response instanceof HttpErrorResponse)) { this.users = response.map(item => ({ userName: item.userFullname, @@ -61,56 +62,116 @@ export class MenuComponent { } }); } - - onUserChange() { +// mapEndpointsToPermissions(endpoints: string[]): PermissionNode[] { +// return [{ +// name: 'menu', +// checked: false, +// expanded: false, +// children: [ +// { name: 'accountToAccount', checked: endpoints.includes(this.uriService.getURIForRequest(URIKey.ACCOUNT_TO_ACCOUNT)), expanded: false }, +// { name: 'glToGl', checked: endpoints.includes(this.uriService.getURIForRequest(URIKey.GL_TO_GL)), expanded: false }, +// { name: 'accountToGl', checked: endpoints.includes(this.uriService.getURIForRequest(URIKey.ACCOUNT_TO_GL)), expanded: false }, +// { name: 'glToAccount', checked: endpoints.includes(this.uriService.getURIForRequest(URIKey.GL_TO_ACCOUNT)), expanded: false } +// ] +// }]; +// } +onUserChange() { this.showPermissions = true; - this.defaultPermissions().subscribe((data: PermissionNode[]) => { - this.permissions = data; - const params = new HttpParams().set('userId', this.permission.get('userCode')?.value); - this.httpService.requestGET(URIKey.USER_GET_PERMISSIONS, params).subscribe((response: any) => { + const userId = this.permission.get('userCode')?.value; + const params = new HttpParams().set('userId', userId); + + this.httpService.requestGET(URIKey.TRANSACTION_PERMISSIONS, params) + .subscribe((response: any) => { if (!(response instanceof HttpErrorResponse)) { - // Step 4 - reverse map endpoints back to booleans - const menuNode = this.permissions.find(x => x.name === 'menu'); - if (menuNode && response.transactionEndpoints) { - menuNode.accountToAccount = response.transactionEndpoints.includes(this.uriService.getURIForRequest(URIKey.ACCOUNT_TO_ACCOUNT)); - menuNode.glToGl = response.transactionEndpoints.includes(this.uriService.getURIForRequest(URIKey.GL_TO_GL)); - menuNode.accountToGl = response.transactionEndpoints.includes(this.uriService.getURIForRequest(URIKey.ACCOUNT_TO_GL)); - menuNode.glToAccount = response.transactionEndpoints.includes(this.uriService.getURIForRequest(URIKey.GL_TO_ACCOUNT)); - } + + const allowedEndpoints: string[] = (response as any[]) + .filter(p => p.allowed) + .map(p => this.normalizeUrl(p.transactionEndpoint)); // ← normalize + + this.uriService.canSubscribe.pipe( + filter(ready => ready === true), + take(1) + ).subscribe(() => { + this.menuItems.forEach(item => { + const resolvedEndpoint = this.normalizeUrl( + this.uriService.getURIForRequest(item.endpoint) // ← normalize + ); + item.checked = allowedEndpoints.includes(resolvedEndpoint); + }); + }); + + } else { + this.menuItems.forEach(item => item.checked = false); } }); - }); } +// Add this helper method to the class +private normalizeUrl(url: string): string { + return url?.trim().toLowerCase().replace(/\/+$/, ''); // trim, lowercase, remove trailing slash +} + + +// savePermissions() { +// const selectedUser = this.permission.get('userCode')?.value; +// const menuNode = this.permissions.find(x => x.name === 'menu'); +// if (!menuNode) return; + +// const nameToURIKey: { [key: string]: URIKey } = { +// accountToAccount: URIKey.ACCOUNT_TO_ACCOUNT, +// glToGl: URIKey.GL_TO_GL, +// accountToGl: URIKey.ACCOUNT_TO_GL, +// glToAccount: URIKey.GL_TO_ACCOUNT +// }; + +// const transactionEndpoints: string[] = (menuNode.children || []) +// .filter(c => c.checked) +// .map(c => this.uriService.getURIForRequest(nameToURIKey[c.name])); + +// const payload = { userId: selectedUser, transactionEndpoints, permissions: JSON.stringify(this.permissions) }; + +// this.httpService.requestPOST(URIKey.TRANSACTION_PERMISSIONS_ASSIGN, payload).subscribe((response: any) => { +// if (!(response instanceof HttpErrorResponse)) { +// this.i18nService.success(SuccessMessages.SAVED_SUCCESSFULLY, []); +// this.permission.get('userCode')?.setValue(selectedUser); +// this.onUserChange(); +// } +// }); +// } + savePermissions() { - // Step 3 - extract menu node and build endpoints array - const menuNode = this.permissions.find(x => x.name === 'menu'); - if (!menuNode) return; - - const transactionEndpoints: string[] = []; - - // Step 2 - map booleans to endpoint strings - if (menuNode.accountToAccount) transactionEndpoints.push(this.uriService.getURIForRequest(URIKey.ACCOUNT_TO_ACCOUNT)); - if (menuNode.glToGl) transactionEndpoints.push(this.uriService.getURIForRequest(URIKey.GL_TO_GL)); - if (menuNode.accountToGl) transactionEndpoints.push(this.uriService.getURIForRequest(URIKey.ACCOUNT_TO_GL)); - if (menuNode.glToAccount) transactionEndpoints.push(this.uriService.getURIForRequest(URIKey.GL_TO_ACCOUNT)); - - // Step 1 - new payload structure - const payload = { - userId: this.permission.get('userCode')?.value, - transactionEndpoints - }; - - this.httpService.requestPUT(URIKey.TRANSACTION_PERMISSIONS_ASSIGN, payload).subscribe((response: any) => { - if (!(response instanceof HttpErrorResponse)) { - this.i18nService.success(SuccessMessages.SAVED_SUCCESSFULLY, []); - this.permission.get('userCode')?.setValue(null); - this.showPermissions = false; - } - }); + if (this.saving) return; // ← guard against double clicks + this.saving = true; -} + const selectedUser = this.permission.get('userCode')?.value; + + this.uriService.canSubscribe.pipe( + filter(ready => ready === true), + take(1) + ).subscribe(() => { + const transactionEndpoints: string[] = this.menuItems + .filter(item => item.checked) + .map(item => this.uriService.getURIForRequest(item.endpoint)); + + const payload = { userId: selectedUser, transactionEndpoints }; + this.httpService.requestPOST(URIKey.TRANSACTION_PERMISSIONS_ASSIGN, payload) + .subscribe({ + next: (response: any) => { + if (!(response instanceof HttpErrorResponse)) { + this.i18nService.success(SuccessMessages.SAVED_SUCCESSFULLY, []); + this.onUserChange(); // this triggers one GET — expected + } + }, + complete: () => { + this.saving = false; // ← reset after done + }, + error: () => { + this.saving = false; + } + }); + }); +} updatePermissions(savedPermissions: PermissionNode[], existingPermissions: PermissionNode[]): void { for (const existingNode of existingPermissions) { const savedNode = savedPermissions.find(node => node.name === existingNode.name); diff --git a/src/app/utils/uri-enums.ts b/src/app/utils/uri-enums.ts index f1eb15d..c1e3236 100644 --- a/src/app/utils/uri-enums.ts +++ b/src/app/utils/uri-enums.ts @@ -20,6 +20,7 @@ export enum URIKey { GL_TO_GL = "GL_TO_GL", ACCOUNT_TO_GL = "ACCOUNT_TO_GL", GL_TO_ACCOUNT = "GL_TO_ACCOUNT", - TRANSACTION_PERMISSIONS_ASSIGN = "TRANSACTION_PERMISSIONS_ASSIGN" + TRANSACTION_PERMISSIONS_ASSIGN = "TRANSACTION_PERMISSIONS_ASSIGN", + TRANSACTION_PERMISSIONS = "TRANSACTION_PERMISSIONS" } \ No newline at end of file diff --git a/src/assets/data/app.uri.json b/src/assets/data/app.uri.json index 4cd7100..9abba5b 100644 --- a/src/assets/data/app.uri.json +++ b/src/assets/data/app.uri.json @@ -111,6 +111,11 @@ "Id": "ENTITY_GL_TO_ACCOUNT", "URI": "/transactions/gl-account", "UUID": "GL_TO_ACCOUNT" + }, + { + "Id": "ENTITY_TRANSACTION_PERMISSIONS", + "URI": "/transaction-permissions/{userId}", + "UUID": "TRANSACTION_PERMISSIONS" } ] } diff --git a/src/assets/data/sideMenu.json b/src/assets/data/sideMenu.json index 7929f92..1e64e9b 100644 --- a/src/assets/data/sideMenu.json +++ b/src/assets/data/sideMenu.json @@ -75,10 +75,6 @@ "route": "/home/menu", "checked": false, "expanded": false, - "accountToAccount": false, - "glToGl": false, - "accountToGl": false, - "glToAccount": false, "children": [] }, {