diff --git a/src/app/shared/icons.module.ts b/src/app/shared/icons.module.ts index 4a5e9330d..fa57559d2 100644 --- a/src/app/shared/icons.module.ts +++ b/src/app/shared/icons.module.ts @@ -35,6 +35,7 @@ import { faExclamationTriangle, faEye, faEyeSlash, + faFile, faFileAlt, faFileUpload, faFileWord, @@ -116,6 +117,7 @@ library.add( faExclamationTriangle, faEye, faEyeSlash, + faFile, faFileAlt, faFileUpload, faFileWord, diff --git a/src/app/system/audit-trails/audit-trail-search-template.resolver.ts b/src/app/system/audit-trails/audit-trail-search-template.resolver.ts new file mode 100644 index 000000000..48a43a142 --- /dev/null +++ b/src/app/system/audit-trails/audit-trail-search-template.resolver.ts @@ -0,0 +1,30 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { SystemService } from '../system.service'; + +/** + * Audit Trail Search Template data resolver. + */ +@Injectable() +export class AuditTrailSearchTemplateResolver implements Resolve { + + /** + * @param {SystemService} systemService System service. + */ + constructor(private systemService: SystemService) {} + + /** + * Returns the Audit Trail Search Template data. + * @returns {Observable} + */ + resolve(): Observable { + return this.systemService.getAuditTrailSearchTemplate(); + } + +} diff --git a/src/app/system/audit-trails/audit-trail.datasource.ts b/src/app/system/audit-trails/audit-trail.datasource.ts new file mode 100644 index 000000000..f7a976781 --- /dev/null +++ b/src/app/system/audit-trails/audit-trail.datasource.ts @@ -0,0 +1,60 @@ +/** Angular Imports */ +import { CollectionViewer, DataSource } from '@angular/cdk/collections'; + +/** rxjs Imports */ +import { Observable, BehaviorSubject } from 'rxjs'; + +/** Custom Services */ +import { SystemService } from '../system.service'; + +/** + * Audit Trails custom data source to implement server side filtering, pagination and sorting. + */ +export class AuditTrailsDataSource implements DataSource { + + /** Audit Trails behavior subject to represent loaded audit trails page. */ + private auditTrailsSubject = new BehaviorSubject([]); + /** Records subject to represent total number of filtered audit trail records. */ + private recordsSubject = new BehaviorSubject(0); + + /** Records observable which can be subscribed to get the value of total number of filtered audit trail records. */ + public records$ = this.recordsSubject.asObservable(); + + /** + * @param {SystemService} systemService System Service. + */ + constructor(private systemService: SystemService) { } + + /** + * Gets audit trails on the basis of provided parameters and emits the value. + * @param {any} filterBy Properties by which entries should be filtered. + * @param {string} orderBy Property by which entries should be sorted. + * @param {string} sortOrder Sort order: ascending or descending. + * @param {number} pageIndex Page number. + * @param {number} limit Number of entries within the page. + */ + getAuditTrails(filterBy: any, orderBy: string = '', sortOrder: string = '', pageIndex: number = 0, limit: number = 10) { + this.auditTrailsSubject.next([]); + this.systemService.getAuditTrails(filterBy, orderBy, sortOrder, pageIndex * limit, limit) + .subscribe((auditTrails: any) => { + this.recordsSubject.next(auditTrails.totalFilteredRecords); + this.auditTrailsSubject.next(auditTrails.pageItems); + }); + } + + /** + * @param {CollectionViewer} collectionViewer + */ + connect(collectionViewer: CollectionViewer): Observable { + return this.auditTrailsSubject.asObservable(); + } + + /** + * @param {CollectionViewer} collectionViewer + */ + disconnect(collectionViewer: CollectionViewer): void { + this.auditTrailsSubject.complete(); + this.recordsSubject.complete(); + } + +} diff --git a/src/app/system/audit-trails/audit-trails.component.html b/src/app/system/audit-trails/audit-trails.component.html new file mode 100644 index 000000000..f080bb915 --- /dev/null +++ b/src/app/system/audit-trails/audit-trails.component.html @@ -0,0 +1,162 @@ +
+ +
+ +
+ + + Resource ID + + + + + Status + + + {{ processingResult.processingResult }} + + + + + + User + + + + + Action + + + + + Entity + + + + + Checker + + + + + Maker From Date + + + + + + + Maker To Date + + + + + + + Checker From Date + + + + + + + Checked To Date + + + + + +
+ + + + + {{ user.username }} + + + + + + {{ action }} + + + + + + {{ entity }} + + + + + + {{ user.username }} + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Trail ID {{ auditTrail.id }} Resource ID {{ auditTrail.resourceId }} Status {{ auditTrail.processingResult }} Made By {{ auditTrail.maker }} Action {{ auditTrail.actionName }} Entity {{ auditTrail.entityName }} Office {{ auditTrail.officeName }} Made Date {{ auditTrail.madeOnDate | date }} Checker {{ auditTrail.checker }} Checked Date {{ auditTrail.checkedOnDate | date }}
+ + + +
diff --git a/src/app/system/audit-trails/audit-trails.component.scss b/src/app/system/audit-trails/audit-trails.component.scss new file mode 100644 index 000000000..de8ec9980 --- /dev/null +++ b/src/app/system/audit-trails/audit-trails.component.scss @@ -0,0 +1,7 @@ +table { + width: 100%; + + .select-row:hover { + cursor: pointer; + } +} diff --git a/src/app/system/audit-trails/audit-trails.component.spec.ts b/src/app/system/audit-trails/audit-trails.component.spec.ts new file mode 100644 index 000000000..0ff2c3ba6 --- /dev/null +++ b/src/app/system/audit-trails/audit-trails.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AuditTrailsComponent } from './audit-trails.component'; + +describe('AuditTrailsComponent', () => { + let component: AuditTrailsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AuditTrailsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AuditTrailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/system/audit-trails/audit-trails.component.ts b/src/app/system/audit-trails/audit-trails.component.ts new file mode 100644 index 000000000..4e8e772b1 --- /dev/null +++ b/src/app/system/audit-trails/audit-trails.component.ts @@ -0,0 +1,417 @@ +/** Angular Imports */ +import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core'; +import { MatPaginator, MatSort } from '@angular/material'; +import { ActivatedRoute } from '@angular/router'; +import { FormControl } from '@angular/forms'; +import { DatePipe } from '@angular/common'; + +/** Custom Data Source */ +import { AuditTrailsDataSource } from './audit-trail.datasource'; + +/** Custom Services */ +import { SystemService } from '../system.service'; + +/** rxjs Imports */ +import { merge } from 'rxjs'; +import { tap, debounceTime, distinctUntilChanged, startWith, map } from 'rxjs/operators'; + +/** + * Audit Trails Component. + */ +@Component({ + selector: 'mifosx-audit-trails', + templateUrl: './audit-trails.component.html', + styleUrls: ['./audit-trails.component.scss'] +}) +export class AuditTrailsComponent implements OnInit, AfterViewInit { + + /** Minimum date allowed. */ + minDate = new Date(2000, 0, 1); + /** Maximum date allowed. */ + maxDate = new Date(); + /** Audit Trails Data */ + auditTrailsData: any; + /** Filtered user data for autocomplete. */ + filteredUserData: any; + /** Filtered action data for autocomplete. */ + filteredActionData: any; + /** Filtered entity data for autocomplete. */ + filteredEntityData: any; + /** Filtered checker data for autocomplete. */ + filteredCheckerData: any; + /** Audit Trail Search Template Data. */ + auditTrailSearchTemplateData: any; + /** Columns to be displayed in audit trails table. */ + displayedColumns: string[] = ['id', 'resourceId', 'processingResult', 'maker', 'actionName', 'entityName', 'officeName', 'madeOnDate', 'checker', 'checkedOnDate']; + /** Data source for audit trails table. */ + dataSource: AuditTrailsDataSource; + /** Audit Trails filter. */ + filterAuditTrailsBy = [ + { + type: 'actionName', + value: '' + }, + { + type: 'entityName', + value: '' + }, + { + type: 'resourceId', + value: '' + }, + { + type: 'makerId', + value: '' + }, + { + type: 'makerDateTimeFrom', + value: '' + }, + { + type: 'makerDateTimeTo', + value: '' + }, + { + type: 'checkerDateTimeFrom', + value: '' + }, + { + type: 'checkerDateTimeTo', + value: '' + }, + { + type: 'checkerId', + value: '' + }, + { + type: 'processingResult', + value: '' + }, + { + type: 'dateFormat', + value: 'yyyy-MM-dd' + }, + { + type: 'locale', + value: 'en' + } + ]; + /** User form control. */ + user = new FormControl(''); + /** From date form control. */ + fromDate = new FormControl(); + /** Checked from date form control. */ + checkedFromDate = new FormControl(); + /** Processing result form control. */ + processingResult = new FormControl(); + /** Action name form control. */ + actionName = new FormControl(); + /** Resource ID form control. */ + resourceId = new FormControl(''); + /** To date form control. */ + toDate = new FormControl(); + /** Checked to date form control. */ + checkedToDate = new FormControl(); + /** Entity name form control. */ + entityName = new FormControl(); + /** Checker form control. */ + checker = new FormControl(); + + /** Paginator for audit trails table. */ + @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for audit trails table. */ + @ViewChild(MatSort) sort: MatSort; + + /** + * Retrieves the audit trail search template data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + * @param {SystemService} systemService System Service. + */ + constructor(private route: ActivatedRoute, + private systemService: SystemService, + private datePipe: DatePipe) { + this.route.data.subscribe((data: { auditTrailSearchTemplate: any }) => { + this.auditTrailSearchTemplateData = data.auditTrailSearchTemplate; + }); + } + + /** + * Sets filtered users, actions and entities for autocomplete and audit trails table. + */ + ngOnInit() { + this.setFilteredUsers(); + this.setFilteredActions(); + this.setFilteredEntities(); + this.setFilteredCheckers(); + this.getAuditTrails(); + } + + /** + * Subscribes to all search filters: + * User Name, From Date, To Date, Checked From Date, Checked To Date, Resource ID, Action Name, Entity Name, Checker + * sort change and page change. + */ + ngAfterViewInit() { + this.user.valueChanges + .pipe( + map(value => value.id ? value.id : ''), + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(filterValue, 'makerId'); + }) + ).subscribe(); + + this.fromDate.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(this.getDate(filterValue), 'makerDateTimeFrom'); + }) + ).subscribe(); + + this.toDate.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(this.getDate(filterValue), 'makerDateTimeTo'); + }) + ).subscribe(); + + this.checkedFromDate.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(this.getDate(filterValue), 'checkerDateTimeFrom'); + }) + ).subscribe(); + + this.checkedToDate.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(this.getDate(filterValue), 'checkerDateTimeTo'); + }) + ).subscribe(); + + this.resourceId.valueChanges + .pipe( + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(filterValue, 'resourceId'); + }) + ).subscribe(); + + this.actionName.valueChanges + .pipe( + map(value => value ? value : ''), + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(filterValue, 'actionName'); + }) + ).subscribe(); + + this.entityName.valueChanges + .pipe( + map(value => value ? value : ''), + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(filterValue, 'entityName'); + }) + ).subscribe(); + + this.checker.valueChanges + .pipe( + map(value => value ? value : ''), + debounceTime(500), + distinctUntilChanged(), + tap((filterValue) => { + this.applyFilter(filterValue, 'checkerId'); + }) + ).subscribe(); + + this.sort.sortChange.subscribe(() => this.paginator.pageIndex = 0); + + merge(this.sort.sortChange, this.paginator.page) + .pipe( + tap(() => this.loadAuditTrailsPage()) + ).subscribe(); + } + + /** + * Initializes the data source for audit trails table and loads the first page. + */ + getAuditTrails() { + this.dataSource = new AuditTrailsDataSource(this.systemService); + this.dataSource.getAuditTrails(this.filterAuditTrailsBy, this.sort.active, this.sort.direction, this.paginator.pageIndex, this.paginator.pageSize); + } + + /** + * Loads a page of audit trails. + */ + loadAuditTrailsPage() { + if (!this.sort.direction) { + delete this.sort.active; + } + this.dataSource.getAuditTrails(this.filterAuditTrailsBy, this.sort.active, this.sort.direction, this.paginator.pageIndex, this.paginator.pageSize); + } + + /** + * Filters data in audit trails table based on passed value and poperty. + * @param {string} filterValue Value to filter data. + * @param {string} property Property to filter data by. + */ + applyFilter(filterValue: string, property: string) { + this.paginator.pageIndex = 0; + const findIndex = this.filterAuditTrailsBy.findIndex(filter => filter.type === property); + this.filterAuditTrailsBy[findIndex].value = filterValue; + this.loadAuditTrailsPage(); + } + + /** + * Displays user name in form control input. + * @param {any} user User data. + * @returns {string} User name if valid otherwise undefined. + */ + displayUserName(user?: any): string | undefined { + return user ? user.name : undefined; + } + + /** + * Displays action name in form control input. + * @param {any} action Action data. + * @returns {string} Action name if valid otherwise undefined. + */ + displayActionName(action?: any): string | undefined { + return action ? action : undefined; + } + + /** + * Displays entity name in form control input. + * @param {any} entity Entity data. + * @returns {string} Entity name if valid otherwise undefined. + */ + displayEntityName(entity?: any): string | undefined { + return entity ? entity : undefined; + } + + /** + * Sets filtered users for autocomplete. + */ + setFilteredUsers() { + this.filteredUserData = this.user.valueChanges + .pipe( + startWith(''), + map((user: any) => typeof user === 'string' ? user : user.name), + map((userName: string) => userName ? this.filterUserAutocompleteData(userName) : this.auditTrailSearchTemplateData.appUsers) + ); + } + + /** + * Sets filtered checkers for autocomplete. + */ + setFilteredCheckers() { + this.filteredCheckerData = this.checker.valueChanges + .pipe( + startWith(''), + map((user: any) => typeof user === 'string' ? user : user.name), + map((userName: string) => userName ? this.filterUserAutocompleteData(userName) : this.auditTrailSearchTemplateData.appUsers) + ); + } + + /** + * Sets filtered actions for autocomplete. + */ + setFilteredActions() { + this.filteredActionData = this.actionName.valueChanges + .pipe( + startWith(''), + map((action: any) => typeof action === 'string' ? action : ''), + map((actionName: string) => actionName ? this.filterActionAutocompleteData(actionName) : this.auditTrailSearchTemplateData.actionNames) + ); + } + + /** + * Sets filtered entities for autocomplete. + */ + setFilteredEntities() { + this.filteredEntityData = this.entityName.valueChanges + .pipe( + startWith(''), + map((entity: any) => typeof entity === 'string' ? entity : ''), + map((entityName: string) => entityName ? this.filterEntityAutocompleteData(entityName) : this.auditTrailSearchTemplateData.entityNames) + ); + } + + /** + * Filters users. + * @param {string} userName User name to filter user by. + * @returns {any} Filtered users. + */ + private filterUserAutocompleteData(userName: string): any { + return this.auditTrailSearchTemplateData.appUsers.filter((user: any) => user.username.toLowerCase().includes(userName.toLowerCase())); + } + + /** + * Filters actions. + * @param {string} actionName Action name to filter action by. + * @returns {any} Filtered actions. + */ + private filterActionAutocompleteData(actionName: string): any { + return this.auditTrailSearchTemplateData.actionNames.filter((action: any) => action.toLowerCase().includes(actionName.toLowerCase())); + } + + /** + * Filters entities. + * @param {string} entityName Entity name to filter action by. + * @returns {any} Filtered entities. + */ + private filterEntityAutocompleteData(entityName: string): any { + return this.auditTrailSearchTemplateData.entityNames.filter((entity: any) => entity.toLowerCase().includes(entityName.toLowerCase())); + } + + /** + * Generates the CSV file of Audit Trails Data. + */ + downloadCSV() { + const dateFormat = 'yyyy-MM-dd'; + const replacer = (key: any, value: any) => value === undefined ? '' : value; + const header = ['ID', 'Resource ID', 'Status', 'Office', 'Made On', 'Maker', 'Checked On', 'Checker', 'Entity', 'Action', 'Client']; + const headerCode = ['id', 'resourceId', 'processingResult', 'officeName', 'madeOnDate', 'maker', 'checkedOnDate', 'checker', 'entityName', 'actionName', 'clientName']; + this.systemService.getAuditTrails(this.filterAuditTrailsBy, this.sort.active ? this.sort.active : '', this.sort.direction, 0, -1).subscribe((response: any) => { + if (response !== undefined) { + let csv = response.pageItems.map((row: any) => headerCode.map(fieldName => (fieldName === 'madeOnDate' || fieldName === 'checkedOnDate') && (JSON.stringify(row[fieldName], replacer) !== '""') + ? this.datePipe.transform(JSON.stringify(row[fieldName], replacer), dateFormat) + : JSON.stringify(row[fieldName], replacer))); + csv.unshift(`data:text/csv;charset=utf-8,${header.join()}`); + csv = csv.join('\r\n'); + const link = document.createElement('a'); + link.setAttribute('href', encodeURI(csv)); + link.setAttribute('download', 'Audit Trails.csv'); + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + }); + } + + /** + * Gets the date from the passed timestamp. + * + * TODO: Update once language and date settings are setup. + * + * @param {any} timestamp Timestamp from which date is to be extracted. + */ + private getDate(timestamp: any) { + const dateFormat = 'yyyy-MM-dd'; + return this.datePipe.transform(timestamp, dateFormat); + } + +} diff --git a/src/app/system/audit-trails/view-audit/audit-trail.resolver.ts b/src/app/system/audit-trails/view-audit/audit-trail.resolver.ts new file mode 100644 index 000000000..7debb6bd0 --- /dev/null +++ b/src/app/system/audit-trails/view-audit/audit-trail.resolver.ts @@ -0,0 +1,31 @@ +/** Angular Imports */ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot } from '@angular/router'; + +/** rxjs Imports */ +import { Observable } from 'rxjs'; + +/** Custom Services */ +import { SystemService } from '../../system.service'; + +/** + * Audit Trail data resolver. + */ +@Injectable() +export class AuditTrailResolver implements Resolve { + + /** + * @param {SystemService} systemService System service. + */ + constructor(private systemService: SystemService) {} + + /** + * Returns the Audit Trail data. + * @returns {Observable} + */ + resolve(route: ActivatedRouteSnapshot): Observable { + const auditTrailId = route.paramMap.get('id'); + return this.systemService.getAuditTrail(auditTrailId); + } + +} diff --git a/src/app/system/audit-trails/view-audit/view-audit.component.html b/src/app/system/audit-trails/view-audit/view-audit.component.html new file mode 100644 index 000000000..3ed5d5ade --- /dev/null +++ b/src/app/system/audit-trails/view-audit/view-audit.component.html @@ -0,0 +1,120 @@ +
+ +
+ + + + + +
+ +
+ ID +
+ +
+ {{ auditTrailData.id }} +
+ +
+ Status +
+ +
+ {{ auditTrailData.processingResult }} +
+ +
+ User +
+ +
+ {{ auditTrailData.maker }} +
+ +
+ Action +
+ +
+ {{ auditTrailData.actionName }} +
+ +
+ Entity +
+ +
+ {{ auditTrailData.entityName }} +
+ +
+ Resource ID +
+ +
+ {{ auditTrailData.resourceId }} +
+ +
+ Date +
+ +
+ {{ auditTrailData.madeOnDate | date }} +
+ +
+ Office +
+ +
+ {{ auditTrailData.officeName }} +
+ +
+ Savings Account No +
+ +
+ {{ auditTrailData.savingsAccountNo }} +
+ +
+ Group Level +
+ +
+ {{ auditTrailData.groupLevelName }} +
+ +
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + +
Command {{ auditTrailCommand.command }} Command Value {{ auditTrailCommand.commandValue }}
+ +
+ +
diff --git a/src/app/system/audit-trails/view-audit/view-audit.component.scss b/src/app/system/audit-trails/view-audit/view-audit.component.scss new file mode 100644 index 000000000..ed2491a72 --- /dev/null +++ b/src/app/system/audit-trails/view-audit/view-audit.component.scss @@ -0,0 +1,14 @@ +.content { + div { + margin: 1rem 0; + word-wrap: break-word; + } +} + +table { + width: 100%; +} + +.column-value-wrapper { + word-break: break-all; +} diff --git a/src/app/system/audit-trails/view-audit/view-audit.component.spec.ts b/src/app/system/audit-trails/view-audit/view-audit.component.spec.ts new file mode 100644 index 000000000..5cbbd9f1f --- /dev/null +++ b/src/app/system/audit-trails/view-audit/view-audit.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ViewAuditComponent } from './view-audit.component'; + +describe('ViewAuditComponent', () => { + let component: ViewAuditComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ ViewAuditComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ViewAuditComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/system/audit-trails/view-audit/view-audit.component.ts b/src/app/system/audit-trails/view-audit/view-audit.component.ts new file mode 100644 index 000000000..73371c0ba --- /dev/null +++ b/src/app/system/audit-trails/view-audit/view-audit.component.ts @@ -0,0 +1,64 @@ +/** Angular Imports */ +import { Component, OnInit, ViewChild } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { MatTableDataSource, MatPaginator, MatSort } from '@angular/material'; + +/** + * View Audit Component. + */ +@Component({ + selector: 'mifosx-view-audit', + templateUrl: './view-audit.component.html', + styleUrls: ['./view-audit.component.scss'] +}) +export class ViewAuditComponent implements OnInit { + + /** Audit Trail Data. */ + auditTrailData: any; + /** Columns to be displayed in audit trail table. */ + displayedColumns: string[] = ['command', 'commandValue']; + /** Data source for audit trail table. */ + dataSource: MatTableDataSource; + + /** Paginator for audit trails table. */ + @ViewChild(MatPaginator) paginator: MatPaginator; + /** Sorter for audit trails table. */ + @ViewChild(MatSort) sort: MatSort; + + /** + * Retrieves the audit trail data from `resolve`. + * @param {ActivatedRoute} route Activated Route. + */ + constructor(private route: ActivatedRoute) { + this.route.data.subscribe((data: { auditTrail: any }) => { + this.auditTrailData = data.auditTrail; + }); + } + + /** + * Sets the audit trail commands table. + */ + ngOnInit() { + this.setAuditTrailCommands(); + } + + /** + * Initalizes Audit Trail Commands Data. + */ + get auditTrailCommandsData() { + return Object.entries(JSON.parse(this.auditTrailData.commandAsJson)) + .map( + ([key, value]) => ({ command: key, commandValue: value }) + ); + } + + /** + * Initializes the data source, paginator and sorter for audit trail commands table. + */ + setAuditTrailCommands() { + this.dataSource = new MatTableDataSource(this.auditTrailCommandsData); + this.dataSource.paginator = this.paginator; + this.dataSource.sort = this.sort; + } + +} diff --git a/src/app/system/system-routing.module.ts b/src/app/system/system-routing.module.ts index e4e9d6578..f04a6e04b 100644 --- a/src/app/system/system-routing.module.ts +++ b/src/app/system/system-routing.module.ts @@ -42,6 +42,8 @@ import { ManageReportsComponent } from './manage-reports/manage-reports.componen import { EditReportComponent } from './manage-reports/edit-report/edit-report.component'; import { CreateReportComponent } from './manage-reports/create-report/create-report.component'; import { ViewReportComponent } from './manage-reports/view-report/view-report.component'; +import { AuditTrailsComponent } from './audit-trails/audit-trails.component'; +import { ViewAuditComponent } from './audit-trails/view-audit/view-audit.component'; /** Custom Resolvers */ import { CodesResolver } from './codes/codes.resolver'; @@ -64,6 +66,8 @@ import { NotificationConfigurationResolver } from './external-services/notificat import { AccountNumberPreferencesResolver } from './account-number-preferences/account-number-preferences.resolver'; import { AccountNumberPreferencesTemplateResolver } from './account-number-preferences/create-account-number-preference/account-number-preferences-template.resolver'; import { AccountNumberPreferenceResolver } from './account-number-preferences/view-account-number-preference/account-number-preference.resolver'; +import { AuditTrailSearchTemplateResolver } from './audit-trails/audit-trail-search-template.resolver'; +import { AuditTrailResolver } from './audit-trails/view-audit/audit-trail.resolver'; import { ReportsResolver } from './manage-reports/reports.resolver'; import { ReportResolver } from './manage-reports/report.resolver'; import { ReportTemplateResolver } from './manage-reports/report-template.resolver'; @@ -439,6 +443,27 @@ const routes: Routes = [ ] } ] + }, + { + path: 'audit-trails', + data: { title: extract('Audit Trails'), breadcrumb: 'Audit Trails' }, + children: [ + { + path: '', + component: AuditTrailsComponent, + resolve: { + auditTrailSearchTemplate: AuditTrailSearchTemplateResolver + } + }, + { + path: ':id', + component: ViewAuditComponent, + data: { title: extract('View Audit'), routeParamBreadcrumb: 'id' }, + resolve: { + auditTrail: AuditTrailResolver + } + } + ] } ] } @@ -471,7 +496,9 @@ const routes: Routes = [ AccountNumberPreferenceResolver, ReportsResolver, ReportResolver, - ReportTemplateResolver + ReportTemplateResolver, + AuditTrailSearchTemplateResolver, + AuditTrailResolver ] }) export class SystemRoutingModule { } diff --git a/src/app/system/system.component.html b/src/app/system/system.component.html index 98025aa14..8a87b3665 100644 --- a/src/app/system/system.component.html +++ b/src/app/system/system.component.html @@ -72,7 +72,7 @@ - + diff --git a/src/app/system/system.module.ts b/src/app/system/system.module.ts index 3465ca457..007851fcd 100644 --- a/src/app/system/system.module.ts +++ b/src/app/system/system.module.ts @@ -39,6 +39,8 @@ import { ManageReportsComponent } from './manage-reports/manage-reports.componen import { ViewReportComponent } from './manage-reports/view-report/view-report.component'; import { CreateReportComponent } from './manage-reports/create-report/create-report.component'; import { EditReportComponent } from './manage-reports/edit-report/edit-report.component'; +import { AuditTrailsComponent } from './audit-trails/audit-trails.component'; +import { ViewAuditComponent } from './audit-trails/view-audit/view-audit.component'; import { EditHookComponent } from './manage-hooks/edit-hook/edit-hook.component'; import { ViewHookComponent } from './manage-hooks/view-hook/view-hook.component'; import { CreateHookComponent } from './manage-hooks/create-hook/create-hook.component'; @@ -92,7 +94,9 @@ import { AddEventDialogComponent } from './manage-hooks/add-event-dialog/add-eve ViewHookComponent, CreateHookComponent, AddEventDialogComponent, - ColumnDialogComponent + ColumnDialogComponent, + AuditTrailsComponent, + ViewAuditComponent ], entryComponents: [ ReportParameterDialogComponent, diff --git a/src/app/system/system.service.ts b/src/app/system/system.service.ts index b82d60302..064c052a9 100644 --- a/src/app/system/system.service.ts +++ b/src/app/system/system.service.ts @@ -352,4 +352,43 @@ export class SystemService { return this.http.delete(`/reports/${reportId}`); } + /** + * @param {any} filterBy Properties by which entries should be filtered. + * @param {string} orderBy Property by which entries should be sorted. + * @param {string} sortOrder Sort order: ascending or descending. + * @param {number} offset Page offset. + * @param {number} limit Number of entries within the page. + * @returns {Observable} Audit Trails. + */ + getAuditTrails(filterBy: any, orderBy: string, sortOrder: string, offset: number, limit: number): Observable { + let httpParams = new HttpParams() + .set('offset', offset.toString()) + .set('limit', limit.toString()) + .set('sortOrder', sortOrder) + .set('orderBy', orderBy) + .set('paged', 'true'); + // filterBy: actionName, entityName, resourceId, makerId, makerDateTimeFrom, makerDateTimeTo, checkerId, checkerDateTimeFrom, checkerDateTimeTo, processingResult + filterBy.forEach(function (filter: any) { + if (filter.value !== '') { + httpParams = httpParams.set(filter.type, filter.value); + } + }); + return this.http.get('/audits', { params: httpParams }); + } + + /** + * @param {string} auditTrailId Audit Trail ID. + * @returns {Observable} + */ + getAuditTrail(auditTrailId: string): Observable { + return this.http.get(`/audits/${auditTrailId}`); + } + + /** + * @returns {Observable} Audit Trail Search Template. + */ + getAuditTrailSearchTemplate(): Observable { + return this.http.get('/audits/searchtemplate'); + } + }