merge angular-17 into dev to upgrade from Angular v16 to v17

This commit is contained in:
gkbishnoi07
2025-06-05 15:51:43 +02:00
committed by Adam Saghy
parent 94e37d129c
commit 1d6b6fd743
65 changed files with 4565 additions and 4571 deletions
+5 -2
View File
@@ -4,9 +4,9 @@ name: build-run
on:
# Triggers the workflow on push or pull request events for specific branches
push:
branches: [main, dev, angular-update]
branches: [main, dev, angular-update-17-2]
pull_request:
branches: [main, dev, angular-update]
branches: [main, dev, angular-update-17-2]
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
@@ -40,6 +40,9 @@ jobs:
- name: Run prettier
run: npx prettier . --check
- name: HTMLHint
run: npx htmlhint "src" --config .htmlhintrc
- name: Run build
run: npm run build:prod
+2 -2
View File
@@ -2,9 +2,9 @@ name: Tests
on:
push:
branches: [main, dev, angular-update]
branches: [main, dev, angular-update-17-2]
pull_request:
branches: [main, dev, angular-update]
branches: [main, dev, angular-update-17-2]
jobs:
testRigor:
+4 -4
View File
@@ -93,14 +93,14 @@
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "mifosx-web-app:build"
"buildTarget": "mifosx-web-app:build"
},
"configurations": {
"development": {
"browserTarget": "mifosx-web-app:build:development"
"buildTarget": "mifosx-web-app:build:development"
},
"production": {
"browserTarget": "mifosx-web-app:build:production"
"buildTarget": "mifosx-web-app:build:production"
}
},
"defaultConfiguration": "development"
@@ -108,7 +108,7 @@
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "mifosx-web-app:build"
"buildTarget": "mifosx-web-app:build"
}
},
"test": {
+3982 -4330
View File
File diff suppressed because it is too large Load Diff
+33 -27
View File
@@ -29,22 +29,21 @@
"cypress:run": "cypress run"
},
"dependencies": {
"@angular-devkit/schematics": "16.2.16",
"@angular-material-components/datetime-picker": "^16.0.1",
"@angular/animations": "16.2.12",
"@angular/cdk": "^16.2.14",
"@angular/common": "16.2.12",
"@angular/compiler": "16.2.12",
"@angular/core": "16.2.12",
"@angular/forms": "16.2.12",
"@angular/localize": "16.2.12",
"@angular/material": "^16.2.14",
"@angular/platform-browser": "16.2.12",
"@angular/platform-browser-dynamic": "16.2.12",
"@angular/router": "16.2.12",
"@angular-devkit/schematics": "17.3.17",
"@angular/animations": "17.3.12",
"@angular/cdk": "^17.3.10",
"@angular/common": "17.3.12",
"@angular/compiler": "17.3.12",
"@angular/core": "17.3.12",
"@angular/forms": "17.3.12",
"@angular/localize": "17.3.12",
"@angular/material": "^17.3.10",
"@angular/platform-browser": "17.3.12",
"@angular/platform-browser-dynamic": "17.3.12",
"@angular/router": "17.3.12",
"@ckeditor/ckeditor5-angular": "6.0.1",
"@ckeditor/ckeditor5-build-classic": "40.2.0",
"@fortawesome/angular-fontawesome": "^0.13.0",
"@fortawesome/angular-fontawesome": "0.14.1",
"@fortawesome/fontawesome-free": "^6.7.2",
"@fortawesome/fontawesome-svg-core": "6.2.1",
"@fortawesome/free-brands-svg-icons": "6.7.2",
@@ -55,6 +54,7 @@
"@tailwindcss/forms": "^0.5.4",
"chart.js": "3.0.0-alpha",
"d3": "^7.9.0",
"exceljs": "^4.4.0",
"html2canvas": "^1.4.1",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
@@ -65,25 +65,25 @@
"tslib": "^2.0.0",
"vkbeautify": "^0.99.3",
"xlsx": "^0.18.5",
"zone.js": "0.13.3"
"zone.js": "0.14.10"
},
"devDependencies": {
"@angular-devkit/build-angular": "^16.2.16",
"@angular-eslint/builder": "^19.3.0",
"@angular-eslint/eslint-plugin": "^19.3.0",
"@angular-eslint/eslint-plugin-template": "^19.3.0",
"@angular-eslint/schematics": "19.3.0",
"@angular-eslint/template-parser": "^19.3.0",
"@angular/cli": "16.2.16",
"@angular/compiler-cli": "16.2.12",
"@angular/language-service": "16.2.12",
"@biesbjerg/ngx-translate-extract": "^7.0.4",
"@angular-devkit/build-angular": "^17.3.17",
"@angular-eslint/builder": "^19.7.0",
"@angular-eslint/eslint-plugin": "^19.7.0",
"@angular-eslint/eslint-plugin-template": "^19.7.0",
"@angular-eslint/schematics": "^19.7.0",
"@angular-eslint/template-parser": "^19.7.0",
"@angular/cli": "17.3.17",
"@angular/compiler-cli": "17.3.12",
"@angular/language-service": "17.3.12",
"@vendure/ngx-translate-extract": "^9.4.0",
"@cypress/schematic": "^2.5.2",
"@types/d3-dispatch": "^3.0.1",
"@types/d3-drag": "^3.0.1",
"@types/d3-scale": "^3.2.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-timer": "^3.0.0",
"@types/d3-scale": "^3.2.2",
"@types/jasmine": "3.3.13",
"@types/jasminewd2": "2.0.6",
"@types/lodash": "4.14.132",
@@ -118,7 +118,7 @@
"stylelint-scss": "^6.8.1",
"tailwindcss": "^3.3.3",
"ts-node": "8.2.0",
"typescript": "~4.9.5"
"typescript": "^5.4.5"
},
"repository": {
"type": "git",
@@ -135,5 +135,11 @@
"*.{js,jsx,ts,tsx}": "eslint --cache",
"*.{css,scss}": "stylelint",
"*": "prettier --check --ignore-unknown"
},
"overrides": {
"exceljs": {
"archiver": "^7.0.1",
"unzipper": "^0.12.3"
}
}
}
@@ -263,7 +263,7 @@ export class CreateStandingInstructionsComponent implements OnInit {
/** Refine Object
* Removes the object param with null or '' values
*/
refineObject(dataObj: Object) {
refineObject(dataObj: { [key: string]: any }) {
const propNames = Object.getOwnPropertyNames(dataObj);
for (let i = 0; i < propNames.length; i++) {
const propName = propNames[i];
@@ -43,6 +43,9 @@ export class CenterActionsComponent {
) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
const name = this.route.snapshot.params['action'];
this.actions[name] = true;
if (name && name in this.actions) {
this.actions[name as keyof typeof this.actions] = true;
}
}
}
@@ -24,6 +24,7 @@ export class ClientActionsComponent {
'Accept Transfer': boolean;
'Reject Transfer': boolean;
Reactivate: boolean;
Activate: boolean;
'Undo Rejection': boolean;
'Add Charge': boolean;
'Take Survey': boolean;
@@ -41,6 +42,7 @@ export class ClientActionsComponent {
'Accept Transfer': false,
'Reject Transfer': false,
Reactivate: false,
Activate: false,
'Undo Rejection': false,
'Add Charge': false,
'Take Survey': false,
@@ -57,7 +59,7 @@ export class ClientActionsComponent {
private router: Router
) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
const name = this.route.snapshot.params['name'];
const name = this.route.snapshot.params['name'] as keyof typeof this.actions;
this.actions[name] = true;
}
}
@@ -72,7 +72,7 @@ export class TakeSurveyComponent {
// TODO: document the function
groupBy(array: any, func: any) {
const groups = {};
const groups: { [key: string]: any[] } = {};
array.forEach((ele: any) => {
const group = JSON.stringify(func(ele));
groups[group] = groups[group] || [];
@@ -39,7 +39,7 @@
</mat-form-field>
<mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-50-minus-25px">
>{{ 'labels.inputs.Is Dependent' | translate }}?
{{ 'labels.inputs.Is Dependent' | translate }}?
</mat-checkbox>
<mat-form-field class="flex-50-minus-25px">
@@ -52,7 +52,10 @@ export class IndividualCollectionSheetComponent implements OnInit {
/** checks and stores the local storage values */
Success: boolean;
/** Bulk Disbursement Transactions Data */
bulkDisbursementTransactionsData = {};
bulkDisbursementTransactionsData: {
bulkRepaymentTransactions?: any[];
bulkSavingsDueTransactions?: any[];
} = {};
/** Bulk Repayment Transactions Data */
bulkRepaymentTransactions: any[] = [];
/** Bulk Savings Due Data */
@@ -261,7 +264,16 @@ export class IndividualCollectionSheetComponent implements OnInit {
if (response.data) {
if (type === 'loans') {
const totalDue = this.getLoanTotalDueAmount(selectedData);
const loanTransaction = {
const loanTransaction: {
loanId: any;
transactionAmount: number;
paymentTypeId?: string;
accountNumber?: any;
checkNumber?: number;
routingCode?: string;
receiptNumber?: number;
bankNumber?: number;
} = {
loanId: selectedData.loanId,
transactionAmount: totalDue
};
@@ -279,7 +291,17 @@ export class IndividualCollectionSheetComponent implements OnInit {
if (isNaN(dueAmount)) {
dueAmount = 0;
}
const savingsTransaction = {
const savingsTransaction: {
savingsId: any;
transactionAmount: any;
depositAccountType: number;
accountNumber?: any;
checkNumber?: number;
routingCode?: string;
receiptNumber?: number;
bankNumber?: number;
paymentTypeId?: string;
} = {
savingsId: selectedData.savingsId,
transactionAmount: dueAmount,
depositAccountType:
@@ -10,7 +10,7 @@ import { environment } from '../../../environments/environment';
import { SettingsService } from 'app/settings/settings.service';
/** Http request (default) options headers. */
const httpOptions = {
const httpOptions: { headers: { [key: string]: string } } = {
headers: {
'Fineract-Platform-TenantId': environment.fineractPlatformTenantId
}
+2 -1
View File
@@ -107,7 +107,8 @@ export class HttpCacheService {
*/
setPersistence(persistence?: 'local' | 'session') {
this.cleanCache();
this.storage = persistence === 'local' || persistence === 'session' ? window[persistence + 'Storage'] : null;
this.storage =
persistence === 'local' ? window.localStorage : persistence === 'session' ? window.sessionStorage : null;
this.loadCacheData();
}
@@ -41,6 +41,8 @@ export class FixedDepositsAccountActionsComponent {
*/
constructor(private route: ActivatedRoute) {
const name = this.route.snapshot.params['name'];
this.actions[name] = true;
if (name && name in this.actions) {
this.actions[name as keyof typeof this.actions] = true;
}
}
}
@@ -5,6 +5,7 @@ import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { ActivatedRoute, Router } from '@angular/router';
import { Currency, PaymentType } from 'app/shared/models/general.model';
import { TransactionCommand, TransactionTypeFlags } from '../../../transaction.model';
@Component({
selector: 'mifosx-fixed-deposits-cash-transaction',
@@ -23,9 +24,9 @@ export class FixedDepositsCashTransactionComponent implements OnInit {
/** Flag to enable payment details fields. */
addPaymentDetailsFlag: Boolean = false;
/** transaction type flag to render required UI */
transactionType: { deposit: boolean; withdrawal: boolean } = { deposit: false, withdrawal: false };
transactionType: TransactionTypeFlags = { deposit: false, withdrawal: false };
/** transaction command for submit request */
transactionCommand: string;
transactionCommand: TransactionCommand;
actionName: string;
/** saving account's Id */
accountId: string;
@@ -53,8 +54,14 @@ export class FixedDepositsCashTransactionComponent implements OnInit {
this.paymentTypeOptions = data.fixedDepositsAccountActionData.paymentTypeOptions;
});
this.actionName = this.route.snapshot.params['name'];
this.transactionCommand = this.actionName.toLowerCase();
this.transactionType[this.transactionCommand] = true;
const lowerName = this.actionName.toLowerCase();
if (lowerName === 'deposit' || lowerName === 'withdrawal') {
this.transactionCommand = lowerName;
this.transactionType[this.transactionCommand] = true;
} else {
throw new Error(`Invalid transaction action: ${this.actionName}`);
}
this.accountId = this.route.parent.snapshot.params['fixedDepositAccountId'];
}
@@ -8,6 +8,7 @@ import { RecurringDepositsService } from '../../recurring-deposits.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { Currency } from 'app/shared/models/general.model';
import { TransactionCommand, TransactionTypeFlags } from '../../../transaction.model';
/**
* Deposits Recurring Deposits Account Component
@@ -39,8 +40,8 @@ export class DepositRecurringDepositsAccountComponent implements OnInit {
action: string;
actionName: string;
transactionCommand: string;
transactionType: { deposit: boolean; withdrawal: boolean } = { deposit: false, withdrawal: false };
transactionCommand: TransactionCommand;
transactionType: TransactionTypeFlags = { deposit: false, withdrawal: false };
/**
* Retrieves action details transactions template data from `resolve`
@@ -66,13 +67,18 @@ export class DepositRecurringDepositsAccountComponent implements OnInit {
data.recurringDepositsAccountActionData.outstandingChargeAmount > 0
) {
this.outstandingChargeAmount = data.recurringDepositsAccountActionData.outstandingChargeAmount;
this.transactionAmount = this.transactionAmount + this.outstandingChargeAmount;
this.transactionAmount += this.outstandingChargeAmount;
}
});
this.actionName = this.route.snapshot.params['name'];
this.action = this.actionName.toLowerCase();
this.transactionCommand = this.action.toLowerCase();
this.transactionType[this.transactionCommand] = true;
if (this.action === 'deposit' || this.action === 'withdrawal') {
this.transactionCommand = this.action;
this.transactionType[this.transactionCommand] = true;
} else {
throw new Error(`Invalid transaction action: ${this.actionName}`);
}
this.accountId = this.route.parent.snapshot.params['recurringDepositAccountId'];
}
@@ -47,6 +47,8 @@ export class RecurringDepositsAccountActionsComponent {
}
});
const name = this.route.snapshot.params['name'];
this.actions[name] = true;
if (name && name in this.actions) {
this.actions[name as keyof typeof this.actions] = true;
}
}
}
@@ -69,8 +69,7 @@
{{ 'labels.inputs.Note' | translate }}
</div>
<div class="flex-50">
*ngIf="transactionData.note">
<div class="flex-50" *ngIf="transactionData.note">
{{ transactionData.note }}
</div>
+5
View File
@@ -0,0 +1,5 @@
export type TransactionCommand = 'deposit' | 'withdrawal';
export type TransactionTypeFlags = {
[key in TransactionCommand]: boolean;
};
@@ -44,6 +44,8 @@ export class GroupActionsComponent {
) {
this.router.routeReuseStrategy.shouldReuseRoute = () => false;
const action = this.route.snapshot.params['action'];
this.actions[action] = true;
if (action && action in this.actions) {
this.actions[action as keyof typeof this.actions] = true;
}
}
}
@@ -85,7 +85,7 @@ export class LoansAccountDatatableStepComponent implements OnInit {
get payload(): any {
const dateFormat = this.settingsService.dateFormat;
const datatableDataValues = this.datatableForm.value;
const data = {
const data: { [key: string]: any } = {
locale: this.settingsService.language.code
};
let existDate = false;
@@ -15,6 +15,12 @@ import { InputBase } from 'app/shared/form-dialog/formfield/model/input-base';
import { Currency } from 'app/shared/models/general.model';
import { CodeName, OptionData } from 'app/shared/models/option-data.model';
interface DisbursementData {
id?: number;
principal: number;
expectedDisbursementDate: string | Date;
}
/**
* Create Loans Account Terms Step
*/
@@ -62,7 +68,7 @@ export class LoansAccountTermsStepComponent implements OnInit, OnChanges {
/** Client Active Loan Data */
clientActiveLoanData: any;
/** Multi Disbursement Data */
disbursementDataSource: {}[] = [];
disbursementDataSource: DisbursementData[] = [];
/** Loan repayment strategies */
transactionProcessingStrategyOptions: any = [];
repaymentStrategyDisabled = false;
@@ -24,7 +24,9 @@ export class EditRepaymentScheduleComponent implements OnInit {
/** Indicates If the Schedule has been validated */
wasValidated = false;
/** Stores the Repayment Schedule data */
repaymentScheduleDetails: any[] | null = null;
repaymentScheduleDetails: {
periods: { period?: number; dueDate: string; totalDueForPeriod?: number; changed?: boolean }[];
} | null = null;
/** Stores the Installments changed */
repaymentScheduleChanges: any = {};
@@ -56,7 +56,12 @@ export class RescheduleLoanTabComponent {
if (response.confirm) {
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
const payload = {
const payload: {
dateFormat: string;
locale: string;
approvedOnDate?: string;
rejectedOnDate?: string;
} = {
dateFormat,
locale
};
@@ -386,7 +386,7 @@ export class TransactionsTabComponent implements OnInit {
value: this.dateUtils.parseDate(transactionDate),
type: 'datetime-local',
required: true,
minDate: transaction.date,
minDate: this.dateUtils.parseDate(transaction.date),
order: 1
}),
new InputBase({
@@ -43,8 +43,7 @@
</mat-form-field>
<div class="flex-fill">
<span class="expandcollapsebutton m-l-10 m-t-40 flex-75">
(click)="addPaymentDetails()">
<span class="expandcollapsebutton m-l-10 m-t-40 flex-75" (click)="addPaymentDetails()">
<mat-slide-toggle>
<div [className]="showPaymentDetails ? 'enabled' : 'disabled'">
<span class="m-l-10">{{ 'labels.inputs.Show Payment Details' | translate }}</span>
@@ -25,9 +25,7 @@
[routerLink]="[routeMap[row.objectType], row.objectId]"
></tr>
<tr *matNoDataRow>
<td [attr.colspan]="displayedColumns.length" style="text-align: center; padding: 16px; color: #999">
You don't have any notifications
</td>
<td [attr.colspan]="displayedColumns.length">You don't have any notifications</td>
</tr>
</table>
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
@@ -37,7 +37,7 @@ export class CreateHolidayComponent implements OnInit {
// Stores office data in a trie-like structure
officesTrie: any;
// Stores office data for access from DOM via Office ID
officesDict = {};
officesDict: { [key: string]: any } = {};
// Angular Material Tree Configuration start ------------
@@ -100,7 +100,7 @@ export class CreateHolidayComponent implements OnInit {
});
}
constructOfficeHierarchy() {
const trie = {};
const trie: Record<string, any> = {};
// Iterates over all the offices
this.officesData.forEach((office: any) => {
this.officesDict[office.id] = office;
@@ -114,7 +114,7 @@ export class CreateHolidayComponent implements OnInit {
// Temporary variable to store current object
let root = trie;
let root: Record<string, any> = trie;
if (hierarchy.length === 0) {
root[office.id] = {};
@@ -161,7 +161,7 @@ export class BusinessRuleParametersComponent implements OnInit, OnChanges {
formattedResponse[newKey] = value;
break;
case 'select':
formattedResponse[newKey] = value['id'];
formattedResponse[newKey] = (value as { id: string | number })['id'];
break;
case 'date':
const dateFormat = this.settingsService.dateFormat;
+1 -1
View File
@@ -48,6 +48,6 @@ export class StatusLookupPipe implements PipeTransform {
'smsCampaignStatus.closed': 'status-closed',
'purchasedSharesStatusType.applied': 'status-pending'
};
return cssClassNameLookup[status];
return cssClassNameLookup[status as keyof typeof cssClassNameLookup] ?? 'status-unknown';
}
}
@@ -91,7 +91,7 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
this.clientClassificationData = this.fixedDepositProductsTemplate.chartTemplate.clientClassificationOptions;
this.incentiveTypeData = this.fixedDepositProductsTemplate.chartTemplate.incentiveTypeOptions;
if (!(this.fixedDepositProductsTemplate === undefined)) {
if (this.fixedDepositProductsTemplate) {
this.assignFormData();
}
}
@@ -145,7 +145,7 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
formArray.push(chartSlabInfo);
// Iterate for every slab in chartSlab
const chartIncentiveControl = chartDetailControl.controls['chartSlabs']['controls'][j];
const chartIncentiveControl = (chartDetailControl.controls['chartSlabs'] as UntypedFormArray).controls[j];
// Iterate to input all the incentive for particular chart slab
this.chartsDetail[i].chartSlabs[j].incentives.forEach((chartIncentiveDetail: any) => {
@@ -158,8 +158,8 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
chartIncentiveDetail.attributeName,
Validators.required
],
attribureValue: [
chartIncentiveDetail.attribureValue,
attributeValue: [
chartIncentiveDetail.attributeValue,
Validators.required
],
conditionType: [
@@ -175,7 +175,7 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
Validators.required
]
});
const newFormArray = chartIncentiveControl['controls']['incentives'] as UntypedFormArray;
const newFormArray = (chartIncentiveControl as UntypedFormGroup).controls['incentives'] as UntypedFormArray;
newFormArray.push(incentiveInfo);
});
});
@@ -183,8 +183,8 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
}
getChartsDetailsData() {
this.chartDetailData.forEach((chartData: any) => {
const chart = {
this.chartDetailData.forEach((chartData: ChartData) => {
const chart: Chart = {
endDate: chartData.endDate ? new Date(chartData.endDate) : '',
fromDate: chartData.fromDate ? new Date(chartData.fromDate) : '',
isPrimaryGroupingByAmount: chartData.isPrimaryGroupingByAmount,
@@ -193,7 +193,7 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
chartSlabs: this.getChartSlabsData(chartData)
};
if (chartData.id) {
chart['id'] = chartData.id;
chart.id = chartData.id;
}
this.chartsDetail.push(chart);
});
@@ -213,7 +213,17 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
}
chartSlabData.forEach((eachChartSlabData: any) => {
const chartSlab = {
const chartSlab: {
id?: any;
periodType: any;
amountRangeFrom: any;
amountRangeTo: any;
annualInterestRate: any;
description: any;
fromPeriod: any;
toPeriod: any;
incentives: any[];
} = {
periodType: eachChartSlabData.periodType.id,
amountRangeFrom: eachChartSlabData.amountRangeFrom,
amountRangeTo: eachChartSlabData.amountRangeTo,
@@ -221,7 +231,7 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
description: eachChartSlabData.description ? eachChartSlabData.description : '',
fromPeriod: eachChartSlabData.fromPeriod,
toPeriod: eachChartSlabData.toPeriod,
incentives: this.getIncentivesData(chartSlabData)
incentives: this.getIncentivesData(eachChartSlabData)
};
if (eachChartSlabData.id) {
chartSlab['id'] = eachChartSlabData.id;
@@ -468,3 +478,28 @@ export class FixedDepositProductInterestRateChartStepComponent implements OnInit
return fixedDepositProductInterestRateChart;
}
}
interface ChartData {
id?: number;
endDate?: string;
fromDate?: string;
isPrimaryGroupingByAmount: boolean;
name: string;
description: string;
chartSlabs: ChartSlab[];
}
interface ChartSlab {
periodType?: string;
fromPeriod?: number;
toPeriod?: number;
amountRangeFrom?: number;
amountRangeTo?: number;
annualInterestRate?: number;
description?: string;
}
interface Chart extends Omit<ChartData, 'endDate' | 'fromDate'> {
endDate: Date | string;
fromDate: Date | string;
}
@@ -378,7 +378,7 @@
<div class="flex-fill layout-row">
<span class="flex-47">{{ 'labels.inputs.Repayment Strategy' | translate }}:</span>
<span class="flex-53" class="repayment-strategy">{{
<span class="flex-53 repayment-strategy">{{
loanProduct.transactionProcessingStrategyName | translateKey: 'catalogs'
}}</span>
</div>
@@ -563,7 +563,7 @@
<span class="flex-53">{{ loanProduct.canUseForTopup | yesNo }}</span>
</div>
<h3 class="mat-h3" class="flex-fill">{{ 'labels.heading.Interest Recalculation' | translate }}</h3>
<h3 class="mat-h3 flex-fill">{{ 'labels.heading.Interest Recalculation' | translate }}</h3>
<mat-divider [inset]="true"></mat-divider>
@@ -212,7 +212,7 @@
<span class="flex-48 hide-lt-md"></span>
<div style="display: flex; justify-content: flex-start; width: 100%">
<div class="full-width-flex">
<div *ngIf="isAdvancedTransactionProcessingStrategy">
<h3 class="mat-h4 flex-98">{{ 'labels.inputs.Loan Charge-off behaviour' | translate }}</h3>
-2
View File
@@ -21,14 +21,12 @@
<fa-icon
(click)="arrowBooleansToggle(0); $event.stopPropagation()"
*ngIf="!arrowBooleans[0]"
style="z-index: 1000"
icon="arrow-down"
size="md"
></fa-icon>
<fa-icon
(click)="arrowBooleansToggle(0); $event.stopPropagation()"
*ngIf="arrowBooleans[0]"
style="z-index: 1000"
icon="arrow-up"
size="md"
></fa-icon>
@@ -145,7 +145,7 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
formArray.push(chartSlabInfo);
// Iterate for every slab in chartSlab
const chartIncentiveControl = chartDetailControl.controls['chartSlabs']['controls'][j];
const chartIncentiveControl = (chartDetailControl.controls['chartSlabs'] as UntypedFormArray).controls[j];
// Iterate to input all the incentive for particular chart slab
this.chartsDetail[i].chartSlabs[j].incentives.forEach((chartIncentiveDetail: any) => {
@@ -175,7 +175,7 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
Validators.required
]
});
const newFormArray = chartIncentiveControl['controls']['incentives'] as UntypedFormArray;
const newFormArray = (chartIncentiveControl as UntypedFormGroup).controls['incentives'] as UntypedFormArray;
newFormArray.push(incentiveInfo);
});
});
@@ -184,7 +184,14 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
getChartsDetailsData() {
this.chartDetailData.forEach((chartData: any) => {
const chart = {
const chart: {
id?: any;
endDate: string | Date;
fromDate: string | Date;
isPrimaryGroupingByAmount: any;
name: any;
chartSlabs: any[];
} = {
endDate: chartData.endDate ? new Date(chartData.endDate) : '',
fromDate: chartData.fromDate ? new Date(chartData.fromDate) : '',
isPrimaryGroupingByAmount: chartData.isPrimaryGroupingByAmount,
@@ -212,7 +219,17 @@ export class RecurringDepositProductInterestRateChartStepComponent implements On
}
chartSlabData.forEach((eachChartSlabData: any) => {
const chartSlab = {
const chartSlab: {
id?: any;
periodType: any;
amountRangeFrom: any;
amountRangeTo: any;
annualInterestRate: any;
description: any;
fromPeriod: any;
toPeriod: any;
incentives: any[];
} = {
periodType: eachChartSlabData.periodType.id,
amountRangeFrom: eachChartSlabData.amountRangeFrom,
amountRangeTo: eachChartSlabData.amountRangeTo,
@@ -13,7 +13,7 @@ import { SelectOption } from '../common-models/select-option.model';
import { Dates } from 'app/core/utils/dates';
import { GlobalConfiguration } from 'app/system/configurations/global-configurations-tab/configuration.model';
import * as XLSX from 'xlsx';
import * as ExcelJS from 'exceljs';
import { AlertService } from 'app/core/alert/alert.service';
/**
@@ -251,7 +251,7 @@ export class RunReportComponent implements OnInit {
formattedResponse[newKey] = value;
break;
case 'select':
formattedResponse[newKey] = value['id'];
formattedResponse[newKey] = (value as { id: string | number })['id'];
break;
case 'date':
if (this.isTableReport()) {
@@ -339,18 +339,46 @@ export class RunReportComponent implements OnInit {
});
}
exportToXLS(reportName: string, csvData: any, displayedColumns: string[]): void {
async exportToXLS(reportName: string, csvData: any, displayedColumns: string[]): Promise<void> {
const fileName = `${reportName}.xlsx`;
// Format data for ExcelJS
const data = csvData.map((object: any) => {
const row = {};
const row: Record<string, any> = {};
for (let i = 0; i < displayedColumns.length; i++) {
row[displayedColumns[i]] = object.row[i];
}
return row;
});
const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(data, { header: displayedColumns });
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'report');
XLSX.writeFile(wb, fileName);
// Create workbook and worksheet
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('report');
// Add header
worksheet.addRow(displayedColumns);
// Add data rows
data.forEach((rowObj: any) => {
worksheet.addRow(displayedColumns.map((col) => rowObj[col]));
});
// Write to buffer and trigger download
const buffer = await workbook.xlsx.writeBuffer();
const blob = new Blob([buffer], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
// Native download logic (no FileSaver)
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 0);
}
}
@@ -14,7 +14,7 @@ import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.componen
import { environment } from 'environments/environment';
import { ProgressBarService } from 'app/core/progress-bar/progress-bar.service';
import * as XLSX from 'xlsx';
import * as ExcelJS from 'exceljs';
/**
* Table and SMS Component
@@ -143,16 +143,33 @@ export class TableAndSmsComponent implements OnChanges {
exportToXLS(): void {
const fileName = `${this.dataObject.report.name}.xlsx`;
const data = this.csvData.map((object: any) => {
const row = {};
const row: { [key: string]: any } = {};
for (let i = 0; i < this.displayedColumns.length; i++) {
row[this.displayedColumns[i]] = object.row[i];
}
return row;
});
const ws: XLSX.WorkSheet = XLSX.utils.json_to_sheet(data, { header: this.displayedColumns });
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Report');
XLSX.writeFile(wb, fileName);
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Report');
// Add header row
worksheet.addRow(this.displayedColumns);
// Add data rows
data.forEach((rowObj: any) => {
worksheet.addRow(this.displayedColumns.map((col) => rowObj[col]));
});
workbook.xlsx.writeBuffer().then((buffer: any) => {
const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'filename.xlsx';
a.click();
URL.revokeObjectURL(url);
});
}
/**
@@ -7,6 +7,15 @@ import { SettingsService } from 'app/settings/settings.service';
import { Currency } from 'app/shared/models/general.model';
import { SystemService } from 'app/system/system.service';
type TransactionCommandType = 'holdamount' | 'blockaccount' | 'blockdeposit' | 'blockwithdrawal';
interface TransactionType {
holdamount: boolean;
blockaccount: boolean;
blockdeposit: boolean;
blockwithdrawal: boolean;
}
@Component({
selector: 'mifosx-manage-savings-account',
templateUrl: './manage-savings-account.component.html',
@@ -22,16 +31,11 @@ export class ManageSavingsAccountComponent implements OnInit {
manageSavingsAccountForm: UntypedFormGroup;
/** Savings Account Id */
savingAccountId: string;
transactionCommand: string;
transactionCommand: TransactionCommandType;
reasonOptions: any = [];
transactionType: {
holdamount: boolean;
blockaccount: boolean;
blockdeposit: boolean;
blockwithdrawal: boolean;
} = {
transactionType: TransactionType = {
holdamount: false,
blockaccount: false,
blockdeposit: false,
@@ -130,7 +134,7 @@ export class ManageSavingsAccountComponent implements OnInit {
submit() {
let command = '';
let payload = {};
let payload: { transactionAmount?: number; [key: string]: any } = {};
if (this.transactionType.holdamount) {
const manageSavingsAccountFormData = this.manageSavingsAccountForm.value;
@@ -68,7 +68,9 @@ export class SavingAccountActionsComponent {
this.currency = data.savingsAccountActionData.currency;
}
});
const name = this.route.snapshot.params['name'];
this.actions[name] = true;
const name = this.route.snapshot.params['name'] as string;
if (name in this.actions) {
this.actions[name as keyof typeof this.actions] = true;
}
}
}
@@ -66,7 +66,7 @@ export class SavingsAccountTransactionsComponent implements OnInit {
}
});
this.transactionCommand = this.route.snapshot.params['name'].toLowerCase();
this.transactionType[this.transactionCommand] = true;
this.transactionType[this.transactionCommand as 'deposit' | 'withdrawal'] = true;
this.savingAccountId = this.route.snapshot.params['savingAccountId'];
}
@@ -85,7 +85,7 @@ export class SavingsAccountViewComponent implements OnInit {
taskPermissionName: 'CREATE_ACCOUNTTRANSFER'
});
}
if (this.savingsAccountData.externalId && environment.interbankTransfers === 'true') {
if (this.savingsAccountData.externalId && environment.interbankTransfers) {
this.buttonConfig.addOption({
name: 'Interbank Transfer',
taskPermissionName: 'CREATE_ACCOUNTTRANSFER'
@@ -38,8 +38,7 @@
</mat-form-field>
<div class="flex-fill">
<span class="expandcollapsebutton m-l-10 m-t-40 flex-75">
(click)="addPaymentDetails()">
<span class="expandcollapsebutton m-l-10 m-t-40 flex-75" (click)="addPaymentDetails()">
<mat-slide-toggle>
<div [className]="showPaymentDetails ? 'enabled' : 'disabled'">
<span class="m-l-10">{{ 'labels.inputs.Show Payment Details' | translate }}</span>
@@ -0,0 +1,15 @@
import { Component, Input } from '@angular/core';
/* eslint-disable @angular-eslint/component-selector */
/* eslint-disable @angular-eslint/prefer-standalone */
@Component({
selector: 'ngx-mat-datetime-picker',
template: `
<mat-datepicker-toggle [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
`,
standalone: false
})
export class DatetimePickerStubComponent {
@Input() enableMeridian: boolean = true;
}
@@ -50,14 +50,12 @@
<mat-label>{{ formfield.label }}</mat-label>
<input
matInput
[ngxMatDatetimePicker]="datetimePicker"
type="datetime-local"
[min]="formfield.minDate"
[max]="formfield.maxDate"
[formControlName]="formfield.controlName"
[required]="formfield.required"
/>
<mat-datepicker-toggle matSuffix [for]="datetimePicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #datetimePicker [enableMeridian]="true"></ngx-mat-datetime-picker>
<mat-error *ngIf="form.controls[formfield.controlName].hasError('required')">
{{ formfield.label }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
@@ -1,13 +1,25 @@
import { FormfieldBase } from './formfield-base';
interface DatepickerOptions {
minDate?: Date;
maxDate?: Date;
controlType?: string;
controlName?: string;
label?: string;
value?: any;
required?: boolean;
order?: number;
type?: string;
}
export class DatepickerBase extends FormfieldBase {
controlType = 'datepicker';
minDate: Date;
maxDate: Date;
constructor(options: {} = {}) {
constructor(options: DatepickerOptions = {}) {
super(options);
this.minDate = options['minDate'] || new Date(2000, 0, 1);
this.maxDate = options['maxDate'] || new Date();
this.minDate = options.minDate || new Date(2000, 0, 1);
this.maxDate = options.maxDate || new Date();
}
}
@@ -1,13 +1,29 @@
import { FormfieldBase } from './formfield-base';
// Define the base options interface based on FormfieldBase requirements
interface FormfieldBaseOptions {
controlType?: string;
controlName?: string;
label?: string;
value?: any;
required?: boolean;
order?: number;
}
// Extend the base options for DateTimePicker
interface DateTimePickerOptions extends FormfieldBaseOptions {
minDate?: Date;
maxDate?: Date;
}
export class DateTimepickerBase extends FormfieldBase {
controlType = 'datetimepicker';
minDate: Date;
maxDate: Date;
constructor(options: {} = {}) {
constructor(options: DateTimePickerOptions = {}) {
super(options);
this.minDate = options['minDate'] || new Date(2000, 0, 1);
this.maxDate = options['maxDate'] || new Date();
this.minDate = options.minDate || new Date(2000, 0, 1);
this.maxDate = options.maxDate || new Date();
}
}
@@ -1,11 +1,25 @@
import { FormfieldBase } from './formfield-base';
interface InputBaseOptions extends FormfieldBaseOptions {
type?: string;
[key: string]: any;
}
interface FormfieldBaseOptions {
controlType?: string;
controlName?: string;
label?: string;
value?: any;
required?: boolean;
order?: number;
}
export class InputBase extends FormfieldBase {
controlType = 'input';
type: string;
constructor(options: {} = {}) {
constructor(options: InputBaseOptions = {}) {
super(options);
this.type = options['type'] || 'text';
this.type = options.type || 'text';
}
}
@@ -1,15 +1,27 @@
import { FormfieldBase } from './formfield-base';
interface SelectOption {
label: string;
value: string;
data: {}[];
}
interface SelectBaseOptions {
options?: SelectOption;
controlType?: string;
controlName?: string;
label?: string;
value?: any;
required?: boolean;
order?: number;
}
export class SelectBase extends FormfieldBase {
controlType = 'select';
options: {
label: string;
value: string;
data: {}[];
};
options: SelectOption;
constructor(options: {} = {}) {
constructor(options: SelectBaseOptions = {}) {
super(options);
this.options = options['options'];
this.options = options.options || { label: '', value: '', data: [] };
}
}
+11 -6
View File
@@ -36,7 +36,7 @@ import { EntityDatatableTabComponent } from './tabs/entity-datatable-tab/entity-
import { DatatableSingleRowComponent } from './tabs/entity-datatable-tab/datatable-single-row/datatable-single-row.component';
import { DatatableMultiRowComponent } from './tabs/entity-datatable-tab/datatable-multi-row/datatable-multi-row.component';
import { SvgIconComponent } from './svg-icon/svg-icon.component';
import { NgxMatDatetimePickerModule, NgxMatNativeDateModule } from '@angular-material-components/datetime-picker';
// import { NgxMatDatetimePickerModule, NgxMatNativeDateModule } from '@angular-material-components/datetime-picker';
import { ViewJournalEntryComponent } from './accounting/view-journal-entry/view-journal-entry.component';
import { ViewJournalEntryTransactionComponent } from './accounting/view-journal-entry-transaction/view-journal-entry-transaction.component';
import { AccountNumberComponent } from './account-number/account-number.component';
@@ -51,6 +51,7 @@ import { LongTextComponent } from './long-text/long-text.component';
import { DropdownComponent } from './dropdown/dropdown.component';
import { InputAmountComponent } from './input-amount/input-amount.component';
import { InputPasswordComponent } from './input-password/input-password.component';
import { DatetimePickerStubComponent } from './datetime-picker-stub/datetime-picker-stub.component'; // import Datetime Picker Stub
/**
* Shared Module
@@ -65,9 +66,9 @@ import { InputPasswordComponent } from './input-password/input-password.componen
ReactiveFormsModule,
TranslateModule,
PipesModule,
DirectivesModule,
NgxMatDatetimePickerModule,
NgxMatNativeDateModule
DirectivesModule
// NgxMatDatetimePickerModule,
// NgxMatNativeDateModule
],
declarations: [
FormfieldComponent,
@@ -108,7 +109,9 @@ import { InputPasswordComponent } from './input-password/input-password.componen
LongTextComponent,
DropdownComponent,
InputAmountComponent,
InputPasswordComponent
InputPasswordComponent,
DatetimePickerStubComponent // temporarily added stub for datetime picker
],
exports: [
FileUploadComponent,
@@ -143,7 +146,9 @@ import { InputPasswordComponent } from './input-password/input-password.componen
ThemeToggleComponent,
LongTextComponent,
DropdownComponent,
InputAmountComponent
InputAmountComponent,
DatetimePickerStubComponent // Export stub for datetime picker
]
})
export class SharedModule {}
@@ -44,6 +44,8 @@ export class SharesAccountActionsComponent {
*/
constructor(private route: ActivatedRoute) {
const name = this.route.snapshot.params['name'];
this.actions[name] = true;
if (name && name in this.actions) {
this.actions[name as keyof typeof this.actions] = true;
}
}
}
@@ -8,31 +8,48 @@ import { PopoverService } from '../../configuration-wizard/popover/popover.servi
import { ConfigurationWizardService } from '../../configuration-wizard/configuration-wizard.service';
import { SystemService } from '../system.service';
interface Permission {
code: string;
id: number;
selected: boolean;
grouping?: string;
}
interface PermissionGroup {
permissions: Permission[];
}
interface PermissionData {
[key: string]: PermissionGroup;
}
interface SubmitPermissionData {
permissions: {
[key: string]: boolean;
};
}
@Component({
selector: 'mifosx-configure-maker-checker-tasks',
templateUrl: './configure-maker-checker-tasks.component.html',
styleUrls: ['./configure-maker-checker-tasks.component.scss']
})
export class ConfigureMakerCheckerTasksComponent implements OnInit, AfterViewInit {
permissionsData: any;
permissionsData: Permission[] = [];
groupings: string[] = [];
currentGrouping = '';
tempPermissionUIData: PermissionData = {};
permissions: PermissionGroup = { permissions: [] };
formData = {};
isDisabled = true;
newEntry: any;
selectedItem = '';
previousGrouping = '';
checkboxesChanged: Boolean = false;
currentGrouping: string;
formGroup: UntypedFormGroup;
backupform: UntypedFormGroup;
permissions: {
permissions: { code: string; id: number }[];
};
tempPermissionUIData: {
permissions: { code: string }[];
}[];
/* Reference of edit button */
@ViewChild('buttonEdit') buttonEdit: ElementRef<any>;
/* Template for popover on edit button */
@@ -95,29 +112,33 @@ export class ConfigureMakerCheckerTasksComponent implements OnInit, AfterViewIni
});
}
setMakerCheckerTask() {
this.tempPermissionUIData = [
{
permissions: []
}
];
for (const i in this.permissionsData) {
if (this.permissionsData[i]) {
if (this.permissionsData[i].grouping !== this.currentGrouping) {
this.currentGrouping = this.permissionsData[i].grouping;
this.groupings.push(this.currentGrouping);
this.tempPermissionUIData[this.currentGrouping] = { permissions: [] };
}
const temp = { code: this.permissionsData[i].code, id: i, selected: this.permissionsData[i].selected };
this.tempPermissionUIData[this.currentGrouping].permissions.push(temp);
setMakerCheckerTask(): void {
this.tempPermissionUIData = {};
for (const permission of this.permissionsData) {
if (permission.grouping !== this.currentGrouping) {
this.currentGrouping = permission.grouping || '';
this.groupings.push(this.currentGrouping);
this.tempPermissionUIData[this.currentGrouping] = { permissions: [] };
}
const temp: Permission = {
code: permission.code,
id: permission.id,
selected: permission.selected
};
this.tempPermissionUIData[this.currentGrouping].permissions.push(temp);
}
}
showPermissions(grouping: string) {
this.permissions = this.tempPermissionUIData[grouping];
this.selectedItem = grouping;
this.previousGrouping = grouping;
showPermissions(grouping: string): void {
const group = this.tempPermissionUIData[grouping];
if (group) {
this.permissions = group;
this.selectedItem = grouping;
this.previousGrouping = grouping;
}
}
permissionName = function (name: any) {
@@ -175,17 +196,20 @@ export class ConfigureMakerCheckerTasksComponent implements OnInit, AfterViewIni
this.formGroup.controls.roster.disable();
}
submit() {
const value = this.formGroup.get('roster').value;
const data = {};
const permissionData = {
submit(): void {
const value = this.formGroup.get('roster')?.value;
const permissionData: SubmitPermissionData = {
permissions: {}
};
for (let i = 0; i < value.length; i++) {
data[value[i].code] = value[i].selected;
if (Array.isArray(value)) {
value.forEach((item) => {
if (item.code && typeof item.selected === 'boolean') {
permissionData.permissions[item.code] = item.selected;
}
});
}
permissionData.permissions = data;
this.formGroup.controls.roster.disable();
this.formGroup.get('roster')?.disable();
this.checkboxesChanged = false;
this.isDisabled = true;
this.systemService.updateMakerCheckerPermission(permissionData).subscribe((response: any) => {});
@@ -108,7 +108,7 @@
<br />
<div class="layout-row-wrap responsive-column">
<p class="mat-title flex-20">{{ 'labels.inputs.Events' | translate }}<span style="color: red">*</span></p>
<p class="mat-title flex-20">{{ 'labels.inputs.Events' | translate }}<span class="red">*</span></p>
<button mat-raised-button class="AddEventButton flex-20" type="button" color="primary" (click)="addEvent()">
<fa-icon icon="plus" class="m-r-10"></fa-icon>
@@ -108,7 +108,7 @@
<br />
<div class="layout-row-wrap responsive-column">
<p class="mat-title flex-20">{{ 'labels.inputs.Events' | translate }} <span style="color: red">*</span></p>
<p class="mat-title flex-20">{{ 'labels.inputs.Events' | translate }} <span class="red">*</span></p>
<button mat-raised-button class="AddEventButton flex-20" type="button" color="primary" (click)="addEvent()">
<fa-icon icon="plus" class="m-r-10"></fa-icon>
@@ -9,7 +9,12 @@ import { FormDialogComponent } from 'app/shared/form-dialog/form-dialog.componen
import { FormfieldBase } from 'app/shared/form-dialog/formfield/model/formfield-base';
import { SelectBase } from 'app/shared/form-dialog/formfield/model/select-base';
import { SystemService } from 'app/system/system.service';
import { JobStep } from './workflow-diagram/workflow-diagram.component';
export interface JobStep {
stepName: string;
stepDescription: string;
order: number;
}
@Component({
selector: 'mifosx-workflow-jobs',
@@ -21,7 +26,7 @@ export class WorkflowJobsComponent implements OnInit {
jobNameOptions: any = [];
jobStepsData: any = [];
jobAvailableStepsData: any = [];
jobAvailableStepsData: JobStep[] = [];
jobStepsDataBase: any = [];
jobStepName: String = null;
@@ -98,7 +98,7 @@
</mat-error>
</mat-form-field>
<mat-form-field class="flex-fill" ="48">
<mat-form-field class="flex-48">
<mat-label>{{ 'labels.inputs.Text' | translate }}</mat-label>
<input matInput maxlength="255" required formControlName="text" />
<mat-error *ngIf="question.controls.text.hasError('required')">
@@ -74,7 +74,7 @@
</mat-error>
</mat-form-field>
<mat-form-field class="flex-fill" ="48">
<mat-form-field class="flex-48">
<mat-label>{{ 'labels.inputs.Text' | translate }}</mat-label>
<input matInput maxlength="255" required formControlName="text" />
<mat-error *ngIf="question.get('text').hasError('required')">
@@ -1,6 +1,6 @@
/** Angular Imports */
import { Component, OnInit } from '@angular/core';
import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms';
import { UntypedFormBuilder, UntypedFormGroup, Validators, FormArray } from '@angular/forms';
import { MatDialog } from '@angular/material/dialog';
import { ActivatedRoute, Router } from '@angular/router';
import * as _ from 'lodash';
@@ -45,12 +45,14 @@ export class ViewRoleComponent implements OnInit {
backupform: UntypedFormGroup;
/** Temporarily stores Permission data */
tempPermissionUIData: {
permissions: { code: string }[];
}[];
[key: string]: {
permissions: { code: string; id: number; selected?: boolean }[];
};
} = {};
/** Stores permissions */
permissions: {
permissions: { code: string; id: number }[];
};
} = { permissions: [] };
/**
* Retrieves the roledetails data from `resolve`.
@@ -121,11 +123,7 @@ export class ViewRoleComponent implements OnInit {
* Groups the permissions based on rules
*/
groupRules() {
this.tempPermissionUIData = [
{
permissions: []
}
];
this.tempPermissionUIData = {};
for (const i in this.rolePermissionService.permissionUsageData) {
if (this.rolePermissionService.permissionUsageData[i]) {
if (this.rolePermissionService.permissionUsageData[i].grouping !== this.currentGrouping) {
@@ -135,7 +133,7 @@ export class ViewRoleComponent implements OnInit {
}
const temp = {
code: this.rolePermissionService.permissionUsageData[i].code,
id: i,
id: +i,
selected: this.rolePermissionService.permissionUsageData[i].selected
};
this.tempPermissionUIData[this.currentGrouping].permissions.push(temp);
@@ -177,7 +175,7 @@ export class ViewRoleComponent implements OnInit {
name = name || '';
// replace '_' with ' '
name = name.replace(/_/g, ' ');
// for reorts replace read with view
// for reports replace read with view
if (this.previousGrouping === 'report') {
name = name.replace(/READ/g, 'View');
}
@@ -185,7 +183,7 @@ export class ViewRoleComponent implements OnInit {
}
/**
* Backups the valued
* Backups the values
*/
backupCheckValues() {
this.backupform = _.cloneDeep(this.formGroup) as UntypedFormGroup;
@@ -220,7 +218,7 @@ export class ViewRoleComponent implements OnInit {
*/
submit() {
const value = this.formGroup.get('roster').value;
const data = {};
const data: { [key: string]: boolean } = {};
const permissionData = {
permissions: {}
};
@@ -238,8 +236,9 @@ export class ViewRoleComponent implements OnInit {
* Selects all the permission of a particular role
*/
selectAll() {
const roster = this.formGroup.get('roster') as FormArray;
for (let i = 0; i < this.permissions.permissions.length; i++) {
this.formGroup.controls.roster['controls'][this.permissions.permissions[i].id].patchValue({
roster.at(this.permissions.permissions[i].id).patchValue({
selected: true
});
}
@@ -249,8 +248,9 @@ export class ViewRoleComponent implements OnInit {
* Deselects all the permissions of a particular role
*/
deselectAll() {
const roster = this.formGroup.get('roster') as FormArray;
for (let i = 0; i < this.permissions.permissions.length; i++) {
this.formGroup.controls.roster['controls'][this.permissions.permissions[i].id].patchValue({
roster.at(this.permissions.permissions[i].id).patchValue({
selected: false
});
}
@@ -15,6 +15,12 @@ import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { TranslateService } from '@ngx-translate/core';
interface OfficeNode {
id: number;
name: string;
loans: any[];
}
@Component({
selector: 'mifosx-loan-approval',
templateUrl: './loan-approval.component.html',
@@ -32,7 +38,7 @@ export class LoanApprovalComponent {
/** Row Selection Data */
selection: SelectionModel<any>;
/** Map data */
idToNodeMap = {};
idToNodeMap: { [key: number]: OfficeNode } = {};
/** Grouped Office Data */
officesArray: any[];
/** List of Requests */
@@ -79,15 +85,15 @@ export class LoanApprovalComponent {
});
this.loans.forEach((loanEle: any) => {
if (loanEle.status.pendingApproval) {
let tempOffice = {};
let tempOffice: OfficeNode | undefined;
if (loanEle.clientOfficeId) {
tempOffice = this.idToNodeMap[loanEle.clientOfficeId];
tempOffice['loans'].push(loanEle);
} else {
if (loanEle.group) {
tempOffice = this.idToNodeMap[loanEle.group.officeId];
tempOffice['loans'].push(loanEle);
}
} else if (loanEle.group?.officeId) {
tempOffice = this.idToNodeMap[loanEle.group.officeId];
}
if (tempOffice) {
tempOffice.loans.push(loanEle);
}
}
});
@@ -15,6 +15,13 @@ import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { TranslateService } from '@ngx-translate/core';
interface RescheduleFormData {
dateFormat: string;
locale: string;
approvedOnDate?: string;
rejectedOnDate?: string;
}
@Component({
selector: 'mifosx-reschedule-loan',
templateUrl: './reschedule-loan.component.html',
@@ -107,14 +114,14 @@ export class RescheduleLoanComponent {
const dateFormat = this.settingsService.dateFormat;
const transactionDate = this.dateUtils.formatDate(this.settingsService.businessDate, dateFormat);
const locale = this.settingsService.language.code;
const formData = {
const formData: RescheduleFormData = {
dateFormat,
locale
};
if (command === 'approve') {
formData['approvedOnDate'] = transactionDate;
formData.approvedOnDate = transactionDate;
} else {
formData['rejectedOnDate'] = transactionDate;
formData.rejectedOnDate = transactionDate;
}
const listSelectedAccounts = this.selection.selected;
this.batchRequests = [];
+25 -25
View File
@@ -6,32 +6,32 @@
// `.env.ts` is generated by the `npm run env` command
import env from './.env';
// The `window['env']` object is loaded in the `index.html` file
const loadedEnv = window['env'] || {};
// The `window.env` object is loaded in the `index.html` file
const loadedEnv = window.env || {};
export const environment = {
production: false,
version: env.mifos_x.version,
hash: env.mifos_x.hash,
// For connecting to server running elsewhere update the tenant identifier
fineractPlatformTenantId: loadedEnv['fineractPlatformTenantId'] || 'default',
fineractPlatformTenantIds: loadedEnv['fineractPlatformTenantIds'] || 'default',
fineractPlatformTenantId: loadedEnv.fineractPlatformTenantId || 'default',
fineractPlatformTenantIds: loadedEnv.fineractPlatformTenantIds || 'default',
// For connecting to others servers running elsewhere update the base API URL
baseApiUrls:
loadedEnv['fineractApiUrls'] ||
loadedEnv.fineractApiUrls ||
'https://sandbox.mifos.community,https://demo.mifos.community,https://localhost:8443,' + window.location.origin,
// For connecting to server running elsewhere set the base API URL
baseApiUrl:
loadedEnv['fineractApiUrl'] ||
(loadedEnv['fineractApiUrls']?.length > 0 ? loadedEnv['fineractApiUrls'].split(',')[0] : window.location.origin),
loadedEnv.fineractApiUrl ||
(loadedEnv.fineractApiUrls?.length > 0 ? loadedEnv.fineractApiUrls.split(',')[0] : window.location.origin),
allowServerSwitch: env.allow_switching_backend_instance,
apiProvider: loadedEnv['apiProvider'] || '/fineract-provider/api',
apiVersion: loadedEnv['apiVersion'] || '/v1',
apiProvider: loadedEnv.apiProvider || '/fineract-provider/api',
apiVersion: loadedEnv.apiVersion || '/v1',
serverUrl: '',
oauth: {
enabled: loadedEnv['oauthServerEnabled'] || false, // For connecting to Mifos X using OAuth2 Authentication change the value to true
serverUrl: loadedEnv['oauthServerUrl'] || '',
appId: loadedEnv['oauthAppId'] || ''
enabled: loadedEnv.oauthServerEnabled || false, // For connecting to Mifos X using OAuth2 Authentication change the value to true
serverUrl: loadedEnv.oauthServerUrl || '',
appId: loadedEnv.oauthAppId || ''
},
warningDialog: {
title: 'Warning',
@@ -39,31 +39,31 @@ export const environment = {
'This system is for authorized use only. Unauthorized access will result in possible legal action. By accessing this system, you acknowledge that you are authorized to do so and that all data stored and processed here is confidential.',
buttonText: 'Close'
},
defaultLanguage: loadedEnv['defaultLanguage'] || 'en-US',
defaultLanguage: loadedEnv.defaultLanguage || 'en-US',
supportedLanguages:
loadedEnv['supportedLanguages'] || 'cs-CS,de-DE,en-US,es-MX,fr-FR,it-IT,ko-KO,lt-LT,lv-LV,ne-NE,pt-PT,sw-SW',
loadedEnv.supportedLanguages || 'cs-CS,de-DE,en-US,es-MX,fr-FR,it-IT,ko-KO,lt-LT,lv-LV,ne-NE,pt-PT,sw-SW',
preloadClients: loadedEnv['preloadClients'] || true,
defaultCharDelimiter: loadedEnv['defaultCharDelimiter'] || ',',
defaultCharDelimiter: loadedEnv.defaultCharDelimiter || ',',
displayBackEndInfo: loadedEnv['displayBackEndInfo'] || 'true',
displayTenantSelector: loadedEnv['displayTenantSelector'] || 'true',
displayBackEndInfo: loadedEnv.displayBackEndInfo || 'true',
displayTenantSelector: loadedEnv.displayTenantSelector || 'true',
// Time in seconds, default 60 seconds
waitTimeForNotifications: loadedEnv['waitTimeForNotifications'] || 60,
waitTimeForNotifications: loadedEnv.waitTimeForNotifications || 60,
// Time in seconds, default 30 seconds
waitTimeForCOBCatchUp: loadedEnv['waitTimeForCOBCatchUp'] || 30,
waitTimeForCOBCatchUp: loadedEnv.waitTimeForCOBCatchUp || 30,
session: {
timeout: {
idleTimeout: loadedEnv['sessionIdleTimeout'] || 300000 // 5 minutes
idleTimeout: loadedEnv.sessionIdleTimeout || 300000 // 5 minutes
}
},
vNextApiUrl: window['env']['vNextApiUrl'] || 'https://apis.flexcore.mx',
vNextApiProvider: window['env']['vNextApiProvider'] || '/vnext1',
vNextApiVersion: window['env']['vNextApiVersion'] || '/v1.0',
interbankTransfers: window['env']['interbankTransfers'] || false,
vNextApiUrl: window.env?.vNextApiUrl || 'https://apis.flexcore.mx',
vNextApiProvider: window.env?.vNextApiProvider || '/vnext1',
vNextApiVersion: window.env?.vNextApiVersion || '/v1.0',
interbankTransfers: window.env?.interbankTransfers || false,
minPasswordLength: loadedEnv['minPasswordLength'] || 12
minPasswordLength: loadedEnv.minPasswordLength || 12
};
// Server URL
+20
View File
@@ -580,6 +580,10 @@
gap: 10px;
}
.menu-right-section fa-icon {
z-index: 1000;
}
.menu-right-section {
margin-left: auto;
display: flex;
@@ -591,3 +595,19 @@
display: contents;
flex: 1;
}
mifosx-notifications-page td {
text-align: center;
padding: 16px;
color: #999;
}
.red {
color: red;
}
.full-width-flex {
display: flex;
justify-content: flex-start;
width: 100%;
}
+2 -1
View File
@@ -28,7 +28,8 @@ import '@angular/localize/init';
/***************************************************************************************************
* Zone JS is required by default for Angular itself.
*/
import 'zone.js/dist/zone'; // Included with Angular CLI.
import 'zone.js';
// Included with Angular CLI.
/***************************************************************************************************
* APPLICATION IMPORTS
+29
View File
@@ -0,0 +1,29 @@
interface Window {
env?: {
[key: string]: any;
fineractPlatformTenantId?: string;
fineractPlatformTenantIds?: string;
fineractApiUrls?: string;
fineractApiUrl?: string;
allow_switching_backend_instance?: boolean;
apiProvider?: string;
apiVersion?: string;
oauthServerEnabled?: boolean;
oauthServerUrl?: string;
oauthAppId?: string;
defaultLanguage?: string;
supportedLanguages?: string;
preloadClients?: boolean;
defaultCharDelimiter?: string;
displayBackEndInfo?: string;
displayTenantSelector?: string;
waitTimeForNotifications?: number;
waitTimeForCOBCatchUp?: number;
sessionIdleTimeout?: number;
vNextApiUrl?: string;
vNextApiProvider?: string;
vNextApiVersion?: string;
interbankTransfers?: boolean;
minPasswordLength?: number;
};
}
+7
View File
@@ -11,6 +11,13 @@ interface NodeModule {
id: string;
}
// Extend the global Window interface to include `env`
interface Window {
env: {
[key: string]: any;
};
}
declare module 'chart.js';
declare module '@ckeditor/ckeditor5-build-classic' {
-1
View File
@@ -12,7 +12,6 @@
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"target": "ES2022",
"typeRoots": [
"node_modules/@types",