Skip to content

Commit

Permalink
System Jobs using confirmation dialog
Browse files Browse the repository at this point in the history
  • Loading branch information
Jose Alberto Hernandez authored and alberto-art3ch committed Dec 6, 2024
1 parent 29cda98 commit c4338b4
Show file tree
Hide file tree
Showing 25 changed files with 332 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<div class="container m-b-20 space-top" fxLayout="row" fxLayoutGap="20px">
<div #schedulerStatus>
<h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="m-l-20 m-r-20">{{ schedulerActive ? ( 'labels.inputs.Active' | translate ) : ('labels.catalogs.Inactive' | translate) }}</span>
<h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="m-l-20 m-r-20">{{ schedulerActive ?
( 'labels.inputs.Active' | translate ) : ('labels.catalogs.Inactive' | translate) }}</span>
</h2>
</div>
<button mat-raised-button class="suspend" (click)="suspendScheduler()" *ngIf="schedulerActive">
Expand All @@ -24,7 +25,7 @@ <h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="
</div>

<div class="space-top" fxLayout="row" fxLayoutGap="20px">
<button mat-raised-button color="primary" (click)="runSelectedJobs()" [disabled]="!isAnyJobSelected()">
<button mat-raised-button color="primary" (click)="openRunSelectedJobsDialog()" [disabled]="!isAnyJobSelected()">
<fa-icon icon="play" class="m-r-10"></fa-icon>
{{'labels.buttons.Run Selected Jobs' | translate}}
</button>
Expand Down Expand Up @@ -75,12 +76,14 @@ <h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'labels.inputs.Previous Run' | translate}} </th>
<td mat-cell *matCellDef="let job">
<div *ngIf="job.lastRunHistory">
<fa-icon *ngIf="job.lastRunHistory.status === 'success'" class="success" [matTooltip]="('tooltips.Successful' | translate)"
matTooltipPosition="right" icon="check-circle" size="lg"></fa-icon>
<fa-icon *ngIf="!(job.lastRunHistory.status === 'success')" class="fail" [matTooltip]="('tooltips.Failed' | translate)"
matTooltipPosition="right" icon="times-circle" size="lg"></fa-icon>
<span *ngIf="job.lastRunHistory" class="m-l-5">
{{ job.lastRunHistory.jobRunStartTime | datetimeFormat}}</span>
<fa-icon *ngIf="job.lastRunHistory.status === 'success'" class="success"
[matTooltip]="('tooltips.Successful' | translate)" matTooltipPosition="right" icon="check-circle"
size="lg"></fa-icon>
<fa-icon *ngIf="!(job.lastRunHistory.status === 'success')" class="fail"
[matTooltip]="('tooltips.Failed' | translate)" matTooltipPosition="right" icon="times-circle"
size="lg"></fa-icon>
<span *ngIf="job.lastRunHistory" class="m-l-5">
{{ job.lastRunHistory.jobRunStartTime | datetimeFormat}}</span>
</div>
</td>
</ng-container>
Expand All @@ -94,8 +97,8 @@ <h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'labels.inputs.Currently Running' | translate}} </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 | yesNo }}" matTooltipPosition="right"
icon="circle" size="lg"></fa-icon>
<fa-icon matTooltip="{{ job.currentlyRunning === true | yesNo }}" matTooltipPosition="right" icon="circle"
size="lg"></fa-icon>
</div>
</td>
</ng-container>
Expand All @@ -104,8 +107,8 @@ <h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="
<th mat-header-cell *matHeaderCellDef mat-sort-header> {{'labels.inputs.Error Log' | translate}} </th>
<td mat-cell *matCellDef="let job" class="center">
<button mat-icon-button class="errorlog" (click)="showErrorLog(job)"
*ngIf="!(job.lastRunHistory && job.lastRunHistory.status === 'success')" matTooltip="{{'labels.inputs.Error Log' | translate}}"
matTooltipPosition="right">
*ngIf="!(job.lastRunHistory && job.lastRunHistory.status === 'success')"
matTooltip="{{'labels.inputs.Error Log' | translate}}" matTooltipPosition="right">
<fa-icon icon="exclamation-circle" size="lg"></fa-icon>
</button>
</td>
Expand All @@ -123,13 +126,9 @@ <h2 class="no-m">{{'labels.heading.Scheduler Status' | translate}}:<span class="
</div>

<div class="container space-top" fxLayout="row" fxLayoutGap="20px">
<button mat-raised-button color="primary" (click)="runSelectedJobs()" [disabled]="!isAnyJobSelected()">
<button mat-raised-button color="primary" (click)="openRunSelectedJobsDialog()" [disabled]="!isAnyJobSelected()">
<fa-icon icon="play" class="m-r-10"></fa-icon>
{{'labels.buttons.Run Selected Jobs' | translate}}<ng-template #templateSchedulerJobs let-data let-popover="popover">
<h2>{{'labels.heading.Template' | translate}}</h2>
<p>{{'labels.text.Template I am trying to use' | translate}}.</p>
<button (click)="popover.close();nextStepSchedulerJobs()">{{'labels.buttons.Next' | translate }}</button>
</ng-template>
{{'labels.buttons.Run Selected Jobs' | translate}}
</button>
<button mat-raised-button color="primary" (click)="refresh()">
<fa-icon icon="sync" class="m-r-10"></fa-icon>
Expand All @@ -141,33 +140,43 @@ <h2>{{'labels.heading.Template' | translate}}</h2>
<h4>{{'labels.heading.Scheduler status' | translate}}.</h4>
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column">
<button mat-raised-button color="warn"
(click)="popover.close();configurationWizardService.closeConfigWizard()">{{'labels.buttons.Close' | translate}}</button>
<button mat-raised-button color="primary" (click)="popover.close();previousStep()">{{'labels.buttons.Back' | translate}}</button>
(click)="popover.close();configurationWizardService.closeConfigWizard()">{{'labels.buttons.Close' |
translate}}</button>
<button mat-raised-button color="primary" (click)="popover.close();previousStep()">{{'labels.buttons.Back' |
translate}}</button>
<button mat-raised-button color="primary"
(click)="popover.close();showPopover(templateFilter, filter, 'bottom', true)">{{'labels.buttons.Next' | translate}}</button>
(click)="popover.close();showPopover(templateFilter, filter, 'bottom', true)">{{'labels.buttons.Next' |
translate}}</button>
</div>
</ng-template>

<ng-template #templateFilter let-data let-popover="popover">
<h4>{{'labels.heading.Search bar to filter jobs' | translate }}.</h4>
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column">
<button mat-raised-button color="warn"
(click)="popover.close();configurationWizardService.closeConfigWizard()">{{'labels.buttons.Close' | translate}}</button>
(click)="popover.close();configurationWizardService.closeConfigWizard()">{{'labels.buttons.Close' |
translate}}</button>
<button mat-raised-button color="primary"
(click)="popover.close();showPopover(templateSchedulerStatus,schedulerStatus, 'bottom', true)">{{'labels.buttons.Back' | translate}}</button>
(click)="popover.close();showPopover(templateSchedulerStatus,schedulerStatus, 'bottom', true)">{{'labels.buttons.Back'
| translate}}</button>
<button mat-raised-button color="primary"
(click)="popover.close();showPopover(templateJobsTable,jobsTable, 'top', true)">{{'labels.buttons.Next' | translate}}</button>
(click)="popover.close();showPopover(templateJobsTable,jobsTable, 'top', true)">{{'labels.buttons.Next' |
translate}}</button>
</div>
</ng-template>

<ng-template #templateJobsTable let-data let-popover="popover">
<h4>{{'labels.heading.List of all scheduled batch jobs' | translate }} <a
href="https://mifosforge.jira.com/wiki/spaces/docs/pages/67895356/Manage+Scheduler+Jobs" target="_blank">{{'labels.heading.Schedular Jobs' | translate}}</a></h4>
href="https://mifosforge.jira.com/wiki/spaces/docs/pages/67895356/Manage+Scheduler+Jobs"
target="_blank">{{'labels.heading.Schedular Jobs' | translate}}</a></h4>
<div fxLayout="row" fxLayoutAlign="end" fxLayoutGap="2%" fxLayout.lt-md="column">
<button mat-raised-button color="warn"
(click)="popover.close();configurationWizardService.closeConfigWizard()">{{'labels.buttons.Close' | translate}}</button>
(click)="popover.close();configurationWizardService.closeConfigWizard()">{{'labels.buttons.Close' |
translate}}</button>
<button mat-raised-button color="primary"
(click)="popover.close();showPopover(templateFilter, filter, 'bottom', true)">{{'labels.buttons.Back' | translate}}</button>
<button mat-raised-button color="primary" (click)="popover.close();nextStep()">{{'labels.buttons.Next' | translate}}</button>
(click)="popover.close();showPopover(templateFilter, filter, 'bottom', true)">{{'labels.buttons.Back' |
translate}}</button>
<button mat-raised-button color="primary" (click)="popover.close();nextStep()">{{'labels.buttons.Next' |
translate}}</button>
</div>
</ng-template>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { NextStepDialogComponent } from '../../../configuration-wizard/next-step
import { CustomParametersPopoverComponent } from './custom-parameters-popover/custom-parameters-popover.component';
import { SchedulerJob } from './models/scheduler-job.model';
import { ErrorLogPopoverComponent } from './error-log-popover/error-log-popover.component';
import { RunSelectedJobsPopoverComponent } from './run-selected-jobs-popover/run-selected-jobs-popover.component';

/**
* Manage scheduler jobs component.
Expand Down Expand Up @@ -264,4 +265,30 @@ export class ManageSchedulerJobsComponent implements OnInit, AfterViewInit {
}
}

/**
* Open Run Selected Jobs Confirmation Dialog
*/
openRunSelectedJobsDialog() {
const dialog = this.dialog.open(RunSelectedJobsPopoverComponent, {
data: {
selectedJobs: this.selection
}
});

dialog.componentInstance.confirmedJobs.subscribe(result => {
if (result) {
const selectedJobs = this.selection.selected;
const confirmedJobIds = result.map(job => job.jobId);

selectedJobs.forEach(job => {
if (!confirmedJobIds.includes(job.jobId)) {
this.selection.deselect(job);
}
});

dialog.close();
}
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<h1 mat-dialog-title>{{'labels.heading.Selected Jobs' | translate}}:</h1>

<mat-dialog-content>
<div class="jobs-container">
<mifosx-run-selected-jobs-table [selectedJobs]="selectedJobs" (confirmedJobs)="runSelectedJobs()">
</mifosx-run-selected-jobs-table>
</div>
</mat-dialog-content>

<mat-list>
<mat-list-item *ngFor="let message of messages" class="message" ngClass]="{'green' : message.status}">
{{message.message}}
</mat-list-item>
</mat-list>
<mat-dialog-actions>
<button mat-raised-button color="primary" (click)="runSelectedJobs()">
<fa-icon icon="play" class="m-r-10"></fa-icon>
{{'labels.buttons.Confirm' | translate}}
</button>
<button mat-raised-button color="warn" [mat-dialog-close]="{ show: 0 }">
{{'labels.buttons.Cancel' | translate}}
</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.message {
height: auto;
font-weight: 500;
color: #f44366;
}

.message.green {
color: #32cd32;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { RunSelectedJobsPopoverComponent } from './run-selected-jobs-popover.component';

describe('RunSelectedJobsPopoverComponent', () => {
let component: RunSelectedJobsPopoverComponent;
let fixture: ComponentFixture<RunSelectedJobsPopoverComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RunSelectedJobsPopoverComponent ]
})
.compileComponents();

fixture = TestBed.createComponent(RunSelectedJobsPopoverComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { SelectionModel } from '@angular/cdk/collections';
import {
Component, EventEmitter, Inject, OnInit, Output, QueryList,
ViewChildren
} from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { SystemService } from 'app/system/system.service';
import { RunSelectedJobsTableComponent } from './run-selected-jobs-table/run-selected-jobs-table.component';

interface SelectedJobsDataType {
selectedJobs: SelectionModel<JobDataType>;
}

export interface JobDataType {
active: boolean;
cronExpression: string;
currentlyRunning: boolean;
displayName: string;
jobId: number;
lastRunHistory: {
jobRunEndTime: string;
jobRunStartTime: string;
status: string;
triggerType: string;
version: number;
};
nextRunTime: string;
}

@Component({
selector: 'mifosx-run-selected-jobs-popover',
templateUrl: './run-selected-jobs-popover.component.html',
styleUrls: ['./run-selected-jobs-popover.component.scss']
})
export class RunSelectedJobsPopoverComponent implements OnInit {

/** Confirmed jobs event emitter */
@Output() confirmedJobs = new EventEmitter<JobDataType[]>();

/** Job table children */
@ViewChildren(RunSelectedJobsTableComponent) tableComponents:
QueryList<RunSelectedJobsTableComponent>;

/** Initialize Selected Jobs */
selectedJobs: JobDataType[] = [];

/** Show modal or not */
show: number;

/** API call response message */
messages: { message: string; status: number }[] = [];

constructor(private systemService: SystemService, @Inject(MAT_DIALOG_DATA)
public data: SelectedJobsDataType) { }
ngOnInit(): void {
this.selectedJobs = this.data.selectedJobs.selected.sort((a, b) => a.jobId - b.jobId);
}

/**
* Run all confirmed jobs from table
*/
runSelectedJobs(): void {
this.messages = [];
let tableData: JobDataType[] = [];
this.tableComponents.forEach((tableComponent) => {
tableData = (tableComponent.getTableData());
});

tableData.forEach((job) => {
this.systemService.runSelectedJob(job.jobId.toString())
.then((response) => {
this.messages.push({
message: `${job.displayName}: ${response.statusText} (${response.status})`,
status: response.ok
});
});
});

this.confirmedJobs.emit(tableData);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<div>
<table mat-table [dataSource]="selectedJobs">
<ng-container matColumnDef="displayName">
<th mat-header-cell *matHeaderCellDef>{{'labels.inputs.Display Name' |
translate}}</th>
<td mat-cell *matCellDef="let element; index as i">{{ element.displayName
}}</td>
</ng-container>

<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef></th>
<td mat-cell *matCellDef="let row; let rowIndex = index">
<button type="button" mat-icon-button color="warn" (click)="removeJobFromSelection(rowIndex)"
matTooltip="Delete" matTooltipPosition="left">
<fa-icon icon="trash"></fa-icon>
</button>
</td>
</ng-container>

<tr mat-header-row *matHeaderRowDef="columnsToDisplay" class="first-row"></tr>
<tr mat-row *matRowDef="let selectedJobs; columns: columnsToDisplay"></tr>
</table>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
table td {
padding-right: 50px;
}

.jobs-container {
display: flex;
flex-direction: column;
gap: 10px;
}

.jobs-container table td {
padding-right: 50px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { RunSelectedJobsTableComponent } from './run-selected-jobs-table.component';

describe('RunSelectedJobsTableComponent', () => {
let component: RunSelectedJobsTableComponent;
let fixture: ComponentFixture<RunSelectedJobsTableComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ RunSelectedJobsTableComponent ]
})
.compileComponents();

fixture = TestBed.createComponent(RunSelectedJobsTableComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Loading

0 comments on commit c4338b4

Please sign in to comment.