Compare commits

...

52 Commits

Author SHA1 Message Date
atif118-mfsys 180ecd38a1 Merge branch 'dev-pending-09-12-2025' into aconnect-UX/1715 8 minutes ago
atif118-mfsys dee23848ea integrated get all users API to provide options for user dropdown in permission management 10 minutes ago
Naeem Ullah 4d090dfff7 Merge pull request 'mazdak/UX-1709' (#17) from mazdak/UX-1709 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/17
4 hours ago
Mazdak Gibran 80a1e62411 onDelete form reset
onDelete form reset
4 hours ago
Mazdak Gibran 207e55cfe3 Merge branch 'dev-pending-09-12-2025' of https://ct.mfsys.com.pk/aConnect/aConnect-UX into mazdak/UX-1709 5 hours ago
Naeem Ullah c93f231dfd Merge pull request 'aconnect-UX/1653' (#14) from aconnect-UX/1653 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/14
6 hours ago
Mazdak Gibran 1976648c08 Integrate deleteUser Api
Integrate deleteUser Api
24 hours ago
Mazdak Gibran d703494c3a Implemented getUser Api
implemented getUser Api to show the view and edit of the selected Id in the form. later removed the edit function
1 day ago
Mazdak Gibran 2b98437f15 change password for first login fixed
change password for first login fixed
2 days ago
Mazdak Gibran bc18bcf946 Setup User API Integration
createUser and getAllUsers API Integration
2 days ago
atif118-mfsys 9ccda5bc86 applied optional chaining to avoid errors. 6 days ago
atif118-mfsys db1e57a008 completed design for permission management screen. 6 days ago
Naeem Ullah 6f2843b805 Merge pull request 'GBRSP/UX-1599' (#13) from GBRSP/UX-1599 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/13
6 days ago
atif118-mfsys 966f0d59bc fixed and revamped first time change password screen logic 7 days ago
atif118-mfsys 72d5d08828 Merge branch 'dev-pending-09-12-2025' into GBRSP/UX-1599 7 days ago
atif118-mfsys 1c0b19e33c added activity guard and added some translations 7 days ago
atif118-mfsys 8340f14035 working on setting up http services as per standard 1 week ago
Naeem Ullah a06a2b6004 Merge pull request 'Change Password Screen view for first time login' (#12) from mazdak/UX-1461 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/12
1 week ago
Mazdak Gibran 7fa0ee6a76 Change Password Screen view for first time login
Show different change password screen view for first time login
1 week ago
Naeem Ullah 9a10b8d37f Merge pull request 'login Api integration' (#11) from mazdak/UX-1381 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/11
2 weeks ago
Mazdak Gibran 77282eeca7 login api integration
integrating login API with aConnect backend
2 weeks ago
Naeem Ullah f93372a5ca Merge pull request 'mazdak/UX-1387' (#10) from mazdak/UX-1387 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/10
2 weeks ago
Mazdak Gibran a6a38d068e sms gateway screen
sms gateway screen dynamically renders different forms
2 weeks ago
Mazdak Gibran c257ed7e6f SMS Gateway
design implementation of SMS Gateway
2 weeks ago
Naeem Ullah f7be4f6c7b Merge pull request 'Configure Transaction Purpose screen' (#9) from mazdak/UX-1373 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/9
2 weeks ago
Mazdak Gibran 7dacf6f9c8 Configure Transaction Purpose screen
Configure Transaction Purpose screen
2 weeks ago
Naeem Ullah 0cc25afccd Merge pull request 'mazdak/UX-1350' (#8) from mazdak/UX-1350 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/8
2 weeks ago
Naeem Ullah 3c3f1b5ee0 Merge pull request 'mazdak/UX-1320' (#7) from mazdak/UX-1320 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/7
2 weeks ago
Mazdak Gibran b0f8311a2c feedback setup update
feedback setup update
2 weeks ago
Mazdak Gibran 7228790cc8 Update feedback-setup.component.html 2 weeks ago
Mazdak Gibran 83dc8b5c7f feedback setup screen
feedback setup screen
2 weeks ago
Mazdak Gibran ebde11ad65 Update ib-unblock-user.component.ts 2 weeks ago
Mazdak Gibran e1ec631421 Update ib-unblock-user.component.html 2 weeks ago
Mazdak Gibran bf3fe98e63 UnBlock IB User screen
design implementation of UnBlock IB User
2 weeks ago
Naeem Ullah cda611495b Merge pull request 'SMS Logger screen' (#6) from mazdak/UX-1310 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/6
2 weeks ago
Mazdak Gibran c4ccfaf24a SMS Logger screen
SMS Logger screen
2 weeks ago
Naeem Ullah 6d88ae8767 Merge pull request 'Logger Manager Screen design' (#5) from mazdak/UX-1272 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/5
2 weeks ago
Mazdak Gibran 88ffd7a35b Logger Manager Screen design
Logger Manager Screen design
2 weeks ago
Mubashar Hussain 084d17e6ad Merge pull request 'mazdak/UX-1213' (#4) from mazdak/UX-1213 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/4
3 weeks ago
Mazdak Gibran 71c767c684 setup user design
setup user design: fixed camel case
3 weeks ago
Mazdak Gibran d0f3d28458 Merge branch 'dev-pending-09-12-2025' into mazdak/UX-1213 3 weeks ago
Mazdak Gibran bf3df61c9d Setup user
setup user
3 weeks ago
Naeem Ullah 65aded50be Merge pull request 'reset user password design' (#3) from mazdak/UX-1202 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/3
3 weeks ago
Mazdak Gibran 9ee1c0f4e2 update reset-passwords
update reset-passwords.ts
3 weeks ago
Mazdak Gibran b6fe4e0398 reset user password design
reset user password design
3 weeks ago
Naeem Ullah 5e38a86049 Merge pull request 'mazdak/UX-1133' (#2) from mazdak/UX-1133 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/2
3 weeks ago
Mazdak Gibran 36904b68fd update change password
update change password
3 weeks ago
Mazdak Gibran 8eaf5948ab Merge branch 'FMFI-PRE-PRODUCTION' into mazdak/UX-1133 3 weeks ago
Mazdak Gibran edfea86870 change password
change password design
3 weeks ago
Naeem Ullah 3fbe09a4ee Merge pull request 'added design for third party registration screen' (#1) from atif/UX-1125 into FMFI-PRE-PRODUCTION
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/1
3 weeks ago
atif118-mfsys 383772db87 added design for third party registration screen 3 weeks ago
atif118-mfsys d59d64987c initial aConnect commit. 3 weeks ago

@ -0,0 +1,17 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.ts]
quote_type = single
ij_typescript_use_double_quotes = false
[*.md]
max_line_length = off
trim_trailing_whitespace = false

43
.gitignore vendored

@ -0,0 +1,43 @@
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
package-lock.json
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

@ -0,0 +1,4 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
"recommendations": ["angular.ng-template"]
}

@ -0,0 +1,20 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "ng serve",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: start",
"url": "http://localhost:4200/"
},
{
"name": "ng test",
"type": "chrome",
"request": "launch",
"preLaunchTask": "npm: test",
"url": "http://localhost:9876/debug.html"
}
]
}

42
.vscode/tasks.json vendored

@ -0,0 +1,42 @@
{
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "start",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
},
{
"type": "npm",
"script": "test",
"isBackground": true,
"problemMatcher": {
"owner": "typescript",
"pattern": "$tsc",
"background": {
"activeOnStart": true,
"beginsPattern": {
"regexp": "(.*?)"
},
"endsPattern": {
"regexp": "bundle generation complete"
}
}
}
}
]
}

@ -0,0 +1,59 @@
# ACONNECTUX
This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.19.
## Development server
To start a local development server, run:
```bash
ng serve
```
Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files.
## Code scaffolding
Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
```bash
ng generate component component-name
```
For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
```bash
ng generate --help
```
## Building
To build the project run:
```bash
ng build
```
This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed.
## Running unit tests
To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
```bash
ng test
```
## Running end-to-end tests
For end-to-end (e2e) testing, run:
```bash
ng e2e
```
Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
## Additional Resources
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.

@ -0,0 +1,117 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"ACONNECT-UX": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/aconnect-ux",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss",
"node_modules/@ng-select/ng-select/themes/default.theme.css",
"node_modules/ngx-toastr/toastr.css",
"src/assets/css/owl.carousel.min.css",
"src/assets/css/bootstrap.min.css",
"src/assets/css/icons.min.css",
"src/assets/css/app.min.css"
],
"scripts": [],
"server": "src/main.server.ts",
"outputMode": "server",
"ssr": {
"entry": "src/server.ts"
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all",
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "ACONNECT-UX:build:production"
},
"development": {
"buildTarget": "ACONNECT-UX:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n"
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"inlineStyleLanguage": "scss",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
}
}
}
}
}

@ -0,0 +1,54 @@
{
"name": "aconnect-ux",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"build:prod": "ng build --configuration=production",
"watch": "ng build --watch --configuration development",
"test": "ng test",
"serve:ssr:ACONNECT-UX": "node dist/aconnect-ux/server/server.mjs"
},
"private": true,
"dependencies": {
"@angular/animations": "^19.1.0",
"@angular/cdk": "^19.1.0",
"@angular/common": "^19.1.0",
"@angular/compiler": "^19.1.0",
"@angular/core": "^19.1.0",
"@angular/forms": "^19.1.0",
"@angular/platform-browser": "^19.1.0",
"@angular/platform-browser-dynamic": "^19.1.0",
"@angular/platform-server": "^19.1.0",
"@angular/router": "^19.1.0",
"@angular/ssr": "^19.1.6",
"@ng-select/ng-select": "^14.8.0",
"@ngx-translate/core": "^17.0.0",
"@ngx-translate/http-loader": "^16.0.1",
"@types/crypto-js": "^4.2.2",
"bootstrap": "^5.3.8",
"crypto-js": "^4.2.0",
"express": "^4.18.2",
"ngx-spinner": "^19.0.0",
"ngx-toastr": "^19.1.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^19.1.0",
"@angular/cli": "^19.1.0",
"@angular/compiler-cli": "^19.1.0",
"@types/express": "^4.17.17",
"@types/jasmine": "~5.1.0",
"@types/node": "^18.18.0",
"jasmine-core": "~5.5.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.7.2"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,3 @@
<app-loader></app-loader>
<app-notifications></app-notifications>
<router-outlet></router-outlet>

@ -0,0 +1,29 @@
import { TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [AppComponent],
}).compileComponents();
});
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have the 'ACONNECT-UX' title`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('ACONNECT-UX');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ACONNECT-UX');
});
});

@ -0,0 +1,45 @@
import { Component, Inject, PLATFORM_ID } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { StorageService } from './shared/services/storage.service';
import { isPlatformBrowser } from '@angular/common';
import { directions, supportedLanguages } from './utils/enums';
import { LoaderComponent } from './shared/components/loader/loader.component';
import { NotificationsComponent } from './shared/components/notifications/notifications.component';
@Component({
selector: 'app-root',
imports: [RouterOutlet, LoaderComponent, NotificationsComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
direction: any;
title = 'ACONNECT-UX';
constructor(private translateService: TranslateService, private storageService: StorageService,
@Inject(PLATFORM_ID) private platformId: object
) { }
ngOnInit() {
if (this.storageService.getItem('language')) {
const currentLanguage = this.storageService.getItem('language')!;
if (isPlatformBrowser(this.platformId)) {
this.translateService.setDefaultLang(currentLanguage);
this.translateService.use(currentLanguage);
}
this.direction = this.storageService.getItem('direction');
}
else {
if (isPlatformBrowser(this.platformId)) {
this.translateService.setDefaultLang('English');
this.translateService.use('English');
}
this.storageService.setItem('language', supportedLanguages.ENGLISH);
this.direction = directions.LTR;
this.storageService.setItem('direction', this.direction);
}
}
}

@ -0,0 +1,14 @@
import { mergeApplicationConfig, ApplicationConfig } from '@angular/core';
import { provideServerRendering } from '@angular/platform-server';
import { provideServerRouting } from '@angular/ssr';
import { appConfig } from './app.config';
import { serverRoutes } from './app.routes.server';
const serverConfig: ApplicationConfig = {
providers: [
provideServerRendering(),
provideServerRouting(serverRoutes)
]
};
export const config = mergeApplicationConfig(appConfig, serverConfig);

@ -0,0 +1,51 @@
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideClientHydration, withEventReplay } from '@angular/platform-browser';
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
import { HTTP_INTERCEPTORS, HttpClient, provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { AuthInterceptor } from './shared/interceptors/auth.interceptor';
import { ToastrModule } from 'ngx-toastr';
import { provideAnimations } from '@angular/platform-browser/animations';
import { LoadingInterceptor } from './shared/interceptors/loading.interceptor';
export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
}
export const appConfig: ApplicationConfig = {
providers: [
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideClientHydration(withEventReplay()),
provideHttpClient(withInterceptorsFromDi()),
provideAnimations(),
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true
},
{
provide: HTTP_INTERCEPTORS,
useClass: LoadingInterceptor,
multi: true
},
importProvidersFrom(
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient],
},
}),
ToastrModule.forRoot({
timeOut: 4000,
positionClass: 'toast-top-right',
newestOnTop: true,
closeButton: true
}),
)
]
};

@ -0,0 +1,125 @@
import { HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { filter, Observable, Observer } from 'rxjs';
import { HttpService } from './shared/services/http.service';
import { URIService } from './app.uri';
import { URIKey } from '../app/utils/uri-enums';
@Injectable(
{ providedIn: 'root' }
)
export class HttpURIService {
constructor(private http: HttpService, private uriService: URIService) {
}
requestGET<T>(uriKey: URIKey | string, params?: HttpParams): Observable<T> {
return new Observable((observer: Observer<any>) => {
this.uriService.canSubscribe.pipe(filter(val => val)).subscribe(val => {
let uri: string = this.uriService.getURIForRequest(uriKey as URIKey)
this.http.requestGET<T>(uri, params).subscribe(t => {
observer.next(t),
observer.complete()
},
error => {
console.error(error);
observer.next(error),
observer.complete()
});
});
});
}
requestPOST<T>(uriKey: URIKey | string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
return new Observable((observer: Observer<any>) => {
this.uriService.canSubscribe.pipe(filter(val => val)).subscribe(val => {
let uri: string = this.uriService.getURIForRequest(uriKey as URIKey)
this.http.requestPOST<T>(uri, body, headers, params).subscribe(
t => {
observer.next(t),
observer.complete()
},
error => {
console.error(error);
observer.next(error),
observer.complete()
}
);
});
});
}
requestPOSTMultipart<T>(uriKey: URIKey | string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
return new Observable((observer: Observer<any>) => {
this.uriService.canSubscribe.pipe(filter(val => val)).subscribe(val => {
let uri: string = this.uriService.getURIForRequest(uriKey as URIKey)
this.http.requestPOSTMultipart<T>(uri, body, headers, params).subscribe(
t => {
observer.next(t),
observer.complete()
},
error => {
console.error(error);
observer.next(error),
observer.complete()
}
);
});
});
}
requestDELETE<T>(uriKey: URIKey | string, params: HttpParams): Observable<T> {
return new Observable((observer: Observer<any>) => {
this.uriService.canSubscribe.pipe(filter(val => val)).subscribe(val => {
let uri: string = this.uriService.getURIForRequest(uriKey as URIKey)
this.http.requestDELETE<T>(uri, params).subscribe(t => {
observer.next(t),
observer.complete()
},
error => {
console.error(error);
observer.next(error),
observer.complete()
}
);
});
});
}
requestPATCH<T>(uriKey: URIKey | string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
return new Observable((observer: Observer<any>) => {
this.uriService.canSubscribe.pipe(filter(val => val)).subscribe(val => {
let uri: string = this.uriService.getURIForRequest(uriKey as URIKey)
this.http.requestPATCH<T>(uri, body, headers, params).subscribe(t => {
observer.next(t),
observer.complete()
},
error => {
console.error(error);
observer.next(error),
observer.complete()
}
);
});
});
}
requestPUT<T>(uriKey: URIKey | string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
return new Observable((observer: Observer<any>) => {
this.uriService.canSubscribe.pipe(filter(val => val)).subscribe(val => {
let uri: string = this.uriService.getURIForRequest(uriKey as URIKey)
this.http.requestPUT<T>(uri, body, headers, params).subscribe(t => {
observer.next(t),
observer.complete()
},
error => {
console.error(error);
observer.next(error),
observer.complete()
}
);
});
});
}
}

@ -0,0 +1,8 @@
import { RenderMode, ServerRoute } from '@angular/ssr';
export const serverRoutes: ServerRoute[] = [
{
path: '**',
renderMode: RenderMode.Prerender
}
];

@ -0,0 +1,143 @@
import { Routes } from '@angular/router';
import { LoginComponent } from './authenticate/login/login.component';
import { ChangePasswordComponent } from './user-management/change-password/change-password.component';
import { FullLayoutComponent } from './full-layout/full-layout.component';
import { AuthenticationGuard } from './shared/guards/authentication.guard';
// import { ActivityGuard } from './shared/guards/activity.guard';
export const routes: Routes = [
{
path: 'login',
component: LoginComponent
},
{
path: 'changepassword',
component: ChangePasswordComponent
},
{
path: '',
redirectTo: 'login',
pathMatch: 'full'
},
{
path: 'home',
component: FullLayoutComponent,
canActivate: [AuthenticationGuard],
children: [
{
path: '',
redirectTo: 'dashboard',
pathMatch: 'full'
},
{
path: 'dashboard',
loadComponent: () =>
import('./dashboard/dashboard.component').then(
m => m.DashboardComponent
)
},
{
path: 'permissions',
// will need this guard in future when permissions are implemented.
// commenting them for now.
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./user-permissions/user-permissions.component').then(
m => m.UserPermissionsComponent
)
},
{
path: 'smsLogger',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./sms-banking/sms-banking.component').then(
m => m.SmsBankingComponent
)
},
{
path: 'smsGateway',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./sms-gateway/sms-gateway.component').then(
m => m.SmsGatewayComponent
)
},
{
path: 'loggerManager',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./logging/logging.component').then(
m => m.LoggingComponent
)
},
{
path: 'analysis',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./data-analysis/data-analysis.component').then(
m => m.DataAnalysisComponent
)
},
{
path: 'ibUnblockUser',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./ib-support/ib-unblock-user/ib-unblock-user.component').then(
m => m.IbUnblockUserComponent
)
},
{
path: 'feedbackSetup',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./ib-support/feedback-setup/feedback-setup.component').then(
m => m.FeedbackSetupComponent
)
},
{
path: 'purposeSetup',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./ib-support/tran-purpose-setup/tran-purpose-setup.component').then(
m => m.TranPurposeSetupComponent
)
},
{
path: 'thirdPartyRegistration',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./user-management/third-party-registration/third-party-registration.component').then(
m => m.ThirdPartyRegistrationComponent
)
},
{
path: 'setupUser',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./user-management/setup-user/setup-user.component').then(
m => m.SetupUserComponent
)
},
{
path: 'resetPassword',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./user-management/reset-password/reset-password.component').then(
m => m.ResetPasswordComponent
)
},
{
path: 'changePassword',
// canActivate: [ActivityGuard],
loadComponent: () =>
import('./user-management/change-password/change-password.component').then(
m => m.ChangePasswordComponent
)
}
]
},
{
path: '**',
redirectTo: 'home/dashboard'
}
];

@ -0,0 +1,84 @@
import { HttpClient } from '@angular/common/http';
import { Injectable, OnInit } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { environment } from '../environments/environment';
import { URIKey } from './utils/uri-enums';
@Injectable({
providedIn: 'root',
})
export class URIService {
canSubscribe: BehaviorSubject<boolean>;
uriMap: Map<URIKey, string>;
constructor(private http: HttpClient) {
this.canSubscribe = new BehaviorSubject(<boolean>false);
this.uriMap = new Map<URIKey, string>();
this.loadURIs();
}
loadURIs(): void {
this.http.get<URIInfo[]>('assets/data/app.uri.json')
.subscribe(data => {
for (const item of data) {
const baseURI = environment.moduleHost.get(item.Id) as string;
if (baseURI) {
for (const module of item.Modules) {
for (const page of module.Pages) {
const uri = `${baseURI}${module.URI}${page.URI}`;
const key = URIKey[page.UUID as keyof typeof URIKey];
if (key !== undefined) {
this.uriMap.set(key, uri);
}
}
}
}
}
this.canSubscribe.next(true);
});
}
getURI(key: URIKey): string | undefined {
return this.uriMap.get(key);
}
getURIForRequest(key: URIKey): string {
let uri = this.getURI(key as URIKey);
if (uri != undefined) {
return uri;
}
else {
let arr = key.split("/");
if (arr.length) {
let db = arr[0];
let baseurl = environment.moduleHost.get(db.toUpperCase() + "_DOMAIN_URI");
if (baseurl != undefined) {
uri = (baseurl).concat("/").concat(key);
return uri;
}
}
}
return key;
}
}
export interface URIInfo {
Id: string;
URI: string;
Modules: URIModule[];
}
interface URIModule {
Id: string;
URI: string;
Pages: URIPage[];
}
interface URIPage {
Id: string;
URI: string;
UUID: string;
}

@ -0,0 +1,23 @@
export interface AuthenticationToken {
token: string;
}
export interface AuthenticationResponse extends AuthenticationToken {
authenticated: boolean
porOrgacode: string;
userId: string;
userType: string;
password: string;
userHomevac: string;
}
export class UserCredentials {
porOrgacode!: string;
userId!: string;
password!: string;
token!: string;
}
export class AuthenticationRequest {
isInProgress: boolean = false;
}

@ -0,0 +1,64 @@
<div>
<div class="col-md-11 mx-auto">
<div class="row">
<div class="page-title-box d-sm-flex align-items-center pt-2 justify-content-between sticky-top">
<div class="col-xl-11"></div>
<div class="col-xl-1 mt-2">
<div class="page-title-right float-end mx-3">
<select class="form-select" [formControl]="currentLanguage" (change)="onLangChange()" style="width: 100px;">
<option [value]="supportedLanguages.ENGLISH">{{"english" | translate}}</option>
<option [value]="supportedLanguages.ARABIC">{{"arabic" | translate}}</option>
</select>
</div>
</div>
</div>
</div>
</div>
<div class="auth-page d-flex align-items-center">
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8 col-lg-6 col-xl-3-4">
<div class="card">
<div class="bg-primary bg-soft text-center py-3 mt-2">
<img src="assets/images/logo.png" class="img-fluid mb-2" height="120" width="150" alt="Logo">
</div>
<div class="card-body">
<form [formGroup]="loginForm" class="form-horizontal">
<div class="mb-3">
<label for="USER_ID" class="form-label">{{"userName" | translate}}</label>
<div class="input-group auth-pass-inputgroup">
<input type="text" class="form-control" id="USER_ID" formControlName="USER_ID">
</div>
<div *ngIf="loginForm.get('USER_ID')?.invalid && loginForm.get('USER_ID')?.touched" class="text-danger">
<small *ngIf="loginForm.get('USER_ID')?.errors?.['required']">{{"userNameRequired" | translate}}</small>
<small *ngIf="loginForm.get('USER_ID')?.errors?.['pattern']">{{"userNamePattterenError" | translate}}</small>
</div>
</div>
<div class="mb-3">
<label for="PASSWORD" class="form-label">{{"password" | translate}}</label>
<div class="input-group auth-pass-inputgroup">
<input type="{{passwordType}}" class="form-control" id="PASSWORD" formControlName="PASSWORD" aria-label="Password" autocomplete="current-password">
<app-password-hide-show [showPassword]="true" (onEyeClick)="togglePasswordType()"></app-password-hide-show>
</div>
<div *ngIf="loginForm.get('PASSWORD')?.invalid && loginForm.get('PASSWORD')?.touched" class="text-danger">
<small *ngIf="loginForm.get('PASSWORD')?.errors?.['required']">{{"PasswordRequired" | translate}}</small>
</div>
</div>
<div class="mt-3 d-grid">
<button class="btn btn-primary waves-effect waves-light" type="submit" (click)="login()" [disabled]="loginForm.invalid">{{"login" | translate}}</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row fixed-bottom">
<div class="col-md-12 float-left">
<p class="Copyright-text"><span class="VersionNumber">{{ versionNumber }} {{ buildDate }}</span></p>
</div>
</div>
</div>

@ -0,0 +1,5 @@
@media (max-width: 768px) {
.VersionNumber{
text-align: center;
}
}

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoginComponent]
})
.compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,124 @@
import { Component, Inject, inject, PLATFORM_ID, ViewChild } from '@angular/core';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { CONSTANTS } from '../../utils/app.constants';
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
import { Router } from '@angular/router';
import { MiscService } from '../../shared/services/misc.service';
import { User } from '../../models/user';
import { CommonModule, isPlatformBrowser } from '@angular/common';
import { StorageService } from '../../shared/services/storage.service';
import { directions, FormConstants, HiddenValues, supportedLanguages } from '../../utils/enums';
import { environment } from '../../../environments/environment';
import { AuthenticationService } from '../../services/authenticate.service';
import { UserCredentials } from '../authenticate';
@Component({
selector: 'app-login',
imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent {
versionNumber: string = '';
buildDate: string = '';
buildNumber: string = '';
loginForm!: FormGroup;
currentLanguage = new FormControl();
passwordType: string = 'password';
direction: string = '';
supportedLanguages = supportedLanguages;
ucred: UserCredentials = new UserCredentials();
@ViewChild(PasswordHideShowComponent) passwordHideShow?: PasswordHideShowComponent;
constructor(
private authService: AuthenticationService,
private translateService: TranslateService,
private router: Router,
private miscService: MiscService,
private storageService: StorageService,
@Inject(PLATFORM_ID) private platformId: object
) {
this.initializeLanguage();
}
ngOnInit() {
this.initializeLoginForm();
}
setVersionNumberAndBuildDate(){
this.translateService.get('versionAndBuildNumber', {
versionNumber: environment.versionNumber,
buildNumber: environment.buildNumber
}).subscribe((res: string) => {
this.versionNumber = res;
});
this.translateService.get('versionBuildDate', {
date: environment.buildDate
}).subscribe((res: string) => {
this.buildDate = res;
})
}
initializeLanguage(): void {
if (isPlatformBrowser(this.platformId)) {
const savedLanguage = this.storageService.getItem('language') || 'English';
this.storageService.setItem('language', savedLanguage);
this.currentLanguage.setValue(savedLanguage)
this.translateService.setDefaultLang(savedLanguage);
this.translateService.use(savedLanguage).subscribe(() => {
this.setVersionNumberAndBuildDate();
});
this.setDirection();
}
}
setDirection() {
let selectedLang = this.currentLanguage.value;
if (selectedLang === supportedLanguages.ENGLISH) {
this.direction = directions.LTR;
this.storageService.setItem('direction', this.direction);
}
else {
this.direction = directions.RTL;
this.storageService.setItem('direction', this.direction);
}
}
initializeLoginForm() {
this.loginForm = new FormGroup({
USER_ID: new FormControl('', [Validators.required, Validators.pattern('^[a-z0-9]*$')]),
PASSWORD: new FormControl('', [Validators.required])
})
}
login() {
if (this.loginForm.valid) {
this.ucred.porOrgacode = HiddenValues.POR_ORGACODE;
this.ucred.password = this.loginForm.get(FormConstants.PASSWORD)?.value;
this.ucred.userId = this.loginForm.get(FormConstants.USER_ID)?.value;
this.authService.authenticate(this.ucred).subscribe( (res: any) => {
});
}
}
onLangChange() {
const selectedLang = this.currentLanguage.value;
this.translateService.setDefaultLang(selectedLang);
this.translateService.use(selectedLang).subscribe(() => {
this.setVersionNumberAndBuildDate();
});
this.storageService.setItem('language', selectedLang);
this.setDirection();
// document.documentElement.setAttribute('dir', this.direction);
}
togglePasswordType() {
this.passwordType = this.passwordHideShow?.showPassword ? 'password' : 'text';
}
}

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DashboardComponent } from './dashboard.component';
describe('DashboardComponent', () => {
let component: DashboardComponent;
let fixture: ComponentFixture<DashboardComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DashboardComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DashboardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
imports: [],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss'
})
export class DashboardComponent {
}

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { DataAnalysisComponent } from './data-analysis.component';
describe('DataAnalysisComponent', () => {
let component: DataAnalysisComponent;
let fixture: ComponentFixture<DataAnalysisComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [DataAnalysisComponent]
})
.compileComponents();
fixture = TestBed.createComponent(DataAnalysisComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-data-analysis',
imports: [],
templateUrl: './data-analysis.component.html',
styleUrl: './data-analysis.component.scss'
})
export class DataAnalysisComponent {
}

@ -0,0 +1,18 @@
<div id="layout-wrapper" data-sidebar="dark" data-keep-enlarged="true">
<app-header></app-header>
<app-side-nav></app-side-nav>
<div class="main-content">
<router-outlet></router-outlet>
<footer class="footer" [dir]="direction">
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<div class="text-sm-end d-none text-sm-center d-sm-block">
{{ footerText }}
</div>
</div>
</div>
</div>
</footer>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FullLayoutComponent } from './full-layout.component';
describe('FullLayoutComponent', () => {
let component: FullLayoutComponent;
let fixture: ComponentFixture<FullLayoutComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FullLayoutComponent]
})
.compileComponents();
fixture = TestBed.createComponent(FullLayoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,42 @@
import { Component } from '@angular/core';
import { SideNavComponent } from '../shared/components/side-nav/side-nav.component';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { RouterOutlet } from '@angular/router';
import { StorageService } from '../shared/services/storage.service';
import { HeaderComponent } from '../shared/components/header/header.component';
@Component({
selector: 'app-full-layout',
imports: [SideNavComponent, RouterOutlet, HeaderComponent, TranslateModule],
templateUrl: './full-layout.component.html',
styleUrl: './full-layout.component.scss'
})
export class FullLayoutComponent {
direction: any;
footerText: string = '';
constructor(private translateService: TranslateService,
private stoargeService: StorageService
){
}
ngOnInit(){
this.translateService.setDefaultLang(this.stoargeService.getItem('language')!);
this.translateService.use(this.stoargeService.getItem('language')!);
this.direction = this.stoargeService.getItem('direction');
this.setFooterText();
}
setFooterText(){
this.footerText = this.translateService.instant('copyRightsReserved', {
currentYearLong: this.currentYearLong()
})
}
currentYearLong(): number {
return new Date().getFullYear();
}
}

@ -0,0 +1,126 @@
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'credentialsTitle' | translate}}
</div>
<div class="card-body">
<form>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="userID" class="text-nowrap">
{{ 'Email' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch">
<input type="text" id="userID"
class="form-control"
placeholder="{{ 'Email' | translate }}" appNoWhitespaces
/>
</div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div>
</div>
<div class="mt-3">
<p class="text-info h5">{{'2-stepAppPassword' | translate}}</p>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="password"
class="text-nowrap mt-2">
{{ 'password' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="password" class="form-control" autocomplete="new-password" type="{{passwordType}}" maxlength="500"
placeholder="{{ 'password' | translate }}" appNoWhitespaces rows="3" />
<app-password-hide-show #psh class="password-eye align-items-stretch" [showPassword]="true"
(onEyeClick)="togglePasswordType()"></app-password-hide-show>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="confirmPassword" class="text-nowrap">
{{ 'confirmPassword' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="confirmPassword" class="form-control" type="{{confirmPasswordType}}"
placeholder="{{ 'confirmPassword' | translate }}" appNoWhitespaces />
<app-password-hide-show #cpsh class="password-eye align-items-stretch" [showPassword]="true"
(onEyeClick)="toggleConfirmPasswordType()"></app-password-hide-show>
<!-- <div class="text-danger">
<div>
{{ 'requiredField' | translate }}
</div>
</div>
<div>
{{ 'expiryBeforeRenewal' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button
class="btn btn-primary waves-effect waves-light"
>{{'save' | translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FeedbackSetupComponent } from './feedback-setup.component';
describe('FeedbackSetupComponent', () => {
let component: FeedbackSetupComponent;
let fixture: ComponentFixture<FeedbackSetupComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [FeedbackSetupComponent]
})
.compileComponents();
fixture = TestBed.createComponent(FeedbackSetupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,26 @@
import { CommonModule } from '@angular/common';
import { Component, ViewChild } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { TranslateModule } from '@ngx-translate/core';
import { PasswordHideShowComponent } from '../../shared/components/password-hide-show/password-hide-show.component';
@Component({
selector: 'app-feedback-setup',
imports: [TranslateModule, ReactiveFormsModule, FormsModule, CommonModule, PasswordHideShowComponent],
templateUrl: './feedback-setup.component.html',
styleUrl: './feedback-setup.component.scss'
})
export class FeedbackSetupComponent {
@ViewChild('psh') passwordHideShow ?: PasswordHideShowComponent
@ViewChild('cpsh') confirmPasswordHideShow ?: PasswordHideShowComponent
passwordType: string = 'password';
confirmPasswordType: string = 'password';
togglePasswordType(){
this.passwordType = this.passwordHideShow?.showPassword ? 'password' : 'text';
}
toggleConfirmPasswordType(){
this.confirmPasswordType = this.confirmPasswordHideShow?.showPassword ? 'password' : 'text';
}
}

@ -0,0 +1,236 @@
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'IBChildTitle' | translate}}
</div>
<div class="card-body">
<form>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="selectIdentValueType" class="text-nowrap">
{{ 'selectIdentValueType' | translate }}<span
class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch">
<ng-select class="custom-select col-md-12 custom-select-sm ms-2 seleted-edit" [(ngModel)]="optionValue"
placeholder="{{ 'selectIdentValueType' | translate }}"
name="optionValue" [clearable]="false" [searchable]="false">
<!-- Select Type (CLICKABLE) -->
<ng-option value="select">
{{ 'selectIdentValueType' | translate }}
</ng-option>
<ng-option value="cnic">
{{ 'cnic_scnic' | translate }}
</ng-option>
<ng-option value="passport">
{{ 'passport' | translate }}
</ng-option>
<ng-option value="nicop">
{{ 'nicop' | translate }}
</ng-option>
<ng-option value="poc">
{{ 'poc' | translate }}
</ng-option>
</ng-select>
</div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="custId" class="text-nowrap">
{{ 'custId' | translate }}<span
class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100">
<input id="custId" type="text" class="form-control"
placeholder="{{ 'enterIdentityValue' | translate }}"
maxlength="500"
appNoWhitespaces rows="3" />
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button
class="btn btn-primary waves-effect waves-light">{{'fetchCustomer'
| translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'unblockUserDetails' | translate}}
<div class="d-flex align-items-center gap-2">
<div class="search-box">
<input type="text" class="form-control form-control-sm"
placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i>
</div>
<i class="materialdesignicons">
<ng-container *ngIf="renewalDataExpanded; else collapsedIcon">
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
<i class="dripicons-chevron-down float-end"></i>
</ng-template>
</i>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table mb-0 border">
<thead class="table-light">
<tr>
<th>{{'firstName' | translate}}</th>
<th>{{'lastName' | translate}}</th>
<th>{{'cmpCuststatus' | translate}}</th>
<th>{{'cmpCustlastlogin' | translate}}</th>
<th>{{'accountNonLocked' | translate}}</th>
<th>{{'lockTime' | translate}}</th>
<th>{{'accountno' | translate}}</th>
<th>{{'phoneno' | translate}}</th>
<th>{{'action' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<div
class="d-flex justify-content-center gap-2">
<button class="btn btn-info btn-sm"
title="View">
<i class="mdi mdi-eye-outline"></i>
</button>
<button class="btn btn-secondary btn-sm"
title="Edit">
<i class="fas fa-pen"></i>
</button>
<button class="btn btn-danger btn-sm"
title="Delete">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div
class="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0">
<ng-select class="form-select-sm"
[items]="pageSizeOptions" bindLabel="label"
bindValue="value" [(ngModel)]="itemsPerPage"
[searchable]="false" [clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted">
{{ 'page' | translate }} {{ 'of' | translate }} ({{
'totalItems' | translate }})
</div>
<div class="btn-group">
<button
class="btn btn-primary waves-effect waves-light">
{{ 'previous' | translate }}
</button>
<button
class="btn btn-primary waves-effect waves-light">
{{ 'next' | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { IbUnblockUserComponent } from './ib-unblock-user.component';
describe('IbUnblockUserComponent', () => {
let component: IbUnblockUserComponent;
let fixture: ComponentFixture<IbUnblockUserComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [IbUnblockUserComponent]
})
.compileComponents();
fixture = TestBed.createComponent(IbUnblockUserComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,22 @@
import { Component } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../../utils/app.constants';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-ib-unblock-user',
imports: [TranslateModule, FormsModule, NgSelectModule, CommonModule, ReactiveFormsModule],
templateUrl: './ib-unblock-user.component.html',
styleUrl: './ib-unblock-user.component.scss'
})
export class IbUnblockUserComponent {
renewalDataExpanded: boolean = true
itemsPerPage: number = 5;
pageSizeOptions = pageSizeOptions
optionValue: any;
itemsPerPageChanged() {}
}

@ -0,0 +1,199 @@
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'purposeSetup' | translate}}
</div>
<div class="card-body">
<form>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="purpcodeLabel" class="text-nowrap">
{{ 'purpcodeLabel' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch">
<input type="text" id="purpcodeLabel"
class="form-control"
placeholder="{{ 'purpcodePlaceholder' | translate }}" appNoWhitespaces
/>
</div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="purpdescLabel"
class="text-nowrap mt-2">
{{ 'purpdescLabel' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="purpdescLabel"
class="form-control"
maxlength="500"
placeholder="{{ 'purpdescPlaceholder' | translate }}" appNoWhitespaces
rows="3" />
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button
class="btn btn-primary waves-effect waves-light"
>{{'save' | translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'transactionDetails' | translate}}
<div class="d-flex align-items-center gap-2">
<div class="search-box">
<input type="text" class="form-control form-control-sm"
placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i>
</div>
<i class="materialdesignicons">
<ng-container *ngIf="renewalDataExpanded; else collapsedIcon">
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
<i class="dripicons-chevron-down float-end"></i>
</ng-template>
</i>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table mb-0 border">
<thead class="table-light">
<tr>
<th>{{'smsOrgaCode' | translate}}</th>
<th>{{'purpcodeLabel' | translate}}</th>
<th>{{'purpdescLabel' | translate}}</th>
<th>{{'action' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td>
<div class="d-flex justify-content-center gap-2">
<button class="btn btn-info btn-sm" title="View">
<i class="mdi mdi-eye-outline"></i>
</button>
<button class="btn btn-secondary btn-sm" title="Edit">
<i class="fas fa-pen"></i>
</button>
<button class="btn btn-danger btn-sm" title="Delete">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div class="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0">
<ng-select class="form-select-sm"
[items]="pageSizeOptions"
bindLabel="label"
bindValue="value"
[(ngModel)]="itemsPerPage"
[searchable]="false"
[clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted">
{{ 'page' | translate }} {{ 'of' | translate }} ({{ 'totalItems' | translate }})
</div>
<div class="btn-group">
<button class="btn btn-primary waves-effect waves-light">
{{ 'previous' | translate }}
</button>
<button class="btn btn-primary waves-effect waves-light">
{{ 'next' | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranPurposeSetupComponent } from './tran-purpose-setup.component';
describe('TranPurposeSetupComponent', () => {
let component: TranPurposeSetupComponent;
let fixture: ComponentFixture<TranPurposeSetupComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TranPurposeSetupComponent]
})
.compileComponents();
fixture = TestBed.createComponent(TranPurposeSetupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,19 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../../utils/app.constants';
@Component({
selector: 'app-tran-purpose-setup',
imports: [TranslateModule, CommonModule, ReactiveFormsModule, FormsModule, NgSelectModule],
templateUrl: './tran-purpose-setup.component.html',
styleUrl: './tran-purpose-setup.component.scss'
})
export class TranPurposeSetupComponent {
pageSizeOptions = pageSizeOptions
renewalDataExpanded: any;
itemsPerPage: number = 5;
}

@ -0,0 +1,209 @@
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'loggerManager' | translate}}
</div>
<div class="card-body">
<form>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="fromDate" class="text-nowrap">
{{ 'fromDate' | translate }}<span
class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100">
<div
class="d-flex flex-row align-items-stretch">
<input type="date" id="fromDate"
class="form-control"
appNoWhitespaces />
</div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="toDate" class="text-nowrap mt-2">
{{ 'toDate' | translate }}<span
class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100">
<input id="toDate" type="date" class="form-control"
maxlength="500"
appNoWhitespaces rows="3" />
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button
class="btn btn-primary waves-effect waves-light">{{'findLogs'
| translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'loggerManagerDetails' | translate}}
<div class="d-flex align-items-center gap-2">
<div class="search-box">
<input type="text" class="form-control form-control-sm"
placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i>
</div>
<i class="materialdesignicons">
<ng-container *ngIf="renewalDataExpanded; else collapsedIcon">
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
<i class="dripicons-chevron-down float-end"></i>
</ng-template>
</i>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table mb-0 border">
<thead class="table-light">
<tr>
<th>{{'loggingID' | translate}}</th>
<th>{{'loggingRequestUri' | translate}}</th>
<th>{{'loggingResponseCode' | translate}}</th>
<th>{{'loggingRemoteIP' | translate}}</th>
<th>{{'loggingTimeTaken' | translate}}</th>
<th>{{'loggingDateTime' | translate}}</th>
<th>{{'loggingMethod' | translate}}</th>
<th>{{'action' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<div
class="d-flex justify-content-center gap-2">
<button class="btn btn-info btn-sm"
title="View">
<i class="mdi mdi-eye-outline"></i>
</button>
<button class="btn btn-secondary btn-sm"
title="Edit">
<i class="fas fa-pen"></i>
</button>
<button class="btn btn-danger btn-sm"
title="Delete">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div
class="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0">
<ng-select class="form-select-sm"
[items]="pageSizeOptions" bindLabel="label"
bindValue="value" [(ngModel)]="itemsPerPage"
[searchable]="false" [clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted">
{{ 'page' | translate }} {{ 'of' | translate }} ({{
'totalItems' | translate }})
</div>
<div class="btn-group">
<button
class="btn btn-primary waves-effect waves-light">
{{ 'previous' | translate }}
</button>
<button
class="btn btn-primary waves-effect waves-light">
{{ 'next' | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoggingComponent } from './logging.component';
describe('LoggingComponent', () => {
let component: LoggingComponent;
let fixture: ComponentFixture<LoggingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoggingComponent]
})
.compileComponents();
fixture = TestBed.createComponent(LoggingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,28 @@
import { Component } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../utils/app.constants';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-logging',
imports: [TranslateModule, FormsModule, NgSelectModule, CommonModule, ReactiveFormsModule],
templateUrl: './logging.component.html',
styleUrl: './logging.component.scss'
})
export class LoggingComponent {
currentPage: number = 1;
pageSizeOptions = pageSizeOptions
renewalDataExpanded: boolean = true
itemsPerPage: number = 5;
searchText: any;
toggleCard(arg0: string) {
throw new Error('Method not implemented.');
}
nextPage() {
throw new Error('Method not implemented.');
}
logsForm: any;
}

@ -0,0 +1,14 @@
export interface ServerException {
error: HttpError;
}
export interface HttpError {
errorCode: string;
arguments: Array<any>;
}
export interface FunctionReturn {
returnCode: number;
messageCode: string;
}
export interface FunctionReturnDetail extends FunctionReturn {
arguments: Array<any>;
}

@ -0,0 +1,13 @@
export class User {
Username: string="";
Email?: string="";
Password: string="";
}
export interface SetupUser {
userId: string;
userFullname: string;
defaultPassword: string;
email: string;
role: string;
}

@ -0,0 +1,14 @@
export interface ServerException {
error: HttpError;
}
export interface HttpError {
errorCode: string;
arguments: Array<any>;
}
export interface FunctionReturn {
returnCode: number;
messageCode: string;
}
export interface FunctionReturnDetail extends FunctionReturn {
arguments: Array<any>;
}

@ -0,0 +1,92 @@
import { Injectable } from '@angular/core';
import { tap } from 'rxjs/operators';
import { HttpService } from '../shared/services/http.service';
import { MiscService } from '../shared/services/misc.service';
import { Router } from '@angular/router';
import { User } from '../models/user';
import { HttpErrorResponse } from '@angular/common/http';
import { CONSTANTS } from '../utils/app.constants';
import { StorageService } from '../shared/services/storage.service';
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private httpService: HttpService, private miscService: MiscService, private router: Router, private storageService: StorageService) { }
firstLogin: boolean = false;
private token: string = "";
async login(User_Data: User) {
let login = false;
let userId = User_Data.Username;
let password = User_Data.Password;
let data = { "userId": userId, "password": password};
let url = '/authentication/login';
let response: any = await this.httpService.postRequest(url, data)!.toPromise();
if (!(response instanceof HttpErrorResponse)) {
if ((await response["errorMessage"] == undefined)) {
if (response) {
login = true;
localStorage.setItem('userId', userId);
let res = JSON.parse(JSON.stringify(response));
// let permission = JSON.parse(res['userPermission']);
// localStorage.setItem('SIDENAV', res['userPermission']);
localStorage.setItem('userFullname', res.user.userFullname);
localStorage.setItem('userId', res.user.userId);
localStorage.setItem('token', res.token);
this.firstLogin = response.requiresPasswordChange;
this.storageService.setItem('firstLogin', this.firstLogin ? 'true' : 'false');
return res;
}
}
let res = response;
return res;
}
}
IsLoggedIn() {
if (this.storageService.getItem('userId') !== null)
return true;
else
return false;
}
logout() {
window.history.state;
localStorage.clear();
this.router.navigate(['login'])
}
getToken(): string {
this.token = localStorage.getItem('token') || "";
return this.token;
}
setToken(token: string) {
this.token = token;
localStorage.setItem('token', token);
}
refreshToken() {
let uCreds = { token: this.getToken() };
let porOrgacode = CONSTANTS.POR_ORGACODE;
let refreshTokenData: any = {
cmpUserId: localStorage.getItem('userId'),
token: uCreds.token,
porOrgacode: porOrgacode
}
return this.httpService.postRequest("/refreshToken", refreshTokenData)!.pipe(
tap((response: any) => {
localStorage.removeItem('token')
localStorage.setItem('token', JSON.stringify(response.token));
this.setToken(response.token);
})
);
}
}

@ -0,0 +1,111 @@
import { HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { BehaviorSubject, Observable, Observer } from 'rxjs';
import { tap } from 'rxjs/operators';
import { ErrorMessages, FormConstants, HiddenValues, SuccessMessages } from '../utils/enums';
import { CredentialService } from './credential.service';
import { AuthenticationToken, UserCredentials } from '../authenticate/authenticate';
import { HttpURIService } from '../app.http.uri.service';
import { URIKey } from '../utils/uri-enums';
import { I18NService } from './i18n.service';
import { StorageService } from '../shared/services/storage.service';
import { ButtonManagementService } from './button-management.service';
@Injectable(
{ providedIn: 'root' }
)
export class AuthenticationService {
showLicenseInfo: boolean = false;
reset: boolean = false;
public onAuthenticationComplete: BehaviorSubject<boolean> = new BehaviorSubject(<boolean>false);
constructor(private buttonManagementService: ButtonManagementService, private httpService: HttpURIService, private router: Router, private credentialService: CredentialService, private i18nService: I18NService, private storageService: StorageService) {
}
authenticate(uCreds: UserCredentials) : Observable<any> {
const observable = new Observable((observer: Observer<any>) => {
if (this.storageService.getItem('user') != null) {
this.i18nService.error(ErrorMessages.ALREADY_LOGGED_IN,[]);
return;
}
this.credentialService.setPorOrgacode(uCreds.porOrgacode);
this.credentialService.setUserId(uCreds.userId);
this.credentialService.setPassword(uCreds.password);
this.storageService.setItem(FormConstants.POR_ORGACODE, uCreds.porOrgacode);
this.storageService.setItem(FormConstants.USER_ID, uCreds.userId);
this.storageService.setItem(FormConstants.PASSWORD, uCreds.password);
this.httpService.requestPOST(URIKey.USER_LOGIN_URI, uCreds).subscribe((data: any) => {
if (!(data instanceof HttpErrorResponse)) {
data.authenticated = true;
this.i18nService.success(SuccessMessages.LOGIN_SUCCESSFULLY, []);
this.storageService.setItem('user', JSON.stringify(data));
this.credentialService.setToken(data.token);
this.credentialService.setUserType(data.userType);
if(data.permission){
this.storageService.setItem('permission', data.permission);
this.credentialService.setPermission(JSON.parse(data.permission));
}
else{
this.storageService.setItem('permission', '[]');
this.credentialService.setPermission([]);
}
this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.isSuperAdminUser());
if(data.user.isFirstLogin){
this.router.navigate(["/changepassword"]);
} else {
this.router.navigate(["/home/dashboard"]);
}
this.onAuthenticationComplete.next(true);
observer.complete();
}
else {
this.onAuthenticationComplete.next(false);
observer.error(false);
}
});
});
return observable;
}
isAuthenticated(): boolean {
if (this.storageService && this.storageService.getItem('user') != null) {
let cachedUser = JSON.parse(this.storageService.getItem('user') || '{}');
return cachedUser.authenticated;
}
return false;
}
isSuperAdminUser(){
if (this.storageService && this.storageService.getItem('user') != null) {
let cachedUser = JSON.parse(this.storageService.getItem('user') || '{}');
return cachedUser.userType === HiddenValues.SUPERADMIN_USER;
}
return false;
}
refreshToken() {
let uCreds: UserCredentials = { porOrgacode: this.credentialService.getPorOrgacode(), userId: this.credentialService.getUserId(), password: this.credentialService.getPassword(), token: this.credentialService.getToken() };
return this.httpService.requestPOST<AuthenticationToken>(URIKey.USER_REFRESH_TOKEN, uCreds).pipe(
tap(response => {
this.credentialService.setToken(response.token);
let cachedUser = JSON.parse(this.storageService.getItem('user') || '{}');
cachedUser.token = response.token;
this.storageService.setItem('user', JSON.stringify(cachedUser));
})
);
}
logout() {
let defaultPermission: string = this.storageService.getItem("defaultPermission") || "{}";
this.storageService.clear();
this.storageService.setItem("defaultPermission", defaultPermission)
this.credentialService.resetService();
this.router.navigate(['/login']);
}
}

@ -0,0 +1,56 @@
import { Injectable } from "@angular/core";
import { Observable } from "rxjs";
import { PermissionNode } from "../utils/app.constants";
import { HttpURIService } from "../app.http.uri.service";
import { StorageService } from "../shared/services/storage.service";
@Injectable({
providedIn: 'root'
})
export class ButtonManagementService {
buttonPermissions: any = {};
defaultPermission: any = {};
constructor(private httpService: HttpURIService, private storageService: StorageService){
this.defaultPermissions().subscribe((data: PermissionNode[]) => {
this.defaultPermission = data;
this.storageService.setItem("defaultPermission", JSON.stringify(data));
});
}
traverse(nodes: any, isSuperAdmin: boolean) {
if (!Array.isArray(nodes)) return;
nodes.forEach(node => {
// Check if the node has buttons
if (node.buttons && Array.isArray(node.buttons) && node.buttons.length > 0) {
// Create an object for this node's buttons
this.buttonPermissions[node.name] = {};
// Map each button's name to its checked value
node.buttons.forEach((button:any) => {
this.buttonPermissions[node.name][button.name] = isSuperAdmin? true : button.checked;
});
}
// Recursively traverse children
if (node.children && Array.isArray(node.children)) {
this.traverse(node.children, isSuperAdmin);
}
});
}
setButtonPermissions(permission: any, isSuperAdmin: boolean){
if (isSuperAdmin){
if (Object.keys(this.defaultPermission).length === 0){
this.defaultPermission = JSON.parse(this.storageService.getItem("defaultPermission") || '{}');
}
this.traverse(this.defaultPermission, isSuperAdmin);
} else{
this.traverse(permission, isSuperAdmin);
}
}
defaultPermissions(): Observable<PermissionNode[]> {
return this.httpService.requestGET<PermissionNode[]>('assets/data/sideMenu.json');
}
}

@ -0,0 +1,79 @@
import { Injectable } from '@angular/core';
import { StorageService } from '../shared/services/storage.service';
@Injectable(
{ providedIn: 'root' }
)
export class CredentialService {
private porOrgacode!: string;
private userId!: string;
private userType!: string;
private password!: string;
private token!: string;
private userHomevac!: string;
private permission: any[] = [];
constructor(private storageService: StorageService){
}
getToken(): string {
return this.token;
}
setToken(token: string) {
this.token = token;
}
getPorOrgacode(): string {
return this.porOrgacode;
}
setPorOrgacode(porOrgacode: string) {
this.porOrgacode = porOrgacode
}
getUserId(): string {
return this.userId
}
setUserId(userId: string) {
this.userId = userId;
}
getUserType(): string {
return this.userType
}
setUserType(userType: string) {
this.userType = userType;
}
getPassword(): string {
return this.password;
}
setPassword(password: string) {
this.password = password;
}
getPermission(): any[] {
return this.permission;
}
setPermission(permission: any[]) {
this.permission = permission;
}
resetService() {
this.setPorOrgacode("");
this.setUserId("");
this.setUserType("");
this.setPassword("");
this.setToken("");
this.setPermission([]);
}
}

@ -0,0 +1,54 @@
import { Injectable } from '@angular/core';
import { AES, enc, pad} from 'crypto-js';
@Injectable({
providedIn: 'root'
})
export class EncryptionService {
constructor() { }
encryptData(request: any): object {
const fromKey1: number = Math.floor(Math.random() * 5) + 1;
const fromKey2: number = Math.floor(Math.random() * 5) + 1;
const key1: string = this.generateRandomKey(16);
const key2: string = this.generateRandomKey(16);
try {
let input: string;
if (typeof request === 'string') {
input = request;
} else {
input = JSON.stringify(request);
}
// Encryption
const encrypted: string = AES.encrypt(input, enc.Utf8.parse(key1), {
iv: enc.Utf8.parse(key2),
padding: pad.Pkcs7
}).toString();
const dataBeforeKey1 = encrypted.substring(0, fromKey1);
let encryptedDataSubstring = encrypted.substring(fromKey1);
const dataBeforeAfterKey2 = encryptedDataSubstring.substring(encryptedDataSubstring.length - fromKey2);
encryptedDataSubstring = encryptedDataSubstring.substring(0, encryptedDataSubstring.length - fromKey2);
const encryptedRequestData = `1${fromKey1}${dataBeforeKey1}${key1}${encryptedDataSubstring}${key2}${dataBeforeAfterKey2}${fromKey2}1`;
const encryptedDataObject = { data: encryptedRequestData };
return encryptedDataObject;
} catch (error) {
console.error(error);
return {};
}
}
generateRandomKey(length: number): string {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let key = '';
for (let i = 0; i < length; i++) {
key += characters.charAt(Math.floor(Math.random() * characters.length));
}
return key;
}
}

@ -0,0 +1,87 @@
import { Injectable } from "@angular/core";
import { TranslateService } from "@ngx-translate/core";
import { BehaviorSubject, Observable } from "rxjs";
import { MESSAGEKEY } from "../utils/enums";
import { NotificationService } from "../shared/services/notification.service";
@Injectable(
{ providedIn: 'root' }
)
export class I18NService {
public translationsMerge: BehaviorSubject<boolean> = new BehaviorSubject(<boolean>false);
constructor(private translate: TranslateService, private notificationService: NotificationService) {
}
success(code: string, customMessage: any[], defaultText?: string): any {
let params = this.keyValueParamter(customMessage);
this.getMessageTranslate(MESSAGEKEY.SUCCESS, code, params).subscribe((res) => {
this.notificationService.success((res[code] == code && defaultText != undefined) ? defaultText : res[code]);
});
}
error(code: string, customMessage: any[], defaultText?: string): any {
let params = this.keyValueParamter(customMessage);
this.getMessageTranslate(MESSAGEKEY.ERROR, code, params).subscribe((res) => {
this.notificationService.error((res[code] == code && defaultText != undefined) ? defaultText : res[code]);
});
}
info(code: string, customMessage: any[]): any {
let params = this.keyValueParamter(customMessage);
this.getMessageTranslate(MESSAGEKEY.INFO, code, params).subscribe((res) => {
this.notificationService.info(res[code]);
});
}
warn(code: string, customMessage: any[]): any {
let params = this.keyValueParamter(customMessage);
this.getMessageTranslate(MESSAGEKEY.WARN, code, params).subscribe((res) => {
this.notificationService.warning(res[code]);
});
}
notification(code: string, customMessage: any[]): string {
let params = this.keyValueParamter(customMessage);
let notification: string = "";
this.getMessageTranslate(MESSAGEKEY.NOTIFICATION, code, params).subscribe((res) => {
notification = res[code] as string;
});
return notification;
}
keyValueParamter(params: object[]): Object {
// Create an object to hold the key-value pairs
const keyValueObject: { [key: string]: string } = {};
// Populate the object
for (let i = 0; i < params?.length; i++) {
keyValueObject[`value${i + 1}`] = String(params[i]);
}
return keyValueObject;
}
getMessageTranslate(type: string, code: string, params: any): Observable<string | any> {
if (type == MESSAGEKEY.SUCCESS) {
return this.translate.get([code, "SUC_APP_F_SUM"], params)
} else if (type == MESSAGEKEY.ERROR) {
return this.translate.get([code, "ERR_APP_F_SUM"], params)
} else if (type == MESSAGEKEY.WARN) {
return this.translate.get([code, "WRN_APP_F_SUM"], params)
} else if (type == MESSAGEKEY.INFO) {
return this.translate.get([code, "INF_APP_F_SUM"], params)
} else if (type == MESSAGEKEY.NOTIFICATION) {
return this.translate.get([code, "NTF_APP_F_SUM"], params)
} else {
return this.translate.get([code, code], params)
}
}
}

@ -0,0 +1,20 @@
import { Injectable } from '@angular/core';
import { ToastrService } from 'ngx-toastr';
@Injectable({
providedIn: 'root'
})
export class MessageService {
constructor(private toastr: ToastrService) { }
Success(Message: string,) {
this.toastr.success(Message, "Success", { tapToDismiss: true, progressBar: true, progressAnimation: 'increasing' });
}
Show(Message: string, Title: string) {
this.toastr.success(Message, Title, { tapToDismiss: true, progressBar: true, progressAnimation: 'increasing' });
}
Error(Message: string) {
this.toastr.error(Message, "Error", { tapToDismiss: true, progressBar: true, progressAnimation: 'increasing' });
}
}

@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SidebarService {
private isSidebarOpen = new BehaviorSubject<boolean>(false);
public sidebarState$ = this.isSidebarOpen.asObservable();
currentSubModule:string;
constructor() {
this.currentSubModule = '';
}
toggleSidebar(): void {
this.isSidebarOpen.next(!this.isSidebarOpen.value);
}
}

@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { NgxSpinnerService } from 'ngx-spinner';
@Injectable({
providedIn: 'root'
})
export class SpinnerService {
constructor(private spinner: NgxSpinnerService) { }
IsBusy(state:boolean) {
if (state == true)
this.spinner.show();
else
this.spinner.hide();
}}

@ -0,0 +1,126 @@
import { Injectable } from '@angular/core';
import { SetupUser } from '../models/user';
import { BehaviorSubject, Observable } from 'rxjs';
import { URIKey } from '../utils/uri-enums';
import { URIService } from '../app.uri';
import { HttpURIService } from '../app.http.uri.service';
import { HttpParams } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UserSetupService {
private usersSubject = new BehaviorSubject<SetupUser[]>([]);
private currentPageSubject = new BehaviorSubject<number>(1);
private totalCountSubject = new BehaviorSubject<number>(0);
private searchTextSubject = new BehaviorSubject<string>('');
private itemsPerPageSubject = new BehaviorSubject<number>(5);
private paginatedUsersSubject = new BehaviorSubject<SetupUser[]>([]);
users$ = this.usersSubject.asObservable();
currentPage$ = this.currentPageSubject.asObservable();
totalCount$ = this.totalCountSubject.asObservable();
searchText$ = this.searchTextSubject.asObservable();
itemsPerPage$ = this.itemsPerPageSubject.asObservable();
paginatedUsers$ = this.paginatedUsersSubject.asObservable();
constructor(private httpURIService: HttpURIService, private uriService: URIService) { }
loadUsers(): void {
this.uriService.canSubscribe.subscribe(can => {
if (can) {
this.httpURIService
.requestGET<any>(URIKey.GET_ALL_USERS)
.subscribe({
next: (res) => {
const users = Array.isArray(res) ? res : res?.data;
this.usersSubject.next(users ?? []);
this.totalCountSubject.next(users.length);
this.applyPagination();
},
error: (err) => console.error(err)
});
}
});
}
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 {
this.itemsPerPageSubject.next(itemsPerPage);
this.currentPageSubject.next(1);
this.applyPagination();
}
nextPage(): void {
const totalPages = this.getTotalPages();
const currentPage = this.currentPageSubject.value;
if (currentPage < totalPages) {
this.currentPageSubject.next(currentPage + 1);
this.applyPagination();
}
}
previousPage(): void {
const currentPage = this.currentPageSubject.value;
if (currentPage > 1) {
this.currentPageSubject.next(currentPage - 1);
this.applyPagination();
}
}
goToPage(page: number): void {
const totalPages = this.getTotalPages();
if (page > 0 && page <= totalPages) {
this.currentPageSubject.next(page);
this.applyPagination();
}
}
getTotalPages(): number {
const totalCount = this.totalCountSubject.value;
const itemsPerPage = this.itemsPerPageSubject.value;
return Math.ceil(totalCount / itemsPerPage);
}
addUser(payload: SetupUser): Observable<SetupUser> {
return this.httpURIService.requestPOST<SetupUser>(URIKey.CREATE_USER, payload);
}
getUserById(userId: any){
const params = new HttpParams().set('userId', userId)
return this.httpURIService.requestGET(URIKey.GET_USER_BY_ID, params);
}
deleteUser(userId: any){
const params = new HttpParams().set('userId', userId)
console.log("params success",params)
return this.httpURIService.requestDELETE(URIKey.DELETE_USER, params)
}
}

@ -0,0 +1,76 @@
<div class="col-md-10 mx-auto" id="page-topbar">
<div class="p-0">
<div class="navbar-header shadow-lg d-flex justify-content-between align-items-center w-100">
<!-- Left Section -->
<div class="d-flex align-items-center">
<!-- Logo Section -->
<div class="navbar-brand-box bg-primary me-2">
<a routerLink="/home/dashboard" class="logo logo-dark">
<span class="logo-sm">
<img src="assets/images/mfsys-logo.png" alt="Logo" height="30" />
</span>
<span class="logo-lg">
<img src="assets/images/mfsys-logoo.png" alt="Logo" height="60" />
</span>
</a>
<a routerLink="/home/dashboard" class="logo logo-light">
<span class="logo-sm">
<img src="assets/images/mfsys-logo.png" alt="Logo" height="30" />
</span>
<span class="logo-lg">
<img src="assets/images/mfsys-logoo.png" alt="Logo" height="60" />
</span>
</a>
</div>
<!-- Vertical Menu Button -->
<button type="button"
class="btn btn-sm px-3 text-muted header-item waves-effect"
id="vertical-menu-btn">
<img src="assets/images/data-transfers.png" alt="Logo" style="width: 16px; margin: -4px;" />
</button>
<!-- Submodule Text -->
<div class="d-none d-lg-block ms-3">
<p class="text-muted mt-3 fw-normal mb-0">
{{ (sidebarService.currentSubModule | translate) }}
</p>
</div>
</div>
<!-- Right Section -->
<div class="d-flex align-items-center">
<div class="d-none d-lg-inline-block me-3">
<label class="text-muted fw-normal mb-0"
[title]="mismatchedDates"
[style]="{'color': dateColor}"
style="font-size: 14px;">
{{'date' | translate}}: {{date}}
</label>
</div>
<div class="dropdown d-inline-block profile-dropdown">
<button type="button"
class="btn header-item waves-effect p-0 d-flex align-items-center"
id="page-header-user-dropdown"
(click)="toggleDropdown()"
aria-haspopup="true"
[attr.aria-expanded]="isDropdownVisible ? 'true' : 'false'">
<img class="rounded-circle header-profile-user" src="assets/images/user-icon.png" alt="user-icon" />
<span class="d-none d-xl-inline-block ms-2 text-muted">{{username}}</span>
<i class="mdi mdi-chevron-down text-muted d-xl-inline-block font-size-22"></i>
</button>
<div class="dropdown-menu dropdown-menu-end" [ngClass]="{'show': isDropdownVisible}">
<a class="dropdown-item text-danger" (click)="logout()">
<i class="bx bx-power-off font-size-16 align-middle me-1 text-danger"></i> {{ 'logout' | translate }}
</a>
</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeaderComponent } from './header.component';
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HeaderComponent]
})
.compileComponents();
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,179 @@
import { Component, HostListener, Inject, PLATFORM_ID } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { SidebarService } from '../../../services/sidebar.service';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { StorageService } from '../../services/storage.service';
import { isPlatformBrowser, formatDate, CommonModule } from '@angular/common';
import { AuthenticationService } from '../../../services/authenticate.service';
@Component({
selector: 'app-header',
imports: [TranslateModule, CommonModule],
templateUrl: './header.component.html',
styleUrl: './header.component.scss'
})
export class HeaderComponent {
isDropdownVisible: boolean;
isVacDropdownVisible: boolean;
isNotificationsVisible: boolean;
notifications = [
{
imgSrc: '',
title: 'Salena Layfield',
message: 'As a skeptical Cambridge friend of mine occidental.',
timeAgo: '1 hour ago'
},
];
direction: string = 'ltr';
userString;
user;
username;
mismatchedDates: string = "";
dateColor = "black";
date: any;
vacName: any;
allVacs: any;
constructor(
public sidebarService: SidebarService,
@Inject(PLATFORM_ID) private platformId: Object,
private storageService: StorageService,
public authService: AuthenticationService
) {
this.isDropdownVisible = false;
this.isVacDropdownVisible = false;
this.isNotificationsVisible = false;
this.userString = this.storageService.getItem('user');
this.user = JSON.parse(this.userString as string);
this.username = this.user?.username;
this.date = new Date().toISOString().split('T')[0];
}
ngOnInit(): void {
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
this.initializeVerticalMenuToggle();
this.initializeFullscreenToggle();
const body = document.body;
if (window.innerWidth >= 992) {
const isCollapsed = body.classList.toggle('sidebar-enable');
this.storageService.setItem('sidebarState', isCollapsed ? 'expanded' : 'collapsed');
}
}
}
ngAfterViewInit(): void {
}
initializeVerticalMenuToggle(): void {
if (isPlatformBrowser(this.platformId)) {
const verticalMenuBtn = document.getElementById('vertical-menu-btn');
if (window.innerWidth <= 992) {
const body = document.body;
body.classList.add('vertical-collpsed');
}
if (verticalMenuBtn) {
verticalMenuBtn.addEventListener('click', this.toggleSidebar.bind(this));
}
}
}
initializeFullscreenToggle(): void {
if (isPlatformBrowser(this.platformId)) {
const fullscreenButton = document.querySelector<HTMLButtonElement>('[data-bs-toggle="fullscreen"]');
if (fullscreenButton) {
fullscreenButton.addEventListener("click", () => {
if (!document.fullscreenElement) {
this.toggleFullscreen(true);
} else {
this.toggleFullscreen(false);
}
});
}
}
}
toggleFullscreen(enter: boolean): void {
if (isPlatformBrowser(this.platformId)) {
if (enter) {
document.documentElement.requestFullscreen();
} else {
document.exitFullscreen();
}
document.body.classList.toggle('fullscreen-enable', enter);
}
}
toggleSidebar(): void {
if (isPlatformBrowser(this.platformId)) {
const body = document.body;
const isSidebarEnabled = body.classList.toggle('sidebar-enable');
if (window.innerWidth >= 992) {
const isCollapsed = body.classList.toggle('vertical-collpsed');
this.storageService.setItem('sidebarState', isCollapsed ? 'collapsed' : 'expanded');
if (isCollapsed) {
const subMenus = document.querySelectorAll('.sub-menu');
subMenus.forEach(menu => {
(menu as HTMLElement).style.display = '';
menu.setAttribute('aria-expanded', 'false');
});
} else {
const subMenus = document.querySelectorAll('.sub-menu');
subMenus.forEach(menu => {
(menu as HTMLElement).style.display = 'none';
menu.setAttribute('aria-expanded', 'false');
});
}
} else {
body.classList.remove('vertical-collpsed');
this.storageService.setItem('sidebarState', 'expanded');
const subMenus = document.querySelectorAll('.sub-menu');
subMenus.forEach(menu => {
(menu as HTMLElement).style.display = 'none';
menu.setAttribute('aria-expanded', 'false');
});
}
}
}
toggleDropdown(): void {
this.isDropdownVisible = !this.isDropdownVisible;
this.isNotificationsVisible = false;
}
toggleVacDropdown(): void {
this.isVacDropdownVisible = !this.isVacDropdownVisible;
this.isNotificationsVisible = false;
}
toggleNotifications(): void {
this.isNotificationsVisible = !this.isNotificationsVisible;
this.isDropdownVisible = false;
}
// toggleSidebar() {
// this.sidebarService.toggleSidebar();
// }
logout() {
this.authService.logout();
}
@HostListener('document:click', ['$event'])
handleClickOutside(event: MouseEvent) {
const targetElement = event.target as HTMLElement;
const isClickInsideProfileDropdown = targetElement.closest('.profile-dropdown');
const isClickInsideVacDropdown = targetElement.closest('.vac-dropdown');
if (!isClickInsideProfileDropdown) {
this.isDropdownVisible = false;
}
if (!isClickInsideVacDropdown) {
this.isVacDropdownVisible = false;
}
}
formatDate(date?: Date) {
if ((date && !isNaN(date.getTime())) || (date != null || date != undefined)) {
// Adil 5152 - Changing the Date Locale based on the language selected
return formatDate(date, 'EEEE, d MMMM yyyy', 'en');
}
return null;
}
}

@ -0,0 +1,5 @@
@if (loading$ | async) {
<div class="loader-overlay">
<div class="loader"></div>
</div>
}

@ -0,0 +1,27 @@
.loader-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loader {
border: 16px solid #f3f3f3;
border-radius: 50%;
border-top: 16px solid #3498db;
width: 120px;
height: 120px;
animation: spin 2s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoaderComponent } from './loader.component';
describe('LoaderComponent', () => {
let component: LoaderComponent;
let fixture: ComponentFixture<LoaderComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [LoaderComponent]
})
.compileComponents();
fixture = TestBed.createComponent(LoaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,18 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { LoadingService } from '../../services/loading.service';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-loader',
imports: [CommonModule],
templateUrl: './loader.component.html',
styleUrl: './loader.component.scss'
})
export class LoaderComponent {
loading$: Observable<boolean>;
constructor(private loadingService: LoadingService) {
this.loading$ = this.loadingService.loading$;
}
}

@ -0,0 +1,4 @@
<div *ngIf="notifications$ | async as notification" class="notification" [ngClass]="notification.type">
{{ notification.message }}
<button class="close-btn" (click)="clearNotification()">×</button>
</div>

@ -0,0 +1,88 @@
.notification {
position: fixed;
top: 20px;
right: 20px;
max-width: 400px;
padding: 16px 48px 16px 20px;
border-radius: 6px;
z-index: 10000;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
background-color: #ffffff;
color: #333333;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 14px;
line-height: 1.4;
transition: opacity 0.4s ease, transform 0.3s ease;
animation: fadeIn 0.3s ease forwards;
border-left: 8px solid #c9c9c9;
display: flex;
align-items: center;
justify-content: space-between;
&.success {
background-color: #e8f5e9;
border-color: #4caf50;
color: #2e7d32;
}
&.error {
background-color: #ffebee;
border-color: #f44336;
color: #c62828;
}
&.warning {
background-color: #fffde7;
border-color: #ffeb3b;
color: #fbc02d;
}
&.info {
background-color: #e3f2fd;
border-color: #2196f3;
color: #1976d2;
}
&.fade-out {
animation: fadeOut 0.4s ease forwards;
}
}
.close-btn {
position: absolute;
top: 12px;
right: 16px;
width: 20px;
height: 20px;
background: none;
border: none;
font-size: 20px;
color: inherit;
cursor: pointer;
transition: color 0.3s;
line-height: 1;
font-weight: bold;
&:hover {
color: #000000;
}
}
/* Animations */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
@keyframes fadeOut {
to {
opacity: 0;
transform: translateX(100px);
}
}

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NotificationsComponent } from './notifications.component';
describe('NotificationsComponent', () => {
let component: NotificationsComponent;
let fixture: ComponentFixture<NotificationsComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [NotificationsComponent]
})
.compileComponents();
fixture = TestBed.createComponent(NotificationsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,21 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { NotificationService } from '../../services/notification.service';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-notifications',
imports: [CommonModule],
templateUrl: './notifications.component.html',
styleUrl: './notifications.component.scss'
})
export class NotificationsComponent {
notifications$: Observable<{ type: string; message: string; } | null>
constructor(private notificationService: NotificationService) {
this.notifications$ = this.notificationService.notifications$;
}
clearNotification() {
this.notificationService.clearNotification();
}
}

@ -0,0 +1 @@
<button class="btn btn-light lh-sm" type="button" (click)="togglePassword()"><i [hidden]="!this.showPassword" class="mdi mdi-eye-off-outline"></i><i class="mdi mdi-eye-outline" [hidden]="this.showPassword"></i></button>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PasswordHideShowComponent } from './password-hide-show.component';
describe('PasswordHideShowComponent', () => {
let component: PasswordHideShowComponent;
let fixture: ComponentFixture<PasswordHideShowComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PasswordHideShowComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PasswordHideShowComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,23 @@
import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'app-password-hide-show',
imports: [],
templateUrl: './password-hide-show.component.html',
styleUrl: './password-hide-show.component.scss'
})
export class PasswordHideShowComponent {
@Output() onEyeClick = new EventEmitter();
@Input() showPassword : boolean = false;
inputType : String = '';
constructor() { }
ngOnInit(): void {
}
togglePassword(){
this.showPassword = !this.showPassword;
this.onEyeClick.emit();
}
}

@ -0,0 +1,108 @@
<div class="vertical-menu bg-primary" (click)="handleMenuClick($event)">
<div id="sidebar-menu" class="hidden-scroll">
<ul class="metismenu list-unstyled" id="side-menu">
<li>
<a href="javascript: void(0);" routerLink="/home/dashboard" routerLinkActive="mm-active">
<i class="fa fa-home"></i>
<span>{{ 'dashboard' | translate }}</span>
</a>
</li>
<li>
<a href="javascript:void(0);" class="has-arrow waves-effect" (click)="toggleMenu($event)">
<i class="fa fa-user-secret"></i>
<span>{{ 'UserManagement' | translate }}</span>
</a>
<ul class="sub-menu" aria-expanded="false">
<li>
<a routerLink="/home/thirdPartyRegistration" routerLinkActive="mm-active">
<span> {{ 'thirdPartyRegistration' | translate }}</span>
</a>
</li>
<li>
<a routerLink="/home/setupUser" routerLinkActive="mm-active">
<span>{{ 'setupUser' | translate }}</span>
</a>
</li>
<li>
<a routerLink="/home/resetPassword" routerLinkActive="mm-active">
<span> {{ 'resetPassword' | translate }}</span>
</a>
</li>
<li>
<a routerLink="/home/changePassword" routerLinkActive="mm-active" (click)="navigateToChangePassword()" style="cursor: pointer">
<span> {{ 'changePassword' | translate }}</span>
</a>
</li>
</ul>
</li>
<li>
<a href="javascript:void(0);" class="has-arrow waves-effect" (click)="toggleMenu($event)">
<i class="fa fa-history"></i>
<span>{{ 'Logging' | translate }}</span>
</a>
<ul class="sub-menu" aria-expanded="false">
<li>
<a routerLink="/home/loggerManager" routerLinkActive="mm-active">
<span> {{ 'loggerManager' | translate }}</span>
</a>
</li>
</ul>
</li>
<li>
<a href="javascript:void(0);" class="has-arrow waves-effect" (click)="toggleMenu($event)">
<i class="mdi mdi-comment-outline"></i>
<span>{{ 'SMSBanking' | translate }}</span>
</a>
<ul class="sub-menu" aria-expanded="false">
<li>
<a routerLink="/home/smsLogger" routerLinkActive="mm-active">
<span> {{ 'smsLogger' | translate }}</span>
</a>
</li>
<li>
<a routerLink="/home/smsGateway" routerLinkActive="mm-active">
<span> {{ 'smsGateway' | translate }}</span>
</a>
</li>
</ul>
</li>
<li>
<a href="javascript:void(0);" class="has-arrow waves-effect" (click)="toggleMenu($event)">
<i class="mdi mdi-comment-outline"></i>
<span>{{ 'ibSupport' | translate }}</span>
</a>
<ul class="sub-menu" aria-expanded="false">
<li>
<a routerLink="/home/ibUnblockUser" routerLinkActive="mm-active">
<span> {{ 'ibUnblockUser' | translate }}</span>
</a>
</li>
<li>
<a routerLink="/home/feedbackSetup" routerLinkActive="mm-active">
<span> {{ 'feedbackSetup' | translate }}</span>
</a>
</li>
<li>
<a routerLink="/home/purposeSetup" routerLinkActive="mm-active">
<span> {{ 'purposeSetup' | translate }}</span>
</a>
</li>
</ul>
</li>
<li>
<a href="javascript:void(0);" class="has-arrow waves-effect" (click)="toggleMenu($event)">
<i class='fa fa-lock'></i>
<span>{{ 'Permissions' | translate }}</span>
</a>
<ul class="sub-menu" aria-expanded="false">
<li>
<a routerLink="/home/permissions" routerLinkActive="mm-active">
<span> {{ 'permissions' | translate }}</span>
</a>
</li>
</ul>
</li>
</ul>
</div>
</div>

@ -0,0 +1,30 @@
.active-submenu {
font-weight: bold;
}
.hidden-scroll {
overflow-y: auto;
-ms-overflow-style: none;
scrollbar-width: none;
}
.hidden-scroll::-webkit-scrollbar {
display: none;
}
#sidebar-menu {
height: calc(100vh - 60px);
}
@media (max-width: 768px) {
#sidebar-menu {
height: calc(100vh - 100px);
}
}
.sub-menu {
display: none;
}
.sub-menu[aria-expanded="true"] {
display:block;
}

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SideNavComponent } from './side-nav.component';
describe('SideNavComponent', () => {
let component: SideNavComponent;
let fixture: ComponentFixture<SideNavComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SideNavComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SideNavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,110 @@
import { Component, Inject, PLATFORM_ID } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { SidebarService } from '../../../services/sidebar.service';
import { StorageService } from '../../services/storage.service';
import { isPlatformBrowser } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { RouterModule } from '@angular/router';
import { Router } from '@angular/router';
@Component({
selector: 'app-side-nav',
imports: [TranslateModule, RouterModule],
templateUrl: './side-nav.component.html',
styleUrl: './side-nav.component.scss',
})
export class SideNavComponent {
isDropdownVisible = false;
isNotificationsVisible = false;
searchForm!: FormGroup;
permissions: any = {};
activeMenu: string | null = null;
direction: string = 'ltr';
constructor(
private sidebarService: SidebarService,
@Inject(PLATFORM_ID) private platformId: Object,
private storageService: StorageService,
private router: Router
) {
// this.credentialService.getPermission().forEach((permission: any) => {
// this.permissions[permission.name] = permission.checked;
// if(permission.children.length>0){
// permission.children.forEach((child: any)=>{
// this.permissions[child.name] = child.checked;
// })
// }
// });
}
ngOnInit(): void {
this.sidebarService.currentSubModule = this.storageService.getItem('currentSubModule') ?? 'dashboard';
this.closeSidebarMenu();
}
navigateToChangePassword() {
this.router.navigate(['/home/changePassword'], {
state: { fromMenu: true }
});
}
closeSidebarMenu(): void {
if (isPlatformBrowser(this.platformId)) {
const subMenus = document.querySelectorAll('#sidebar-menu .sub-menu');
subMenus.forEach(menu => {
(menu as HTMLElement).style.display = 'none';
menu.setAttribute('aria-expanded', 'false');
});
}
}
toggleMenu(event: Event): void {
const target = event.currentTarget as HTMLElement;
const submenu = target.nextElementSibling as HTMLElement;
if (submenu && submenu.classList.contains('sub-menu')) {
const isExpanded = submenu.getAttribute('aria-expanded') === 'true';
submenu.style.display = isExpanded ? 'none' : '';
submenu.setAttribute('aria-expanded', isExpanded ? 'false' : 'true');
this.storageService.setItem('menuState' + submenu.id, isExpanded ? 'false' : 'true'); // Saving state per submenu
if (window.innerWidth <= 992) {
const links = submenu.querySelectorAll('a');
links.forEach(link => {
link.addEventListener('click', () => {
const body = document.body;
body.classList.remove('sidebar-enable'); // Hide the sidebar
this.storageService.setItem('sidebarState', 'collapsed'); // Store collapsed state
});
});
}
}
}
toggleDropdown(): void {
this.isDropdownVisible = !this.isDropdownVisible;
this.isNotificationsVisible = false;
}
toggleNotifications(): void {
this.isNotificationsVisible = !this.isNotificationsVisible;
this.isDropdownVisible = false;
}
handleMenuClick(event: Event) {
const target = event.target as HTMLElement;
const linkElement = target.closest('a[routerLink]');
if (linkElement) {
const routerLink = linkElement.getAttribute('routerLink');
if (routerLink) {
this.onModuleClick(routerLink);
}
}
}
onModuleClick(route: string) {
const routeParts = route.split('/').filter(part => part.length > 0);
const lastRoutePart = routeParts[routeParts.length - 1];
this.sidebarService.currentSubModule = lastRoutePart;
if (isPlatformBrowser(this.platformId)) {
this.storageService.setItem('currentSubModule', lastRoutePart);
}
}
}

@ -0,0 +1,59 @@
import { LocationStrategy } from '@angular/common';
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { AuthenticationService } from '../../services/authenticate.service';
import { I18NService } from '../../services/i18n.service';
import { ErrorMessages, FormConstants } from '../../utils/enums';
import { CredentialService } from '../../services/credential.service';
@Injectable(
{ providedIn: 'root' }
)
export class ActivityGuard implements CanActivate {
constructor(private router: Router, private authService: AuthenticationService, private i18nService: I18NService, private credentialService: CredentialService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (typeof window !== 'undefined' && window.localStorage) {
let permissions = JSON.parse(window.localStorage.getItem('permission') || '[]');
if (this.authService.isAuthenticated()) {
if (this.authService.isSuperAdminUser()){
return true;
}
let routeLink = (state.url.split('?'))[0];
if (this.isRouteAuthorized(routeLink, route.queryParams, permissions)) {
return true;
}
this.i18nService.error(ErrorMessages.ACCESS_DENIED, []);
window.localStorage.setItem('currentSubModule','dashboard');
this.router.navigate(["/home/dashboard"]);
return false;
} else {
this.authService.logout();
return false;
}
}
return false;
}
isRouteAuthorized(routerLink: string, queryParams: any, permissions: any): boolean {
let routePermissions : any = {}
let permissionName : any = {}
permissions.forEach((permission: any) => {
routePermissions[permission.route] = permission.checked;
permissionName[permission.name] = permission.checked;
if(permission.children.length>0){
permission.children.forEach((child: any)=>{
routePermissions[child.route] = child.checked;
permissionName[child.name] = child.checked;
})
}
});
if(routePermissions[routerLink]){
return true;
}
return false;
}
}

@ -0,0 +1,40 @@
import { LocationStrategy } from '@angular/common';
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { AuthenticationResponse } from '../../authenticate/authenticate';
import { AuthenticationService } from '../../services/authenticate.service';
import { CredentialService } from '../../services/credential.service';
import { FormConstants } from '../../utils/enums';
import { ButtonManagementService } from '../../services/button-management.service';
@Injectable(
{ providedIn: 'root' }
)
export class AuthenticationGuard implements CanActivate {
constructor(private router: Router, private authService: AuthenticationService, private location: LocationStrategy, private credentialService: CredentialService,private buttonManagementService: ButtonManagementService) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
if (typeof window !== 'undefined' && window.localStorage) {
let data = JSON.parse(window.localStorage.getItem('user') || '{}') as AuthenticationResponse;
let permission = JSON.parse(window.localStorage.getItem('permission') || '[]');
if (this.authService.isAuthenticated()) {
this.credentialService.setPorOrgacode(window.localStorage.getItem(FormConstants.POR_ORGACODE) || '');
this.credentialService.setUserId(window.localStorage.getItem(FormConstants.USER_ID) || '');
this.credentialService.setPassword(window.localStorage.getItem(FormConstants.PASSWORD) || '');
this.credentialService.setToken(data.token);
this.credentialService.setUserType(data.userType);
this.credentialService.setPermission(permission);
this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.authService.isSuperAdminUser());
this.authService.onAuthenticationComplete.next(true);
return true;
} else {
this.authService.logout();
return false;
}
}
return false;
}
}

@ -0,0 +1,133 @@
import { Injectable, Injector } from '@angular/core';
import {HttpRequest,HttpHandler,HttpEvent,HttpInterceptor,HttpErrorResponse} from '@angular/common/http';
import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, switchMap, take } from 'rxjs/operators';
import { ErrorMessages } from '../../utils/enums';
import { environment } from '../../../environments/environment';
import { CredentialService } from '../../services/credential.service';
import { EncryptionService } from '../../services/encryption.service';
import { NotificationService } from '../services/notification.service';
import { I18NService } from '../../services/i18n.service';
import { AuthenticationService } from '../../services/authenticate.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private isRefreshing = false;
private refreshTokenSubject: BehaviorSubject<any> = new BehaviorSubject<any>(null);
private enableEncryption = environment.enableEncryption;
constructor(private injector: Injector, private credentialService: CredentialService, private encryptionService: EncryptionService, private notificationService: NotificationService, private i18nService: I18NService) {}
intercept(request: HttpRequest<any>, handler: HttpHandler): Observable<HttpEvent<any>> {
if (this.credentialService.getPorOrgacode()!= undefined){
request = this.setDefaultHeaders(request);
}
// FOR BIOMETRIC SECUGEN WE BYPASS THESE URIS AS SECUGEN DRIVERS IS USING LOCAL ENDPOINTS.
if (this.credentialService.getToken()&& !request.url.endsWith("/SGIFPCapture")&& !request.url.endsWith("/CreateTemplate")&& !request.url.endsWith("/verifyUserBiometric")) {
request = this.addToken(request, this.credentialService.getToken());
}
if(this.enableEncryption && (request.method === "POST" || request.method === "PATCH" ) && !request.url.endsWith("/SGIFPCapture")&& !request.url.endsWith("/createTemplate")&& !request.url.endsWith("/verifyUserBiometric"))
{
request = this.setEncryptionHeader(request);
request = this.encryptRequestBody(request);
}
return handler.handle(request).pipe(catchError(error => {
if (error instanceof HttpErrorResponse && error.status === 401) {
return this.handleAuthError(request, handler);
} else {
this.handleServerError(error);
return throwError(error);
}
}));
}
private encryptRequestBody(request: HttpRequest<any>): HttpRequest<any> {
if (Object.keys(request.body).length > 0) {
const encryptedData: object = this.encryptionService.encryptData(request.body);
const encryptedRequest: any = request.clone({ body: encryptedData });
return encryptedRequest;
}
return request;
}
private handleAuthError(request: HttpRequest<any>, handler: HttpHandler) {
if (!this.isRefreshing) {
this.isRefreshing = true;
this.refreshTokenSubject.next(null);
let authService: AuthenticationService = this.injector.get(AuthenticationService);
return authService.refreshToken().pipe(
switchMap((response: any) => {
this.isRefreshing = false;
this.refreshTokenSubject.next(response.token);
return handler.handle(this.addToken(request, response.token)).pipe(catchError(error => {
this.handleServerError(error);
return throwError(error);
}));
}));
} else {
return this.refreshTokenSubject.pipe(
filter(token => token != null),
take(1),
switchMap(token => {
return handler.handle(this.addToken(request, token));
}));
}
}
private handleServerError(error: HttpErrorResponse) {
let url: string = error.url as string;
let moduleName: string = "";
if (url != null && url != undefined) {
moduleName = url.split(':').length>2 ?
url.split(':')[2].split('/')[1]:
url.split('/')[3];
}
let authService: AuthenticationService = this.injector.get(AuthenticationService);
switch (error.status) {
case 400:
let errorResponse:any = error ;
if (errorResponse.error && errorResponse.error.errorCode != null) {
this.i18nService.error(errorResponse.error.errorCode, errorResponse.error.arguments);
} else {
this.i18nService.error(ErrorMessages.BAD_REQUEST,[moduleName.toUpperCase()]);
}
break;
case 401:
this.i18nService.error(ErrorMessages.UNAUTHORIZED_REQUEST,[]);
authService.logout();
break;
case 403:
this.i18nService.error(ErrorMessages.FORBIDDEN_REQUEST,[]);
authService.logout();
break;
case 500:
this.i18nService.error(ErrorMessages.INTERNAL_SERVER_ERROR,[moduleName.toUpperCase()]);
break;
case 0:
this.i18nService.error(ErrorMessages.CONNECTION_ERROR,[moduleName.toUpperCase()]);
break;
}
}
private addToken(request: HttpRequest<any>, token: string) {
return request.clone({
setHeaders: {
'Authorization': `Bearer ${token}`
}
});
}
private setDefaultHeaders(request: HttpRequest<any>): HttpRequest<any> {
const modifiedHeaders = request.headers.set('userId', this.credentialService.getUserId())
.append('porOrgacode', this.credentialService.getPorOrgacode())
return request.clone({ headers: modifiedHeaders });
}
private setEncryptionHeader(request: HttpRequest<any>): HttpRequest<any> {
const modifiedHeaders = request.headers.set('X-Encrypted', this.enableEncryption.toString());
return request.clone({ headers: modifiedHeaders });
}
}

@ -0,0 +1,18 @@
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { LoadingService } from '../services/loading.service';
@Injectable()
export class LoadingInterceptor implements HttpInterceptor {
constructor(private loadingService: LoadingService) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.loadingService.showLoader();
return next.handle(request).pipe(
finalize(() => this.loadingService.hideLoader())
);
}
}

@ -0,0 +1,108 @@
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { FormConstants, HiddenValues } from '../../utils/enums';
@Injectable(
{ providedIn: 'root' }
)
export class HttpService {
private loadingSubject = new BehaviorSubject<boolean>(false);
loading$ = this.loadingSubject.asObservable();
private setLoading(loading: boolean) {
this.loadingSubject.next(loading);
}
constructor(private http: HttpClient) {
}
requestPOST<T>(url: string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
this.setLoading(true);
if (headers == undefined) {
headers = new HttpHeaders().set('Content-Type', 'application/json');
}
headers = headers.set(FormConstants.POR_ORGACODE, HiddenValues.POR_ORGACODE).set(FormConstants.CHANNEL_CODE, HiddenValues.CHANNEL_CODE)
if (params == undefined) {
return this.http.post<T>(url, body, { headers: headers })
} else {
url = this.substituePathVariables(url, params);
return this.http.post<T>(url, body, { params: params, headers: headers });
}
}
requestPOSTMultipart<T>(url: string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
this.setLoading(true);
if (headers == undefined) {
headers = new HttpHeaders();
}
headers = headers.set(FormConstants.POR_ORGACODE, HiddenValues.POR_ORGACODE).set(FormConstants.CHANNEL_CODE, HiddenValues.CHANNEL_CODE)
if (params == undefined) {
return this.http.post<T>(url, body, { headers: headers })
} else {
url = this.substituePathVariables(url, params);
return this.http.post<T>(url, body, { params: params, headers: headers });
}
}
requestGET<T>(url: string, reqParams?: HttpParams): Observable<T> {
this.setLoading(true);
let httpHeaders: HttpHeaders = new HttpHeaders();
httpHeaders = httpHeaders.set(FormConstants.POR_ORGACODE,HiddenValues.POR_ORGACODE).set(FormConstants.CHANNEL_CODE,HiddenValues.CHANNEL_CODE);
if (reqParams == undefined) {
return this.http.get<T>(url, { headers: httpHeaders })
} else {
url = this.substituePathVariables(url, reqParams);
return this.http.get<T>(url, { params: reqParams, headers: httpHeaders });
}
}
requestDELETE<T>(url: string, reqParams: HttpParams): Observable<T> {
this.setLoading(true);
url = this.substituePathVariables(url, reqParams);
let httpHeaders: HttpHeaders = new HttpHeaders().set(FormConstants.POR_ORGACODE,HiddenValues.POR_ORGACODE).set(FormConstants.CHANNEL_CODE,HiddenValues.CHANNEL_CODE);
if (reqParams.keys().length > 0) {
return this.http.delete<T>(url, { params: reqParams, headers: httpHeaders });
}
else {
return this.http.delete<T>(url, { headers: httpHeaders });
}
}
requestPATCH<T>(url: string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
this.setLoading(true);
if (headers == undefined) {
headers = new HttpHeaders().set('Content-Type', 'application/json');
}
if (params != undefined) {
url = this.substituePathVariables(url, params);
}
headers = headers.set(FormConstants.POR_ORGACODE,HiddenValues.POR_ORGACODE).set(FormConstants.CHANNEL_CODE, HiddenValues.CHANNEL_CODE);
return this.http.patch<T>(url, body, { headers: headers, params: params });
}
requestPUT<T>(url: string, body: any, headers?: HttpHeaders, params?: HttpParams): Observable<T> {
this.setLoading(true);
if (headers == undefined) {
headers = new HttpHeaders().set('Content-Type', 'application/json');
}
if (params != undefined) {
url = this.substituePathVariables(url, params);
}
headers = headers.set(FormConstants.POR_ORGACODE,HiddenValues.POR_ORGACODE).set(FormConstants.CHANNEL_CODE, HiddenValues.CHANNEL_CODE);
return this.http.put<T>(url, body, { headers: headers, params: params });
}
private substituePathVariables(url: string, params: HttpParams): string {
params.keys().forEach(param => {
let pathVariable: string = `{${param}}`;
let pathValue = params.get(param);
if (url.includes(pathVariable) && pathValue != null) {
url = url.replace(pathVariable, pathValue);
params.delete(param);
}
});
return url;
}
}

@ -0,0 +1,19 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class LoadingService {
private loadingSubject = new BehaviorSubject<boolean>(false);
loading$ = this.loadingSubject.asObservable();
showLoader() {
this.loadingSubject.next(true);
}
hideLoader() {
this.loadingSubject.next(false);
}
}

@ -0,0 +1,48 @@
import { Injectable } from '@angular/core';
import { MessageService } from '../../services/message.service';
import { SpinnerService } from '../../services/spinner.service';
import { TranslateService } from '@ngx-translate/core';
@Injectable({
providedIn: 'root'
})
export class MiscService {
constructor(private message: MessageService, private spinnerService: SpinnerService,private translateService: TranslateService,) { }
showLoader(): void {
this.spinnerService.IsBusy(true);
}
hideLoader(): void {
this.spinnerService.IsBusy(false);
}
handleSuccess(message: string): void {
this.message.Success(message);
}
handleError(errorMessage: string, argumentValues: string[] = []): void {
const translatedErrorMessage = this.translateService.instant(errorMessage, { value1: argumentValues[0], value2: argumentValues[1], value3: argumentValues[2] });
this.message.Error(translatedErrorMessage);
}
getErrorMessageTranslation(key: string) {
return this.getTranslation(`${key}`);
}
getTranslation(key: string, defaultLabel?: string): string {
if (key == null || key === "" || key == undefined) {
return defaultLabel ? defaultLabel : "";
}
let translated = this.translateService.instant(String(key));
if (translated == key || translated == "") {
if (defaultLabel && defaultLabel.length > 0) {
return defaultLabel;
}
else {
return key;
}
} else {
return translated;
}
}
}

@ -0,0 +1,40 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, timer } from 'rxjs';
import { switchMap, startWith } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class NotificationService {
private notificationSubject = new BehaviorSubject<{ type: string, message: string } | null>(null);
notifications$ = this.notificationSubject.asObservable();
success(message: string) {
this.notify('success', 'Success: ' + message);
}
error(message: string) {
this.notify('error', 'Error: ' + message);
}
warning(message: string) {
this.notify('warning', 'Warning: ' + message);
}
info(message: string) {
this.notify('info', 'Info: ' + message);
}
private notify(type: string, message: string) {
this.notificationSubject.next({ type, message });
// Automatically clear notification after 3 seconds using RxJS timer
this.notifications$.pipe(
startWith(null),
switchMap(() => timer(5000))
).subscribe(() => this.notificationSubject.next(null));
}
clearNotification() {
this.notificationSubject.next(null);
}
}

@ -0,0 +1,33 @@
import { isPlatformBrowser } from '@angular/common';
import { Inject, Injectable, PLATFORM_ID } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class StorageService {
private isBrowser: boolean = false;
constructor(@Inject(PLATFORM_ID) platformId: object) {
this.isBrowser = isPlatformBrowser(platformId);
}
getItem(key: string): string | null {
if (this.isBrowser) {
return localStorage.getItem(key);
}
return null;
}
setItem(key: string, value: string) {
if (this.isBrowser) {
localStorage.setItem(key, value);
}
}
clear() {
if (this.isBrowser) {
localStorage.clear();
}
}
}

@ -0,0 +1,207 @@
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'smsLogger' | translate}}
</div>
<div class="card-body">
<form>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="fromDate" class="text-nowrap">
{{ 'fromDate' | translate }}<span
class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100">
<div
class="d-flex flex-row align-items-stretch">
<input type="date" id="fromDate"
class="form-control"
appNoWhitespaces />
</div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="toDate" class="text-nowrap mt-2">
{{ 'toDate' | translate }}<span
class="mandatory">*</span>
</label>
<div
class="password-wrapper position-relative w-100">
<input id="toDate" type="date" class="form-control"
maxlength="500"
appNoWhitespaces rows="3" />
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button
class="btn btn-primary waves-effect waves-light">{{'findLogs'
| translate}}</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
{{'smsLoggerDetails' | translate}}
<div class="d-flex align-items-center gap-2">
<div class="search-box">
<input type="text" class="form-control form-control-sm"
placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i>
</div>
<i class="materialdesignicons">
<ng-container *ngIf="renewalDataExpanded; else collapsedIcon">
<i class="dripicons-chevron-up float-end"></i>
</ng-container>
<ng-template #collapsedIcon>
<i class="dripicons-chevron-down float-end"></i>
</ng-template>
</i>
</div>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table mb-0 border">
<thead class="table-light">
<tr>
<th>{{'smsTrackingID' | translate}}</th>
<th>{{'smsMessage' | translate}}</th>
<th>{{'smsNo' | translate}}</th>
<th>{{'smsOrgaCode' | translate}}</th>
<th>{{'smsDate' | translate}}</th>
<th>{{'smsStatus' | translate}}</th>
<th>{{'action' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<div
class="d-flex justify-content-center gap-2">
<button class="btn btn-info btn-sm"
title="View">
<i class="mdi mdi-eye-outline"></i>
</button>
<button class="btn btn-secondary btn-sm"
title="Edit">
<i class="fas fa-pen"></i>
</button>
<button class="btn btn-danger btn-sm"
title="Delete">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div
class="d-flex justify-content-between align-items-center mt-3">
<div class="form-group mb-0">
<ng-select class="form-select-sm"
[items]="pageSizeOptions" bindLabel="label"
bindValue="value" [(ngModel)]="itemsPerPage"
[searchable]="false" [clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted">
{{ 'page' | translate }} {{ 'of' | translate }} ({{
'totalItems' | translate }})
</div>
<div class="btn-group">
<button
class="btn btn-primary waves-effect waves-light">
{{ 'previous' | translate }}
</button>
<button
class="btn btn-primary waves-effect waves-light">
{{ 'next' | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SmsBankingComponent } from './sms-banking.component';
describe('SmsBankingComponent', () => {
let component: SmsBankingComponent;
let fixture: ComponentFixture<SmsBankingComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SmsBankingComponent]
})
.compileComponents();
fixture = TestBed.createComponent(SmsBankingComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,21 @@
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { pageSizeOptions } from '../utils/app.constants';
@Component({
selector: 'app-sms-banking',
imports: [TranslateModule, ReactiveFormsModule, NgSelectModule, CommonModule, FormsModule],
templateUrl: './sms-banking.component.html',
styleUrl: './sms-banking.component.scss'
})
export class SmsBankingComponent {
currentPage: number = 1;
pageSizeOptions = pageSizeOptions
renewalDataExpanded: boolean = true
itemsPerPage: number = 5;
searchText: any;
}

@ -0,0 +1,280 @@
<div id="layout-wrapper">
<div class="inner-pg-sp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<div class="d-sm-flex align-items-center justify-content-between navbar-header p-0">
</div>
</div>
</div>
<div class="container-fluid">
<div class="col-xl-12 mt-4">
<div class="card border">
<div class="card-body">
<div class="table-section">
<div class="row">
<div class="col-lg-12">
<div class="card-body mt-2 p-0">
<div class="card mb-0 mt-2">
<div
class="card-header font-edit-13-child d-flex justify-content-between align-items-center">
<span *ngIf="!selectedGateway"></span>
<span *ngIf="selectedGateway">{{(selectedGateway === selectedGatewayType.SYRIATEL ? 'syriatelCredentials' : (selectedGateway === selectedGatewayType.TWILIO ? 'twilioCredentials' : (selectedGateway === selectedGatewayType.JAZZ ? 'jazzCredentials' : ''))) | translate}}</span>
<select class="form-select"style="min-width: 200px; width: auto;" [(ngModel)]="selectedGateway">
<option value="">{{'SMSGatewaySelect' | translate}}</option>
<option [value]="selectedGatewayType.SYRIATEL">{{'SMSGatewaySyriatel' | translate}}</option>
<option [value]="selectedGatewayType.TWILIO">{{'SMSGatewayTwillio' | translate}}</option>
<option [value]="selectedGatewayType.JAZZ">{{'SMSGatewayJazz' | translate}}</option>
</select>
</div>
<div class="card-body">
<form *ngIf="selectedGateway==='Jazz' || selectedGateway==='Twilio'">
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="accountSID" class="text-nowrap">
{{ 'accountSID' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch">
<input type="text" id="accountSID"
class="form-control"
placeholder="{{ 'accountSID' | translate }}" appNoWhitespaces
/>
</div>
<!-- <div class="text-danger">
{{ 'requiredField' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="authToken"
class="text-nowrap mt-2">
{{ 'authToken' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="authToken" class="form-control" autocomplete="new-password" maxlength="500"
placeholder="{{ 'authToken' | translate }}" appNoWhitespaces rows="3" />
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="fromNumber" class="text-nowrap">
{{ 'fromNumber' | translate }}<span
class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input id="fromNumber" class="form-control"
placeholder="{{ 'fromNumber' | translate }}" appNoWhitespaces />
<!-- <div class="text-danger">
<div>
{{ 'requiredField' | translate }}
</div>
</div>
<div>
{{ 'expiryBeforeRenewal' | translate }}
</div> -->
</div>
</div>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label class="text-nowrap">
{{ 'notificationType' | translate }}<span
class="mandatory">*</span>
</label>
<div class="w-100 d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="notificationType" id="message" value="Message">
<label class="form-check-label" for="message">
{{ 'message' | translate }}
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="notificationType" id="template" value="Template">
<label class="form-check-label" for="template">
{{ 'template' | translate }}
</label>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label class="text-nowrap">
{{ 'language' | translate }}<span
class="mandatory">*</span>
</label>
<div class="w-100 d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="language" id="languageArabic" value="Arabic">
<label class="form-check-label" for="languageArabic">
{{ 'arabic' | translate }}
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="language" id="languageEnglish" value="English">
<label class="form-check-label" for="languageEnglish">
{{ 'english' | translate }}
</label>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button
class="btn btn-primary waves-effect waves-light"
>{{'save' | translate}}</button>
</div>
</div>
</form>
<form *ngIf="selectedGateway==='Syriatel'">
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="userName" class="text-nowrap">
{{ 'userName' | translate }}<span class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<div class="d-flex flex-row align-items-stretch">
<input type="text" id="userName" class="form-control" placeholder="{{ 'userNamePlaceHolder' | translate }}"
appNoWhitespaces />
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label for="senderName" class="text-nowrap">
{{ 'senderName' | translate }}<span class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input type="text" id="senderName" class="form-control" placeholder="{{ 'senderNamePlaceHolder' | translate }}"
appNoWhitespaces />
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-start gap-2">
<label for="password" class="text-nowrap mt-2">
{{ 'password' | translate }}<span class="mandatory">*</span>
</label>
<div class="password-wrapper position-relative w-100">
<input type="password" id="password" class="form-control" autocomplete="new-password"
maxlength="500" placeholder="{{ 'passwordPlaceholder' | translate }}" appNoWhitespaces />
</div>
</div>
</div>
<div class="col-md-6">
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label class="text-nowrap">
{{ 'notificationType' | translate }}<span class="mandatory">*</span>
</label>
<div class="w-100 d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="notificationType" id="message"
value="Message">
<label class="form-check-label" for="message">
{{ 'message' | translate }}
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="notificationType" id="template"
value="Template">
<label class="form-check-label" for="template">
{{ 'template' | translate }}
</label>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="d-flex align-items-center gap-2">
<label class="text-nowrap">
{{ 'language' | translate }}<span class="mandatory">*</span>
</label>
<div class="w-100 d-flex gap-3">
<div class="form-check">
<input class="form-check-input" type="radio" name="language" id="languageArabic" value="Arabic">
<label class="form-check-label" for="languageArabic">
{{ 'arabic' | translate }}
</label>
</div>
<div class="form-check">
<input class="form-check-input" type="radio" name="language" id="languageEnglish"
value="English">
<label class="form-check-label" for="languageEnglish">
{{ 'english' | translate }}
</label>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button class="btn btn-primary waves-effect waves-light">
{{'save' | translate}}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save