From d8525ab40643c5d1615def218b5992ab7965ac4c Mon Sep 17 00:00:00 2001 From: dipan dhali Date: Sun, 1 Sep 2024 20:22:41 +0530 Subject: [PATCH] ehancements to operations web --- .gitignore | 1 + env.sample | 16 + .../account-mapper-routing.module.ts | 52 +- .../account-mapper/account-mapper.module.ts | 18 +- .../account-mapper.component.html | 88 +- .../account-mapper.component.scss | 28 +- .../account-mapper.component.ts | 89 +- .../beneficiaries.component.html | 161 + .../beneficiaries.component.scss | 66 + .../beneficiaries.component.spec.ts | 21 + .../beneficiaries/beneficiaries.component.ts | 264 ++ .../create-beneficiaries.component.html | 87 + .../create-beneficiaries.component.scss | 100 + .../create-beneficiaries.component.spec.ts | 21 + .../create-beneficiaries.component.ts | 116 + .../filter-selector.component.html | 9 + .../filter-selector.component.scss | 16 + .../filter-selector.component.spec.ts | 21 + .../filter-selector.component.ts | 68 + .../services/account-mapper.service.ts | 28 +- src/app/app.module.ts | 14 +- .../configuration-routing.module.ts | 89 + src/app/configuration/configuration.module.ts | 29 + .../configuration.component.html | 7 + .../configuration.component.scss | 16 + .../configuration.component.spec.ts | 21 + .../configuration/configuration.component.ts | 10 + .../create-g2p-payment.component.html | 77 + .../create-g2p-payment.component.scss | 83 + .../create-g2p-payment.component.spec.ts | 21 + .../create-g2p-payment.component.ts | 84 + .../g2p-payment/g2p-payment.component.html | 167 ++ .../g2p-payment/g2p-payment.component.scss | 83 + .../g2p-payment/g2p-payment.component.spec.ts | 21 + .../g2p-payment/g2p-payment.component.ts | 177 ++ .../g2p-section-filter.component.html | 9 + .../g2p-section-filter.component.scss | 0 .../g2p-section-filter.component.spec.ts | 21 + .../g2p-section-filter.component.ts | 58 + .../resolver/g2p-payment-configs.resolver.ts | 30 + .../resolver/template.resolver.ts | 30 + .../resolver/view-payment.resolver.ts | 30 + .../services/g2p-payment-config.service.ts | 122 + .../update-g2p-payment.component.html | 92 + .../update-g2p-payment.component.scss | 100 + .../update-g2p-payment.component.spec.ts | 21 + .../update-g2p-payment.component.ts | 126 + .../authentication.interceptor.ts | 23 +- .../authentication/authentication.service.ts | 44 +- .../keycloak/oauth-keycloak.service.ts | 12 + .../authentication/o-auth2-token.model.ts | 1 + src/app/core/core.module.ts | 4 +- .../core/shell/content/content.component.html | 2 +- .../core/shell/content/content.component.scss | 20 +- src/app/core/shell/shell.component.html | 2 - src/app/core/shell/shell.component.scss | 9 +- .../core/shell/sidenav/sidenav.component.html | 54 +- .../core/shell/sidenav/sidenav.component.scss | 187 +- .../core/shell/sidenav/sidenav.component.ts | 12 + .../core/shell/toolbar/toolbar.component.html | 27 +- .../core/shell/toolbar/toolbar.component.scss | 75 +- .../core/shell/toolbar/toolbar.component.ts | 24 + src/app/core/utils/passwords-utility.ts | 37 + .../directives/has-role/has-role.directive.ts | 42 +- .../home/dashboard/dashboard.component.html | 11 +- .../home/dashboard/dashboard.component.scss | 13 + src/app/home/dashboard/dashboard.component.ts | 50 +- src/app/home/home-routing.module.ts | 8 +- src/app/home/home.component.scss | 12 - src/app/home/home.component.spec.ts | 25 - src/app/home/home.component.ts | 48 - src/app/home/home.module.ts | 2 - .../login-form/login-form.component.html | 89 +- .../login-form/login-form.component.scss | 57 +- src/app/login/login.component.html | 23 +- src/app/login/login.component.scss | 50 +- src/app/login/login.component.ts | 2 +- .../batches/batches.component.html | 372 ++- .../batches/batches.component.scss | 47 +- .../payment-hub/batches/batches.component.ts | 219 +- .../payment-hub/batches/batches.service.ts | 5 +- .../filter-selector.component.html | 1 + .../filter-selector.component.ts | 13 +- .../filter-selector/section-model.ts | 2 +- .../payment-hub/paymenthub-routing.module.ts | 133 +- src/app/payment-hub/paymenthub.module.ts | 6 +- .../batch-summary.component.html | 32 + .../batch-summary.component.scss | 11 + .../batch-summary.component.spec.ts | 21 + .../batch-summary/batch-summary.component.ts | 16 + .../sub-batches/sub-batches.component.html | 353 ++- .../sub-batches/sub-batches.component.scss | 75 +- .../sub-batches/sub-batches.component.ts | 198 +- .../sub-batches/sub-batches.service.ts | 3 +- .../sub-batch-summary.component.html | 40 + .../sub-batch-summary.component.scss | 11 + .../sub-batch-summary.component.spec.ts | 21 + .../sub-batch-summary.component.ts | 19 + .../transfers/transfers.component.html | 437 +-- .../transfers/transfers.component.scss | 63 +- .../transfers/transfers.component.ts | 248 +- .../transfers/transfers.service.ts | 5 +- .../view-transfer-details.component.html | 14 +- .../view-transfer-details.component.ts | 2 +- src/app/pipes/datetime-format.pipe.ts | 3 +- .../drag-drop-file.component.html | 19 +- .../drag-drop-file.component.scss | 11 + src/app/shared/icons.module.ts | 20 +- .../language-selector.component.scss | 5 +- .../language-selector.component.ts | 2 +- .../shared/list-item/list-item.component.html | 8 +- .../shared/list-item/list-item.component.scss | 106 +- src/app/shared/material.module.ts | 4 +- src/app/shared/shared.module.ts | 13 +- .../success-dialog.component.html | 11 + .../success-dialog.component.scss | 21 + .../success-dialog.component.spec.ts | 21 + .../success-dialog.component.ts | 18 + .../update-password-dialog.component.html | 101 + .../update-password-dialog.component.scss | 38 + .../update-password-dialog.component.spec.ts | 21 + .../update-password-dialog.component.ts | 61 + .../upload-dialog.component.html | 10 + .../upload-dialog.component.scss | 15 + .../upload-dialog.component.spec.ts | 21 + .../upload-dialog/upload-dialog.component.ts | 117 + .../create-user/create-user.component.html | 244 +- .../create-user/create-user.component.scss | 93 +- .../create-user/create-user.component.ts | 191 +- src/app/users/models/jbpm.model.ts | 44 + src/app/users/models/user.model.ts | 27 + src/app/users/services/users-jbpm.service.ts | 162 + .../users/services/users-keycloak.service.ts | 249 ++ src/app/users/services/users.service.ts | 328 +++ .../user-requests.component.html | 237 ++ .../user-requests.component.scss | 59 + .../user-requests.component.spec.ts | 21 + .../user-requests/user-requests.component.ts | 158 + src/app/users/user.resolver.ts | 8 +- src/app/users/users-routing.module.ts | 16 +- src/app/users/users-template.resolver.ts | 2 +- src/app/users/users.component.html | 50 - src/app/users/users.component.scss | 7 - src/app/users/users.component.ts | 67 - src/app/users/users.module.ts | 6 +- src/app/users/users.resolver.ts | 6 +- src/app/users/users.service.ts | 69 - src/app/users/users/users.component.html | 100 + src/app/users/users/users.component.scss | 49 + .../users/{ => users}/users.component.spec.ts | 0 src/app/users/users/users.component.ts | 120 + .../users/view-user/view-user.component.html | 162 +- .../users/view-user/view-user.component.scss | 67 +- .../users/view-user/view-user.component.ts | 191 +- .../visualizations-routing.module.ts | 47 + .../visualizations-selector.component.html} | 5 +- .../visualizations-selector.component.scss | 13 + .../visualizations-selector.component.spec.ts | 21 + .../visualizations-selector.component.ts | 34 + .../visualizations/visualizations.module.ts | 26 + .../visualizations.component.html | 3 + .../visualizations.component.scss | 0 .../visualizations.component.spec.ts | 21 + .../visualizations.component.ts | 28 + src/app/vouchers/services/vouchers.service.ts | 5 +- .../voucher-management.component.html | 250 +- .../voucher-management.component.scss | 52 +- .../voucher-management.component.ts | 173 +- .../vouchers-bulk-import.component.html | 178 +- .../vouchers-bulk-import.component.scss | 57 + .../vouchers-bulk-import.component.ts | 149 +- src/app/vouchers/vouchers-routing.module.ts | 87 +- .../vouchers-section-filter.component.html | 1 + .../vouchers-section-filter.component.ts | 4 +- .../vouchers/vouchers/vouchers.component.scss | 2 +- src/assets/env.template.js | 12 + src/assets/images/square-parking-solid.svg | 1 + src/assets/mock/payment-hub/batches.mock.json | 1511 ++++++++++ .../payment-hub/sub-batches-details.mock.json | 2142 ++++++++++++++ .../mock/payment-hub/sub-batches.mock.json | 2606 +++++++++++++++++ .../mock/payment-hub/transactions.mock.json | 738 ++++- src/assets/mock/voucher.mock.json | 169 ++ src/assets/styles/_content.scss | 14 +- src/assets/styles/_helper.scss | 71 +- src/assets/styles/_loader.scss | 61 +- src/assets/translations/de.json | 200 ++ src/assets/translations/en.json | 110 +- src/assets/translations/es.json | 295 +- src/assets/translations/fr.json | 294 +- src/assets/translations/it.json | 200 ++ src/environments/environment.prod.ts | 14 + src/environments/environment.ts | 14 + src/index.html | 8 +- src/theme/theme.scss | 10 +- 194 files changed, 16590 insertions(+), 2555 deletions(-) create mode 100644 src/app/account-mapper/beneficiaries/beneficiaries.component.html create mode 100644 src/app/account-mapper/beneficiaries/beneficiaries.component.scss create mode 100644 src/app/account-mapper/beneficiaries/beneficiaries.component.spec.ts create mode 100644 src/app/account-mapper/beneficiaries/beneficiaries.component.ts create mode 100644 src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.html create mode 100644 src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.scss create mode 100644 src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.spec.ts create mode 100644 src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.ts create mode 100644 src/app/account-mapper/filter-selector/filter-selector.component.html create mode 100644 src/app/account-mapper/filter-selector/filter-selector.component.scss create mode 100644 src/app/account-mapper/filter-selector/filter-selector.component.spec.ts create mode 100644 src/app/account-mapper/filter-selector/filter-selector.component.ts create mode 100644 src/app/configuration/configuration-routing.module.ts create mode 100644 src/app/configuration/configuration.module.ts create mode 100644 src/app/configuration/configuration/configuration.component.html create mode 100644 src/app/configuration/configuration/configuration.component.scss create mode 100644 src/app/configuration/configuration/configuration.component.spec.ts create mode 100644 src/app/configuration/configuration/configuration.component.ts create mode 100644 src/app/configuration/create-g2p-payment/create-g2p-payment.component.html create mode 100644 src/app/configuration/create-g2p-payment/create-g2p-payment.component.scss create mode 100644 src/app/configuration/create-g2p-payment/create-g2p-payment.component.spec.ts create mode 100644 src/app/configuration/create-g2p-payment/create-g2p-payment.component.ts create mode 100644 src/app/configuration/g2p-payment/g2p-payment.component.html create mode 100644 src/app/configuration/g2p-payment/g2p-payment.component.scss create mode 100644 src/app/configuration/g2p-payment/g2p-payment.component.spec.ts create mode 100644 src/app/configuration/g2p-payment/g2p-payment.component.ts create mode 100644 src/app/configuration/g2p-section-filter/g2p-section-filter.component.html create mode 100644 src/app/configuration/g2p-section-filter/g2p-section-filter.component.scss create mode 100644 src/app/configuration/g2p-section-filter/g2p-section-filter.component.spec.ts create mode 100644 src/app/configuration/g2p-section-filter/g2p-section-filter.component.ts create mode 100644 src/app/configuration/resolver/g2p-payment-configs.resolver.ts create mode 100644 src/app/configuration/resolver/template.resolver.ts create mode 100644 src/app/configuration/resolver/view-payment.resolver.ts create mode 100644 src/app/configuration/services/g2p-payment-config.service.ts create mode 100644 src/app/configuration/update-g2p-payment/update-g2p-payment.component.html create mode 100644 src/app/configuration/update-g2p-payment/update-g2p-payment.component.scss create mode 100644 src/app/configuration/update-g2p-payment/update-g2p-payment.component.spec.ts create mode 100644 src/app/configuration/update-g2p-payment/update-g2p-payment.component.ts create mode 100644 src/app/core/utils/passwords-utility.ts delete mode 100644 src/app/home/home.component.scss delete mode 100644 src/app/home/home.component.spec.ts delete mode 100644 src/app/home/home.component.ts create mode 100644 src/app/payment-hub/sub-batches/batch-summary/batch-summary.component.html create mode 100644 src/app/payment-hub/sub-batches/batch-summary/batch-summary.component.scss create mode 100644 src/app/payment-hub/sub-batches/batch-summary/batch-summary.component.spec.ts create mode 100644 src/app/payment-hub/sub-batches/batch-summary/batch-summary.component.ts create mode 100644 src/app/payment-hub/transfers/sub-batch-summary/sub-batch-summary.component.html create mode 100644 src/app/payment-hub/transfers/sub-batch-summary/sub-batch-summary.component.scss create mode 100644 src/app/payment-hub/transfers/sub-batch-summary/sub-batch-summary.component.spec.ts create mode 100644 src/app/payment-hub/transfers/sub-batch-summary/sub-batch-summary.component.ts create mode 100644 src/app/shared/success-dialog/success-dialog.component.html create mode 100644 src/app/shared/success-dialog/success-dialog.component.scss create mode 100644 src/app/shared/success-dialog/success-dialog.component.spec.ts create mode 100644 src/app/shared/success-dialog/success-dialog.component.ts create mode 100644 src/app/shared/update-password-dialog/update-password-dialog.component.html create mode 100644 src/app/shared/update-password-dialog/update-password-dialog.component.scss create mode 100644 src/app/shared/update-password-dialog/update-password-dialog.component.spec.ts create mode 100644 src/app/shared/update-password-dialog/update-password-dialog.component.ts create mode 100644 src/app/shared/upload-dialog/upload-dialog.component.html create mode 100644 src/app/shared/upload-dialog/upload-dialog.component.scss create mode 100644 src/app/shared/upload-dialog/upload-dialog.component.spec.ts create mode 100644 src/app/shared/upload-dialog/upload-dialog.component.ts create mode 100644 src/app/users/models/jbpm.model.ts create mode 100644 src/app/users/models/user.model.ts create mode 100644 src/app/users/services/users-jbpm.service.ts create mode 100644 src/app/users/services/users-keycloak.service.ts create mode 100644 src/app/users/services/users.service.ts create mode 100644 src/app/users/user-requests/user-requests.component.html create mode 100644 src/app/users/user-requests/user-requests.component.scss create mode 100644 src/app/users/user-requests/user-requests.component.spec.ts create mode 100644 src/app/users/user-requests/user-requests.component.ts delete mode 100644 src/app/users/users.component.html delete mode 100644 src/app/users/users.component.scss delete mode 100644 src/app/users/users.component.ts delete mode 100644 src/app/users/users.service.ts create mode 100644 src/app/users/users/users.component.html create mode 100644 src/app/users/users/users.component.scss rename src/app/users/{ => users}/users.component.spec.ts (100%) create mode 100644 src/app/users/users/users.component.ts create mode 100644 src/app/visualizations/visualizations-routing.module.ts rename src/app/{home/home.component.html => visualizations/visualizations-selector/visualizations-selector.component.html} (57%) create mode 100644 src/app/visualizations/visualizations-selector/visualizations-selector.component.scss create mode 100644 src/app/visualizations/visualizations-selector/visualizations-selector.component.spec.ts create mode 100644 src/app/visualizations/visualizations-selector/visualizations-selector.component.ts create mode 100644 src/app/visualizations/visualizations.module.ts create mode 100644 src/app/visualizations/visualizations/visualizations.component.html create mode 100644 src/app/visualizations/visualizations/visualizations.component.scss create mode 100644 src/app/visualizations/visualizations/visualizations.component.spec.ts create mode 100644 src/app/visualizations/visualizations/visualizations.component.ts create mode 100644 src/assets/images/square-parking-solid.svg create mode 100644 src/assets/mock/payment-hub/batches.mock.json create mode 100644 src/assets/mock/payment-hub/sub-batches-details.mock.json create mode 100644 src/assets/mock/payment-hub/sub-batches.mock.json create mode 100644 src/assets/mock/voucher.mock.json create mode 100644 src/assets/translations/de.json create mode 100644 src/assets/translations/it.json diff --git a/.gitignore b/.gitignore index 174ed728..643d11aa 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ xcuserdata/ *.sublime-workspace # IDE - VSCode +.vscode .vscode/* !.vscode/settings.json !.vscode/tasks.json diff --git a/env.sample b/env.sample index 132e58cc..526d0f54 100644 --- a/env.sample +++ b/env.sample @@ -7,6 +7,8 @@ export PH_VOU_CALLBACK_URL=https://webhook.site/ PH_ACT_BACKEND_SERVER_URL=https://paymenthub.qa.oneacrefund.org/opsapp/api/v1 +export PH_G2P_PAYMENT_CONFIG_URL=https://gateway.mifos.community/v1.0/g2p-payment + export PH_PLATFORM_TENANT_ID=phdefault export PH_PLATFORM_TENANT_IDS=phdefault @@ -27,10 +29,24 @@ export PH_OAUTH_CLIENT_ID=opsapp export PH_OAUTH_CLIENT_SECRET=Y2xpZW50Og= +export PH_OAUTH_CLIENT_UUUID=Y2xpZW50Og= + export PH_OAUTH_BASIC_AUTH=true export PH_OAUTH_BASIC_AUTH_TOKEN=Y2xpZW50Og== +export PH_JBPM_API_URL=http://localhost:8180/kie-server/services/rest/server + +export PH_JBPM_CONTAINER_ID=PaymentHubEE_1.0.0-SNAPSHOT + +export PH_JBPM_CREDENTIALS_ADMIN_MAKER=Y2xpZW50Og== + +export PH_JBPM_CREDENTIALS_ADMIN_CHECKER=Y2xpZW50Og== + +export PH_JBPM_CREDENTIALS_BOTH=Y2xpZW50Og== + +export PH_GRAFANA_DASHBOARD_URL=http://localhost:3000/d/keycloak/keycloak-metrics?orgId=1&refresh=5s&theme=light&kiosk=tv&fullscreen=true&refresh=5s&from=now-5m&to=now + export PH_DEFAULT_LANGUAGE=en export PH_SUPPORTED_LANGUAGES=en,fr,es diff --git a/src/app/account-mapper/account-mapper-routing.module.ts b/src/app/account-mapper/account-mapper-routing.module.ts index 9010c72b..4ecb2a7c 100644 --- a/src/app/account-mapper/account-mapper-routing.module.ts +++ b/src/app/account-mapper/account-mapper-routing.module.ts @@ -1,21 +1,55 @@ +/** Angular Imports */ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; + +/** Custom Services */ import { Route } from 'app/core/route/route.service'; -import { AccountMapperComponent } from './account-mapper/account-mapper.component'; +/** Custom Components */ +import { AccountMapperComponent } from './account-mapper/account-mapper.component'; +import { BeneficiariesComponent } from './beneficiaries/beneficiaries.component'; +import { CreateBeneficiariesComponent } from './create-beneficiaries/create-beneficiaries.component'; +/** + * Account Mapper Routes + */ const routes: Routes = [ - Route.withShell([ + Route.withShell([ + { + path: 'dashboard', + children: [ { - path: 'account-mapper', - component: AccountMapperComponent, - data: { title: 'Account Mapper', breadcrumb: 'Account Mapper' }, - children: [ - ] - } - ]) + path: 'account-mapper', + component: AccountMapperComponent, + data: { title: 'Account Mapper', breadcrumb: 'Account Mapper' }, + children: [ + { + path: "", + redirectTo: "beneficiaries", + pathMatch: "full", + }, + { + path: "beneficiaries", + component: BeneficiariesComponent, + data: { breadcrumb: { skip: true } }, + }, + { + path: "create-beneficiaries", + component: CreateBeneficiariesComponent, + data: { breadcrumb: { skip: true } }, + }, + ], + }, + ], + }, + ]), ]; +/** + * Account Mapper Routing Module + * + * Configures the routes for the Account Mapper Module + */ @NgModule({ imports: [RouterModule.forChild(routes)], exports: [RouterModule], diff --git a/src/app/account-mapper/account-mapper.module.ts b/src/app/account-mapper/account-mapper.module.ts index f7aa684d..7ed9e321 100644 --- a/src/app/account-mapper/account-mapper.module.ts +++ b/src/app/account-mapper/account-mapper.module.ts @@ -1,15 +1,27 @@ +/** Angular Imports */ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; + +/** Custom Components */ import { AccountMapperComponent } from './account-mapper/account-mapper.component'; +import { FilterSelectorComponent } from './filter-selector/filter-selector.component'; +import { CreateBeneficiariesComponent } from './create-beneficiaries/create-beneficiaries.component'; +import { BeneficiariesComponent } from './beneficiaries/beneficiaries.component'; + +/** Custom Modules */ import { SharedModule } from 'app/shared/shared.module'; import { PipesModule } from 'app/pipes/pipes.module'; import { AccountMapperRoutingModule } from './account-mapper-routing.module'; - - +/** + * Account Mapper Module + */ @NgModule({ declarations: [ - AccountMapperComponent + AccountMapperComponent, + FilterSelectorComponent, + CreateBeneficiariesComponent, + BeneficiariesComponent ], imports: [ AccountMapperRoutingModule, diff --git a/src/app/account-mapper/account-mapper/account-mapper.component.html b/src/app/account-mapper/account-mapper/account-mapper.component.html index 09f75a0a..4075b121 100644 --- a/src/app/account-mapper/account-mapper/account-mapper.component.html +++ b/src/app/account-mapper/account-mapper/account-mapper.component.html @@ -1,84 +1,6 @@ -
- - - - - -

{{"labels.inputs.Filters" | translate}}

- -
- -
-
- - - {{"labels.inputs.Financial Institution" | translate}} - - - - - {{"labels.inputs.Functional ID" | translate}} - - - - - {{"labels.inputs.Financial Address" | translate}} - - - -
- -
- -
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{{"labels.inputs.Government Entity" | translate}} {{item.registeringInstitutionId }} {{"labels.inputs.Financial Institution" | translate}} {{ item.bankingInstitutionCode }} {{"labels.inputs.Functional ID" | translate}} {{ item.payeeIdentity }} {{"labels.inputs.Financial Address" | translate}} - - {{"labels.inputs.Payment Modality" | translate}} {{paymentModalityDescription(item.paymentModality)}}
- - - - -
- +
+ +
\ No newline at end of file diff --git a/src/app/account-mapper/account-mapper/account-mapper.component.scss b/src/app/account-mapper/account-mapper/account-mapper.component.scss index e194fc33..a864388b 100644 --- a/src/app/account-mapper/account-mapper/account-mapper.component.scss +++ b/src/app/account-mapper/account-mapper/account-mapper.component.scss @@ -1,24 +1,14 @@ .container { - width: 80%; - } - - .account-mapper-wrap { - flex-wrap: wrap; - width: 100%; - - mifosx-batches, - mifosx-sub-batches, - mifosx-filter-selector { - width: 100%; - } - + width: 90%; } -.filter-options { - width: 100%; - margin-bottom: 20px; -} +.account-wrap { + flex-wrap: wrap; + width: 100%; -.push-end { - text-align: end; + mifosx-voucher-management, + mifosx-vouchers-bulk-import, + mifosx-filter-selector { + width: 100%; + } } \ No newline at end of file diff --git a/src/app/account-mapper/account-mapper/account-mapper.component.ts b/src/app/account-mapper/account-mapper/account-mapper.component.ts index 37ce742b..d0f28080 100644 --- a/src/app/account-mapper/account-mapper/account-mapper.component.ts +++ b/src/app/account-mapper/account-mapper/account-mapper.component.ts @@ -1,93 +1,8 @@ -import { Component, OnInit, ViewChild } from '@angular/core'; -import { AccountMapperService } from '../services/account-mapper.service'; -import { UntypedFormControl } from '@angular/forms'; -import { MatPaginator, PageEvent } from '@angular/material/paginator'; -import { MatSort } from '@angular/material/sort'; -import { MatTableDataSource } from '@angular/material/table'; -import { AccountData } from '../models/account-mapper.model'; -import { Dates } from 'app/core/utils/dates'; +import { Component } from '@angular/core'; @Component({ selector: 'mifosx-account-mapper', templateUrl: './account-mapper.component.html', styleUrls: ['./account-mapper.component.scss'] }) -export class AccountMapperComponent implements OnInit { - - @ViewChild(MatPaginator) paginator: MatPaginator; - @ViewChild(MatSort) sort: MatSort; - - /** government Entity form control. */ - governmentEntity = new UntypedFormControl(); - /** financial Institution form control. */ - financialInstitution = new UntypedFormControl(); - /** functional Id form control. */ - functionalId = new UntypedFormControl(); - /** financial Address form control. */ - financialAddress = new UntypedFormControl(); - - /** Columns to be displayed in transactions table. */ - displayedColumns: string[] = ['governmentEntity', 'financialInstitution', 'functionalId', 'financialAddress', 'paymentModality']; - /** Data source for transactions table. */ - dataSource = new MatTableDataSource(); - - totalRows = 0; - currentPage = 0; - pageSize = 10; - isLoading = false; - - accountsData: AccountData; - - constructor(private dates: Dates, - private accountMapperService: AccountMapperService) { } - - ngOnInit(): void { - this.getAccounts(); - } - - getAccounts(): void { - this.isLoading = true; - this.accountMapperService.getAccounts(this.currentPage, this.pageSize, 'requestFile', 'asc') - .subscribe((accounts: AccountData) => { - this.dataSource = new MatTableDataSource(accounts.content); - this.dataSource.paginator = this.paginator; - this.dataSource.sort = this.sort; - this.totalRows = accounts.totalElements; - this.isLoading = false; - }, (error: any) => { - this.isLoading = false; - }); - } - - convertTimestampToUTCDate(timestamp: any) { - if (!timestamp) { - return undefined; - } - return this.dates.formatUTCDate(new Date(timestamp)); - } - - pageChanged(event: PageEvent) { - this.currentPage = event.pageIndex; - this.pageSize = event.pageSize; - this.getAccounts(); - } - - paymentModalityDescription(value: string): string { - if (value === '0' || value === '00') { - return '(00) Bank Account'; - } else if (value === '1' || value === '01') { - return '(01) Mobile Money'; - } else if (value === '2' || value === '02') { - return '(02) Voucher'; - } else if (value === '3' || value === '03') { - return '(03) Digital Wallet'; - } else if (value === '4' || value === '04') { - return '(04) Proxy'; - } - return value; - } - - searchAccounts(): void { - - } -} +export class AccountMapperComponent {} diff --git a/src/app/account-mapper/beneficiaries/beneficiaries.component.html b/src/app/account-mapper/beneficiaries/beneficiaries.component.html new file mode 100644 index 00000000..50426b9f --- /dev/null +++ b/src/app/account-mapper/beneficiaries/beneficiaries.component.html @@ -0,0 +1,161 @@ +
+ +
+ + + + + +
+ + + + + + + + + +
+ +
+ +

+ {{ 'labels.inputs.Filters' | translate }} +

+ +
+ +
+
+
+
+ +
+ {{ 'labels.inputs.Government Entity' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Financial Institution' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Functional ID' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Financial Address' | translate }} + + + +
+ +
+ +
+ +
+
+
+
+
+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ 'labels.inputs.Government Entity' | translate }}{{ item.registeringInstitutionId }}{{ 'labels.inputs.Financial Institution' | translate }}{{ item.bankingInstitutionCode }}{{ 'labels.inputs.Functional ID' | translate }}{{ item.payeeIdentity }}{{ 'labels.inputs.Financial Address' | translate }} + + {{ 'labels.inputs.Payment Modality' | translate }}{{ paymentModalityDescription(item.paymentModality) }}
{{'labels.texts.No data matching data found'| translate}}/td> +
+
+ + +
+ +
+ +
diff --git a/src/app/account-mapper/beneficiaries/beneficiaries.component.scss b/src/app/account-mapper/beneficiaries/beneficiaries.component.scss new file mode 100644 index 00000000..023cb622 --- /dev/null +++ b/src/app/account-mapper/beneficiaries/beneficiaries.component.scss @@ -0,0 +1,66 @@ +@import "theme/theme"; + +.container { + width: 100%; + gap: 1rem; + margin-top: 1rem; +} + +.account-mapper-wrap { + flex-wrap: wrap; + width: 100%; + + mifosx-batches, + mifosx-sub-batches, + mifosx-filter-selector { + width: 100%; + } +} + +.equal-width { + width: 100%; + box-sizing: border-box; + margin-right: 0 !important; +} + +.filter-options { + width: 100%; + margin-bottom: 20px; +} + +.filter-header { + padding: 0.3rem; +} + +.mat-expansion-panel-header:hover, +.mat-expansion-panel-header:focus { + background-color: inherit !important; + box-shadow: none !important; +} + +.mat-expansion-panel-header { + height: fit-content; +} + +.active-filter-button { + background-color: $green; + color: $white; +} + +.push-end { + text-align: end; +} + +.filter-button { + background-color: $light-grey; +} + +.search-button { + width: 12rem; + border-radius: 2rem; + margin-top: 1rem; + background-color: $green; + padding: 0.5rem 0; + box-shadow: none; + color: $white; +} diff --git a/src/app/account-mapper/beneficiaries/beneficiaries.component.spec.ts b/src/app/account-mapper/beneficiaries/beneficiaries.component.spec.ts new file mode 100644 index 00000000..590af677 --- /dev/null +++ b/src/app/account-mapper/beneficiaries/beneficiaries.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BeneficiariesComponent } from './beneficiaries.component'; + +describe('BeneficiariesComponent', () => { + let component: BeneficiariesComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BeneficiariesComponent] + }); + fixture = TestBed.createComponent(BeneficiariesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/account-mapper/beneficiaries/beneficiaries.component.ts b/src/app/account-mapper/beneficiaries/beneficiaries.component.ts new file mode 100644 index 00000000..ce662469 --- /dev/null +++ b/src/app/account-mapper/beneficiaries/beneficiaries.component.ts @@ -0,0 +1,264 @@ +/** Angular Imports */ +import { ChangeDetectorRef, Component, OnInit, ViewChild } from '@angular/core'; +import { UntypedFormControl } from '@angular/forms'; +import { MatPaginator, PageEvent } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatExpansionPanel } from '@angular/material/expansion'; + +/** Custom Services */ +import { AccountMapperService } from '../services/account-mapper.service'; +import { AuthenticationService } from 'app/core/authentication/authentication.service'; + +/** Custom Models */ +import { Account, AccountData } from '../models/account-mapper.model'; +import { Dates } from 'app/core/utils/dates'; + +/** + * Beneficiaries Component + */ +@Component({ + selector: 'mifosx-beneficiaries', + templateUrl: './beneficiaries.component.html', + styleUrls: ['./beneficiaries.component.scss'], +}) +export class BeneficiariesComponent implements OnInit { + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) set matSort(sort: MatSort) { + this.dataSource.sort = sort; + this.setSortingAccessor(); + } + @ViewChild('panel') panel: MatExpansionPanel; + + /** government Entity form control. */ + governmentEntity = new UntypedFormControl(); + /** financial Institution form control. */ + financialInstitution = new UntypedFormControl(); + /** functional Id form control. */ + functionalId = new UntypedFormControl(); + /** financial Address form control. */ + financialAddress = new UntypedFormControl(); + + /** Toggle buttons */ + showGovernmentEntityButton = true; + showFinancialInstitutionButton = true; + + /** Columns to be displayed in transactions table. */ + displayedColumns: string[] = ['governmentEntity', 'financialInstitution', 'functionalId', 'financialAddress', 'paymentModality']; + /** Data source for transactions table. */ + dataSource = new MatTableDataSource(); + + totalRows = 0; + currentPage = 0; + pageSize = 10; + isLoading = false; + credentials: any; + + accountsData: AccountData; + + /** Track selected filters */ + selectedFilters = { + governmentEntity: false, + financialInstitution: false, + functionalId: false, + financialAddress: false, + }; + + constructor(private dates: Dates, + private accountMapperService: AccountMapperService, + private cdr: ChangeDetectorRef, + private authenticationService: AuthenticationService) { + this.credentials = this.authenticationService.userDetails; + } + + ngOnInit(): void { + const userType = this.displayUserType(); + if (userType === 'Govt. Entity User') { + this.displayedColumns = this.displayedColumns.filter((column) => column !== 'governmentEntity'); + this.showGovernmentEntityButton = false; + } + + if (userType === 'FSP User') { + this.displayedColumns = this.displayedColumns.filter((column) => column !== 'financialInstitution'); + this.showFinancialInstitutionButton = false; + } + this.getAccounts(); + } + + /** Set sorting accessor for table columns */ + private setSortingAccessor(): void { + this.dataSource.sortingDataAccessor = (item: Account, property) => { + switch (property) { + case 'governmentEntity': + return item.registeringInstitutionId; + case 'financialInstitution': + return item.bankingInstitutionCode; + case 'functionalId': + return item.payeeIdentity; + default: + return item.paymentModality; + } + }; + } + + /** + * Get All accounts. + * */ + getAccounts(): void { + this.isLoading = true; + this.accountMapperService.getAccounts(this.currentPage, this.pageSize, 'requestFile', 'asc').subscribe( + (accounts: AccountData) => { + this.dataSource = new MatTableDataSource(accounts.content); + this.cdr.detectChanges(); + this.dataSource.paginator = this.paginator; + this.totalRows = accounts.totalElements; + this.isLoading = false; + }, + (error: any) => { + this.isLoading = false; + } + ); + } + + /** + * Display user type. + * */ + displayUserType(): string { + if (this.authenticationService.isOauthKeyCloak()) { + return this.credentials ? this.credentials.userType : ''; + } else { + return this.credentials ? this.credentials.userType : ''; + } + } + + /** + * Convert timestamp to UTC date. + * + * @param {any} timestamp Timestamp + * @returns {string} UTC date + */ + convertTimestampToUTCDate(timestamp: any): string { + if (!timestamp) { + return undefined; + } + return this.dates.formatUTCDate(new Date(timestamp)); + } + + /** + * Page changed. + * + * @param {PageEvent} event Page event + */ + pageChanged(event: PageEvent) { + this.currentPage = event.pageIndex; + this.pageSize = event.pageSize; + this.getAccounts(); + } + + /** + * Get payment modality description. + * + * @param {string} value Value + * @returns {string} Payment modality description + */ + paymentModalityDescription(value: string): string { + if (value === '0' || value === '00') { + return '(00) Bank Account'; + } else if (value === '1' || value === '01') { + return '(01) Mobile Money'; + } else if (value === '2' || value === '02') { + return '(02) Voucher'; + } else if (value === '3' || value === '03') { + return '(03) Digital Wallet'; + } else if (value === '4' || value === '04') { + return '(04) Proxy'; + } + return value; + } + + /** + * Search accounts by filtering with form control values. + */ + searchAccounts(): void { + const filterValues = this.extractFilterValues(); + this.setFilterPredicate(); + this.applyFilters(filterValues); + this.updateSelectedFilters(); + this.panel.close(); + } + + /** + * Extract filter values from form controls. + * @returns {any} Filter values + */ + private extractFilterValues(): any { + return { + governmentEntity: this.governmentEntity.value ? this.governmentEntity.value.trim().toLowerCase() : '', + financialInstitution: this.financialInstitution.value ? this.financialInstitution.value.trim().toLowerCase() : '', + functionalId: this.functionalId.value ? this.functionalId.value.trim().toLowerCase() : '', + financialAddress: this.financialAddress.value ? this.financialAddress.value.trim().toLowerCase() : '' + }; + } + + /** + * Set the filter predicate for the MatTableDataSource. + */ + private setFilterPredicate(): void { + this.dataSource.filterPredicate = (data: Account, filter: string) => { + const searchTerms = JSON.parse(filter); + return ( + data.registeringInstitutionId.toLowerCase().includes(searchTerms.governmentEntity) && + data.bankingInstitutionCode.toLowerCase().includes(searchTerms.financialInstitution) && + data.payeeIdentity.toLowerCase().includes(searchTerms.functionalId) && + data.financialAddress.toLowerCase().includes(searchTerms.financialAddress) + ); + }; + } + + /** + * Apply the filters to the dataSource. + * @param {any} filterValues The values to filter the dataSource with + */ + private applyFilters(filterValues: any): void { + this.dataSource.filter = JSON.stringify(filterValues); + + // Reset paginator to the first page after applying filters + if (this.dataSource.paginator) { + this.dataSource.paginator.firstPage(); + } + } + + /** + * Update the selected filters based on the current form control values. + */ + private updateSelectedFilters(): void { + this.selectedFilters.governmentEntity = !!this.governmentEntity.value; + this.selectedFilters.financialInstitution = !!this.financialInstitution.value; + this.selectedFilters.functionalId = !!this.functionalId.value; + this.selectedFilters.financialAddress = !!this.financialAddress.value; + } + + /** + * Toggle the filter panel. + * + * @param {MouseEvent} event Mouse event + */ + togglePanel(event?: MouseEvent) { + if (event) { + event.stopPropagation(); + } + this.panel.toggle(); + } + + /** + * Remove a filter. + * + * @param {string} filterType Filter type + * @param {MouseEvent} event Mouse event + */ + removeFilter(filterType: string, event?: MouseEvent): void { + event?.stopPropagation(); + (this as any)[filterType].reset(); + this.searchAccounts(); + } +} diff --git a/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.html b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.html new file mode 100644 index 00000000..c8963297 --- /dev/null +++ b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.html @@ -0,0 +1,87 @@ +
+ +
+ +
+
+ Request ID * + + + +
+
+ Source BBID * + + + + {{ dfsp.name }} + + + + Source BBID is required + + +
+
+ Payee Identity * + + + + Payee Identity is required + + +
+
+ Payment Modality * + + + Bank Account + Mobile Money + Voucher + Digital Wallet + Proxy + + + Payment Modality is required + + +
+ +
+ Financial Address * + + + + Financial Address is required + + +
+
+ Banking Institute Code * + + + + {{ dfsp.name }} + + + + Banking Institute Code is required + + +
+
+
+ + + + +
+
+
diff --git a/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.scss b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.scss new file mode 100644 index 00000000..6900f7f3 --- /dev/null +++ b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.scss @@ -0,0 +1,100 @@ +@import "theme/theme"; +.container { + max-width: 90%; + .mat-card{ + padding: 2rem 5rem 0 5rem; + background-color: $light-grey; + box-shadow: none; + + h1 { + color: $primary; + } + +} + + .content { + div { + margin: 1rem 0; + word-wrap: break-word; + + span { + display: block; + } + } + .input-box{ + display: flex; + justify-content: space-between; + align-items: center; + margin: 1rem 0; + flex-direction: column; + + } + } +} + +.edit-b{ + width: 8rem; + background-color: $primary; +} + +.delete-b{ + width: 8rem; + background-color: $red; +} + +.update-b{ + width: 12rem; + background-color: $primary; + +} + +.custom-button { + border-radius: 5px; + padding: 0.2rem 0; + box-shadow: none; + color: $white; +} + + +.not-allowed { + cursor: not-allowed; +} + +::ng-deep .custom-view-user.mat-form-field-appearance-outline .mat-form-field-outline { + color: black; + background-color: $white; + border-radius: 5px; +} +::ng-deep .custom-view-user.mat-form-field input:disabled{ + color: black; +} +::ng-deep .custom-view-user.mat-form-field select:disabled{ + color: black; +} + +.buttons-group { + + display: flex; + justify-content: flex-end; + align-items: center; + gap: 1rem; + + .button { + padding: 0.3rem 4rem; + border-radius: $border-size; + background-color: $white; + color: $primary; + cursor: pointer; + } + + .submit { + background-color: $primary; + color: $white; + } + + .submit:disabled { + background-color: $light-black; + color: $white; + cursor: not-allowed; + } +} \ No newline at end of file diff --git a/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.spec.ts b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.spec.ts new file mode 100644 index 00000000..b5a205ca --- /dev/null +++ b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateBeneficiariesComponent } from './create-beneficiaries.component'; + +describe('CreateBeneficiariesComponent', () => { + let component: CreateBeneficiariesComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [CreateBeneficiariesComponent] + }); + fixture = TestBed.createComponent(CreateBeneficiariesComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.ts b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.ts new file mode 100644 index 00000000..5d45499c --- /dev/null +++ b/src/app/account-mapper/create-beneficiaries/create-beneficiaries.component.ts @@ -0,0 +1,116 @@ +/** Angular Imports */ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; + +/** Custom Services */ +import { G2PPaymentConfigService } from "app/configuration/services/g2p-payment-config.service"; +import { AccountMapperService } from "../services/account-mapper.service"; + +/** Router Imports */ +import { Router } from "@angular/router"; + +/** + * Create Beneficiaries Component + */ +@Component({ + selector: 'mifosx-create-beneficiaries', + templateUrl: './create-beneficiaries.component.html', + styleUrls: ['./create-beneficiaries.component.scss'] +}) +export class CreateBeneficiariesComponent implements OnInit { + programForm: FormGroup; + govtEntities: any[] = []; + programs: any[] = []; + dfsps: any[] = []; + + /** + * @param {FormBuilder} fb Form builder. + * @param {G2PPaymentConfigService} g2pPaymentService G2P Payment service. + * @param {AccountMapperService} accountMapperService Account mapper service. + * @param {Router} router Router. + */ + constructor( + private fb: FormBuilder, + private g2pPaymentService: G2PPaymentConfigService, + private accountMapperService: AccountMapperService, + private router: Router + ) { } + + ngOnInit(): void { + this.createProgramForm(); + this.loadDropdownData(); + } + + /** + * Create the program form. + */ + createProgramForm() { + this.programForm = this.fb.group({ + requestId: [{ value: this.generateID(12), disabled: true }, Validators.required], + sourceBBID: ["", Validators.required], + payeeIdentity: ["", Validators.required], + paymentModality: ["", Validators.required], + financialAddress: ["", Validators.required], + bankingInstitutionCode: ["", Validators.required], + }); + } + + /** + * Generate a random ID. + * @param {number} n Number of digits. + * @returns {string} Random ID. + */ + generateID(n: number): string { + const max = Math.pow(10, n); + const min = max / 10; + return Math.floor(Math.random() * (max - min) + min).toString(); + } + + + /** + * Load dropdown data. + */ + loadDropdownData() { + this.g2pPaymentService.getTemplateData().subscribe( + (data: any) => { + this.govtEntities = data.govtEntities; + this.programs = data.programs; + this.dfsps = data.payerDfsps; + }, + (error: any) => { + console.log('Error:', error); + } + ); + } + + /** + * Submit the form. + */ + submit() { + if (this.programForm.valid) { + const formData = this.programForm.getRawValue(); + const accountData = { + requestID: formData.requestId, + sourceBBID: formData.sourceBBID, + beneficiaries: [ + { + payeeIdentity: formData.payeeIdentity, + paymentModality: formData.paymentModality, + financialAddress: formData.financialAddress, + bankingInstitutionCode: formData.bankingInstitutionCode + } + ] + }; + + this.accountMapperService.createAccount(accountData).subscribe( + (response: any) => { + //route to the program list page + this.router.navigate(['dashboard/account-mapper/beneficiaries']); + }, + (error: any) => { + console.log('Error:', error); + } + ); + } + } +} \ No newline at end of file diff --git a/src/app/account-mapper/filter-selector/filter-selector.component.html b/src/app/account-mapper/filter-selector/filter-selector.component.html new file mode 100644 index 00000000..59b2bc8e --- /dev/null +++ b/src/app/account-mapper/filter-selector/filter-selector.component.html @@ -0,0 +1,9 @@ +
+ + + +
\ No newline at end of file diff --git a/src/app/account-mapper/filter-selector/filter-selector.component.scss b/src/app/account-mapper/filter-selector/filter-selector.component.scss new file mode 100644 index 00000000..c12ef83e --- /dev/null +++ b/src/app/account-mapper/filter-selector/filter-selector.component.scss @@ -0,0 +1,16 @@ +@import 'theme/theme'; + +.payment-hub-ee-wrap { + flex-wrap: wrap; +} + +.title { + font-size: 1.25rem; + color: $dark-grey; +} + +.mat-nav-list .mat-list-item:focus, +.mat-action-list .mat-list-item:focus, +.mat-list-option:focus { + background: #76b900 !important; +} diff --git a/src/app/account-mapper/filter-selector/filter-selector.component.spec.ts b/src/app/account-mapper/filter-selector/filter-selector.component.spec.ts new file mode 100644 index 00000000..dc81d0f2 --- /dev/null +++ b/src/app/account-mapper/filter-selector/filter-selector.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FilterSelectorComponent } from './filter-selector.component'; + +describe('FilterSelectorComponent', () => { + let component: FilterSelectorComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [FilterSelectorComponent] + }); + fixture = TestBed.createComponent(FilterSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/account-mapper/filter-selector/filter-selector.component.ts b/src/app/account-mapper/filter-selector/filter-selector.component.ts new file mode 100644 index 00000000..e4105dae --- /dev/null +++ b/src/app/account-mapper/filter-selector/filter-selector.component.ts @@ -0,0 +1,68 @@ +/** Angular Imports */ +import { Component } from '@angular/core'; + +/** Custom Services */ +import { AuthenticationService } from 'app/core/authentication/authentication.service'; + +/** Custom Models */ +import { Section } from 'app/payment-hub/filter-selector/section-model'; + + +/** + * Filter selector component. + */ +@Component({ + selector: 'mifosx-filter-selector', + templateUrl: './filter-selector.component.html', + styleUrls: ['./filter-selector.component.scss'] +}) +export class FilterSelectorComponent { + sections: Section[]; + credentials: any; + + /** + * @param {AuthenticationService} authenticationService Authentication service. + */ + constructor(private authenticationService: AuthenticationService) { + this.credentials = this.authenticationService.userDetails; + } + + ngOnInit(): void { + this.sections = [ + { label: 'Beneficiaries', routeTo: ['dashboard','account-mapper', 'beneficiaries'], active: true, disabled: false }, + { label: 'Create Beneficiaries', routeTo: ['dashboard','account-mapper', 'create-beneficiaries'], active: false, disabled: this.isDisabled() }, + ]; + } + + /** + * Get the user type. + */ + getUserType(): string { + if (this.authenticationService.isOauthKeyCloak()) { + return this.credentials ? this.credentials.userType : ''; + } else { + return this.credentials ? this.credentials.userType : ''; + } + } + + /** + * Check if the user is a government entity user. + */ + isDisabled(): boolean { + return this.getUserType() !== 'Govt. Entity User'; + } + + /** + * Set the active section. + * + * @param {Section} s Section to set active. + */ + setActive(s: Section): void { + this.sections.forEach((section: Section) => { + section.active = false; + if (section.label === s.label) { + section.active = true; + } + }); + } +} diff --git a/src/app/account-mapper/services/account-mapper.service.ts b/src/app/account-mapper/services/account-mapper.service.ts index 6cd1d3dc..716c8d7b 100644 --- a/src/app/account-mapper/services/account-mapper.service.ts +++ b/src/app/account-mapper/services/account-mapper.service.ts @@ -1,8 +1,13 @@ import { HttpClient, HttpParams } from '@angular/common/http'; import { Injectable } from '@angular/core'; -import { environment } from 'environments/environment'; import { Observable } from 'rxjs'; +/** Environment Configuration */ +import { environment } from 'environments/environment'; + +/** + * Account Mapper Service + */ @Injectable({ providedIn: 'root' }) @@ -10,19 +15,38 @@ export class AccountMapperService { apiPrefix: string = environment.backend.account; + /** + * @param {HttpClient} http HttpClient + */ constructor(private http: HttpClient) { } /** + * @param {number} page Page number + * @param {number} size Page size + * @param {string} orderBy Order by + * @param {string} sortOrder Sort order * @returns {Observable} Users data */ getAccounts(page: number, size: number, orderBy: string, sortOrder: string): Observable { const httpParams = new HttpParams() .set('page', page) - .set('size', size) + .set('pageSize', size) .set('sortOrder', sortOrder) .set('orderBy', orderBy); return this.http.get(this.apiPrefix + '/beneficiaries', { params: httpParams }); + // return this.http.get("../../../assets/mock/account-mapper.mock.json") } + + /** + * @param {any} accountData Account data + * @returns {Observable} Account data + */ + createAccount(accountData: any): Observable { + const headers = { + 'X-CallbackURL': environment.backend.voucherCallbackUrl, + }; + return this.http.post(this.apiPrefix + '/beneficiary', accountData, { headers }); + } } diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 843f2a75..7f0c816a 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -3,6 +3,7 @@ import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { HttpClient, HttpClientModule } from '@angular/common/http'; +import { DatePipe, LocationStrategy } from '@angular/common'; /** Tanslation Imports */ import { TranslateHttpLoader } from '@ngx-translate/http-loader'; @@ -25,15 +26,16 @@ import { SettingsModule } from './settings/settings.module'; import { SystemModule } from './system/system.module'; import { UsersModule } from './users/users.module'; import { PaymentHubModule } from './payment-hub/paymenthub.module'; +import { VouchersModule } from './vouchers/vouchers.module'; +import { AccountMapperModule } from './account-mapper/account-mapper.module'; +import { KeycloakAngularModule } from 'keycloak-angular'; +import { VisualizationsModule } from './visualizations/visualizations.module'; +import { ConfigurationModule } from './configuration/configuration.module'; /** Main Routing Module */ import { AppRoutingModule } from './app-routing.module'; -import { DatePipe, LocationStrategy } from '@angular/common'; -import { VouchersModule } from './vouchers/vouchers.module'; -import { AccountMapperModule } from './account-mapper/account-mapper.module'; -import { KeycloakAngularModule } from 'keycloak-angular'; /** * App Module @@ -65,7 +67,9 @@ import { KeycloakAngularModule } from 'keycloak-angular'; PaymentHubModule, VouchersModule, AccountMapperModule, - AppRoutingModule, + VisualizationsModule, + ConfigurationModule, + AppRoutingModule ], declarations: [WebAppComponent, NotFoundComponent], providers: [ diff --git a/src/app/configuration/configuration-routing.module.ts b/src/app/configuration/configuration-routing.module.ts new file mode 100644 index 00000000..a1c5136d --- /dev/null +++ b/src/app/configuration/configuration-routing.module.ts @@ -0,0 +1,89 @@ +/** Angular Imports */ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +/** Routing Imports */ +import { Route } from 'app/core/route/route.service'; + +/** Custom Components */ +import { ConfigurationComponent } from './configuration/configuration.component'; +import { G2pPaymentComponent } from './g2p-payment/g2p-payment.component'; +import { UpdateG2pPaymentComponent } from './update-g2p-payment/update-g2p-payment.component'; +import { CreateG2pPaymentComponent } from './create-g2p-payment/create-g2p-payment.component'; + +/** Custom Resolvers */ +import { ViewPaymentConfigResolver } from './resolver/view-payment.resolver'; +import { G2PTemplateResolver } from './resolver/template.resolver'; +import { G2PPaymentConfigsResolver } from './resolver/g2p-payment-configs.resolver'; + + +/** + * Configuration Routes + */ +const routes: Routes = [ + Route.withShell([ + { + path: "dashboard", + children: [ + { + path: "configuration", + component: ConfigurationComponent, + data: { breadcrumb: { skip: true } }, + children: [ + { + path: "", + redirectTo: "g2p-payment-config", + pathMatch: "full", + }, + { + path: "g2p-payment-config", + + data: { breadcrumb: "G2P Payment Config" }, + children: [ + { + path: "", + component: G2pPaymentComponent, + resolve: { + g2pPayments: G2PPaymentConfigsResolver + }, + }, + { + path: ":id", + resolve: { + program: ViewPaymentConfigResolver, + template: G2PTemplateResolver + }, + component: UpdateG2pPaymentComponent, + data: { breadcrumb: { skip: true } }, + } + + ] + + }, + { + path: "create", + component: CreateG2pPaymentComponent, + data: { breadcrumb: "Create"}, + resolve: { + template: G2PTemplateResolver + } + }, + + ], + }, + ], + }, + ]) +]; + +/** + * Configuration Routing Module + * + * Configures the routes for the Configuration Module + */ +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], + providers: [ViewPaymentConfigResolver, G2PTemplateResolver, G2PPaymentConfigsResolver] +}) +export class ConfigurationRoutingModule { } diff --git a/src/app/configuration/configuration.module.ts b/src/app/configuration/configuration.module.ts new file mode 100644 index 00000000..bb31f27f --- /dev/null +++ b/src/app/configuration/configuration.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ConfigurationRoutingModule } from './configuration-routing.module'; +import { G2pSectionFilterComponent } from './g2p-section-filter/g2p-section-filter.component'; +import { ConfigurationComponent } from './configuration/configuration.component'; +import { SharedModule } from 'app/shared/shared.module'; +import { PipesModule } from 'app/pipes/pipes.module'; +import { G2pPaymentComponent } from './g2p-payment/g2p-payment.component'; +import { CreateG2pPaymentComponent } from './create-g2p-payment/create-g2p-payment.component'; +import { UpdateG2pPaymentComponent } from './update-g2p-payment/update-g2p-payment.component'; + + +@NgModule({ + declarations: [ + G2pPaymentComponent, + G2pSectionFilterComponent, + ConfigurationComponent, + CreateG2pPaymentComponent, + UpdateG2pPaymentComponent + + ], + imports: [ + CommonModule, + ConfigurationRoutingModule, + SharedModule, + PipesModule + ] +}) +export class ConfigurationModule { } diff --git a/src/app/configuration/configuration/configuration.component.html b/src/app/configuration/configuration/configuration.component.html new file mode 100644 index 00000000..9a5d93f3 --- /dev/null +++ b/src/app/configuration/configuration/configuration.component.html @@ -0,0 +1,7 @@ +
+
+ +
+ + +
\ No newline at end of file diff --git a/src/app/configuration/configuration/configuration.component.scss b/src/app/configuration/configuration/configuration.component.scss new file mode 100644 index 00000000..6f98a634 --- /dev/null +++ b/src/app/configuration/configuration/configuration.component.scss @@ -0,0 +1,16 @@ +.container { + width: 90%; + } + + .vouchers-wrap { + flex-wrap: wrap; + width: 100%; + + mifosx-voucher-management, + mifosx-vouchers-bulk-import, + mifosx-filter-selector { + width: 100%; + } + + } + \ No newline at end of file diff --git a/src/app/configuration/configuration/configuration.component.spec.ts b/src/app/configuration/configuration/configuration.component.spec.ts new file mode 100644 index 00000000..f9a950d2 --- /dev/null +++ b/src/app/configuration/configuration/configuration.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfigurationComponent } from './configuration.component'; + +describe('ConfigurationComponent', () => { + let component: ConfigurationComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ConfigurationComponent] + }); + fixture = TestBed.createComponent(ConfigurationComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/configuration/configuration/configuration.component.ts b/src/app/configuration/configuration/configuration.component.ts new file mode 100644 index 00000000..01643731 --- /dev/null +++ b/src/app/configuration/configuration/configuration.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'mifosx-configuration', + templateUrl: './configuration.component.html', + styleUrls: ['./configuration.component.scss'] +}) +export class ConfigurationComponent { + +} diff --git a/src/app/configuration/create-g2p-payment/create-g2p-payment.component.html b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.html new file mode 100644 index 00000000..ecbba4b0 --- /dev/null +++ b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.html @@ -0,0 +1,77 @@ +
+ +
+ +
+
+ Goverment Entity * + + + + {{ entity.name }} + + + Government Entity required + +
+ +
+ Program * + + + + {{ program.programName }} + + + Program is required + +
+ +
+ Payer DFSP * + + + + {{ dfsp.name }} + + + Payer DFSP is required + +
+ +
+ Payment Account * + + + Account is required + + Account Number cannot contain any special character or alphabets + + Account Number should be of 12 digits + +
+ +
+ Status * + + + Active + Inactive + + Status is required + +
+
+
+ + + + + +
+
+
diff --git a/src/app/configuration/create-g2p-payment/create-g2p-payment.component.scss b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.scss new file mode 100644 index 00000000..da8e6f7b --- /dev/null +++ b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.scss @@ -0,0 +1,83 @@ +@import "theme/theme"; + +.container { + max-width: 90%; + + .mat-card{ + padding: 2rem 5rem 0 5rem; + background-color: $light-grey; + box-shadow: none; + + h1 { + color: $primary; + } + +} + + .content { + div { + margin: 1rem 0; + } + } +} + +.edit-b{ + width: 8rem; + background-color: $primary; +} + +.delete-b{ + width: 8rem; + background-color: $red; +} + +.update-b{ + width: 12rem; + background-color: $primary; +} + +.custom-button { + border-radius: 5px; + padding: 0.2rem 0; + box-shadow: none; + color: $white; +} + +.not-allowed { + cursor: not-allowed; +} + +.buttons-group { + gap: 1rem; + + .button { + padding: 0.3rem 4rem; + border-radius: $border-size; + background-color: $white; + color: $primary; + cursor: pointer; + } + + .submit { + background-color: $primary; + color: $white; + } + + .submit:disabled { + background-color: $light-black; + color: $white; + cursor: not-allowed; + } +} + +::ng-deep .custom-g2p-field.mat-form-field-appearance-outline .mat-form-field-outline { + color: $black; + background-color: $white; + border-radius: 5px; +} +::ng-deep .custom-g2p-field.mat-form-field input:disabled{ + color: $black; +} +::ng-deep .custom-g2p-field.mat-form-field select:disabled{ + color: $black; +} \ No newline at end of file diff --git a/src/app/configuration/create-g2p-payment/create-g2p-payment.component.spec.ts b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.spec.ts new file mode 100644 index 00000000..2707761f --- /dev/null +++ b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CreateG2pProgramComponent } from './create-g2p-program.component'; + +describe('CreateG2pProgramComponent', () => { + let component: CreateG2pProgramComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [CreateG2pProgramComponent] + }); + fixture = TestBed.createComponent(CreateG2pProgramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/configuration/create-g2p-payment/create-g2p-payment.component.ts b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.ts new file mode 100644 index 00000000..90d2723c --- /dev/null +++ b/src/app/configuration/create-g2p-payment/create-g2p-payment.component.ts @@ -0,0 +1,84 @@ +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { G2PPaymentConfigService } from "../services/g2p-payment-config.service"; +import { ActivatedRoute, Router } from "@angular/router"; + +@Component({ + selector: 'mifosx-create-g2p-payment', + templateUrl: './create-g2p-payment.component.html', + styleUrls: ['./create-g2p-payment.component.scss'] +}) +export class CreateG2pPaymentComponent implements OnInit { + + programForm: FormGroup; + govtEntities: any[] = []; + programs: any[] = []; + payerDfsps: any[] = []; + + /** + * @param fb FormBuilder + * @param g2pPaymentService G2PPaymentConfigService + * @param router Router + * @param route ActivatedRoute + */ + constructor( + private fb: FormBuilder, + private g2pPaymentService: G2PPaymentConfigService, + private router: Router, + private route: ActivatedRoute) { + this.route.data.subscribe((data) => { + this.govtEntities = data.template.govtEntities; + this.programs = data.template.programs; + this.payerDfsps = data.template.payerDfsps; + }) + } + + ngOnInit(): void { + this.createProgramForm(); + } + + /** + * Create the program form + */ + createProgramForm() { + this.programForm = this.fb.group({ + governmentEntity: ["", [Validators.required]], + program: ["", [Validators.required]], + account: ["", [Validators.required, Validators.minLength(12), Validators.pattern("^[0-9]*$")]], + payerDfsp: ["", [Validators.required]], + status: ["", [Validators.required]], + }); + } + + /** + * Submit the form + */ + submit() { + if (this.programForm.valid) { + const formData = this.programForm.getRawValue(); + // Create the payload to send to the API + const payload = { + governmentEntity: { + govInstId: formData.governmentEntity + }, + dfsp: { + fspId: formData.payerDfsp + }, + program: { + programId: formData.program + }, + account: formData.account, + status: formData.status + }; + + this.g2pPaymentService.createG2pPayment(payload).subscribe( + (response) => { + this.router.navigate(['dashboard/configuration/g2p-payment-config']); + }, + (error) => { + console.log('Error:', error); + } + ); + } + } +} \ No newline at end of file diff --git a/src/app/configuration/g2p-payment/g2p-payment.component.html b/src/app/configuration/g2p-payment/g2p-payment.component.html new file mode 100644 index 00000000..a9fc224c --- /dev/null +++ b/src/app/configuration/g2p-payment/g2p-payment.component.html @@ -0,0 +1,167 @@ +
+
+ + + + +
+ + + + + + + + + + +
+ +
+ +

{{ 'labels.inputs.Filters' | translate }}

+ +
+ +
+
+
+
+ +
+ {{ 'labels.inputs.Government Entity' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Program' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Payer DFSP' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Payment Account' | translate }} + + + +
+ +
+ {{ 'labels.inputs.Status' | translate }} + + + +
+
+ +
+ +
+ +
+
+
+
+ +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ 'labels.inputs.Government Entity' | translate }} + + {{ item?.governmentEntity?.name }} + + {{ 'labels.inputs.Program' | translate }} + + {{ item?.program?.programName }} + + {{ 'labels.inputs.Payer DFSP' | translate }} + + {{ item.dfsp?.name }} + + {{ 'labels.inputs.Payment Account' | translate }} + {{ item.account }} + {{ 'labels.inputs.Status' | translate }} + + {{ item.status }} +
No data matching data found
+
+ +
+
+
\ No newline at end of file diff --git a/src/app/configuration/g2p-payment/g2p-payment.component.scss b/src/app/configuration/g2p-payment/g2p-payment.component.scss new file mode 100644 index 00000000..e485ef2b --- /dev/null +++ b/src/app/configuration/g2p-payment/g2p-payment.component.scss @@ -0,0 +1,83 @@ +@import "theme/theme"; + +.container { + width: 100%; + gap: 1rem; +} + +td , th { + white-space: normal; + word-wrap: break-word; + max-width: 200px; +} + +.filter-header { + padding: 0.3rem; +} + +.search-button { + width: 12rem; + border-radius: 2rem; + margin-top: 1rem; + background-color: $green; + padding: 0.5rem 0; + box-shadow: none; + color: $white; +} + +.mat-expansion-panel-header:hover, +.mat-expansion-panel-header:focus { + background-color: inherit !important; + box-shadow: none !important; +} + +.mat-expansion-panel-header { + height: fit-content; +} + +.active-filter-button { + background-color: $green; + color: $white; +} + +.filter-button { + background-color: $light-grey; +} + +.push-end { + text-align: end; +} + +.grey { + color: grey; +} + +.black { + color: black; +} + +.blue { + color: blue; +} + +.green { + color: green; +} + +.orange { + color: orange; +} + +.ticket{ + color: $primary; +} + +.red { + color: red; +} + +.equal-width { + width: 100%; + box-sizing: border-box; + margin-right: 0 !important; +} diff --git a/src/app/configuration/g2p-payment/g2p-payment.component.spec.ts b/src/app/configuration/g2p-payment/g2p-payment.component.spec.ts new file mode 100644 index 00000000..a9dcef12 --- /dev/null +++ b/src/app/configuration/g2p-payment/g2p-payment.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { G2pProgramComponent } from './g2p-payment.component'; + +describe('G2pProgramComponent', () => { + let component: G2pProgramComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [G2pProgramComponent] + }); + fixture = TestBed.createComponent(G2pProgramComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/configuration/g2p-payment/g2p-payment.component.ts b/src/app/configuration/g2p-payment/g2p-payment.component.ts new file mode 100644 index 00000000..d2c52a50 --- /dev/null +++ b/src/app/configuration/g2p-payment/g2p-payment.component.ts @@ -0,0 +1,177 @@ +/** Angular Imports */ +import { ChangeDetectorRef, Component, ViewChild } from '@angular/core'; +import { UntypedFormControl } from '@angular/forms'; +import { MatPaginator } from '@angular/material/paginator'; +import { MatSort } from '@angular/material/sort'; +import { MatTableDataSource } from '@angular/material/table'; +import { MatExpansionPanel } from '@angular/material/expansion'; + +import { G2PPaymentConfigService } from '../services/g2p-payment-config.service'; +import { ActivatedRoute } from '@angular/router'; + +@Component({ + selector: 'mifosx-g2p-payment', + templateUrl: './g2p-payment.component.html', + styleUrls: ['./g2p-payment.component.scss'], +}) +export class G2pPaymentComponent { + @ViewChild(MatPaginator) paginator: MatPaginator; + @ViewChild(MatSort) set matSort(sort: MatSort) { + this.dataSource.sort = sort; + this.dataSource.sortingDataAccessor = (item: any, property) => { + switch (property) { + case 'governmentEntity': + return item.governmentEntity.name; + case 'program': + return item.program.programName; + case 'payerDfsp': + return item.dfsp.name; + case 'account': + return item.account; + default: + return item.status; + } + }; + } + @ViewChild('panel') panel: MatExpansionPanel; + + /** government Entity form control. */ + governmentEntity = new UntypedFormControl(); + /** serial Number form control. */ + program = new UntypedFormControl(); + /** functional Id form control. */ + payerDfsp = new UntypedFormControl(); + /** Paymnent Account form control. */ + account = new UntypedFormControl(); + /** status form control. */ + status = new UntypedFormControl(); + + /** Columns to be displayed in transactions table. */ + displayedColumns: string[] = ['governmentEntity', 'program', 'payerDfsp', 'account', 'status']; + /** Data source for transactions table. */ + dataSource = new MatTableDataSource(); + + programData: any; + isLoading = false; + + /** Track selected filters. */ + selectedFilters = { + governmentEntity: false, + program: false, + payerDfsp: false, + account: false, + status: false, + }; + + /** + * @param route ActivatedRoute + */ + constructor(private route: ActivatedRoute, private cdr: ChangeDetectorRef) { + this.route.data.subscribe((data: any) => { + this.programData = data.g2pPayments; + }); + } + + ngOnInit(): void { + this.getConfigs(); + } + + /** + * Get the G2P payment configurations. + * @returns The G2P payment configurations + */ + getConfigs(): void { + this.dataSource = new MatTableDataSource(this.programData); + this.cdr.detectChanges(); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.matSort; + } + + /** + * Get the status style based on the status code. + * @param code - The status code + * @returns The style class to be applied + */ + statusStyle(code: string): string { + if (code === 'Inactive') { + return 'red'; + } else if (code === 'Active') { + return 'green'; + } else { + return ''; + } + } + + /** + * Apply filters to the data source. + */ + searchConfigs(): void { + const filterValues = this.collectFilterValues(); + this.applyFilters(filterValues); + this.updateSelectedFilters(); + this.panel.close(); + } + + /** + * Collect values from the form controls for filtering. + * @returns Object containing filter values + */ + private collectFilterValues(): any { + return { + governmentEntity: this.governmentEntity.value ? this.governmentEntity.value.trim().toLowerCase() : '', + program: this.program.value ? this.program.value.trim().toLowerCase() : '', + payerDfsp: this.payerDfsp.value ? this.payerDfsp.value.trim().toLowerCase() : '', + status: this.status.value ? this.status.value.trim().toLowerCase() : '', + account: this.account.value ? this.account.value : '', + }; + } + + /** + * Apply filter criteria to the data source based on the user input. + * @param filterValues - The values to be used for filtering + */ + private applyFilters(filterValues: any): void { + this.dataSource.filterPredicate = (data: any, filter: string) => { + const searchTerms = JSON.parse(filter); + + return ( + data.governmentEntity.name.toLowerCase().includes(searchTerms.governmentEntity) && + data.program.programName.toLowerCase().includes(searchTerms.program) && + data.dfsp.name.toLowerCase().includes(searchTerms.payerDfsp) && + data.status.toLowerCase().includes(searchTerms.status) && + data.account === searchTerms.account + ); + }; + + this.dataSource.filter = JSON.stringify(filterValues); + } + + /** + * Update the selected filters state to reflect which filters are active. + */ + private updateSelectedFilters(): void { + this.selectedFilters.governmentEntity = !!this.governmentEntity.value; + this.selectedFilters.program = !!this.program.value; + this.selectedFilters.payerDfsp = !!this.payerDfsp.value; + this.selectedFilters.status = !!this.status.value; + this.selectedFilters.account = !!this.account.value; + } + + /** Clear filter */ + removeFilter(filterType: string, event?: MouseEvent): void { + event?.stopPropagation(); + (this as any)[filterType].reset(); + this.getConfigs(); + } + + /** + * Toggle the expansion panel. + * @param event + */ + togglePanel(event?: MouseEvent) { + if (event) { + event.stopPropagation(); + } + this.panel.toggle(); + } +} diff --git a/src/app/configuration/g2p-section-filter/g2p-section-filter.component.html b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.html new file mode 100644 index 00000000..59b2bc8e --- /dev/null +++ b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.html @@ -0,0 +1,9 @@ +
+ + + +
\ No newline at end of file diff --git a/src/app/configuration/g2p-section-filter/g2p-section-filter.component.scss b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/src/app/configuration/g2p-section-filter/g2p-section-filter.component.spec.ts b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.spec.ts new file mode 100644 index 00000000..f6ca4b7a --- /dev/null +++ b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { G2pSectionFilterComponent } from './g2p-section-filter.component'; + +describe('G2pSectionFilterComponent', () => { + let component: G2pSectionFilterComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [G2pSectionFilterComponent] + }); + fixture = TestBed.createComponent(G2pSectionFilterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/configuration/g2p-section-filter/g2p-section-filter.component.ts b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.ts new file mode 100644 index 00000000..14849433 --- /dev/null +++ b/src/app/configuration/g2p-section-filter/g2p-section-filter.component.ts @@ -0,0 +1,58 @@ +/** Angular Imports */ +import { Component } from '@angular/core'; + +/** Custom Models */ +import { Section } from 'app/payment-hub/filter-selector/section-model'; +import { NavigationEnd, Router } from '@angular/router'; +import { filter } from 'rxjs'; + +/** + * G2P Section Filter Component + */ +@Component({ + selector: 'mifosx-g2p-section-filter', + templateUrl: './g2p-section-filter.component.html', + styleUrls: ['./g2p-section-filter.component.scss'] +}) +export class G2pSectionFilterComponent { + sections: Section[]; + + constructor(private router: Router) { } + + ngOnInit(): void { + + // Define sections for the filter + this.sections = [ + { label: 'G2P Payment', routeTo: ['dashboard', 'configuration', 'g2p-payment-config'], active: true, disabled: false }, + { label: 'Create', routeTo: ['dashboard', 'configuration', 'create'], active: false, disabled: false }, + ]; + + // Subscribe to router events to mark active section + this.router.events.pipe( + filter(event => event instanceof NavigationEnd) + ).subscribe((event: NavigationEnd) => { + if (event.url.indexOf('/create') > 0) { + this.markActiveSection('Create'); + } else if (event.url.indexOf('/g2p-payment-config') > 0) { + this.markActiveSection('G2P Payment'); + } + }); + } + + /** Set active section */ + setActive(s: Section): void { + this.markActiveSection(s.label); + } + + /** Mark active section */ + markActiveSection(name: string): void { + this.sections.forEach((section: Section) => { + section.active = false; + if (section.label === name) { + section.active = true; + } + }); + } + + +} diff --git a/src/app/configuration/resolver/g2p-payment-configs.resolver.ts b/src/app/configuration/resolver/g2p-payment-configs.resolver.ts new file mode 100644 index 00000000..93a862aa --- /dev/null +++ b/src/app/configuration/resolver/g2p-payment-configs.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; + + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { G2PPaymentConfigService } from '../services/g2p-payment-config.service'; + +/** + * G2P Payment Configs resolver. + */ +@Injectable() +export class G2PPaymentConfigsResolver { + + /** + * @param {G2PPaymentConfigService} g2pPaymentConfigService Users service. + */ + constructor(private g2pPaymentConfigService: G2PPaymentConfigService) { } + + /** + * Returns the G2p Payment Configs data. + * @returns {Observable} + */ + resolve(): Observable { + return this.g2pPaymentConfigService.getG2pPayments(); + } + +} diff --git a/src/app/configuration/resolver/template.resolver.ts b/src/app/configuration/resolver/template.resolver.ts new file mode 100644 index 00000000..75173121 --- /dev/null +++ b/src/app/configuration/resolver/template.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; + + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { G2PPaymentConfigService } from '../services/g2p-payment-config.service'; + +/** + * G2P template data resolver. + */ +@Injectable() +export class G2PTemplateResolver { + + /** + * @param {G2PPaymentConfigService} g2pPaymentConfigService Users service. + */ + constructor(private g2pPaymentConfigService: G2PPaymentConfigService) { } + + /** + * Returns the G2p template data. + * @returns {Observable} + */ + resolve(): Observable { + return this.g2pPaymentConfigService.getTemplateData(); + } + +} diff --git a/src/app/configuration/resolver/view-payment.resolver.ts b/src/app/configuration/resolver/view-payment.resolver.ts new file mode 100644 index 00000000..2334db5a --- /dev/null +++ b/src/app/configuration/resolver/view-payment.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { G2PPaymentConfigService } from '../services/g2p-payment-config.service'; + +/** + * View resolver. + */ +@Injectable() +export class ViewPaymentConfigResolver { + + /** + * @param {G2PPaymentConfigService} g2pPaymentConfigService Users service. + */ + constructor(private g2pPaymentConfigService: G2PPaymentConfigService) {} + + /** + * Returns the G2p Payment Config data by ID. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const config_id = route.paramMap.get('id'); + return this.g2pPaymentConfigService.getG2pPaymentById(config_id); + } +} diff --git a/src/app/configuration/services/g2p-payment-config.service.ts b/src/app/configuration/services/g2p-payment-config.service.ts new file mode 100644 index 00000000..c1814f76 --- /dev/null +++ b/src/app/configuration/services/g2p-payment-config.service.ts @@ -0,0 +1,122 @@ +/** Angular Imports */ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +/** Environment Configuration */ +import { environment } from 'environments/environment'; + +/** rxjs Imports */ +import { forkJoin, Observable } from 'rxjs'; + +/** + * G2P Payment Config Service. + */ +@Injectable({ + providedIn: 'root' +}) +export class G2PPaymentConfigService { + + /** API URL Prefix */ + apiPrefix: string = environment.backend.g2pPaymentConfigApi; + + /** + * @param {HttpClient} http HttpClient. + */ + constructor(private http: HttpClient) { } + + /** + * @returns {Observable} Users data + */ + getG2pPayments(): Observable { + return this.http.get(this.apiPrefix + '/g2pPaymentConfig'); + + //mock data for g2p program config + // return this.http.get('../../assets/mock/g2p-program-config.mock.json'); + } + + + /** Create G2P Payment Config + * @param {any} payload G2P Payment Config payload + * @returns {Observable} G2P Payment Config data + */ + createG2pPayment( payload: any): Observable { + return this.http.post(this.apiPrefix + '/g2pPaymentConfig', payload); + } + + + /** Get G2P Payment Config by ID + * @param {string} id G2P Payment Config ID + * @returns {Observable} G2P Payment Config data + * */ + getG2pPaymentById(id: string): Observable { + return this.http.get(this.apiPrefix + '/g2pPaymentConfig/' + id); + } + + /** Update G2P Payment Config + * @param {string} id G2P Payment Config ID + * @param {any} payload G2P Payment Config payload + * @returns {Observable} G2P Payment Config + * */ + updateG2pPayment(id: string, payload: any): Observable { + return this.http.put(this.apiPrefix + '/g2pPaymentConfig/' + id, payload); + } + + /** Delete G2P Payment Config + * @param {string} id G2P Payment Config ID + * @returns {Observable} G2P Payment Config + * */ + deleteG2pPayment(id: string): Observable { + return this.http.delete(this.apiPrefix + '/g2pPaymentConfig/' + id); + } + + /** Get All Government Entities + * @returns {Observable} Government Entities data + * */ + getGovtEntities(): Observable { + return this.http.get(this.apiPrefix + '/governmentEntity'); + } + + /** Get Government Entity by ID + * @param {number} id Government Entity ID + * @returns {Observable} Government Entity data + * */ + getGovtEntityById(id: number): Observable { + return this.http.get(this.apiPrefix + '/governmentEntity/' + id); + } + + /** Get All Programs + * @returns {Observable} Programs data + * */ + getPrograms(): Observable { + return this.http.get(this.apiPrefix + '/program'); + } + + + /** Get All Payer DFSPs + * @returns {Observable} Payer DFSPs data + * */ + getPayerDfsps(): Observable { + return this.http.get(this.apiPrefix + '/dfsp'); + } + + /** Get Payer DFSP by ID + * @param {number} id Payer DFSP ID + * @returns {Observable} Payer DFSP data + * */ + getPayerDfspsbyId(id: number): Observable { + return this.http.get(this.apiPrefix + '/dfsp/' + id); + } + + /** Get Template Data + * @returns {Observable} Template data + * */ + getTemplateData(): Observable { + + return forkJoin({ + govtEntities: this.getGovtEntities(), + programs: this.getPrograms(), + payerDfsps: this.getPayerDfsps() + }); + } + +} diff --git a/src/app/configuration/update-g2p-payment/update-g2p-payment.component.html b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.html new file mode 100644 index 00000000..e7123648 --- /dev/null +++ b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.html @@ -0,0 +1,92 @@ +
+ + +
+ +
+ +
+ + +
+ +
+ Goverment Entity * + + + + + + + {{ govt.name }} + + + +
+ +
+ Program * + + + + + + + {{ program.programName }} + + + + +
+ +
+ Payer DFSP * + + + + + + + {{ dfsp.name }} + + + +
+ +
+ Payment Account * + + + +
+ +
+ Status * + + + + + + + Active + Inactive + + + +
+ +
+ +
+ + + + + + +
+
diff --git a/src/app/configuration/update-g2p-payment/update-g2p-payment.component.scss b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.scss new file mode 100644 index 00000000..a890a4c4 --- /dev/null +++ b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.scss @@ -0,0 +1,100 @@ +@import "theme/theme"; +.container { + max-width: 90%; + .mat-card{ + padding: 2rem 5rem 0 5rem; + background-color: $light-grey; + box-shadow: none; + + h1 { + color: $primary; + } + +} + + .content { + div { + margin: 1rem 0; + word-wrap: break-word; + + span { + display: block; + } + } + .input-box{ + display: flex; + justify-content: space-between; + align-items: center; + margin: 1rem 0; + flex-direction: column; + + } + } +} + +.edit-b{ + width: 8rem; + background-color: $primary; +} + +.delete-b{ + width: 8rem; + background-color: $red; +} + +.update-b{ + width: 12rem; + background-color: $primary; + +} + +.custom-button { + border-radius: 5px; + padding: 0.2rem 0; + box-shadow: none; + color: $white; +} + + +.not-allowed { + cursor: not-allowed; +} + +::ng-deep .custom-g2p-field.mat-form-field-appearance-outline .mat-form-field-outline { + color: black; + background-color: $white; + border-radius: 5px; +} +::ng-deep .custom-g2p-field.mat-form-field input:disabled{ + color: black; +} +::ng-deep .custom-g2p-field.mat-form-field select:disabled{ + color: black; +} + +.buttons-group { + + display: flex; + justify-content: flex-end; + align-items: center; + gap: 1rem; + + .button { + padding: 0.3rem 4rem; + border-radius: $border-size; + background-color: $white; + color: $primary; + cursor: pointer; + } + + .submit { + background-color: $primary; + color: $white; + } + + .submit:disabled { + background-color: $light-black; + color: $white; + cursor: not-allowed; + } +} \ No newline at end of file diff --git a/src/app/configuration/update-g2p-payment/update-g2p-payment.component.spec.ts b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.spec.ts new file mode 100644 index 00000000..df0ab2b9 --- /dev/null +++ b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { UpdateG2pPaymentComponent } from './update-g2p-payment.component'; + +describe('UpdateG2pPaymentComponent', () => { + let component: UpdateG2pPaymentComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [UpdateG2pPaymentComponent] + }); + fixture = TestBed.createComponent(UpdateG2pPaymentComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/configuration/update-g2p-payment/update-g2p-payment.component.ts b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.ts new file mode 100644 index 00000000..27d28aa9 --- /dev/null +++ b/src/app/configuration/update-g2p-payment/update-g2p-payment.component.ts @@ -0,0 +1,126 @@ +/** Angular Imports */ +import { Component } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import { MatLegacyDialog as MatDialog } from '@angular/material/legacy-dialog'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; + +/** Custom Components */ +import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; + +/** Custom Services */ +import { AlertService } from 'app/core/alert/alert.service'; +import { G2PPaymentConfigService } from '../services/g2p-payment-config.service'; + +/** + * Update G2P Payment Component + */ +@Component({ + selector: 'mifosx-update-g2p-payment', + templateUrl: './update-g2p-payment.component.html', + styleUrls: ['./update-g2p-payment.component.scss'], +}) +export class UpdateG2pPaymentComponent { + programForm: FormGroup; + isEditing: boolean; + initialFormValues: any; + programData: any; + configTemplate: any; + + /** + * + * @param route ActivatedRoute + * @param router Router + * @param dialog MatDialog + * @param fb FormBuilder + * @param alertService AlertService + * @param g2PPaymentConfigService G2PPaymentConfigService + */ + constructor( + private route: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private fb: FormBuilder, + private alertService: AlertService, + private g2PPaymentConfigService: G2PPaymentConfigService + ) { + this.route.data.subscribe((data: any) => { + this.programData = data.program; + this.configTemplate = data.template; + }); + } + + ngOnInit(): void { + this.createProgramForm(); + } + + /** + * Create form + */ + createProgramForm() { + this.programForm = this.fb.group({ + governmentEntity: [{ value: this.programData.governmentEntity.name, disabled: true }, Validators.required], + program: [{ value: this.programData.program.programName, disabled: true }, [Validators.required]], + account: [{ value: this.programData.account, disabled: true }, [Validators.required]], + payerDfsp: [{ value: this.programData.dfsp.name, disabled: true }, [Validators.required]], + status: [{ value: this.programData.status, disabled: true }, [Validators.required]], + }); + this.initialFormValues = this.programForm.getRawValue(); + } + + /** + * Submit form data + */ + submit() { + if (this.programForm.valid) { + const formData = this.programForm.getRawValue(); + // Mapping form data to update request + const updateRequest = { + governmentEntity: { + govInstId: formData.governmentEntity, + }, + dfsp: { + fspId: formData.payerDfsp, + }, + program: { + programId: formData.program, + }, + account: formData.account, + status: formData.status, + }; + + const programId = this.route.snapshot.params['id']; + this.g2PPaymentConfigService.updateG2pPayment(programId, updateRequest).subscribe((response) => { + this.alertService.alert({ type: 'success', message: 'User updated successfully' }); + this.router.navigate(['dashboard/configuration/g2p-payment-config']); + }); + } + } + + toggleEdit() { + this.isEditing = !this.isEditing; + if (this.isEditing) { + Object.keys(this.programForm.controls).forEach((control) => { + this.programForm.get(control).enable(); + }); + } else { + // Reset form to initial values + this.programForm.reset(this.initialFormValues); + this.programForm.disable(); + } + } + + delete() { + const deleteUserDialogRef = this.dialog.open(DeleteDialogComponent, { + data: { deleteContext: `Program Configuration ${this.route.snapshot.params['id']}` }, + }); + deleteUserDialogRef.afterClosed().subscribe((response: any) => { + if (response.delete) { + console.log('Delete Program Config:', this.programForm.getRawValue()); + this.g2PPaymentConfigService.deleteG2pPayment(this.route.snapshot.params['id']).subscribe((response) => { + this.alertService.alert({ type: 'success', message: 'User deleted successfully' }); + this.router.navigate(['dashboard/configuration/g2p-payment-config']); + }); + } + }); + } +} diff --git a/src/app/core/authentication/authentication.interceptor.ts b/src/app/core/authentication/authentication.interceptor.ts index c42b74fa..36e59f77 100644 --- a/src/app/core/authentication/authentication.interceptor.ts +++ b/src/app/core/authentication/authentication.interceptor.ts @@ -5,9 +5,10 @@ import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/c /** rxjs Imports */ import { Observable, Subject, BehaviorSubject } from 'rxjs'; -import { environment } from '../../../environments/environment'; +/** Custom Services */ import { SettingsService } from 'app/settings/settings.service'; +/** External Imports */ import * as uuid from 'uuid'; /** Http request options headers. */ @@ -27,6 +28,19 @@ export class AuthenticationInterceptor implements HttpInterceptor { private accessExpired = false; private refreshTokenSubject: Subject = new BehaviorSubject(null); + /** Key to store user data in storage. */ + private oAuthUserDetailsStorageKey = 'pheeOAuthUserDetails'; + private getStoreageItem(key: string): string { + return localStorage.getItem(key) || sessionStorage.getItem(key); + } + + getInstitueId(): string { + const userData = JSON.parse(this.getStoreageItem(this.oAuthUserDetailsStorageKey)); + return userData.govtId || userData.fspId || ''; + } + getUserType(): string { + return JSON.parse(this.getStoreageItem(this.oAuthUserDetailsStorageKey)).userType; +} constructor(private settingsService: SettingsService) {} /** @@ -45,7 +59,12 @@ export class AuthenticationInterceptor implements HttpInterceptor { delete httpOptions.headers['X-CallbackURL']; } if ((url.indexOf('/vouchers') > 0) || (url.indexOf('/benefici') > 0)) { - httpOptions.headers['x-registering-institution-id'] = environment.backend.registeringInstituionId; + if(this.getUserType()==='Govt. Entity User') { + httpOptions.headers['x-registering-institution-id'] = this.getInstitueId(); + } + if(this.getUserType()==='FSP User') { + httpOptions.headers['x-banking-institution-Code'] = this.getInstitueId(); + } delete httpOptions.headers['X-Correlation-ID']; delete httpOptions.headers['Platform-TenantId']; } diff --git a/src/app/core/authentication/authentication.service.ts b/src/app/core/authentication/authentication.service.ts index 10792b68..a6bfa3eb 100644 --- a/src/app/core/authentication/authentication.service.ts +++ b/src/app/core/authentication/authentication.service.ts @@ -8,6 +8,8 @@ import { map } from 'rxjs/operators'; /** Custom Services */ import { AlertService } from '../alert/alert.service'; +import { OauthKeycloakService } from './keycloak/oauth-keycloak.service'; +import { G2PPaymentConfigService } from 'app/configuration/services/g2p-payment-config.service'; /** Environment Configuration */ import { environment } from '../../../environments/environment'; @@ -17,8 +19,8 @@ import { LoginContext } from './login-context.model'; import { Credentials } from './credentials.model'; import { Introspect, OAuth2Token } from './o-auth2-token.model'; +/** External Imports */ import jwt_decode from 'jwt-decode'; -import { OauthKeycloakService } from './keycloak/oauth-keycloak.service'; /** * Authentication workflow. @@ -45,6 +47,10 @@ export class AuthenticationService { private oAuthTokenDetailsStorageKey = 'pheeOAuthTokenDetails'; /** Key to store user roles details in storage. */ private oAuthUserDetailsStorageKey = 'pheeOAuthUserDetails'; + /** Key to store dfsps in storage. */ + private dfspsStorageKey = 'pheeDfsps'; + /** Key to store govt entities in storage. */ + private govtEntitiesStorageKey = 'pheeGovtEntities'; private refreshAccessToken = false; private loggedIn = false; @@ -58,10 +64,13 @@ export class AuthenticationService { * credentials are presently in storage or not. * @param {HttpClient} http Http Client to send requests. * @param {AlertService} alertService Alert Service. + * @param {OauthKeycloakService} oauthKeycloakService Oauth Keycloak Service. + * @param {G2PPaymentConfigService} g2pPaymentService G2P Payment Service. */ constructor(private http: HttpClient, private alertService: AlertService, - private oauthKeycloakService: OauthKeycloakService) { + private oauthKeycloakService: OauthKeycloakService, + private g2pPaymentService: G2PPaymentConfigService) { this.storage = sessionStorage; this.init(); @@ -156,6 +165,26 @@ export class AuthenticationService { } } + /** + * Retrieves the payer dfsps. + * @returns {void} Sets the payer dfsps in storage. + */ + getDfsps(): void { + this.g2pPaymentService.getPayerDfsps().subscribe((dfsps: any) => { + sessionStorage.setItem(this.dfspsStorageKey, JSON.stringify(dfsps)); + }); + } + + /** + * Retrieves the government entities. + * @returns {void} Sets the government entities in storage + */ + getGovtEntities(): void { + this.g2pPaymentService.getGovtEntities().subscribe((govtEntities: any) => { + sessionStorage.setItem(this.govtEntitiesStorageKey, JSON.stringify(govtEntities)); + }); + } + /** * Retrieves the user details after oauth2 authentication. * @@ -164,7 +193,7 @@ export class AuthenticationService { */ private getUserDetails(loginContext: LoginContext, tokenResponse: OAuth2Token) { if (this.isOauthKeyCloak()) { - this.oauthKeycloakService.introspect(tokenResponse).subscribe((userDetails: Introspect) => { + this.oauthKeycloakService.getUserInfo(tokenResponse).subscribe((userDetails: Introspect) => { this.storage.setItem(this.oAuthUserDetailsStorageKey, JSON.stringify(userDetails)); this.onLoginSuccess({ username: loginContext.username, accessToken: tokenResponse.access_token, authenticated: true, tenantId: loginContext.tenant } as any); @@ -228,6 +257,8 @@ export class AuthenticationService { this.loggedIn = true; if (environment.oauth.enabled) { this.authorizationToken = `Bearer ${credentials.accessToken}`; + this.getGovtEntities(); + this.getDfsps(); } else { this.authorizationToken = `Basic ${credentials.base64EncodedAuthenticationKey}`; } @@ -240,7 +271,6 @@ export class AuthenticationService { this.alertService.alert({ type: 'Authentication Success', message: `${credentials.username} successfully logged in!` }); delete this.credentials; } - } /** @@ -278,6 +308,10 @@ export class AuthenticationService { return JSON.parse(this.getStoreageItem(this.credentialsStorageKey)); } + /** + * Gets the user details. + * @returns {Introspect} The user details if the user is authenticated otherwise null. + */ get userDetails(): Introspect | null { return JSON.parse(this.getStoreageItem(this.oAuthUserDetailsStorageKey)); } @@ -299,6 +333,8 @@ export class AuthenticationService { this.storage.removeItem(this.credentialsStorageKey); this.storage.removeItem(this.oAuthTokenDetailsStorageKey); this.storage.removeItem(this.oAuthUserDetailsStorageKey); + this.storage.removeItem(this.dfspsStorageKey); + this.storage.removeItem(this.govtEntitiesStorageKey); this.loggedIn = false; } } diff --git a/src/app/core/authentication/keycloak/oauth-keycloak.service.ts b/src/app/core/authentication/keycloak/oauth-keycloak.service.ts index 92af2717..96e90cd7 100644 --- a/src/app/core/authentication/keycloak/oauth-keycloak.service.ts +++ b/src/app/core/authentication/keycloak/oauth-keycloak.service.ts @@ -91,6 +91,18 @@ export class OauthKeycloakService { return this.http.post(url, payload.toString(), httpOptions); } + getUserInfo(token: OAuth2Token): Observable { + + const httpOptions = { + headers: new HttpHeaders({ + Authorization: `Bearer ${token.access_token}`, + 'Content-Type': 'application/json' + }) + }; + const url: string = `${this.url()}/userinfo`; + return this.http.get(url, httpOptions); + } + private url(): string { return `${environment.oauth.serverUrl}/realms/${environment.oauth.realm}/protocol/openid-connect`; } diff --git a/src/app/core/authentication/o-auth2-token.model.ts b/src/app/core/authentication/o-auth2-token.model.ts index 75e267b4..77834140 100644 --- a/src/app/core/authentication/o-auth2-token.model.ts +++ b/src/app/core/authentication/o-auth2-token.model.ts @@ -40,6 +40,7 @@ export interface RealmAccess { } export interface ResourceAccess { + opsapp: RealmAccess; 'realm-management': RealmAccess; broker: RealmAccess; account: RealmAccess; diff --git a/src/app/core/core.module.ts b/src/app/core/core.module.ts index bbcac44c..35a305f0 100644 --- a/src/app/core/core.module.ts +++ b/src/app/core/core.module.ts @@ -27,6 +27,7 @@ import { RouteReusableStrategy } from './route/route-reusable-strategy'; /** Custom Modules */ import { SharedModule } from '../shared/shared.module'; +import { DirectivesModule } from 'app/directives/directives.module'; /** Custom Components */ import { ShellComponent } from './shell/shell.component'; @@ -44,7 +45,8 @@ import { ContentComponent } from './shell/content/content.component'; SharedModule, HttpClientModule, TranslateModule, - RouterModule + RouterModule, + DirectivesModule ], declarations: [ ShellComponent, diff --git a/src/app/core/shell/content/content.component.html b/src/app/core/shell/content/content.component.html index 4020fabe..9c0bf3fc 100644 --- a/src/app/core/shell/content/content.component.html +++ b/src/app/core/shell/content/content.component.html @@ -6,7 +6,7 @@ - chevron_right +
diff --git a/src/app/core/shell/content/content.component.scss b/src/app/core/shell/content/content.component.scss index efe5f3b9..0271e766 100644 --- a/src/app/core/shell/content/content.component.scss +++ b/src/app/core/shell/content/content.component.scss @@ -1,9 +1,23 @@ -.container { - width: 80%; +@import 'theme/theme'; +.container { + width: 90%; + margin: 0 auto; .breadcrumb { padding-left: 18%; margin: 0; - font-size: x-large; + font-size: 1.6rem; + font-weight: 600; } } + +::ng-deep .xng-breadcrumb-link{ + color: $primary !important; + font-size:1.3rem; + font-weight: 400; + cursor: pointer; +} + +::ng-deep .xng-breadcrumb-link:hover{ + text-decoration: none !important; +} diff --git a/src/app/core/shell/shell.component.html b/src/app/core/shell/shell.component.html index d0a2a802..9926116f 100644 --- a/src/app/core/shell/shell.component.html +++ b/src/app/core/shell/shell.component.html @@ -26,10 +26,8 @@ -
-
diff --git a/src/app/core/shell/shell.component.scss b/src/app/core/shell/shell.component.scss index 5521eed4..97da48f6 100644 --- a/src/app/core/shell/shell.component.scss +++ b/src/app/core/shell/shell.component.scss @@ -9,15 +9,10 @@ } .sidebar-full { - width: 22rem; + width: 18rem; } .sidebar-compact { - width: 8rem; - } - - .vertical-space { - line-height: 40px; - height: 40px; + width: 6rem; } } diff --git a/src/app/core/shell/sidenav/sidenav.component.html b/src/app/core/shell/sidenav/sidenav.component.html index c0111d5f..ba1d9098 100644 --- a/src/app/core/shell/sidenav/sidenav.component.html +++ b/src/app/core/shell/sidenav/sidenav.component.html @@ -8,10 +8,10 @@
- diff --git a/src/app/core/shell/toolbar/toolbar.component.scss b/src/app/core/shell/toolbar/toolbar.component.scss index 68252029..fadd6a3d 100644 --- a/src/app/core/shell/toolbar/toolbar.component.scss +++ b/src/app/core/shell/toolbar/toolbar.component.scss @@ -1,57 +1,28 @@ @import 'theme/theme'; - -#mifosx-toolbar { - position: relative; - background-color: transparent; - - .toolbar { - width: 96%; - border: 0.06rem solid; - border-radius: 15px; - - .content { - margin-left: 2%; - margin-right: 2%; - - .mat-fab { - margin-top: 20px; - background-color: $white; - color: $black; - border: 0.06rem solid; - } - - .username { - vertical-align: middle; - text-align: center; - color: $dark-grey; - margin-right: 40px; - margin-top: 35px; - } +.content { + display: flex; + justify-content: space-between; + align-items: center; + margin: 1rem 1rem 0 1rem; + padding: 1rem; + border: 1px solid $mid-grey; + border-radius: 4px; + + .right-toolbar{ + display: flex; + justify-content: center; + align-items: center; + gap: 1rem; + + mat-card{ + padding: 10px; } - } - - .tab-link { - min-width: auto; - padding: 0 1rem; - } - - .search-bar { - .search { - max-width: 400px; - width: 100%; - font-size: 1rem; + + .mat-fab { + background-color: $white; + color: $black; + border: 1px solid $mid-grey; + box-shadow: none; } } - - .username { - text-align: right; - font-size: medium; - } - - .language { - min-width: 120px; - width: 120px; - margin: auto; - } - } diff --git a/src/app/core/shell/toolbar/toolbar.component.ts b/src/app/core/shell/toolbar/toolbar.component.ts index f9d5c603..39805f85 100644 --- a/src/app/core/shell/toolbar/toolbar.component.ts +++ b/src/app/core/shell/toolbar/toolbar.component.ts @@ -93,6 +93,30 @@ export class ToolbarComponent implements OnInit { } } + displayUserType(): string { + if (this.authenticationService.isOauthKeyCloak()) { + return this.credentials ? this.credentials.userType : ''; + } else { + return this.credentials ? this.credentials.userType : ''; + } + } + + displayInstituteName(): string { + if (this.authenticationService.isOauthKeyCloak()) { + return this.credentials ? this.credentials.govtName || this.credentials.fspName : ''; + } else { + return this.credentials ? this.credentials.govtName || this.credentials.fspName : ''; + } + }; + + displayInstitueId(): string { + if (this.authenticationService.isOauthKeyCloak()) { + return this.credentials ? this.credentials.govtId || this.credentials.fspId : ''; + } else { + return this.credentials ? this.credentials.govtId || this.credentials.fspId : ''; + } + } + /** * Toggles the current state of sidenav. */ diff --git a/src/app/core/utils/passwords-utility.ts b/src/app/core/utils/passwords-utility.ts new file mode 100644 index 00000000..850a300c --- /dev/null +++ b/src/app/core/utils/passwords-utility.ts @@ -0,0 +1,37 @@ +import { Injectable } from '@angular/core'; +import { AbstractControl, ValidationErrors, ValidatorFn, Validators } from '@angular/forms'; +import { Subscription } from 'rxjs'; + + +@Injectable({ + providedIn: 'root' +}) + +export class PasswordsUtility { + // password regex pattern + public static PASSWORD_REGEX = '^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*[#$@$!%*?&])[A-Za-z\d#$@$!%*?&].{8,}$'; + + public getPasswordValidators(): ValidatorFn[] { + return [Validators.required, Validators.pattern(PasswordsUtility.PASSWORD_REGEX), Validators.maxLength(50), Validators.minLength(8)]; + } + + /** + * Confirm Change Password of Users + * @param controlNameToCompare Form Control Name to be compared. + */ + public confirmPassword(controlNameToCompare: string): ValidatorFn { + return (c: AbstractControl): ValidationErrors|null => { + if (c.value == null || c.value.length === 0) { + return null; + } + const controlToCompare = c.root.get(controlNameToCompare); + if (controlToCompare) { + const subscription: Subscription = controlToCompare.valueChanges.subscribe(() => { + c.updateValueAndValidity(); + subscription.unsubscribe(); + }); + } + return controlToCompare && controlToCompare.value !== c.value ? {'notequal': true} : null; + }; + } +} diff --git a/src/app/directives/has-role/has-role.directive.ts b/src/app/directives/has-role/has-role.directive.ts index cc88c155..ea87612e 100644 --- a/src/app/directives/has-role/has-role.directive.ts +++ b/src/app/directives/has-role/has-role.directive.ts @@ -7,8 +7,9 @@ import { environment } from 'environments/environment'; }) export class HasRoleDirective { - public static ADMIN_ROLE = 'admin'; - public static OPERATOR_ROLE = 'operator'; + public static ADMIN_MAKER = 'Admin Maker'; + public static ADMIN_CHECKER = 'Admin Checker'; + public static NORMAL_USER = 'Normal User'; /** User Roles */ private userRoles: any[]; @@ -24,9 +25,9 @@ export class HasRoleDirective { private authenticationService: AuthenticationService) { const userDetails = this.authenticationService.userDetails; if (environment.auth.enabled === 'false') { - this.userRoles = [HasRoleDirective.ADMIN_ROLE]; + this.userRoles = [HasRoleDirective.NORMAL_USER]; } else { - this.userRoles = userDetails.realm_access.roles; + this.userRoles = userDetails.resource_access.opsapp.roles; } } @@ -34,41 +35,24 @@ export class HasRoleDirective { * Evaluates the condition to show template. */ @Input() - set userHasRole(role: any) { - if (typeof role !== 'string') { - throw new Error('hasRole value must be a string'); + set userHasRole(roles: string[]) { + if (!Array.isArray(roles)) { + throw new Error('userHasRole value must be an array'); } /** Clear the template beforehand to prevent overlap OnChanges. */ this.viewContainer.clear(); /** Shows Template if user has role */ - if (this.hasRole(role)) { + if (this.hasRole(roles)) { this.viewContainer.createEmbeddedView(this.templateRef); } } /** * Checks if user is permitted. - * @param {string} role Role - * @returns {true} - * -`ALL_FUNCTIONS`: user is a Super user. - * -`ALL_FUNCTIONS_READ`: user has all read roles and passed role is 'read' type. - * - User has special role to access that feature. - * @returns {false} - * - Passed role doesn't fall under either of above given role grants. - * - No value was passed to the has role directive. + * @param {string[]} roles Roles + * @returns {boolean} True if user has role */ - private hasRole(role: string) { - role = role.trim(); - if (this.userRoles.includes(HasRoleDirective.ADMIN_ROLE)) { - return true; - } else if (role !== '') { - if (this.userRoles.includes(role)) { - return true; - } else { - return false; - } - } else { - return false; - } + private hasRole(roles: string[]): boolean { + return roles.some(role => this.userRoles.includes(role)); } } diff --git a/src/app/home/dashboard/dashboard.component.html b/src/app/home/dashboard/dashboard.component.html index d3fcf014..7adecb34 100644 --- a/src/app/home/dashboard/dashboard.component.html +++ b/src/app/home/dashboard/dashboard.component.html @@ -1,7 +1,6 @@ -
- -

- TODO: dashboard component. -

- +
+
+ + +
diff --git a/src/app/home/dashboard/dashboard.component.scss b/src/app/home/dashboard/dashboard.component.scss index e69de29b..e381e7ef 100644 --- a/src/app/home/dashboard/dashboard.component.scss +++ b/src/app/home/dashboard/dashboard.component.scss @@ -0,0 +1,13 @@ +.container { + flex-wrap: wrap; + + } + + .payment-hub-ee-wrap { + flex-wrap: wrap; + width: 100%; + + mifosx-filter-selector { + width: 100%; + } + } \ No newline at end of file diff --git a/src/app/home/dashboard/dashboard.component.ts b/src/app/home/dashboard/dashboard.component.ts index cf7487d8..73ac18f3 100644 --- a/src/app/home/dashboard/dashboard.component.ts +++ b/src/app/home/dashboard/dashboard.component.ts @@ -1,6 +1,9 @@ /** Angular Imports */ import { Component, OnInit } from '@angular/core'; +/** Section Model */ +import { Section } from 'app/payment-hub/filter-selector/section-model'; + /** * Dashboard component. */ @@ -11,10 +14,55 @@ import { Component, OnInit } from '@angular/core'; }) export class DashboardComponent implements OnInit { - constructor() { } + sections: Section[]; ngOnInit() { } + constructor( ) { + this.sections = [ + { + label: 'Payment Hub', + routeTo: ['dashboard','paymenthub'], + roleName: ['Normal User', 'Admin Maker', 'Admin Checker'], + icon: 'money-bill-alt', + active: false, + disabled: false + }, + { + label: 'Vouchers', + routeTo: ['dashboard','vouchers'], + roleName: ['Normal User', 'Admin Maker', 'Admin Checker'], + icon: 'ticket', + active: false, + disabled: false + }, + { + label: 'Account Mapper', + routeTo: ['dashboard','account-mapper'], + roleName: ['Normal User', 'Admin Maker', 'Admin Checker'], + icon: 'users', + active: false, + disabled: false + }, + { + label:'Visualizations', + routeTo: ['dashboard','visualizations'], + roleName: ['Normal User', 'Admin Maker', 'Admin Checker'], + icon: 'chart-line', + active: false, + disabled: false + }, + { + label: 'G2P Payment Config', + routeTo: ['dashboard','configuration'], + roleName: ['Normal User', 'Admin Maker', 'Admin Checker'], + icon: 'file-alt', + active: false, + disabled: false + } + ]; + } } + diff --git a/src/app/home/home-routing.module.ts b/src/app/home/home-routing.module.ts index 7a044eaa..6ccb7027 100644 --- a/src/app/home/home-routing.module.ts +++ b/src/app/home/home-routing.module.ts @@ -6,7 +6,6 @@ import { Routes, RouterModule } from '@angular/router'; import { Route } from '../core/route/route.service'; /** Custom Components */ -import { HomeComponent } from './home.component'; import { DashboardComponent } from './dashboard/dashboard.component'; /** Home and Dashboard Routes */ @@ -14,14 +13,9 @@ const routes: Routes = [ Route.withShell([ { path: '', - redirectTo: '/home', + redirectTo: '/dashboard', pathMatch: 'full' }, - { - path: 'home', - component: HomeComponent, - data: { breadcrumb: {alias: 'Batches'} } - }, { path: 'dashboard', component: DashboardComponent, diff --git a/src/app/home/home.component.scss b/src/app/home/home.component.scss deleted file mode 100644 index e30fa438..00000000 --- a/src/app/home/home.component.scss +++ /dev/null @@ -1,12 +0,0 @@ -.container { - width: 80%; -} - -.payment-hub-ee-wrap { - flex-wrap: wrap; - width: 100%; - - mifosx-filter-selector { - width: 100%; - } -} diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts deleted file mode 100644 index 490e81bd..00000000 --- a/src/app/home/home.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { HomeComponent } from './home.component'; - -describe('HomeComponent', () => { - let component: HomeComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ HomeComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(HomeComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts deleted file mode 100644 index 5486e195..00000000 --- a/src/app/home/home.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -/** Angular Imports */ -import { Component } from '@angular/core'; - -/** Custom Services */ -import { Section } from 'app/payment-hub/filter-selector/section-model'; - -/** - * Home component. - */ -@Component({ - selector: 'mifosx-home', - templateUrl: './home.component.html', - styleUrls: ['./home.component.scss'] -}) -export class HomeComponent { - - sections: Section[]; - - constructor() { - this.sections = [ - { - label: 'Payment Hub', - routeTo: ['paymenthub'], - roleName: 'operations', - icon: 'money-bill-alt', - active: false, - disabled: false - }, - { - label: 'Vouchers', - routeTo: ['vouchers'], - roleName: 'vouchers', - icon: 'ticket', - active: false, - disabled: false - }, - { - label: 'Account Management', - routeTo: ['account-mapper'], - roleName: 'account-mapper', - icon: 'users', - active: false, - disabled: false - } - ]; - } - -} diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts index 1888cfb7..dd1e32e5 100644 --- a/src/app/home/home.module.ts +++ b/src/app/home/home.module.ts @@ -6,7 +6,6 @@ import { SharedModule } from '../shared/shared.module'; import { HomeRoutingModule } from './home-routing.module'; /** Custom Components */ -import { HomeComponent } from './home.component'; import { DashboardComponent } from './dashboard/dashboard.component'; import { TranslateModule } from '@ngx-translate/core'; import { DirectivesModule } from 'app/directives/directives.module'; @@ -24,7 +23,6 @@ import { DirectivesModule } from 'app/directives/directives.module'; DirectivesModule ], declarations: [ - HomeComponent, DashboardComponent ] }) diff --git a/src/app/login/login-form/login-form.component.html b/src/app/login/login-form/login-form.component.html index b92dc4d7..52d70c09 100644 --- a/src/app/login/login-form/login-form.component.html +++ b/src/app/login/login-form/login-form.component.html @@ -1,52 +1,57 @@
- - - - - +
+ +
+ {{'labels.texts.Remember me' | translate}} + - - {{'labels.texts.Password is' | translate}} {{'labels.texts.required' | translate}} - - - {{'labels.texts.Remember me' | translate}} +
- - - + + \ No newline at end of file diff --git a/src/app/login/login-form/login-form.component.scss b/src/app/login/login-form/login-form.component.scss index f4770acc..0285ed18 100644 --- a/src/app/login/login-form/login-form.component.scss +++ b/src/app/login/login-form/login-form.component.scss @@ -1,16 +1,63 @@ +@import 'theme/theme'; + #login-form { - .login-input { - width: 14rem; - margin-bottom: 0.6rem; + color: $white; + + .custom-form-field { + margin-bottom: 1rem; } - .login-button { - width: 14rem; + .custom-button { + width: 100%; margin-top: 1rem; + background-color: $green; + padding: 0.5rem 0; + box-shadow: none; + color: $white; + gap: 2rem; + } + + .custom-button:disabled { + background-color: $mid-grey; + color: $black; + } + + .icon { + color: $black; + padding-bottom: 1rem; } mat-spinner { + margin: 0 auto; float: right; margin: 0.5rem 0; } } + +//custom angular checkbox styles +::ng-deep .mat-checkbox-frame { + border-color: $white +} + +::ng-deep .mat-checkbox-ripple .mat-ripple-element .mat-checkbox-frame { + background-color: $white !important; +} + +::ng-deep .mat-checkbox-checked.mat-accent .mat-checkbox-background { + background-color: $green !important; +} + +//custom angular spinner styles +::ng-deep .mat-progress-spinner circle, .mat-spinner circle { + stroke: $white; +} + +//custom angular form field styles +::ng-deep .custom-form-field.mat-form-field-appearance-outline .mat-form-field-outline { + color: $white; + background-color: $white; + border-radius: 5px; +} +::ng-deep .custom-form-field.mat-form-field input { + color: $black; +} diff --git a/src/app/login/login.component.html b/src/app/login/login.component.html index 01267ef8..abdab5b0 100644 --- a/src/app/login/login.component.html +++ b/src/app/login/login.component.html @@ -1,19 +1,21 @@ - -
+
-
- +