Compare commits

...

52 Commits

Author SHA1 Message Date
atif118-mfsys 180ecd38a1 Merge branch 'dev-pending-09-12-2025' into aconnect-UX/1715 4 hours ago
atif118-mfsys dee23848ea integrated get all users API to provide options for user dropdown in permission management 4 hours 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
8 hours ago
Mazdak Gibran 80a1e62411 onDelete form reset
onDelete form reset
8 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 9 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
9 hours ago
Mazdak Gibran 1976648c08 Integrate deleteUser Api
Integrate deleteUser Api
1 day 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 1 week ago
atif118-mfsys 72d5d08828 Merge branch 'dev-pending-09-12-2025' into GBRSP/UX-1599 1 week ago
atif118-mfsys 1c0b19e33c added activity guard and added some translations 1 week 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