Compare commits

..

114 Commits

Author SHA1 Message Date
Mazdak Gibran 150fb0f88b add success message
add success message to change password and reset password, and fixed the routing in change password.
2 weeks ago
Naeem Ullah e122f8bf9e Merge pull request 'live test' (#39) from mazdak/UX-live-test into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/39
3 weeks ago
Mazdak Gibran 0b013393d5 Update side-nav.component.html 3 weeks ago
Naeem Ullah d0b8d7f374 Update environment.test.ts 3 weeks ago
Naeem Ullah 6e459179ce Merge pull request 'mazdak/UX-fix-changePassword' (#38) from mazdak/UX-fix-changePassword into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/38
3 weeks ago
Naeem Ullah 819e11b3d3 added profiler 3 weeks ago
Mazdak Gibran 3ac1eb2bd9 show username in header 3 weeks ago
Mazdak Gibran aa3b22c65f Update change-password.component.html 3 weeks ago
Naeem Ullah cbe3b93043 Merge pull request 'fixed small bugs and adjusted styling' (#37) from mazdak/UX-styling into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/37
3 weeks ago
Mazdak Gibran 6252fed4f4 delete pipes 3 weeks ago
Mazdak Gibran 9130b2db6b fixed small bugs and adjusted styling
fixed small bugs and adjusted styling
3 weeks ago
Naeem Ullah 39635ada54 Merge pull request 'transaction-logs updated' (#36) from mazdak/UX-2025 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/36
3 weeks ago
Mazdak Gibran 8dd969fbf6 transaction-logs updated
transaction-logs updated, removed extra service file and matched architecture with other components
3 weeks ago
Naeem Ullah 66c51a9e70 Merge pull request 'setup-user updated' (#35) from mazdak/UX-1985 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/35
3 weeks ago
Mazdak Gibran 30641db59e setup-user updated
setup-user updated to match architecture of other components.
3 weeks ago
Naeem Ullah bcf4b208cc Merge pull request 'setup user email' (#34) from mazdak/setup-user-email into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/34
3 weeks ago
Mazdak Gibran 314e899394 setup user email
setup user email, fixing bugs, pagination and added search filter for transaction logs
3 weeks ago
Naeem Ullah c7c1a10dbb Merge pull request 'added export to excel functionality in transaction logs' (#33) from aconnect-UX/1962 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/33
3 weeks ago
atif118-mfsys 2dd260fc51 added export to excel functionality in transaction logs 3 weeks ago
Naeem Ullah 60b0ceddf9 Merge pull request 'added export data in excel and added som validations in logger manager screen' (#32) from aconnect-UX/1942 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/32
3 weeks ago
atif118-mfsys 58cc256169 added export data in excel and added som validations in logger manager screen 3 weeks ago
Naeem Ullah 273d3e98be Merge pull request 'aconnect-UX/1576' (#31) from aconnect-UX/1576 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/31
3 weeks ago
atif118-mfsys bf8ac0b2ed Merge branch 'dev-pending-01-01-2026' into aconnect-UX/1576 3 weeks ago
atif118-mfsys e3bd2193b8 integrated GET call in logger manager to get logging details 3 weeks ago
Naeem Ullah 6aed9f6b42 Merge pull request 'adding fields to user setup' (#30) from mazdak/UX-1903 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/30
3 weeks ago
Mazdak Gibran 1aacd37b80 styling adjustments 3 weeks ago
Mazdak Gibran 52c51c9ed1 css adjustment
styling sms banking
4 weeks ago
Mazdak Gibran 0573a880ff adjust styling 4 weeks ago
Mazdak Gibran 82a032affd adding fields to user setup
adding fields to user setup  and error handling
4 weeks ago
Naeem Ullah a57de088ff Merge pull request 'aconnect-UX/1876' (#29) from aconnect-UX/1876 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/29
4 weeks ago
atif118-mfsys 82d9060b4d Merge branch 'dev-pending-01-01-2026' into aconnect-UX/1876 4 weeks ago
atif118-mfsys 274659a429 added button level permissions on edit and delete buttons 4 weeks ago
Naeem Ullah 162662f81f Merge pull request 'transaction log Api integration' (#28) from mazdak/UX-1887 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/28
4 weeks ago
Mazdak Gibran 921b28f27b transaction log Api integration
transaction log Api integration and showing the data in tabular form.
4 weeks ago
Naeem Ullah c6b660a80e Merge pull request 'reset user password api' (#27) from mazdak/UX-1861 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/27
4 weeks ago
Mazdak Gibran 37f0489f05 Update setup-user.component.ts 4 weeks ago
Mazdak Gibran 0163c1c469 fixing setup-user component
added porOrgacode to payload for createUser
4 weeks ago
Mazdak Gibran 5955ad90a5 reset user password api
reset user password api integration
4 weeks ago
Naeem Ullah 2756a3faa5 Merge pull request 'third part registration' (#24) from mazdak/UX-1814 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/24
4 weeks ago
Mazdak Gibran 1126264d7a Merge branch 'dev-pending-01-01-2026' of https://ct.mfsys.com.pk/aConnect/aConnect-UX into mazdak/UX-1814 4 weeks ago
Naeem Ullah 38830228c2 Merge pull request 'change password api' (#26) from mazdak/UX-1852 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/26
4 weeks ago
Mazdak Gibran 50e3dfec72 change password api
change password api integration
4 weeks ago
Naeem Ullah 24d3c810c3 Merge pull request 'revamped the code and design for permission management screen' (#25) from aconnect-UX/1826 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/25
4 weeks ago
atif118-mfsys d872c5fd65 revamped the code and design for permission management screen 4 weeks ago
Mazdak Gibran 0efdedca6e third part registration
third part registration reactive form with validations and send payload
4 weeks ago
Naeem Ullah ee6138d555 Merge pull request 'aconnect-UX/1765' (#23) from aconnect-UX/1765 into dev-pending-01-01-2026
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/23
4 weeks ago
atif118-mfsys 9d48e0acf2 pushing missing code 4 weeks ago
Mazdak Gibran 33e0c86a1f change password screen fix
change password screen fix for first time login
1 month ago
atif118-mfsys db1eba9c54 Merge branch 'dev-pending-09-12-2025' into aconnect-UX/1765 1 month ago
atif118-mfsys c278b0f89c implemented permissions checks to prevent users accessing the content they do not have permission for. 1 month ago
Naeem Ullah 822f4860d9 Merge pull request 'creating payload for reset and change password' (#22) from mazdak/UX-1570 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/22
1 month ago
Mazdak Gibran c57bc708d4 Merge branch 'dev-pending-09-12-2025' of https://ct.mfsys.com.pk/aConnect/aConnect-UX into mazdak/UX-1570 1 month ago
Naeem Ullah 321e157033 Merge pull request 'integrated the API to save permissions for a specific user' (#21) from aconnect-UX/1717 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/21
1 month ago
Mazdak Gibran ba5ca98da9 removed unused imports 1 month ago
Mazdak Gibran 61df1a4ac2 creating payload for reset and change password
creating payload for reset and change password before the apis are ready
1 month ago
Naeem Ullah aacb190702 Merge pull request 'mazdak/UX-1729' (#20) from mazdak/UX-1729 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/20
1 month ago
atif118-mfsys 7ba6a8b65f integrated the API to save permissions for a specific user 1 month ago
Mazdak Gibran 25d1e49475 add missing translation
add missing translation
1 month ago
Mazdak Gibran 35c23badcc updated the form with Reactive Forms
updated the form with Reactive Forms
1 month ago
Naeem Ullah 1867e11502 Merge pull request 'integrated get permission for a specific user API' (#19) from aconnect-UX/1716 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/19
1 month ago
atif118-mfsys 23d65b82d2 integrated get permission for a specific user API 1 month ago
Naeem Ullah 7788668973 Merge pull request 'aconnect-UX/1715' (#18) from aconnect-UX/1715 into dev-pending-09-12-2025
Reviewed-on: https://ct.mfsys.com.pk/aConnect/aConnect-UX/pulls/18
1 month ago
atif118-mfsys 180ecd38a1 Merge branch 'dev-pending-09-12-2025' into aconnect-UX/1715 1 month ago
atif118-mfsys dee23848ea integrated get all users API to provide options for user dropdown in permission management 1 month 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
1 month ago
Mazdak Gibran 80a1e62411 onDelete form reset
onDelete form reset
1 month ago
Mazdak Gibran 207e55cfe3 Merge branch 'dev-pending-09-12-2025' of https://ct.mfsys.com.pk/aConnect/aConnect-UX into mazdak/UX-1709 1 month 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
1 month ago
Mazdak Gibran 1976648c08 Integrate deleteUser Api
Integrate deleteUser Api
1 month 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 month ago
Mazdak Gibran 2b98437f15 change password for first login fixed
change password for first login fixed
1 month ago
Mazdak Gibran bc18bcf946 Setup User API Integration
createUser and getAllUsers API Integration
1 month ago
atif118-mfsys 9ccda5bc86 applied optional chaining to avoid errors. 1 month ago
atif118-mfsys db1e57a008 completed design for permission management screen. 1 month 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
1 month ago
atif118-mfsys 966f0d59bc fixed and revamped first time change password screen logic 1 month ago
atif118-mfsys 72d5d08828 Merge branch 'dev-pending-09-12-2025' into GBRSP/UX-1599 1 month ago
atif118-mfsys 1c0b19e33c added activity guard and added some translations 1 month ago
atif118-mfsys 8340f14035 working on setting up http services as per standard 1 month 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 month ago
Mazdak Gibran 7fa0ee6a76 Change Password Screen view for first time login
Show different change password screen view for first time login
1 month 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 months ago
Mazdak Gibran 77282eeca7 login api integration
integrating login API with aConnect backend
2 months 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 months ago
Mazdak Gibran a6a38d068e sms gateway screen
sms gateway screen dynamically renders different forms
2 months ago
Mazdak Gibran c257ed7e6f SMS Gateway
design implementation of SMS Gateway
2 months 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 months ago
Mazdak Gibran 7dacf6f9c8 Configure Transaction Purpose screen
Configure Transaction Purpose screen
2 months 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 months 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 months ago
Mazdak Gibran b0f8311a2c feedback setup update
feedback setup update
2 months ago
Mazdak Gibran 7228790cc8 Update feedback-setup.component.html 2 months ago
Mazdak Gibran 83dc8b5c7f feedback setup screen
feedback setup screen
2 months ago
Mazdak Gibran ebde11ad65 Update ib-unblock-user.component.ts 2 months ago
Mazdak Gibran e1ec631421 Update ib-unblock-user.component.html 2 months ago
Mazdak Gibran bf3fe98e63 UnBlock IB User screen
design implementation of UnBlock IB User
2 months 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 months ago
Mazdak Gibran c4ccfaf24a SMS Logger screen
SMS Logger screen
2 months 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 months ago
Mazdak Gibran 88ffd7a35b Logger Manager Screen design
Logger Manager Screen design
2 months 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
2 months ago
Mazdak Gibran 71c767c684 setup user design
setup user design: fixed camel case
2 months ago
Mazdak Gibran d0f3d28458 Merge branch 'dev-pending-09-12-2025' into mazdak/UX-1213 2 months ago
Mazdak Gibran bf3df61c9d Setup user
setup user
2 months 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
2 months ago
Mazdak Gibran 9ee1c0f4e2 update reset-passwords
update reset-passwords.ts
2 months ago
Mazdak Gibran b6fe4e0398 reset user password design
reset user password design
2 months 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
2 months ago
Mazdak Gibran 36904b68fd update change password
update change password
2 months ago
Mazdak Gibran 8eaf5948ab Merge branch 'FMFI-PRE-PRODUCTION' into mazdak/UX-1133 2 months ago
Mazdak Gibran edfea86870 change password
change password design
2 months 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
2 months ago
atif118-mfsys 383772db87 added design for third party registration screen 2 months ago
atif118-mfsys d59d64987c initial aConnect commit. 2 months 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,141 @@
{
"$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:browser",
"options": {
"outputPath": "dist/aconnect-ux/browser",
"index": "src/index.html",
"main": "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": []
},
"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,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.dev.ts"
}
]
},
"test": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.test.ts"
}
]
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "buildTarget": "ACONNECT-UX:build:production" },
"development": { "buildTarget": "ACONNECT-UX:build:development" },
"test": { "buildTarget": "ACONNECT-UX:build:test" }
},
"defaultConfiguration": "development"
},
"server": {
"builder": "@angular-devkit/build-angular:server",
"options": {
"outputPath": "dist/aconnect-ux/server",
"main": "src/main.server.ts",
"tsConfig": "tsconfig.server.json"
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
]
},
"development": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.dev.ts"
}
]
},
"test": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.test.ts"
}
]
}
}
},
"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,63 @@
{
"name": "aconnect-ux",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve --configuration=development",
"start:dev": "ng serve --configuration=development",
"start:test": "ng serve --configuration=test",
"start:prod": "ng serve --configuration=production",
"build": "ng build --configuration=development",
"build:dev": "ng build --configuration=development",
"build:test": "ng build --configuration=test",
"build:prod": "ng build --configuration=production",
"watch:dev": "ng build --watch --configuration=development",
"watch:test": "ng build --watch --configuration=test",
"test": "ng test",
"serve:ssr": "node dist/aconnect-ux/server/main.js"
},
"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",
"@types/file-saver": "^2.0.7",
"bootstrap": "^5.3.8",
"crypto-js": "^4.2.0",
"express": "^4.18.2",
"file-saver": "^2.0.5",
"ngx-spinner": "^19.0.0",
"ngx-toastr": "^19.1.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"xlsx": "^0.18.5",
"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,149 @@
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',
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: 'transactionLogs',
canActivate: [ActivityGuard],
loadComponent: () =>
import('./transaction-logs/transaction-logs.component').then(
m => m.TransactionLogsComponent
)
},
{
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;
password: string;
userHomevac: string;
user: any
}
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">
<ng-select class="form-select" [formControl]="currentLanguage" [items]="languageOptions" bindLabel="label"
bindValue="value" [clearable]="false" [searchable]="false" style="width: 100px;" (change)="onLangChange()">
</ng-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,128 @@
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';
import { NgSelectModule } from '@ng-select/ng-select';
@Component({
selector: 'app-login',
imports: [TranslateModule, ReactiveFormsModule, CommonModule, PasswordHideShowComponent, NgSelectModule],
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 = '';
languageOptions = [
{ label: 'English', value: supportedLanguages.ENGLISH },
{ label: 'Arabic', value: supportedLanguages.ARABIC }
];
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') || supportedLanguages.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.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 *ngIf="buttonPermissions?.edit" class="btn btn-secondary btn-sm"
title="Edit">
<i class="fas fa-pen"></i>
</button>
<button *ngIf="buttonPermissions?.delete" 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,37 @@
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';
import { ButtonManagementService } from '../../services/button-management.service';
@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;
buttonPermissions: any;
constructor(
private buttonManagementService: ButtonManagementService
){}
ngOnInit(){
this.getButtonPermissions();
}
itemsPerPageChanged() {}
getButtonPermissions() {
this.buttonPermissions = this.buttonManagementService.buttonPermissions["ibUnblockUser"];
}
}

@ -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 *ngIf="buttonPermissions?.edit" class="btn btn-secondary btn-sm" title="Edit">
<i class="fas fa-pen"></i>
</button>
<button *ngIf="buttonPermissions?.delete" 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,32 @@
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';
import { ButtonManagementService } from '../../services/button-management.service';
@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;
buttonPermissions: any;
constructor(
private buttonManagementService: ButtonManagementService
){}
ngOnInit(){
this.getButtonPermissions();
}
getButtonPermissions() {
this.buttonPermissions = this.buttonManagementService.buttonPermissions["thirdPartyRegistration"];
}
}

@ -0,0 +1,224 @@
<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 [formGroup]="logsSearchForm">
<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 formControlName="fromDate"
type="date" id="fromDate"
class="form-control" appNoWhitespaces />
</div>
<div class="text-danger"
*ngIf="logsSearchForm.get('fromDate')?.touched && logsSearchForm.get('fromDate')?.invalid">
{{ 'fieldRequired' | 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 formControlName="toDate" id="toDate"
type="date" class="form-control"
maxlength="500" appNoWhitespaces rows="3" />
<div class="text-danger" *ngIf="
logsSearchForm.get('toDate')?.touched &&
(logsSearchForm.get('toDate')?.invalid || logsSearchForm.errors?.['toDateInvalid'] || logsSearchForm.errors?.['toDateGreaterThanToday'])
">
<div
*ngIf="logsSearchForm.get('toDate')?.hasError('required')">
{{ 'fieldRequired' | translate }}
</div>
<div
*ngIf="logsSearchForm.errors?.['toDateInvalid']">
{{ 'toDateInvalidError' | translate }}
</div>
<div
*ngIf="logsSearchForm.errors?.['toDateGreaterThanToday']">
{{ 'toDateGreaterThanToday' | translate }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3 mb-3">
<div class="col-md-6 ms-auto text-end">
<button [disabled]="logsSearchForm.invalid"
(click)="getlogsData()"
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" *ngIf="allItems.length">
<div class="search-box">
<input type="text" class="form-control form-control-sm" [(ngModel)]="searchText"
placeholder="{{ 'search' | translate }}">
<i class="fas fa-search search-icon"></i>
</div>
<i (click)="exportDataInExcel()" id="downloadReport" class="fa fa-download"></i>
<i class="materialdesignicons" (click)="toggleTableCard()">
<ng-container *ngIf="logsDataExpanded; 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" *ngIf="logsDataExpanded && allItems.length; else noRecordsFound">
<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>{{'loggingDateTime' | translate}}</th>
<th>{{'loggingMethod' | translate}}</th>
</tr>
</thead>
<tbody>
<tr
*ngFor="let logs of (allItems | tableFilter: searchText: ['id','method','remoteIp','requestBody','requestUri','responseCode','userId','dateTime']).slice((currentPage-1)*itemsPerPage, currentPage*itemsPerPage)">
<td>{{logs?.id}}</td>
<td>{{logs?.requestUri}}</td>
<td>{{logs?.responseCode}}</td>
<td>{{logs?.remoteIp}}</td>
<td>{{logs?.dateTime | date:'dd-MM-yyyy, hh:mm a'}}</td>
<td>{{logs?.method}}</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"
(change)="itemsPerPageChanged()" [clearable]="false"
[dropdownPosition]="'top'">
</ng-select>
</div>
<div class="text-muted" *ngIf="allItems.length > 1">
{{'page' | translate}} {{currentPage}} {{'of' |
translate}} {{totalPages()}} ({{allItems.length}}
{{'totalItems' | translate}})
</div>
<div class="text-muted" *ngIf="allItems.length === 0">
{{'no_record' | translate}}
</div>
<div class="text-muted" *ngIf="allItems.length === 1">
{{'page' | translate}} {{currentPage}} {{'of' |
translate}} {{totalPages()}} ({{allItems.length}}
{{'record' | translate}})
</div>
<div class="btn-group">
<button class="btn btn-primary waves-effect waves-light"
(click)="previousPage()"
[disabled]="currentPage === 1">
{{ 'previous' | translate }}
</button>
<button class="btn btn-primary waves-effect waves-light"
(click)="nextPage()"
[disabled]="currentPage >= totalPages()">
{{ 'next' | translate }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<ng-template #noRecordsFound>
<div *ngIf="!isLoading && logsList.length === 0" class="text-center text-muted mt-3">
<p>{{'noLoggingDetailsFound' | translate}}</p>
</div>
</ng-template>

@ -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,122 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { NgSelectModule } from '@ng-select/ng-select';
import { TranslateModule } from '@ngx-translate/core';
import { LOGGING_DETAILS_FILE_NAME, pageSizeOptions, toDateAfterFromDateValidator } from '../utils/app.constants';
import { CommonModule, DatePipe } from '@angular/common';
import { HttpParams } from '@angular/common/http';
import { URIKey } from '../utils/uri-enums';
import { HttpURIService } from '../app.http.uri.service';
import { LogsManagementResponse } from '../utils/app.interfaces';
import { TableFilterPipe } from '../shared/pipes/table-filter.pipe';
import { ExcelExportService } from '../shared/services/excel-export.service';
@Component({
selector: 'app-logging',
imports: [ TranslateModule, FormsModule, NgSelectModule, CommonModule, ReactiveFormsModule,
TableFilterPipe
],
providers: [ DatePipe ],
templateUrl: './logging.component.html',
styleUrl: './logging.component.scss'
})
export class LoggingComponent implements OnInit {
currentPage: number = 1;
pageSizeOptions = pageSizeOptions
logsDataExpanded: boolean = true
itemsPerPage: number = 5;
searchText: any = '';
allItems: any[] = [];
pagedItems: any[] = [];
logsList: LogsManagementResponse[] = [];
logsSearchForm!: FormGroup;
isLoading: boolean = false;
constructor(
private fb: FormBuilder,
private httpService: HttpURIService,
private datePipe: DatePipe,
private excelExportServic: ExcelExportService
) { }
ngOnInit() {
this.initializeLogsSearchForm();
}
initializeLogsSearchForm() {
this.logsSearchForm = this.fb.group({
fromDate: ['', Validators.required],
toDate: ['', Validators.required]
},
{
validators: toDateAfterFromDateValidator
})
}
getlogsData(){
if(this.logsSearchForm.invalid)
return;
let formValues = this.logsSearchForm.value;
this.isLoading = true;
let fromDateTransformed = this.datePipe.transform(formValues.fromDate, "dd-MM-yyyy");
let toDateTransformed = this.datePipe.transform(formValues.toDate, "dd-MM-yyyy");
const params = new HttpParams()
.set('fromDate', fromDateTransformed!)
.set('toDate', toDateTransformed!);
this.httpService.requestGET<LogsManagementResponse[]>(URIKey.LOGGER_MANAGER_URI, params).subscribe({
next: (response) => {
this.logsList = response;
this.allItems = [...this.logsList];
this.updatePagedItems();
this.isLoading = false;
},
error: (err) => {
console.error('Error fetching logging details data:', err);
this.logsList = [];
this.isLoading = false;
}
});
}
updatePagedItems(): void {
const startIndex = (this.currentPage - 1) * this.itemsPerPage;
const endIndex = startIndex + this.itemsPerPage;
this.pagedItems = this.allItems.slice(startIndex, endIndex);
}
totalPages(): number {
return Math.ceil(this.allItems.length / this.itemsPerPage);
}
previousPage(): void {
if (this.currentPage > 1) {
this.currentPage--;
this.updatePagedItems();
}
}
nextPage(): void {
if (this.currentPage < this.totalPages()) {
this.currentPage++;
this.updatePagedItems();
}
}
itemsPerPageChanged(): void {
this.currentPage = 1;
this.updatePagedItems();
}
toggleTableCard(){
this.logsDataExpanded = !this.logsDataExpanded;
}
exportDataInExcel(){
this.excelExportServic.exportExcel(this.logsList, LOGGING_DETAILS_FILE_NAME)
}
}

@ -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,28 @@
export class User {
Username: string="";
Email?: string="";
Password: string="";
}
export interface SetupUser {
userId: string;
userFullname: string;
password: string;
porOrgacode: string | null;
email: string;
role: string;
}
export interface TransactionLog {
logId: number;
porOrgacode: string;
transactionID: string;
drMbmbkmsnumber: string;
crMbmbkmsnumber: string;
ppmPymdcode: string;
sgtGntrdate: string;
channelCode: string;
createdAt: 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,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(HiddenValues.POR_ORGACODE);
this.credentialService.setUserId(uCreds.userId);
this.credentialService.setPassword(uCreds.password);
this.storageService.setItem(FormConstants.POR_ORGACODE, HiddenValues.POR_ORGACODE);
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.role);
if(data.user.permissions){
this.storageService.setItem('permission', data.user.permissions);
this.credentialService.setPermission(JSON.parse(data.user.permissions));
}
else{
this.storageService.setItem('permission', '[]');
this.credentialService.setPermission([]);
}
this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.isAdminUser());
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;
}
isAdminUser(){
if (this.storageService && this.storageService.getItem('user') != null) {
let cachedUser = JSON.parse(this.storageService.getItem('user') || '{}');
return cachedUser.user.role === HiddenValues.ADMIN_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.interfaces";
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,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 fw-sm">{{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,178 @@
import { Component, HostListener, Inject, OnInit, 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 implements OnInit{
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';
user: any;
userObj: any;
username : string = '';
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.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');
}
}
this.userObj = JSON.parse(this.storageService.getItem('user') || '{}');
this.username = this.userObj?.user?.userFullname ?? '';
}
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 | translate }}
<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,22 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { NotificationService } from '../../services/notification.service';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
@Component({
selector: 'app-notifications',
imports: [CommonModule, TranslateModule],
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,29 @@
import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
@Component({
selector: 'app-password-hide-show',
imports: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PasswordHideShowComponent),
multi: true
}],
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,107 @@
<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 *ngIf="permissions.UserManagement || authService.isAdminUser()">
<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 *ngIf="permissions.thirdPartyRegistration || authService.isAdminUser()">
<a routerLink="/home/thirdPartyRegistration" routerLinkActive="mm-active">
<span> {{ 'thirdPartyRegistration' | translate }}</span>
</a>
</li> -->
<li *ngIf="permissions.setupUser || authService.isAdminUser()">
<a routerLink="/home/setupUser" routerLinkActive="mm-active">
<span>{{ 'setupUser' | translate }}</span>
</a>
</li>
<li *ngIf="permissions.resetPassword || authService.isAdminUser()">
<a routerLink="/home/resetPassword" routerLinkActive="mm-active">
<span> {{ 'resetPassword' | translate }}</span>
</a>
</li>
<li *ngIf="permissions.changePassword || authService.isAdminUser()">
<a routerLink="/home/changePassword" routerLinkActive="mm-active" style="cursor: pointer">
<span> {{ 'changePassword' | translate }}</span>
</a>
</li>
</ul>
</li>
<li *ngIf="permissions.Logging || authService.isAdminUser()">
<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 *ngIf="permissions.loggerManager || authService.isAdminUser()">
<a routerLink="/home/loggerManager" routerLinkActive="mm-active">
<span> {{ 'loggerManager' | translate }}</span>
</a>
</li>
<li *ngIf="permissions.transactionLogs || authService.isAdminUser()">
<a routerLink="/home/transactionLogs" routerLinkActive="mm-active">
<span>{{ 'transactionLogs' | translate }}</span>
</a>
</li>
</ul>
</li>
<!-- <li *ngIf="permissions.SMSBanking || authService.isAdminUser()">
<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 *ngIf="permissions.smsLogger || authService.isAdminUser()">
<a routerLink="/home/smsLogger" routerLinkActive="mm-active">
<span> {{ 'smsLogger' | translate }}</span>
</a>
</li>
<li *ngIf="permissions.smsGateway || authService.isAdminUser()">
<a routerLink="/home/smsGateway" routerLinkActive="mm-active">
<span> {{ 'smsGateway' | translate }}</span>
</a>
</li>
</ul>
</li> -->
<!-- <li *ngIf="permissions.ibSupport || authService.isAdminUser()">
<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 *ngIf="permissions.ibUnblockUser || authService.isAdminUser()">
<a routerLink="/home/ibUnblockUser" routerLinkActive="mm-active">
<span> {{ 'ibUnblockUser' | translate }}</span>
</a>
</li>
<li *ngIf="permissions.feedbackSetup || authService.isAdminUser()">
<a routerLink="/home/feedbackSetup" routerLinkActive="mm-active">
<span> {{ 'feedbackSetup' | translate }}</span>
</a>
</li>
<li *ngIf="permissions.purposeSetup || authService.isAdminUser()">
<a routerLink="/home/purposeSetup" routerLinkActive="mm-active">
<span> {{ 'purposeSetup' | translate }}</span>
</a>
</li>
</ul>
</li> -->
<li *ngIf="permissions.permissions || authService.isAdminUser()">
<a routerLink="/home/permissions" routerLinkActive="mm-active">
<i class='fa fa-lock'></i>
<span> {{ 'permissions' | translate }}</span>
</a>
</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,111 @@
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 { CommonModule, isPlatformBrowser } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
import { RouterModule } from '@angular/router';
import { Router } from '@angular/router';
import { CredentialService } from '../../../services/credential.service';
import { AuthenticationService } from '../../../services/authenticate.service';
@Component({
selector: 'app-side-nav',
imports: [TranslateModule, RouterModule, CommonModule],
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,
private credentialService: CredentialService,
public authService: AuthenticationService
) {
}
ngOnInit(): void {
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;
})
}
});
this.sidebarService.currentSubModule = this.storageService.getItem('currentSubModule') ?? 'dashboard';
this.closeSidebarMenu();
}
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.isAdminUser()){
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.user.role);
this.credentialService.setPermission(permission);
this.buttonManagementService.setButtonPermissions(this.credentialService.getPermission(), this.authService.isAdminUser());
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,25 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'tableFilter',
standalone: true
})
export class TableFilterPipe implements PipeTransform {
transform(items: any[], searchText: string, filterProperties: string[]): any[] {
if (!items) return [];
if (!searchText.trim()) return items;
searchText = searchText.toLowerCase().trim();
return items.filter(item => {
return filterProperties.some(prop => {
const value = this.getNestedProperty(item, prop)?.toString().toLowerCase();
return value?.includes(searchText);
});
});
}
private getNestedProperty(obj: any, path: string): any {
return path.split('.').reduce((o, p) => o?.[p], obj);
}
}

@ -0,0 +1,28 @@
import { Injectable } from '@angular/core';
import { saveAs } from 'file-saver';
import * as XLSX from 'xlsx';
import { EXCEL_FILE_EXTENSION, EXCEL_FILE_TYPE } from '../../utils/app.constants';
@Injectable({
providedIn: 'root'
})
export class ExcelExportService {
constructor() { }
private fileType = EXCEL_FILE_TYPE;
private fileExtension = EXCEL_FILE_EXTENSION;
public exportExcel(jsonData: any[], fileName: string): void {
const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(jsonData);
const wb: XLSX.WorkBook = { Sheets: { 'data': ws }, SheetNames: ['data'] };
const excelBuffer: any = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });
this.saveExcelFile(excelBuffer, fileName);
}
private saveExcelFile(buffer: any, fileName: string): void {
const data: Blob = new Blob([buffer], {type: this.fileType});
saveAs.saveAs(data, fileName + this.fileExtension);
}
}

@ -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,186 @@
<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>
</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-sms-gateway"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