Enhacement to management funds (#1735)

Co-authored-by: Jose Alberto Hernandez <alberto@MacBook-Pro.local>
This commit is contained in:
José Alberto Hernández
2023-04-07 20:35:27 -06:00
committed by GitHub
co-authored by Jose Alberto Hernandez
parent 452a1adc17
commit bf700cc100
28 changed files with 530 additions and 270 deletions
@@ -0,0 +1,37 @@
<div class="container">
<mat-card>
<form [formGroup]="fundForm" (ngSubmit)="submit()">
<mat-card-content>
<div fxLayout="column">
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput required autofocus formControlName="name">
<mat-error *ngIf="fundForm.controls.name.hasError('required')">
Name <strong>is required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>External Id</mat-label>
<input matInput autofocus formControlName="externalId">
</mat-form-field>
</div>
</mat-card-content>
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
<button mat-raised-button color="primary" [disabled]="!fundForm.valid" *mifosxHasPermission="'CREATE_FUND'">Submit</button>
</mat-card-actions>
</form>
</mat-card>
</div>
@@ -0,0 +1,3 @@
.container {
max-width: 37rem;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CreateFundComponent } from './create-fund.component';
describe('CreateFundComponent', () => {
let component: CreateFundComponent;
let fixture: ComponentFixture<CreateFundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ CreateFundComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(CreateFundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,53 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { OrganizationService } from 'app/organization/organization.service';
@Component({
selector: 'mifosx-create-fund',
templateUrl: './create-fund.component.html',
styleUrls: ['./create-fund.component.scss']
})
export class CreateFundComponent implements OnInit {
/** Charge form. */
fundForm: FormGroup;
/**
* Retrieves the charge data from `resolve`.
* @param {ProductsService} productsService Products Service.
* @param {FormBuilder} formBuilder Form Builder.
* @param {ActivatedRoute} route Activated Route.
* @param {Router} router Router for navigation.
*/
constructor(private organizationService: OrganizationService,
private formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute) {
}
ngOnInit() {
this.createFundForm();
}
/**
* Edit Fund form.
*/
createFundForm() {
this.fundForm = this.formBuilder.group({
'name': ['', Validators.required],
'externalId': ['']
});
}
submit() {
const payload = this.fundForm.getRawValue();
this.organizationService.createFund(payload)
.subscribe((response: any) => {
this.router.navigate(['../'], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,37 @@
<div class="container">
<mat-card>
<form [formGroup]="fundForm" (ngSubmit)="submit()">
<mat-card-content>
<div fxLayout="column">
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput required autofocus formControlName="name">
<mat-error *ngIf="fundForm.controls.name.hasError('required')">
Name <strong>is required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>External Id</mat-label>
<input matInput autofocus formControlName="externalId">
</mat-form-field>
</div>
</mat-card-content>
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
<button mat-raised-button color="primary" [disabled]="!fundForm.valid" *mifosxHasPermission="'UPDATE_FUND'">Submit</button>
</mat-card-actions>
</form>
</mat-card>
</div>
@@ -0,0 +1,3 @@
.container {
max-width: 37rem;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { EditFundComponent } from './edit-fund.component';
describe('EditFundComponent', () => {
let component: EditFundComponent;
let fixture: ComponentFixture<EditFundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ EditFundComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(EditFundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,56 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ActivatedRoute, Router } from '@angular/router';
import { OrganizationService } from 'app/organization/organization.service';
@Component({
selector: 'mifosx-edit-fund',
templateUrl: './edit-fund.component.html',
styleUrls: ['./edit-fund.component.scss']
})
export class EditFundComponent implements OnInit {
/** Selected Data. */
fundData: any;
/** Charge form. */
fundForm: FormGroup;
/**
* Retrieves the charge data from `resolve`.
* @param {ProductsService} productsService Products Service.
* @param {FormBuilder} formBuilder Form Builder.
* @param {ActivatedRoute} route Activated Route.
* @param {Router} router Router for navigation.
*/
constructor(private organizationService: OrganizationService,
private formBuilder: FormBuilder,
private router: Router,
private route: ActivatedRoute) {
this.route.data.subscribe((data: { fundData: any }) => {
this.fundData = data.fundData;
});
}
ngOnInit() {
this.createFundForm();
}
/**
* Edit Fund form.
*/
createFundForm() {
this.fundForm = this.formBuilder.group({
'name': [this.fundData.name, Validators.required],
'externalId': [this.fundData.externalId]
});
}
submit() {
const payload = this.fundForm.getRawValue();
this.organizationService.editFund(this.fundData.id.toString(), payload)
.subscribe((response: any) => {
this.router.navigate(['../'], { relativeTo: this.route });
});
}
}
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ManageFundResolver } from './manage-fund.resolver';
describe('ManageFundResolver', () => {
let resolver: ManageFundResolver;
beforeEach(() => {
TestBed.configureTestingModule({});
resolver = TestBed.inject(ManageFundResolver);
});
it('should be created', () => {
expect(resolver).toBeTruthy();
});
});
@@ -0,0 +1,23 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Resolve } from '@angular/router';
import { Observable } from 'rxjs';
import { OrganizationService } from '../organization.service';
@Injectable({
providedIn: 'root'
})
export class ManageFundResolver implements Resolve<boolean> {
/**
* @param {OrganizationService} organizationService Organization service.
*/
constructor(private organizationService: OrganizationService) {}
/**
* Returns the manage funds data.
* @returns {Observable<any>}
*/
resolve(route: ActivatedRouteSnapshot): Observable<any> {
const fundId = route.paramMap.get('id');
return this.organizationService.getFund(fundId);
}
}
@@ -1,49 +1,46 @@
<div class="container m-b-20" fxLayout="row" fxLayoutAlign="end" fxLayoutGap="20px">
<div #buttonCreateCharge class="in-block">
<button mat-raised-button color="primary" [routerLink]="['create']" *mifosxHasPermission="'CREATE_FUND'">
<fa-icon icon="plus" class="m-r-10"></fa-icon>
Create Fund
</button>
</div>
</div>
<div class="container">
<mat-card>
<div #filter fxLayout="row" fxLayoutGap="20px">
<mat-form-field fxFlex>
<mat-label>{{ 'labels.inputs.Filter' | translate }}</mat-label>
<input matInput (keyup)="applyFilter($event.target.value)">
</mat-form-field>
</div>
<div #fundFormRef>
<div #chargesTable class="mat-elevation-z8">
<form #formRef="ngForm" [formGroup]="fundForm" fxLayout="row" fxLayoutAlign="start baseline" fxLayoutGap="20px"
(ngSubmit)="addFund()" *mifosxHasPermission="'CREATE_FUND'">
<table mat-table [dataSource]="dataSource" matSort>
<mat-form-field fxFlex="calc(90%-20px)">
<textarea formControlName="name" matInput placeholder="New Fund"></textarea>
<mat-error *ngIf="fundForm.controls.name.hasError('required')">
<strong>Required field</strong>
</mat-error>
</mat-form-field>
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
<td mat-cell *matCellDef="let charge"> {{ charge.name }} </td>
</ng-container>
<div #buttonAddFund fxFlex>
<button mat-raised-button color="primary" [disabled]="!fundForm.valid">
<fa-icon icon="plus"></fa-icon>
</button>
</div>
<ng-container matColumnDef="externalId">
<th mat-header-cell *matHeaderCellDef mat-sort-header> External Id </th>
<td mat-cell *matCellDef="let charge"> {{ charge.externalId }} </td>
</ng-container>
</form>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="[row.id]" class="select-row"></tr>
</div>
</table>
<mat-grid-list cols="2" rowHeight="50px">
<mat-grid-tile *ngFor="let fund of fundsData; index as i">
<input matInput [value]="fund.name">
<div fxLayout="row" fxLayoutAlign="flex-start">
<button mat-button color="primary" (click)="editFund(fund.id,fund.name,i)" *mifosxHasPermission="'UPDATE_FUND'">
<fa-icon icon="edit"></fa-icon>
</button>
</div>
</mat-grid-tile>
</mat-grid-list>
</mat-card>
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
</div>
</div>
<ng-template #templateFundFormRef let-popover="popover">
<h2>Manage Funds</h2>
<p class="mw300">This form will be used to add fund. If you would like to add fund click on add funds button and fill the fund name then click on + button else click Next or Back. For more details click: <a href="https://mifosforge.jira.com/wiki/spaces/docs/pages/67141736/Manage+Funds" target="_blank">Manage Funds</a></p>
@@ -1,3 +1,11 @@
.container{
width: 74rem;
}
table {
width: 100%;
.select-row:hover {
cursor: pointer;
}
}
@@ -18,6 +18,9 @@ import { ConfigurationWizardService } from '../../configuration-wizard/configura
/** Custom Dialog Component */
import { ContinueSetupDialogComponent } from '../../configuration-wizard/continue-setup-dialog/continue-setup-dialog.component';
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
/**
* Manage Funds component.
@@ -40,6 +43,15 @@ export class ManageFundsComponent implements OnInit, AfterViewInit {
@ViewChild('fundFormRef') fundFormRef: ElementRef<any>;
/* Template for popover on funds form */
@ViewChild('templateFundFormRef') templateFundFormRef: TemplateRef<any>;
/** Columns to be displayed in funds table. */
displayedColumns: string[] = ['name', 'externalId'];
/** Data source for Funds table. */
dataSource: MatTableDataSource<any>;
/** Paginator for charges table. */
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
/** Sorter for charges table. */
@ViewChild(MatSort, { static: true }) sort: MatSort;
/**
* Retrieves the manage funds data from `resolve`.
@@ -64,7 +76,18 @@ export class ManageFundsComponent implements OnInit, AfterViewInit {
}
ngOnInit() {
this.createFundForm();
this.dataSource = new MatTableDataSource(this.fundsData);
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
/**
* Filters data in charges table based on passed value.
* @param {string} filterValue Value to filter data.
*/
applyFilter(filterValue: string) {
this.dataSource.filter = filterValue.trim().toLowerCase();
}
/**
@@ -0,0 +1,41 @@
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column" class="container m-b-20">
<button mat-raised-button color="primary" [routerLink]="['edit']" *mifosxHasPermission="'UPDATE_FUND'">
<fa-icon icon="edit" class="m-r-10"></fa-icon>
Edit
</button>
</div>
<div class="container">
<mat-card>
<mat-card-content>
<div fxLayout="row wrap" class="content">
<div fxFlex="50%" class="mat-body-strong">
Name
</div>
<div fxFlex="50%">
{{ fundData.name }}
</div>
<div fxFlex="50%" class="mat-body-strong">
External Id
</div>
<div fxFlex="50%">
{{ fundData.externalId }}
</div>
</div>
<div fxLayout="row" fxLayoutAlign="center" fxLayoutGap="2%" fxLayout.lt-md="column">
<button type="button" mat-raised-button color="primary" [routerLink]="['../']">{{ 'Back' | translate }}</button>
</div>
</mat-card-content>
</mat-card>
</div>
@@ -0,0 +1,10 @@
.container {
max-width: 37rem;
.content {
div {
margin: 1rem 0;
word-wrap: break-word;
}
}
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ViewFundComponent } from './view-fund.component';
describe('ViewFundComponent', () => {
let component: ViewFundComponent;
let fixture: ComponentFixture<ViewFundComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ ViewFundComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ViewFundComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,27 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'mifosx-view-fund',
templateUrl: './view-fund.component.html',
styleUrls: ['./view-fund.component.scss']
})
export class ViewFundComponent implements OnInit {
/** Fund data. */
fundData: any;
/**
* Retrieves the charge data from `resolve`.
* @param {Router} router Router for navigation.
*/
constructor(private route: ActivatedRoute) {
this.route.data.subscribe((data: { fundData: any }) => {
this.fundData = data.fundData;
});
}
ngOnInit() {
}
}
@@ -101,6 +101,10 @@ import { LoanProvisioningCriteriaTemplateResolver } from './loan-provisioning-cr
import { LoanProvisioningCriteriaAndTemplateResolver } from './loan-provisioning-criteria/common-resolvers/loan-provisioning-criteria-and-template.resolver';
import { StandingInstructionsTemplateResolver } from './standing-instructions-history/standing-instructions-template.resolver';
import { AdvanceSearchTemplateResolver } from './fund-mapping/advance-search-template.resolver';
import { ManageFundResolver } from './manage-funds/manage-fund.resolver';
import { ViewFundComponent } from './manage-funds/view-fund/view-fund.component';
import { EditFundComponent } from './manage-funds/edit-fund/edit-fund.component';
import { CreateFundComponent } from './manage-funds/create-fund/create-fund.component';
/** Organization Routes */
const routes: Routes = [
@@ -570,8 +574,45 @@ const routes: Routes = [
},
{
path: 'manage-funds',
component: ManageFundsComponent,
data: { title: extract('Manage Funds Days'), breadcrumb: 'Manage Funds' },
data: { title: extract('Manage Funds'), breadcrumb: 'Manage Funds' },
children: [
{
path: '',
component: ManageFundsComponent,
resolve: {
funds: ManageFundsResolver
},
},
{
path: 'create',
component: CreateFundComponent,
data: { title: extract('Create Fund'), breadcrumb: 'Create' }
},
{
path: ':id',
data: { title: extract('View Fund'), breadcrumb: 'id', routeParamBreadcrumb: 'id' },
resolve: {
fundData: ManageFundResolver
},
children: [
{
path: '',
component: ViewFundComponent,
resolve: {
fundData: ManageFundResolver
},
},
{
path: 'edit',
component: EditFundComponent,
data: { title: extract('Edit Fund'), breadcrumb: 'Edit', routeParamBreadcrumb: false },
resolve: {
fundData: ManageFundResolver
}
},
]
},
],
resolve: {
funds: ManageFundsResolver
}
@@ -74,6 +74,9 @@ import { OfficeItemNode } from './holidays/create-holiday/office-item.class';
import { OfficeItemFlatNode } from './holidays/create-holiday/office-flat-item.class';
import { ChecklistDatabase } from './holidays/create-holiday/checklist-db.class';
import { CreateHoliday } from './holidays/create-holiday/create-holiday.service';
import { CreateFundComponent } from './manage-funds/create-fund/create-fund.component';
import { EditFundComponent } from './manage-funds/edit-fund/edit-fund.component';
import { ViewFundComponent } from './manage-funds/view-fund/view-fund.component';
/**
@@ -148,6 +151,9 @@ import { CreateHoliday } from './holidays/create-holiday/create-holiday.service'
StandingInstructionsHistoryComponent,
FundMappingComponent,
CreateHolidayComponent,
CreateFundComponent,
EditFundComponent,
ViewFundComponent,
],
providers: [
CreateHoliday,
@@ -680,6 +680,13 @@ export class OrganizationService {
return this.http.get('/funds');
}
/**
* @returns {Observable<any>} Fund data
*/
getFund(fundId: string): Observable<any> {
return this.http.get(`/funds/${fundId}`);
}
/**
* @param {any} fund Fund to be created.
* @returns {Observable<any>}