Manage scheduler and cob jobs
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
<div class="container">
|
||||
|
||||
<mat-card>
|
||||
|
||||
<form [formGroup]="jobForm" (ngSubmit)="submit()">
|
||||
|
||||
<mat-card-content>
|
||||
|
||||
<div fxLayout="column">
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Job Name</mat-label>
|
||||
<input matInput required formControlName="displayName">
|
||||
<mat-error *ngIf="jobForm.controls.displayName.hasError('required')">
|
||||
Job Name is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-form-field>
|
||||
<mat-label>Cron Expression</mat-label>
|
||||
<input matInput required formControlName="cronExpression">
|
||||
<mat-error *ngIf="jobForm.controls.cronExpression.hasError('required')">
|
||||
Cron Expression is <strong>required</strong>
|
||||
</mat-error>
|
||||
</mat-form-field>
|
||||
|
||||
<mat-checkbox labelPosition="before" formControlName="active">
|
||||
Is job active?
|
||||
</mat-checkbox>
|
||||
|
||||
</div>
|
||||
|
||||
<mat-card-actions fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" fxLayoutGap="5px">
|
||||
<button type="button" mat-raised-button [routerLink]="['../']">Cancel</button>
|
||||
<button mat-raised-button color="primary" [disabled]="!jobForm.valid" *mifosxHasPermission="'UPDATE_SCHEDULER'">Submit</button>
|
||||
</mat-card-actions>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
</form>
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
.container {
|
||||
max-width: 37rem;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { EditSchedulerJobComponent } from './edit-scheduler-job.component';
|
||||
|
||||
describe('EditSchedulerJobComponent', () => {
|
||||
let component: EditSchedulerJobComponent;
|
||||
let fixture: ComponentFixture<EditSchedulerJobComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ EditSchedulerJobComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(EditSchedulerJobComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FormBuilder, Validators } from '@angular/forms';
|
||||
|
||||
/** Custom Services */
|
||||
import { SystemService } from 'app/system/system.service';
|
||||
|
||||
/**
|
||||
* Edit scheduler job component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-edit-scheduler-job',
|
||||
templateUrl: './edit-scheduler-job.component.html',
|
||||
styleUrls: ['./edit-scheduler-job.component.scss']
|
||||
})
|
||||
export class EditSchedulerJobComponent implements OnInit {
|
||||
|
||||
/** Job Data. */
|
||||
jobData: any;
|
||||
/** Job Form. */
|
||||
jobForm: any;
|
||||
|
||||
/**
|
||||
* Retrieves the selected job data from `resolve`.
|
||||
* @param {FormBuilder} formBuilder Form Builder.
|
||||
* @param {SystemService} systemService System Service.
|
||||
* @param {Router} router Router for navigation.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private systemService: SystemService,
|
||||
private router: Router,
|
||||
private formBuilder: FormBuilder ) {
|
||||
this.route.data.subscribe((data: { jobSelected: any }) => {
|
||||
this.jobData = data.jobSelected;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets job form.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.createJobForm();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and sets job form.
|
||||
*/
|
||||
createJobForm() {
|
||||
this.jobForm = this.formBuilder.group({
|
||||
'displayName': [this.jobData.displayName, Validators.required],
|
||||
'cronExpression': [this.jobData.cronExpression, Validators.required],
|
||||
'active': [this.jobData.active]
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the edit job form.
|
||||
*/
|
||||
submit() {
|
||||
this.systemService.updateScheduler(this.jobData.jobId, this.jobForm.value)
|
||||
.subscribe(() => {
|
||||
this.router.navigate(['../'], { relativeTo: this.route });
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
* Edit Scheduler Job data resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ManageSchedulerJobResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {SystemService} systemService System service.
|
||||
*/
|
||||
constructor(private systemService: SystemService) {}
|
||||
|
||||
/**
|
||||
* Returns the edit scheduler jobs data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const jobId = route.paramMap.get('id');
|
||||
return this.systemService.getSelectedJob(jobId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayoutGap="20px">
|
||||
<div #schedulerStatus>
|
||||
<h2 class="no-m">Scheduler Status: {{ schedulerActive ? 'Active' : 'Inactive' }}</h2>
|
||||
</div>
|
||||
<button mat-raised-button class="suspend">
|
||||
<fa-icon icon="times-circle"></fa-icon>
|
||||
Suspend
|
||||
</button>
|
||||
<button mat-raised-button class="activate" (click)="activateScheduler()" *ngIf="!schedulerActive">
|
||||
<fa-icon icon="times-circle"></fa-icon>
|
||||
Activate
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<div #filter fxLayout="row" fxLayoutGap="20px">
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Filter</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="container space-top" fxLayout="row" fxLayoutGap="20px">
|
||||
<button mat-raised-button color="primary" (click)="runSelectedJobs()">
|
||||
<fa-icon icon="play"></fa-icon>
|
||||
Run Selected Jobs
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div #jobsTable class="mat-elevation-z8 space-top">
|
||||
|
||||
<table mat-table [dataSource]="dataSource" matSort>
|
||||
|
||||
<ng-container matColumnDef="select">
|
||||
<th mat-header-cell *matHeaderCellDef>
|
||||
<mat-checkbox (change)="$event ? masterToggle() : null" [checked]="selection.hasValue() && isAllSelected()"
|
||||
[indeterminate]="selection.hasValue() && !isAllSelected()">
|
||||
</mat-checkbox>
|
||||
</th>
|
||||
<td mat-cell *matCellDef="let row">
|
||||
<mat-checkbox (click)="$event.stopPropagation()" (change)="$event ? selection.toggle(row) : null"
|
||||
[checked]="selection.isSelected(row)">
|
||||
</mat-checkbox>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="displayName">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Name </th>
|
||||
<td mat-cell *matCellDef="let job"> {{ job.displayName }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="nextRunTime">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Next Run </th>
|
||||
<td mat-cell *matCellDef="let job"> {{ job.nextRunTime }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="previousRunTime">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Previous Run </th>
|
||||
<td mat-cell *matCellDef="let job"><span *ngIf="job.lastRunHistory">{{ job.lastRunHistory.jobRunStartTime
|
||||
}}</span></td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="previousRunStatus">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Previous Run Status </th>
|
||||
<td mat-cell *matCellDef="let job" class="center">
|
||||
<div *ngIf="job.lastRunHistory">
|
||||
<fa-icon *ngIf="job.lastRunHistory.status === 'success'" class="success" matTooltip="Successful"
|
||||
matTooltipPosition="right" icon="check-circle" size="lg"></fa-icon>
|
||||
<fa-icon *ngIf="!(job.lastRunHistory.status == 'success')" class="fail" matTooltip="Failed"
|
||||
matTooltipPosition="right" icon="times-circle" size="lg"></fa-icon>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="currentlyRunning">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Currently Running </th>
|
||||
<td mat-cell *matCellDef="let job" class="center">
|
||||
<div [className]="job.currentlyRunning === true ? 'currently-running' : 'not-currently-running'">
|
||||
<fa-icon matTooltip="{{ job.currentlyRunning === true ? 'Yes' : 'No' }}" matTooltipPosition="right"
|
||||
icon="circle" size="lg"></fa-icon>
|
||||
</div>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="errorLog">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Error Log </th>
|
||||
<td mat-cell *matCellDef="let job" class="center">
|
||||
<button mat-icon-button class="errorlog"
|
||||
*ngIf="!(job.lastRunHistory && job.lastRunHistory.status == 'success')" matTooltip="Error Log"
|
||||
matTooltipPosition="right">
|
||||
<fa-icon icon="exclamation-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" [routerLink]="[row.jobId]" class="select-row"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="[50, 100]" showFirstLastButtons></mat-paginator>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="container space-top" fxLayout="row" fxLayoutGap="20px">
|
||||
<button mat-raised-button color="primary" (click)="runSelectedJobs()">
|
||||
<fa-icon icon="play"></fa-icon>
|
||||
Run Selected Jobs<ng-template #templateSchedulerJobs let-data let-popover="popover">
|
||||
<h2>Template</h2>
|
||||
<p>This is the template I am trying to use. Is that fine.</p>
|
||||
<button (click)="popover.close();nextStepSchedulerJobs()">Next</button>
|
||||
</ng-template>
|
||||
</button>
|
||||
<button mat-raised-button color="primary" (click)="refresh()">
|
||||
<fa-icon icon="sync"></fa-icon>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ng-template #templateSchedulerStatus let-data let-popover="popover">
|
||||
<h4>Scheduler Status. Button will be used to change the status.</h4>
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<button mat-raised-button color="warn" (click)="popover.close();configurationWizardService.closeConfigWizard()">Close</button>
|
||||
<button mat-raised-button color="primary" (click)="popover.close();previousStep()">Back</button>
|
||||
<button mat-raised-button color="primary" (click)="popover.close();showPopover(templateFilter, filter, 'bottom', true)">Next</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #templateFilter let-data let-popover="popover">
|
||||
<h4>Search bar to filter jobs.</h4>
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<button mat-raised-button color="warn" (click)="popover.close();configurationWizardService.closeConfigWizard()">Close</button>
|
||||
<button mat-raised-button color="primary" (click)="popover.close();showPopover(templateSchedulerStatus,schedulerStatus, 'bottom', true)">Back</button>
|
||||
<button mat-raised-button color="primary" (click)="popover.close();showPopover(templateJobsTable,jobsTable, 'top', true)">Next</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #templateJobsTable let-data let-popover="popover">
|
||||
<h4>List of all scheduled batch jobs <a href="https://mifosforge.jira.com/wiki/spaces/docs/pages/67895356/Manage+Scheduler+Jobs" target="_blank">Schedular Jobs</a></h4>
|
||||
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column">
|
||||
<button mat-raised-button color="warn" (click)="popover.close();configurationWizardService.closeConfigWizard()">Close</button>
|
||||
<button mat-raised-button color="primary" (click)="popover.close();showPopover(templateFilter, filter, 'bottom', true)">Back</button>
|
||||
<button mat-raised-button color="primary" (click)="popover.close();nextStep()">Next</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
@@ -0,0 +1,41 @@
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
.select-row:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #32cd32;
|
||||
}
|
||||
|
||||
.fail {
|
||||
color: #f44366;
|
||||
}
|
||||
|
||||
.currently-running {
|
||||
color: #32cd32;
|
||||
}
|
||||
|
||||
.not-currently-running {
|
||||
color: #f44366;
|
||||
}
|
||||
|
||||
.errorlog {
|
||||
color: #ffa726;
|
||||
}
|
||||
|
||||
.suspend {
|
||||
background: #32cd32;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.activate {
|
||||
background: #f44366;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.space-top {
|
||||
margin-top: 30px;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ManageSchedulerJobsComponent } from './manage-scheduler-jobs.component';
|
||||
|
||||
describe('ManageSchedulerJobsComponent', () => {
|
||||
let component: ManageSchedulerJobsComponent;
|
||||
let fixture: ComponentFixture<ManageSchedulerJobsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ManageSchedulerJobsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ManageSchedulerJobsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,237 @@
|
||||
/** Angular Imports */
|
||||
import { Component, OnInit, TemplateRef, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { SelectionModel } from '@angular/cdk/collections';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { of } from 'rxjs';
|
||||
import { SystemService } from '../../system.service';
|
||||
|
||||
/** Custom Services */
|
||||
import { PopoverService } from '../../../configuration-wizard/popover/popover.service';
|
||||
import { ConfigurationWizardService } from '../../../configuration-wizard/configuration-wizard.service';
|
||||
|
||||
/** Custom Dialog Component */
|
||||
import { NextStepDialogComponent } from '../../../configuration-wizard/next-step-dialog/next-step-dialog.component';
|
||||
|
||||
/**
|
||||
* Manage scheduler jobs component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-manage-scheduler-jobs',
|
||||
templateUrl: './manage-scheduler-jobs.component.html',
|
||||
styleUrls: ['./manage-scheduler-jobs.component.scss']
|
||||
})
|
||||
export class ManageSchedulerJobsComponent implements OnInit, AfterViewInit {
|
||||
|
||||
/** Jobs data. */
|
||||
jobData: any;
|
||||
/** Scheduler data */
|
||||
schedulerData: any;
|
||||
/** Columns to be displayed in manage scheduler jobs table. */
|
||||
displayedColumns: string[] = ['select', 'displayName', 'nextRunTime', 'previousRunTime', 'previousRunStatus', 'currentlyRunning', 'errorLog'];
|
||||
/** Data source for manage scheduler jobs table. */
|
||||
dataSource: MatTableDataSource<any>;
|
||||
/** Initialize Selection */
|
||||
selection = new SelectionModel<any>(true, []);
|
||||
/** Scheduler Status */
|
||||
schedulerActive = false;
|
||||
/** Jobs counter */
|
||||
jobsCounter: any = 0;
|
||||
|
||||
/** Paginator for table. */
|
||||
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
||||
/** Sorter for manage scheduler jobs table. */
|
||||
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
||||
|
||||
/* Reference of scheduler status */
|
||||
@ViewChild('schedulerStatus', { static: true }) schedulerStatus: ElementRef<any>;
|
||||
/* Template for popover on scheduler status */
|
||||
@ViewChild('templateSchedulerStatus', { static: true }) templateSchedulerStatus: TemplateRef<any>;
|
||||
/* Reference of jobs table */
|
||||
@ViewChild('jobsTable', { static: true }) jobsTable: ElementRef<any>;
|
||||
/* Template for popover on jobs table */
|
||||
@ViewChild('templateJobsTable') templateJobsTable: TemplateRef<any>;
|
||||
|
||||
/**
|
||||
* Retrieves the scheduler jobs data from `resolve`.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
* @param {Matdialog} matdialog Matdialog.
|
||||
* @param {Router} router Router for navigation.
|
||||
* @param {ConfigurationWizardService} configurationWizardService ConfigurationWizard Service.
|
||||
* @param {PopoverService} popoverService PopoverService.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private systemService: SystemService,
|
||||
private router: Router,
|
||||
private dialog: MatDialog,
|
||||
private configurationWizardService: ConfigurationWizardService,
|
||||
private popoverService: PopoverService) {
|
||||
this.route.data.subscribe((data: { jobsScheduler: any }) => {
|
||||
this.jobData = data.jobsScheduler[0];
|
||||
this.schedulerData = data.jobsScheduler[1];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters data in manage scheduler jobs table based on passed value.
|
||||
* @param {string} filterValue Value to filter data.
|
||||
*/
|
||||
applyFilter(filterValue: string) {
|
||||
this.dataSource.filter = filterValue.trim().toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the number of selected elements matches the total number of rows.
|
||||
*/
|
||||
isAllSelected() {
|
||||
const numSelected = this.selection.selected.length;
|
||||
return numSelected === this.jobsCounter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects all rows if they are not all selected; otherwise clear selection.
|
||||
*/
|
||||
masterToggle() {
|
||||
this.isAllSelected() ?
|
||||
this.selection.clear() :
|
||||
this.dataSource.data.forEach(row => this.selection.select(row));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the manage scheduler jobs table.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.setJobs();
|
||||
this.getScheduler();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the data source, paginator and sorter for manage scheduler jobs table.
|
||||
*/
|
||||
setJobs() {
|
||||
this.systemService.getJobs()
|
||||
.subscribe((jobData: any) => {
|
||||
this.dataSource = new MatTableDataSource(jobData);
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
this.jobsCounter = jobData.length;
|
||||
this.selection.clear();
|
||||
this.dataSource.sortingDataAccessor = (item, property) => {
|
||||
switch (property) {
|
||||
case 'previousRunStatus': return item.lastRunHistory.status;
|
||||
case 'errorLog': return item.lastRunHistory.status;
|
||||
case 'previousRunTime': return new Date(item.lastRunHistory.jobRunStartTime);
|
||||
case 'nextRunTime': return new Date(item.nextRunTime);
|
||||
default: return item[property];
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
getScheduler() {
|
||||
this.systemService.getScheduler()
|
||||
.subscribe((schedulerData: any) => {
|
||||
this.schedulerData = schedulerData;
|
||||
this.schedulerActive = this.schedulerData.active;
|
||||
});
|
||||
}
|
||||
|
||||
suspendScheduler(): void {
|
||||
this.systemService.runCommandOnScheduler('stop')
|
||||
.subscribe(() => {
|
||||
this.getScheduler();
|
||||
});
|
||||
}
|
||||
|
||||
activateScheduler(): void {
|
||||
this.systemService.runCommandOnScheduler('start')
|
||||
.subscribe(() => {
|
||||
this.getScheduler();
|
||||
});
|
||||
}
|
||||
|
||||
runSelectedJobs(): void {
|
||||
this.selection.selected.forEach((job) => {
|
||||
this.systemService.runSelectedJob(job.jobId);
|
||||
});
|
||||
}
|
||||
|
||||
refresh(): void {
|
||||
this.setJobs();
|
||||
}
|
||||
/**
|
||||
* Popover function
|
||||
* @param template TemplateRef<any>.
|
||||
* @param target HTMLElement | ElementRef<any>.
|
||||
* @param position String.
|
||||
* @param backdrop Boolean.
|
||||
*/
|
||||
showPopover(template: TemplateRef<any>, target: HTMLElement | ElementRef<any>, position: string, backdrop: boolean): void {
|
||||
setTimeout(() => this.popoverService.open(template, target, position, backdrop, {}), 200);
|
||||
}
|
||||
|
||||
/**
|
||||
* To show popover.
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
if (this.configurationWizardService.showSchedulerJobsPage === true) {
|
||||
setTimeout(() => {
|
||||
this.showPopover(this.templateSchedulerStatus, this.schedulerStatus.nativeElement, 'bottom', true);
|
||||
});
|
||||
}
|
||||
if (this.configurationWizardService.showSchedulerJobsList === true) {
|
||||
setTimeout(() => {
|
||||
this.showPopover(this.templateJobsTable, this.jobsTable.nativeElement, 'top', true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens Dialog for next step.
|
||||
*/
|
||||
nextStep() {
|
||||
this.configurationWizardService.showSchedulerJobsPage = false;
|
||||
this.configurationWizardService.showSchedulerJobsList = false;
|
||||
this.openNextStepDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Previous Step (Scheduler Jobs) Configuration Wizard.
|
||||
*/
|
||||
previousStep() {
|
||||
this.configurationWizardService.showSchedulerJobsPage = false;
|
||||
this.configurationWizardService.showSchedulerJobsList = false;
|
||||
this.configurationWizardService.showSchedulerJobs = true;
|
||||
this.router.navigate(['/system']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Next Step (Accounting) Dialog Configuration Wizard.
|
||||
*/
|
||||
openNextStepDialog() {
|
||||
const nextStepDialogRef = this.dialog.open( NextStepDialogComponent, {
|
||||
data: {
|
||||
nextStepName: 'Setup Accounting',
|
||||
previousStepName: 'System',
|
||||
stepPercentage: 60
|
||||
},
|
||||
});
|
||||
nextStepDialogRef.afterClosed().subscribe((response: { nextStep: boolean }) => {
|
||||
if (response.nextStep) {
|
||||
this.configurationWizardService.showSchedulerJobsPage = false;
|
||||
this.configurationWizardService.showSchedulerJobsList = false;
|
||||
this.configurationWizardService.showChartofAccounts = true;
|
||||
this.router.navigate(['/accounting']);
|
||||
} else {
|
||||
this.configurationWizardService.showSchedulerJobsPage = false;
|
||||
this.configurationWizardService.showSchedulerJobsList = false;
|
||||
this.router.navigate(['/home']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/** Angular Imports */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Resolve } from '@angular/router';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { Observable, forkJoin } from 'rxjs';
|
||||
|
||||
/** Custom Services */
|
||||
import { SystemService } from '../system.service';
|
||||
|
||||
/**
|
||||
* Manage Scheduler Jobs data resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ManageSchedulerJobsResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {SystemService} systemService System service.
|
||||
*/
|
||||
constructor(private systemService: SystemService) {}
|
||||
|
||||
/**
|
||||
* Returns the manage scheduler jobs data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve() {
|
||||
return forkJoin([
|
||||
this.systemService.getJobs(),
|
||||
this.systemService.getScheduler()
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<div class="container">
|
||||
|
||||
<div fxLayout="row" fxLayoutGap="20px">
|
||||
|
||||
<mat-form-field fxFlex>
|
||||
<mat-label>Filter By Version</mat-label>
|
||||
<input matInput (keyup)="applyFilter($event.target.value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<div class="mat-elevation-z8">
|
||||
|
||||
<table mat-table [dataSource]="dataSource" matSort>
|
||||
|
||||
<ng-container matColumnDef="version" >
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Version </th>
|
||||
<td mat-cell *matCellDef="let jobHistoryData"> {{ jobHistoryData.version }} </td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="run_start_time">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Start time </th>
|
||||
<td mat-cell *matCellDef="let jobHistoryData">{{ jobHistoryData.jobRunStartTime | datetimeFormat }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="run_end_time">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> End time </th>
|
||||
<td mat-cell *matCellDef="let jobHistoryData">{{ jobHistoryData.jobRunEndTime | datetimeFormat }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="status">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Status </th>
|
||||
<td mat-cell *matCellDef="let jobHistoryData">
|
||||
<fa-icon *ngIf="jobHistoryData.status === 'success'" class="success" matTooltip="Successful" matTooltipPosition="right" icon="check-circle" size="lg"></fa-icon>
|
||||
<fa-icon *ngIf="!(jobHistoryData.status == 'success')" class="fail" matTooltip="Failed" matTooltipPosition="right" icon="times-circle" size="lg"></fa-icon>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="run_type">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Run Type </th>
|
||||
<td mat-cell *matCellDef="let jobHistoryData">{{ jobHistoryData.triggerType }}</td>
|
||||
</ng-container>
|
||||
|
||||
<ng-container matColumnDef="error_log">
|
||||
<th mat-header-cell *matHeaderCellDef mat-sort-header> Error Log </th>
|
||||
<td mat-cell *matCellDef="let jobHistoryData">
|
||||
<button mat-icon-button class="errorlog" *ngIf="!(jobHistoryData.status == 'success')" matTooltip="Error Log" matTooltipPosition="right" (click)="openError(jobHistoryData.version)">
|
||||
<fa-icon icon="exclamation-circle" size="lg"></fa-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;" class="select-row"></tr>
|
||||
|
||||
</table>
|
||||
|
||||
<mat-paginator [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
table {
|
||||
width: 100%;
|
||||
|
||||
.select-row:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #32cd32;
|
||||
}
|
||||
|
||||
.fail {
|
||||
color: #f44366;
|
||||
}
|
||||
|
||||
.currently-running {
|
||||
color: #32cd32;
|
||||
}
|
||||
|
||||
.not-currently-running {
|
||||
color: #f44366;
|
||||
}
|
||||
|
||||
.errorlog {
|
||||
color: #ffa726;
|
||||
}
|
||||
|
||||
.suspend {
|
||||
background: #ffa726;
|
||||
color: #ffffff;
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ViewHistorySchedulerJobComponent } from './view-history-scheduler-job.component';
|
||||
|
||||
describe('ViewHistorySchedulerJobComponent', () => {
|
||||
let component: ViewHistorySchedulerJobComponent;
|
||||
let fixture: ComponentFixture<ViewHistorySchedulerJobComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ViewHistorySchedulerJobComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ViewHistorySchedulerJobComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
/** Angular Imports. */
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ErrorDialogComponent } from 'app/shared/error-dialog/error-dialog.component';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'mifosx-view-history-scheduler-job',
|
||||
templateUrl: './view-history-scheduler-job.component.html',
|
||||
styleUrls: ['./view-history-scheduler-job.component.scss']
|
||||
})
|
||||
export class ViewHistorySchedulerJobComponent implements OnInit {
|
||||
|
||||
/** Job History data. */
|
||||
jobHistoryData: any;
|
||||
/** Columns to be displayed in Scheduler Job History. */
|
||||
displayedColumns: string[] = ['version', 'run_start_time', 'run_end_time', 'status', 'run_type', 'error_log'];
|
||||
/** Data source for Scheduler Job History table. */
|
||||
dataSource: MatTableDataSource<any>;
|
||||
|
||||
/** Paginator for Scheduler Job History table. */
|
||||
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
|
||||
/** Sorter for Scheduler Job History table. */
|
||||
@ViewChild(MatSort, { static: true }) sort: MatSort;
|
||||
|
||||
/**
|
||||
* Retrieves the scheduler Job History data from `resolve`.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute,
|
||||
private dialog: MatDialog,
|
||||
private router: Router ) {
|
||||
this.route.data.subscribe(( data: { jobsSchedulerHistory: any }) => {
|
||||
this.jobHistoryData = data.jobsSchedulerHistory;
|
||||
console.log(this.jobHistoryData);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters data in scheduler Job History table based on passed value.
|
||||
* @param {string} filterValue Value to filter data.
|
||||
*/
|
||||
applyFilter(filterValue: string) {
|
||||
const filterObject = [{
|
||||
id: 'version',
|
||||
value: filterValue
|
||||
}];
|
||||
this.dataSource.filter = JSON.stringify(filterObject);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.setJobHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the data source, paginator and sorter for scheduler Job History table.
|
||||
*/
|
||||
setJobHistory() {
|
||||
this.dataSource = new MatTableDataSource(this.jobHistoryData.pageItems);
|
||||
this.dataSource.paginator = this.paginator;
|
||||
/** Search By Version */
|
||||
this.dataSource.filterPredicate =
|
||||
(data: any, filtersJson: string) => {
|
||||
const matchFilter: any[] = [];
|
||||
const filters = JSON.parse(filtersJson);
|
||||
filters.forEach((filter: any) => {
|
||||
const val = data[filter.id] === null ? '' : data[filter.id];
|
||||
if (filter.value !== '') {
|
||||
matchFilter.push(val === parseInt(filter.value, 10));
|
||||
} else if (filter.value === '') {
|
||||
matchFilter.push(val.toString().toLowerCase().includes(filter.value.toLowerCase()));
|
||||
}
|
||||
});
|
||||
return matchFilter.every(Boolean);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Open Error Dialog.
|
||||
* @param version version Id.
|
||||
*/
|
||||
openError(version: any) {
|
||||
const openErrorLogDialog = this.dialog.open(ErrorDialogComponent, {
|
||||
width: '400px',
|
||||
data: this.jobHistoryData.pageItems.filter( (data: any) => data.version === version )[0].jobRunErrorLog
|
||||
});
|
||||
openErrorLogDialog.afterClosed().subscribe((response: any) => {
|
||||
this.router.navigate(['']);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/** Angular Imports */
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
|
||||
|
||||
/** rxjs Imports */
|
||||
import { Observable, forkJoin } from 'rxjs';
|
||||
|
||||
/** Custom Services */
|
||||
import { SystemService } from '../../../system.service';
|
||||
|
||||
/**
|
||||
* View History Scheduler Jobs data resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ViewHistorySchedulerJobsResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {SystemService} systemService System service.
|
||||
*/
|
||||
constructor(private systemService: SystemService) {}
|
||||
|
||||
/**
|
||||
* Returns the Scheduler Jobs History data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const jobId = route.paramMap.get('id');
|
||||
return this.systemService.getHistoryScheduler(jobId);
|
||||
}
|
||||
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
<div class="container m-b-20" fxLayout="row" fxLayout.lt-md="column" fxLayoutAlign="end" fxLayoutGap="2%">
|
||||
<button mat-raised-button color="primary" [routerLink]="['edit']">
|
||||
<fa-icon icon="edit"></fa-icon>
|
||||
Edit
|
||||
</button>
|
||||
<button mat-raised-button color="primary" [routerLink]="['viewhistory']">
|
||||
View History
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<mat-card>
|
||||
|
||||
<mat-card-content>
|
||||
|
||||
<div fxLayout="row wrap" class="content">
|
||||
|
||||
<div fxFlex="50%" class="mat-body-strong">
|
||||
Job Name
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ jobData.displayName }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="mat-body-strong">
|
||||
Cron Expression
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ jobData.cronExpression }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="mat-body-strong">
|
||||
Is Active Job?
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
{{ jobData.active }}
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%" class="mat-body-strong">
|
||||
Follow
|
||||
</div>
|
||||
|
||||
<div fxFlex="50%">
|
||||
<a href="http://www.cronmaker.com/"> Click Here To Generate Cron Expression </a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</mat-card-content>
|
||||
|
||||
</mat-card>
|
||||
|
||||
</div>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
a{
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 37rem;
|
||||
|
||||
.content {
|
||||
div {
|
||||
margin: 1rem 0;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ViewSchedulerJobComponent } from './view-scheduler-job.component';
|
||||
|
||||
describe('ViewSchedulerJobComponent', () => {
|
||||
let component: ViewSchedulerJobComponent;
|
||||
let fixture: ComponentFixture<ViewSchedulerJobComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ ViewSchedulerJobComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ViewSchedulerJobComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/** Angular Imports */
|
||||
import { Component} from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
/**
|
||||
* View Scheduler Job component.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'mifosx-view-scheduler-job',
|
||||
templateUrl: './view-scheduler-job.component.html',
|
||||
styleUrls: ['./view-scheduler-job.component.scss']
|
||||
})
|
||||
export class ViewSchedulerJobComponent {
|
||||
|
||||
/** Job Data. */
|
||||
jobData: any;
|
||||
|
||||
/**
|
||||
* Retrieves the selected job data from `resolve`.
|
||||
* @param {ActivatedRoute} route Activated Route.
|
||||
*/
|
||||
constructor(private route: ActivatedRoute) {
|
||||
this.route.data.subscribe((data: { selectedJob: any }) => {
|
||||
this.jobData = data.selectedJob;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+31
@@ -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';
|
||||
|
||||
/**
|
||||
* Selected Scheduler Jobs data resolver.
|
||||
*/
|
||||
@Injectable()
|
||||
export class ViewSchedulerJobResolver implements Resolve<Object> {
|
||||
|
||||
/**
|
||||
* @param {SystemService} systemService System service.
|
||||
*/
|
||||
constructor(private systemService: SystemService) {}
|
||||
|
||||
/**
|
||||
* Returns the selected scheduler job data.
|
||||
* @returns {Observable<any>}
|
||||
*/
|
||||
resolve(route: ActivatedRouteSnapshot): Observable<any> {
|
||||
const jobId = route.paramMap.get('id');
|
||||
return this.systemService.getSelectedJob(jobId);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user