Login form with Tenant dropdown menu

This commit is contained in:
Jose Alberto Hernandez
2022-11-29 22:18:54 -06:00
parent 8f1c17acdf
commit 7275ba8dda
25 changed files with 189 additions and 20 deletions
+7 -1
View File
@@ -133,11 +133,17 @@ https://localhost:8443
``` ```
FINERACT_PLATFORM_TENANT_IDENTIFIER FINERACT_PLATFORM_TENANT_IDENTIFIER
``` ```
Fineract Tenant identifier to be used, It must be aligned with the Fineract `tenants` table. Default value: Fineract Tenant identifier to be used by default, It must be aligned with the Fineract `tenants` table. Default value:
``` ```
default default
``` ```
```
FINERACT_PLATFORM_TENANTS_IDENTIFIER
```
Fineract Tenant identifier list to be used, Those must be aligned with the Fineract `tenants` table.
Setting for Languages (i18n) still under development Setting for Languages (i18n) still under development
``` ```
MIFOS_DEFAULT_LANGUAGE=en-US MIFOS_DEFAULT_LANGUAGE=en-US
+4
View File
@@ -1,4 +1,5 @@
# Fineract URLs list delimited by colon
FINERACT_API_URLS=https://dev.mifos.io,https://demo.mifos.io,https://qa.mifos.io,https://staging.mifos.io,https://mobile.mifos.io,https://demo.fineract.dev,https://localhost:8443 FINERACT_API_URLS=https://dev.mifos.io,https://demo.mifos.io,https://qa.mifos.io,https://staging.mifos.io,https://mobile.mifos.io,https://demo.fineract.dev,https://localhost:8443
FINERACT_API_URL=https://localhost:8443 FINERACT_API_URL=https://localhost:8443
@@ -9,6 +10,9 @@ FINERACT_API_VERSION=/v1
FINERACT_PLATFORM_TENANT_IDENTIFIER=default FINERACT_PLATFORM_TENANT_IDENTIFIER=default
# Tenant Identifiers list delimited by colon
FINERACT_PLATFORM_TENANTS_IDENTIFIER=default
MIFOS_DEFAULT_LANGUAGE=en-US MIFOS_DEFAULT_LANGUAGE=en-US
MIFOS_SUPPORTED_LANGUAGES=en-US,fr-FR MIFOS_SUPPORTED_LANGUAGES=en-US,fr-FR
@@ -100,12 +100,12 @@ export class CreateClientComponent {
setDatatables(): void { setDatatables(): void {
this.datatables = []; this.datatables = [];
let legalFormTypeVal = 'Person'; let legalFormTypeVal = 'person';
if (this.legalFormType === 2) { if (this.legalFormType === 2) {
legalFormTypeVal = 'Entity'; legalFormTypeVal = 'entity';
} }
this.clientTemplate.datatables.forEach((datatable: any) => { this.clientTemplate.datatables.forEach((datatable: any) => {
if (datatable.entitySubType === legalFormTypeVal) { if (datatable.entitySubType.toLowerCase() === legalFormTypeVal) {
this.datatables.push(datatable); this.datatables.push(datatable);
} }
}); });
@@ -5,10 +5,11 @@ import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/c
/** rxjs Imports */ /** rxjs Imports */
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
/** Environment Configuration */ /** Custom Imports */
import { environment } from '../../../environments/environment'; import { environment } from '../../../environments/environment';
import { SettingsService } from 'app/settings/settings.service';
/** Http request options headers. */ /** Http request (default) options headers. */
const httpOptions = { const httpOptions = {
headers: { headers: {
'Fineract-Platform-TenantId': environment.fineractPlatformTenantId 'Fineract-Platform-TenantId': environment.fineractPlatformTenantId
@@ -26,12 +27,15 @@ const twoFactorAccessTokenHeader = 'Fineract-Platform-TFA-Token';
@Injectable() @Injectable()
export class AuthenticationInterceptor implements HttpInterceptor { export class AuthenticationInterceptor implements HttpInterceptor {
constructor() {} constructor(private settingsService: SettingsService) {}
/** /**
* Intercepts a Http request and sets the request headers. * Intercepts a Http request and sets the request headers.
*/ */
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
if (this.settingsService.tenantIdentifier) {
httpOptions.headers['Fineract-Platform-TenantId'] = this.settingsService.tenantIdentifier;
}
request = request.clone({ setHeaders: httpOptions.headers }); request = request.clone({ setHeaders: httpOptions.headers });
return next.handle(request); return next.handle(request);
} }
@@ -17,7 +17,7 @@
<img src="assets/images/user_placeholder.png" alt="app-user-photo"> <img src="assets/images/user_placeholder.png" alt="app-user-photo">
</div> </div>
<span class="app-user-name mb-05"> <span class="app-user-name mb-05">
{{ username }} {{ tenantIdentifier }} / {{ username }}
</span> </span>
<div #userPanel class="app-user-controls"> <div #userPanel class="app-user-controls">
<button class="xs-button" mat-icon-button matTooltip="{{ 'tooltips.Settings' | translate }}" [routerLink]="['/settings']"> <button class="xs-button" mat-icon-button matTooltip="{{ 'tooltips.Settings' | translate }}" [routerLink]="['/settings']">
@@ -14,6 +14,7 @@ import { ConfigurationWizardService } from '../../../configuration-wizard/config
/** Custom Imports */ /** Custom Imports */
import { frequentActivities } from './frequent-activities'; import { frequentActivities } from './frequent-activities';
import { SettingsService } from 'app/settings/settings.service';
/** /**
* Sidenav component. * Sidenav component.
@@ -51,12 +52,14 @@ export class SidenavComponent implements OnInit, AfterViewInit {
* @param {Router} router Router for navigation. * @param {Router} router Router for navigation.
* @param {MatDialog} dialog Mat Dialog * @param {MatDialog} dialog Mat Dialog
* @param {AuthenticationService} authenticationService Authentication Service. * @param {AuthenticationService} authenticationService Authentication Service.
* @param {SettingsService} settingsService Settings Service.
* @param {ConfigurationWizardService} configurationWizardService ConfigurationWizard Service. * @param {ConfigurationWizardService} configurationWizardService ConfigurationWizard Service.
* @param {PopoverService} popoverService PopoverService. * @param {PopoverService} popoverService PopoverService.
*/ */
constructor(private router: Router, constructor(private router: Router,
public dialog: MatDialog, public dialog: MatDialog,
private authenticationService: AuthenticationService, private authenticationService: AuthenticationService,
private settingsService: SettingsService,
private configurationWizardService: ConfigurationWizardService, private configurationWizardService: ConfigurationWizardService,
private popoverService: PopoverService) { private popoverService: PopoverService) {
this.userActivity = JSON.parse(localStorage.getItem('mifosXLocation')); this.userActivity = JSON.parse(localStorage.getItem('mifosXLocation'));
@@ -207,4 +210,12 @@ export class SidenavComponent implements OnInit, AfterViewInit {
this.router.onSameUrlNavigation = 'reload'; this.router.onSameUrlNavigation = 'reload';
this.router.navigate(['/home']); this.router.navigate(['/home']);
} }
get tenantIdentifier(): string {
if (!this.settingsService.tenantIdentifier || this.settingsService.tenantIdentifier === '') {
return 'default';
}
return this.settingsService.tenantIdentifier;
}
} }
@@ -1,8 +1,8 @@
<form fxLayout="column" [formGroup]="loginForm" (ngSubmit)="login()" id="login-form"> <form fxLayout="column" [formGroup]="loginForm" (ngSubmit)="login()" id="login-form">
<mat-form-field fxFlexAlign="center" class="login-input"> <mat-form-field fxFlexAlign="center" class="login-input">
<span matPrefix> <span matPrefix class="m-r-10">
<fa-icon icon="user-circle" size="lg"></fa-icon>&nbsp;&nbsp; <fa-icon icon="user-circle" size="lg"></fa-icon>
</span> </span>
<mat-label>{{ 'labels.inputs.Username' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Username' | translate }}</mat-label>
<input matInput type="text" autocomplete="off" formControlName="username"> <input matInput type="text" autocomplete="off" formControlName="username">
@@ -12,8 +12,8 @@
</mat-form-field> </mat-form-field>
<mat-form-field fxFlexAlign="center" class="login-input"> <mat-form-field fxFlexAlign="center" class="login-input">
<span matPrefix> <span matPrefix class="m-r-10">
<fa-icon icon="lock" size="lg"></fa-icon>&nbsp;&nbsp; <fa-icon icon="lock" size="lg"></fa-icon>
</span> </span>
<mat-label>{{ 'labels.inputs.Password' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Password' | translate }}</mat-label>
<input matInput type="{{ passwordInputType }}" formControlName="password"> <input matInput type="{{ passwordInputType }}" formControlName="password">
+5
View File
@@ -43,6 +43,11 @@
<img src="assets/images/mifos-logo-flat.png" alt="{{ 'APP_NAME' | translate }} Logo" class="img-container"> <img src="assets/images/mifos-logo-flat.png" alt="{{ 'APP_NAME' | translate }} Logo" class="img-container">
</div> </div>
<!-- Tenant Identifier Name -->
<div fxLayout="row" fxLayoutAlign="center center" fxFlex="1 0 auto">
<mifosx-tenant-selector></mifosx-tenant-selector>
</div>
<!-- <!--
Type of Form: Type of Form:
Login Login
@@ -76,7 +76,7 @@ export class AllocateCashComponent implements OnInit {
const locale = this.settingsService.language.code; const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat; const dateFormat = this.settingsService.dateFormat;
const txnDate = this.allocateCashForm.value.txnDate; const txnDate = this.allocateCashForm.value.txnDate;
if (allocateCashFormData.closureDate instanceof Date) { if (allocateCashFormData.txnDate instanceof Date) {
allocateCashFormData.txnDate = this.dateUtils.formatDate(txnDate, dateFormat); allocateCashFormData.txnDate = this.dateUtils.formatDate(txnDate, dateFormat);
} }
const data = { const data = {
@@ -114,7 +114,7 @@
</ng-container> </ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="select-row" [routerLink]="[row.id]"></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table> </table>
+9 -1
View File
@@ -16,6 +16,14 @@
<div fxLayout="row wrap" class="content"> <div fxLayout="row wrap" class="content">
<div fxFlex="50%" class="header">
Tenant Id
</div>
<div fxFlex="50%">
{{ tenantIdentifier }}
</div>
<div fxFlex="50%" class="header"> <div fxFlex="50%" class="header">
User Id User Id
</div> </div>
@@ -81,4 +89,4 @@
</mat-card> </mat-card>
</div> </div>
+9 -3
View File
@@ -8,6 +8,7 @@ import { Router } from '@angular/router';
import { AuthenticationService } from 'app/core/authentication/authentication.service'; import { AuthenticationService } from 'app/core/authentication/authentication.service';
import { ChangePasswordDialogComponent } from 'app/shared/change-password-dialog/change-password-dialog.component'; import { ChangePasswordDialogComponent } from 'app/shared/change-password-dialog/change-password-dialog.component';
import { UserService } from 'app/self-service/users/user.service'; import { UserService } from 'app/self-service/users/user.service';
import { SettingsService } from 'app/settings/settings.service';
/** /**
* Profile Component. * Profile Component.
@@ -36,9 +37,10 @@ export class ProfileComponent implements OnInit {
* @param {MatDialog} dialog Mat Dialog * @param {MatDialog} dialog Mat Dialog
*/ */
constructor(private authenticationService: AuthenticationService, constructor(private authenticationService: AuthenticationService,
private userService: UserService, private settingsService: SettingsService,
private router: Router, private userService: UserService,
public dialog: MatDialog) { private router: Router,
public dialog: MatDialog) {
this.profileData = authenticationService.getCredentials(); this.profileData = authenticationService.getCredentials();
} }
@@ -66,4 +68,8 @@ export class ProfileComponent implements OnInit {
}); });
} }
get tenantIdentifier(): string {
return this.settingsService.tenantIdentifier || 'default';
}
} }
+30
View File
@@ -63,6 +63,22 @@ export class SettingsService {
localStorage.setItem('mifosXServers', JSON.stringify(list)); localStorage.setItem('mifosXServers', JSON.stringify(list));
} }
/**
* Sets Tenant Identifiers list setting throughout the app.
* @param {string[]} list List of default tenants
*/
setTenantIdentifiers(list: string[]) {
localStorage.setItem('mifosXTenantIdentifiers', JSON.stringify(list));
}
/**
* Sets Tenant Identifier setting throughout the app.
* @param {string} Tenant Identifier
*/
setTenantIdentifier(tenantIdentifier: string) {
localStorage.setItem('mifosXTenantIdentifier', tenantIdentifier);
}
/** /**
* Sets server Date setting for max datepicker, default today * Sets server Date setting for max datepicker, default today
* @param {string} date * @param {string} date
@@ -141,6 +157,20 @@ export class SettingsService {
return this.maxAllowedDate; return this.maxAllowedDate;
} }
/**
* Returns list of Tenant Identifiers
*/
get tenantIdentifiers(): any {
return JSON.parse(localStorage.getItem('mifosXTenantIdentifiers'));
}
/**
* Returns Tenant Identifier
*/
get tenantIdentifier(): string {
return localStorage.getItem('mifosXTenantIdentifier');
}
/** /**
* Validate If the enable_business_date configuration is enabled or disabled. * Validate If the enable_business_date configuration is enabled or disabled.
*/ */
+5 -2
View File
@@ -21,6 +21,7 @@ import { NotificationsTrayComponent } from './notifications-tray/notifications-t
import { SearchToolComponent } from './search-tool/search-tool.component'; import { SearchToolComponent } from './search-tool/search-tool.component';
import { KeyboardShortcutsDialogComponent } from './keyboard-shortcuts-dialog/keyboard-shortcuts-dialog.component'; import { KeyboardShortcutsDialogComponent } from './keyboard-shortcuts-dialog/keyboard-shortcuts-dialog.component';
import { ServerSelectorComponent } from './server-selector/server-selector.component'; import { ServerSelectorComponent } from './server-selector/server-selector.component';
import { TenantSelectorComponent } from './tenant-selector/tenant-selector.component';
/** Custom Modules */ /** Custom Modules */
import { IconsModule } from './icons.module'; import { IconsModule } from './icons.module';
@@ -57,7 +58,8 @@ import { TranslateModule } from '@ngx-translate/core';
ErrorDialogComponent, ErrorDialogComponent,
NotificationsTrayComponent, NotificationsTrayComponent,
SearchToolComponent, SearchToolComponent,
ServerSelectorComponent ServerSelectorComponent,
TenantSelectorComponent
], ],
exports: [ exports: [
FileUploadComponent, FileUploadComponent,
@@ -73,7 +75,8 @@ import { TranslateModule } from '@ngx-translate/core';
MaterialModule, MaterialModule,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
TranslateModule TranslateModule,
TenantSelectorComponent
] ]
}) })
export class SharedModule { } export class SharedModule { }
@@ -0,0 +1,11 @@
<mat-form-field fxFlexAlign="center" id="tenant-selector">
<span matPrefix class="m-r-10">
<fa-icon icon="building" size="lg"></fa-icon>
</span>
<mat-label>{{ 'labels.inputs.Tenant' | translate }}</mat-label>
<mat-select [disabled]="!allowSelection()" [formControl]="tenantSelector" (selectionChange)="setTenantIdentifier()" class="tenantselector">
<mat-option *ngFor="let tenant of tenants" [value]="tenant">
{{ tenant }}
</mat-option>
</mat-select>
</mat-form-field>
@@ -0,0 +1,5 @@
#tenant-selector,
.tenantselector {
width: 14rem;
margin-bottom: 0.6rem;
}
@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TenantSelectorComponent } from './tenant-selector.component';
describe('TenantSelectorComponent', () => {
let component: TenantSelectorComponent;
let fixture: ComponentFixture<TenantSelectorComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TenantSelectorComponent ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(TenantSelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,42 @@
import { Component, OnInit } from '@angular/core';
import { FormControl } from '@angular/forms';
import { SettingsService } from 'app/settings/settings.service';
@Component({
selector: 'mifosx-tenant-selector',
templateUrl: './tenant-selector.component.html',
styleUrls: ['./tenant-selector.component.scss']
})
export class TenantSelectorComponent implements OnInit {
/** Tenant selector form control. */
tenantSelector = new FormControl();
/**
* Sets the Tenant Identifier of the application in the selector on initial setup.
* @param {SettingsService} settingsService Settings Service.
*/
constructor(private settingsService: SettingsService) {
this.tenantSelector.setValue(this.settingsService.tenantIdentifier);
}
ngOnInit(): void {
}
/**
* Returns all the languages supported by the application.
* @return {string[]} Supported languages.
*/
get tenants(): string[] {
return this.settingsService.tenantIdentifiers || [];
}
setTenantIdentifier(): void {
this.settingsService.setTenantIdentifier(this.tenantSelector.value);
}
allowSelection(): boolean {
return (this.tenants.length > 1);
}
}
+3
View File
@@ -150,6 +150,9 @@ export class WebAppComponent implements OnInit {
this.settingsService.setBusinessDate(this.dateUtils.formatDate(new Date(), SettingsService.businessDateFormat)); this.settingsService.setBusinessDate(this.dateUtils.formatDate(new Date(), SettingsService.businessDateFormat));
// Set the server list from the env var FINERACT_API_URLS // Set the server list from the env var FINERACT_API_URLS
this.settingsService.setServers(environment.baseApiUrls.split(',')); this.settingsService.setServers(environment.baseApiUrls.split(','));
// Set the Tenant Identifier(s) list from the env var
this.settingsService.setTenantIdentifier(environment.fineractPlatformTenantId || 'default');
this.settingsService.setTenantIdentifiers(environment.fineractPlatformTenantIds.split(','));
} }
logout() { logout() {
+1
View File
@@ -9,6 +9,7 @@
window["env"]["apiVersion"] = '$FINERACT_API_VERSION'; window["env"]["apiVersion"] = '$FINERACT_API_VERSION';
window["env"]["fineractPlatformTenantId"] = '$FINERACT_PLATFORM_TENANT_IDENTIFIER'; window["env"]["fineractPlatformTenantId"] = '$FINERACT_PLATFORM_TENANT_IDENTIFIER';
window["env"]["fineractPlatformTenantIds"] = '$FINERACT_PLATFORM_TENANTS_IDENTIFIER';
// Language Environment variables // Language Environment variables
window["env"]["defaultLanguage"] = '$MIFOS_DEFAULT_LANGUAGE'; window["env"]["defaultLanguage"] = '$MIFOS_DEFAULT_LANGUAGE';
+1
View File
@@ -23,6 +23,7 @@
"is required": "is required" "is required": "is required"
}, },
"inputs": { "inputs": {
"Tenant": "Tenant",
"Language": "Language", "Language": "Language",
"Server": "Server", "Server": "Server",
"Url": "Url", "Url": "Url",
+1
View File
@@ -23,6 +23,7 @@
"is required": "es requerido" "is required": "es requerido"
}, },
"inputs": { "inputs": {
"Tenant": "Entidad",
"Language": "Lenguaje", "Language": "Lenguaje",
"Server": "Servidor", "Server": "Servidor",
"Url": "Url", "Url": "Url",
+1
View File
@@ -23,6 +23,7 @@
"is required": "is required" "is required": "is required"
}, },
"inputs": { "inputs": {
"Tenant": "Tenant",
"Language": "Language", "Language": "Language",
"Server": "Server", "Server": "Server",
"Url": "Url", "Url": "Url",
+1
View File
@@ -7,6 +7,7 @@ export const environment = {
hash: env.mifos_x.hash, hash: env.mifos_x.hash,
// For connecting to server running elsewhere update the tenant identifier // For connecting to server running elsewhere update the tenant identifier
fineractPlatformTenantId: window['env']['fineractPlatformTenantId'] || 'default', fineractPlatformTenantId: window['env']['fineractPlatformTenantId'] || 'default',
fineractPlatformTenantIds: window['env']['fineractPlatformTenantIds'] || 'default',
// For connecting to others servers running elsewhere update the base API URL // For connecting to others servers running elsewhere update the base API URL
baseApiUrls: window['env']['fineractApiUrls'] || baseApiUrls: window['env']['fineractApiUrls'] ||
'https://dev.mifos.io,https://demo.mifos.io,https://qa.mifos.io,https://staging.mifos.io,https://mobile.mifos.io,https://demo.fineract.dev,https://localhost:8443', 'https://dev.mifos.io,https://demo.mifos.io,https://qa.mifos.io,https://staging.mifos.io,https://mobile.mifos.io,https://demo.fineract.dev,https://localhost:8443',
+1
View File
@@ -12,6 +12,7 @@ export const environment = {
hash: env.mifos_x.hash, hash: env.mifos_x.hash,
// For connecting to server running elsewhere update the tenant identifier // For connecting to server running elsewhere update the tenant identifier
fineractPlatformTenantId: window['env']['fineractPlatformTenantId'] || 'default', fineractPlatformTenantId: window['env']['fineractPlatformTenantId'] || 'default',
fineractPlatformTenantIds: window['env']['fineractPlatformTenantIds'] || 'default',
// For connecting to others servers running elsewhere update the base API URL // For connecting to others servers running elsewhere update the base API URL
baseApiUrls: window['env']['fineractApiUrls'] || baseApiUrls: window['env']['fineractApiUrls'] ||
'https://dev.mifos.io,https://demo.mifos.io,https://qa.mifos.io,https://staging.mifos.io,https://mobile.mifos.io,https://demo.fineract.dev,https://localhost:8443', 'https://dev.mifos.io,https://demo.mifos.io,https://qa.mifos.io,https://staging.mifos.io,https://mobile.mifos.io,https://demo.fineract.dev,https://localhost:8443',