diff --git a/src/sql/parts/jobManagement/agent/agentView.component.html b/src/sql/parts/jobManagement/agent/agentView.component.html index 4e8ae961fb..35a114c6a0 100644 --- a/src/sql/parts/jobManagement/agent/agentView.component.html +++ b/src/sql/parts/jobManagement/agent/agentView.component.html @@ -5,7 +5,7 @@ *--------------------------------------------------------------------------------------------*/ --> - +
diff --git a/src/sql/parts/jobManagement/agent/agentView.component.ts b/src/sql/parts/jobManagement/agent/agentView.component.ts index d839ca5c7e..6d2745e32c 100644 --- a/src/sql/parts/jobManagement/agent/agentView.component.ts +++ b/src/sql/parts/jobManagement/agent/agentView.component.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!../common/media/jobs'; +import 'sql/parts/dashboard/common/dashboardPanelStyles'; + import * as nls from 'vs/nls'; import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable} from '@angular/core'; import * as Utils from 'sql/parts/connection/common/utils'; diff --git a/src/sql/parts/jobManagement/common/agentJobUtilities.ts b/src/sql/parts/jobManagement/common/agentJobUtilities.ts new file mode 100644 index 0000000000..6991ec6c48 --- /dev/null +++ b/src/sql/parts/jobManagement/common/agentJobUtilities.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import * as nls from 'vs/nls'; + +export class AgentJobUtilities { + + public static convertToStatusString(status: number): string { + switch(status) { + case(0): return nls.localize('agentUtilities.failed','Failed'); + case(1): return nls.localize('agentUtilities.succeeded', 'Succeeded'); + case(3): return nls.localize('agentUtilities.canceled', 'Canceled'); + case(5): return nls.localize('agentUtilities.statusUnknown', 'Status Unknown'); + default: return nls.localize('agentUtilities.statusUnknown', 'Status Unknown'); + } + } + + public static convertToExecutionStatusString(status: number): string { + switch(status) { + case(1): return nls.localize('agentUtilities.executing', 'Executing'); + case(2): return nls.localize('agentUtilities.waitingForThread', 'Waiting for Thread'); + case(3): return nls.localize('agentUtilities.betweenRetries', 'Between Retries'); + case(4): return nls.localize('agentUtilities.idle', 'Idle'); + case(5): return nls.localize('agentUtilities.suspended', 'Suspended'); + case(6): return nls.localize('agentUtilities.obsolete', '[Obsolete]'); + case(7): return nls.localize('agentUtilities.performingCompletionActions', 'PerformingCompletionActions'); + default: return nls.localize('agentUtilities.statusUnknown', 'Status Unknown'); + } + } + + public static convertToResponse(bool: boolean) { + return bool ? nls.localize('agentUtilities.yes', 'Yes') : nls.localize('agentUtilities.no', 'No'); + } + + public static convertToNextRun(date: string) { + if (date.includes('1/1/0001')) { + return nls.localize('agentUtilities.notScheduled', 'Not Scheduled'); + } else { + return date; + } + } + + public static convertToLastRun(date: string) { + if (date.includes('1/1/0001')) { + return nls.localize('agentUtilities.neverRun', 'Never Run'); + } else { + return date; + } + } +} \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/failed.svg b/src/sql/parts/jobManagement/common/media/failed.svg new file mode 100644 index 0000000000..ce68311533 --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/failed.svg @@ -0,0 +1 @@ +failed \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/jobs.css b/src/sql/parts/jobManagement/common/media/jobs.css index e946d5f10f..d30b48db97 100644 --- a/src/sql/parts/jobManagement/common/media/jobs.css +++ b/src/sql/parts/jobManagement/common/media/jobs.css @@ -21,10 +21,16 @@ jobhistory-component { display: block; } +.vs-dark .job-heading-container { + height: 32px; + border-bottom: 3px solid #444444; +} + #jobsDiv .jobview-grid { + padding-top: 15px; height: 100%; width : 100%; - display: block; + display: block; } .vs-dark #jobsDiv .slick-header-column { @@ -67,6 +73,21 @@ jobhistory-component { background: green; } +#jobsDiv .jobview-jobnameindicatorfailure { + width: 5px; + background: red; +} + +#jobsDiv .jobview-jobnameindicatorcancel { + width: 5px; + background: orange; +} + +#jobsDiv .jobview-jobnameindicatorunknown { + width: 5px; + background: yellow; +} + #jobsDiv .jobview-jobnametext { width: 100%; } @@ -123,16 +144,15 @@ jobhistory-component { background: #faf5f8 !important; } - .jobsview-icon { - content: url('./job.svg'); - width: 25px; + background-image: url('./job.svg'); } .vs-dark .jobsview-icon { - content: url('./job_inverse.svg'); + background-image: url('./job_inverse.svg'); } -agentview-component .tabbedPanel .tabList .tab .tabLabel.icon { - padding: 20px 15px !important; +agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.even > .slick-cell, +agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell { + cursor: pointer; } \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/step.svg b/src/sql/parts/jobManagement/common/media/step.svg new file mode 100644 index 0000000000..329becfad0 --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/step.svg @@ -0,0 +1 @@ +step \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/step_inverse.svg b/src/sql/parts/jobManagement/common/media/step_inverse.svg new file mode 100644 index 0000000000..b263f0a93a --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/step_inverse.svg @@ -0,0 +1 @@ +step_inverse \ No newline at end of file diff --git a/src/sql/parts/jobManagement/common/media/success_complete.svg b/src/sql/parts/jobManagement/common/media/success_complete.svg new file mode 100644 index 0000000000..3123332093 --- /dev/null +++ b/src/sql/parts/jobManagement/common/media/success_complete.svg @@ -0,0 +1 @@ +success_complete \ No newline at end of file diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.html b/src/sql/parts/jobManagement/views/jobHistory.component.html index 408047bb49..7fcdbb0d03 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.html +++ b/src/sql/parts/jobManagement/views/jobHistory.component.html @@ -82,7 +82,7 @@
-
+
@@ -103,39 +103,39 @@ - - - - - - - - - - - - -
+

Status:

+

{{_runStatus}}

+ Job ID: + {{agentJobHistoryInfo?.jobId || agentJobInfo?.jobId}}
+ Message: + {{agentJobHistoryInfo?.message}}
+ Duration: + {{agentJobHistoryInfo?.runDuration}}
+ Server: @@ -143,23 +143,23 @@
+ SQL message ID: + {{agentJobHistoryInfo?.sqlMessageId}}
+ Retries Attempted: + {{agentJobHistoryInfo?.retriesAttempted}}
- +

No Steps Available

diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.ts b/src/sql/parts/jobManagement/views/jobHistory.component.ts index 13d84a75d9..5a7bcbc876 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.ts +++ b/src/sql/parts/jobManagement/views/jobHistory.component.ts @@ -5,7 +5,7 @@ import 'vs/css!./jobHistory'; -import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy } from '@angular/core'; +import { OnInit, OnChanges, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core'; import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachListStyler } from 'vs/platform/theme/common/styler'; @@ -25,6 +25,7 @@ import { JobHistoryController, JobHistoryDataSource, import { JobStepsViewComponent } from 'sql/parts/jobManagement/views/jobStepsView.component'; import { JobStepsViewRow } from './jobStepsViewTree'; import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService'; +import { AgentJobUtilities } from '../common/agentJobUtilities'; export const DASHBOARD_SELECTOR: string = 'jobhistory-component'; @@ -33,6 +34,7 @@ export const DASHBOARD_SELECTOR: string = 'jobhistory-component'; templateUrl: decodeURI(require.toUrl('./jobHistory.component.html')), changeDetection: ChangeDetectionStrategy.OnPush }) +@Injectable() export class JobHistoryComponent extends Disposable implements OnInit { private _jobManagementService: IJobManagementService; @@ -102,28 +104,16 @@ export class JobHistoryComponent extends Disposable implements OnInit { } else { tree.setFocus(element, payload); tree.setSelection([element], payload); - self.agentJobHistoryInfo = self._treeController.jobHistories.filter(history => history.instanceId === element.instanceID)[0]; - if (self.agentJobHistoryInfo) { - self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate); - if (self.agentJobHistoryInfo.steps) { - self._stepRows = self.agentJobHistoryInfo.steps.map(step => { - let stepViewRow = new JobStepsViewRow(); - stepViewRow.message = step.message; - stepViewRow.runStatus = JobHistoryRow.convertToStatusString(self.agentJobHistoryInfo.runStatus); - self._runStatus = stepViewRow.runStatus; - stepViewRow.stepName = step.stepName; - stepViewRow.stepID = step.stepId.toString(); - return stepViewRow; - }); - this._showSteps = true; - } else { - this._showSteps = false; - } - self._cd.detectChanges(); - } + self.setStepsTree(element); } return true; }; + this._treeController.onKeyDown = (tree, event) => { + this._treeController.onKeyDownWrapper(tree, event); + let element = tree.getFocus(); + self.setStepsTree(element); + return true; + } this._tree = new Tree(this._tableContainer.nativeElement, { controller: this._treeController, dataSource: this._treeDataSource, @@ -145,6 +135,7 @@ export class JobHistoryComponent extends Disposable implements OnInit { if (jobHistories && jobHistories.length > 0) { const self = this; if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) { + this._showPreviousRuns = true; this.buildHistoryTree(self, jobHistories); this._cd.detectChanges(); } @@ -185,6 +176,29 @@ export class JobHistoryComponent extends Disposable implements OnInit { }); } + private setStepsTree(element: any) { + const self = this; + self.agentJobHistoryInfo = self._treeController.jobHistories.filter(history => history.instanceId === element.instanceID)[0]; + if (self.agentJobHistoryInfo) { + self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate); + if (self.agentJobHistoryInfo.steps) { + self._stepRows = self.agentJobHistoryInfo.steps.map(step => { + let stepViewRow = new JobStepsViewRow(); + stepViewRow.message = step.message; + stepViewRow.runStatus = AgentJobUtilities.convertToStatusString(step.runStatus); + self._runStatus = AgentJobUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus); + stepViewRow.stepName = step.stepName; + stepViewRow.stepID = step.stepId.toString(); + return stepViewRow; + }); + this._showSteps = true; + } else { + this._showSteps = false; + } + self._cd.detectChanges(); + } + } + private buildHistoryTree(self: any, jobHistories: AgentJobHistoryInfo[]) { self._treeController.jobHistories = jobHistories; self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, jobHistories); @@ -247,7 +261,7 @@ export class JobHistoryComponent extends Disposable implements OnInit { private convertToJobHistoryRow(historyInfo: AgentJobHistoryInfo): JobHistoryRow { let jobHistoryRow = new JobHistoryRow(); jobHistoryRow.runDate = historyInfo.runDate; - jobHistoryRow.runStatus = JobHistoryRow.convertToStatusString(historyInfo.runStatus); + jobHistoryRow.runStatus = AgentJobUtilities.convertToStatusString(historyInfo.runStatus); jobHistoryRow.instanceID = historyInfo.instanceId; return jobHistoryRow; } @@ -264,5 +278,9 @@ export class JobHistoryComponent extends Disposable implements OnInit { this._showSteps = value; this._cd.detectChanges(); } + + public get stepRows() { + return this._stepRows; + } } diff --git a/src/sql/parts/jobManagement/views/jobHistory.css b/src/sql/parts/jobManagement/views/jobHistory.css index 68cec7e72d..11bc888882 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.css +++ b/src/sql/parts/jobManagement/views/jobHistory.css @@ -186,6 +186,10 @@ table.step-list tr.step-row td { height: 100%; } +.history-details > .job-steps > .step-list { + padding-bottom: 10px; +} + .vs-dark .step-table .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content:before { background-image: none; } @@ -202,11 +206,13 @@ table.step-list tr.step-row td { height: 10px; width: 10px; display: inline-block; + vertical-align: middle; } .step-table .list-row .label { padding-left: 10px; display: inline-block; + vertical-align: middle; } .job-passed { @@ -228,5 +234,5 @@ table.step-list tr.step-row td { .steps-tree .monaco-tree .monaco-tree-row { white-space: normal; - height: 40px !important; + min-height: 40px !important; } \ No newline at end of file diff --git a/src/sql/parts/jobManagement/views/jobHistoryTree.ts b/src/sql/parts/jobManagement/views/jobHistoryTree.ts index 78b8b88c01..ec83bbbb32 100644 --- a/src/sql/parts/jobManagement/views/jobHistoryTree.ts +++ b/src/sql/parts/jobManagement/views/jobHistoryTree.ts @@ -28,20 +28,14 @@ import { OEAction } from 'sql/parts/objectExplorer/viewlet/objectExplorerActions import { Builder, $, withElementById } from 'vs/base/browser/builder'; import { AgentJobHistoryInfo } from 'sqlops'; import { Agent } from 'vs/base/node/request'; +import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { JobHistoryComponent } from './jobHistory.component'; export class JobHistoryRow { runDate: string; runStatus: string; instanceID: number; rowID: string = generateUuid(); - - public static convertToStatusString(status: number): string { - switch(status) { - case(1): return 'Succeeded'; - case(0): return 'Failed'; - default: return 'Unknown'; - } - } } // Empty class just for tree input @@ -56,10 +50,6 @@ export class JobHistoryController extends TreeDefaults.DefaultController { return true; } - public onContextMenu(tree: tree.ITree, element: JobHistoryRow, event: tree.ContextMenuEvent): boolean { - return true; - } - public set jobHistories(value: AgentJobHistoryInfo[]) { this._jobHistories = value; } @@ -68,6 +58,17 @@ export class JobHistoryController extends TreeDefaults.DefaultController { return this._jobHistories; } + public onKeyDownWrapper(tree: tree.ITree, event: IKeyboardEvent): boolean { + if (event.code === 'ArrowDown' || event.keyCode === 40) { + return super.onDown(tree, event); + } else if (event.code === 'ArrowUp' || event.keyCode === 38) { + return super.onUp(tree, event); + } else { + event.preventDefault(); + event.stopPropagation(); + return true; + } + } } export class JobHistoryDataSource implements tree.IDataSource { @@ -119,7 +120,7 @@ export class JobHistoryRenderer implements tree.IRenderer { private _statusIcon: HTMLElement; public getHeight(tree: tree.ITree, element: JobHistoryRow): number { - return 22; + return 30; } public getTemplateId(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): string { diff --git a/src/sql/parts/jobManagement/views/jobStepsView.component.html b/src/sql/parts/jobManagement/views/jobStepsView.component.html index a6f68f325e..e33afdd7a6 100644 --- a/src/sql/parts/jobManagement/views/jobStepsView.component.html +++ b/src/sql/parts/jobManagement/views/jobStepsView.component.html @@ -5,7 +5,10 @@ *--------------------------------------------------------------------------------------------*/ --> -

Steps

+
+
+

Steps

+
diff --git a/src/sql/parts/jobManagement/views/jobStepsView.component.ts b/src/sql/parts/jobManagement/views/jobStepsView.component.ts index 7006d9cda4..98b49485a8 100644 --- a/src/sql/parts/jobManagement/views/jobStepsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobStepsView.component.ts @@ -5,7 +5,7 @@ import 'vs/css!./jobStepsView'; -import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnChanges, ViewChild, Input, Injectable } from '@angular/core'; +import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, Injectable, AfterContentChecked } from '@angular/core'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachListStyler } from 'vs/platform/theme/common/styler'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; @@ -17,6 +17,7 @@ import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboar import { AgentJobHistoryInfo } from 'sqlops'; import { JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter, JobStepsViewRenderer, JobStepsViewRow, JobStepsViewModel} from 'sql/parts/jobManagement/views/jobStepsViewTree'; +import { JobHistoryComponent } from 'sql/parts/jobManagement/views/jobHistory.component'; export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component'; @@ -24,7 +25,7 @@ export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component'; selector: JOBSTEPSVIEW_SELECTOR, templateUrl: decodeURI(require.toUrl('./jobStepsView.component.html')) }) -export class JobStepsViewComponent extends Disposable implements OnInit, OnChanges { +export class JobStepsViewComponent extends Disposable implements OnInit, AfterContentChecked { private _jobManagementService: IJobManagementService; private _tree: Tree; @@ -36,18 +37,35 @@ export class JobStepsViewComponent extends Disposable implements OnInit, OnChang @ViewChild('table') private _tableContainer: ElementRef; - @Input() public stepRows: JobStepsViewRow[] = []; constructor( @Inject(BOOTSTRAP_SERVICE_ID) private bootstrapService: IBootstrapService, @Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef, - @Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface + @Inject(forwardRef(() => DashboardServiceInterface)) private _dashboardService: DashboardServiceInterface, + @Inject(forwardRef(() => JobHistoryComponent)) private _jobHistoryComponent: JobHistoryComponent ) { super(); this._jobManagementService = bootstrapService.jobManagementService; } + ngAfterContentChecked() { + if (this._jobHistoryComponent.stepRows.length > 0) { + this._treeDataSource.data = this._jobHistoryComponent.stepRows; + if (!this._tree) { + this._tree = new Tree(this._tableContainer.nativeElement, { + controller: this._treeController, + dataSource: this._treeDataSource, + filter: this._treeFilter, + renderer: this._treeRenderer + }); + this._register(attachListStyler(this._tree, this.bootstrapService.themeService)); + } + this._tree.layout(JobStepsViewComponent._pageSize); + this._tree.setInput(new JobStepsViewModel()); + } + } + ngOnInit() { let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; this._tree = new Tree(this._tableContainer.nativeElement, { @@ -56,22 +74,7 @@ export class JobStepsViewComponent extends Disposable implements OnInit, OnChang filter: this._treeFilter, renderer: this._treeRenderer }); - } - - ngOnChanges() { - if (this.stepRows.length > 0) { - this._treeDataSource.data = this.stepRows; - if (!this._tree) { - this._tree = new Tree(this._tableContainer.nativeElement, { - controller: this._treeController, - dataSource: this._treeDataSource, - filter: this._treeFilter, - renderer: this._treeRenderer - }); - } - this._tree.layout(JobStepsViewComponent._pageSize); - this._tree.setInput(new JobStepsViewModel()); - } + this._register(attachListStyler(this._tree, this.bootstrapService.themeService)); } } diff --git a/src/sql/parts/jobManagement/views/jobStepsView.css b/src/sql/parts/jobManagement/views/jobStepsView.css index 443e366fd7..d5e52af051 100644 --- a/src/sql/parts/jobManagement/views/jobStepsView.css +++ b/src/sql/parts/jobManagement/views/jobStepsView.css @@ -3,28 +3,29 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.steps-tree .list-row .status-icon { - height: 10px; - width: 10px; - display: inline-block; - margin-top: 4px; +.steps-tree .list-row .status-icon.step-passed { + content: url("../common/media/success_complete.svg"); + height: 100%; +} + +.steps-tree .list-row .status-icon.step-failed { + content: url("../common/media/failed.svg"); + height: 100%; } .steps-tree .list-row .label { padding-left: 10px; display: flex; text-align: center; + vertical-align: middle; + user-select: initial; } -.step-passed { - background: green; -} - -.step-failed { - background: red; -} - -.step-unknown { +.steps-tree .list-row .status-icon.step-unknown { + height: 10px; + width: 10px; + display: inline-block; + margin-top: 4px; background: yellow; } @@ -35,6 +36,7 @@ .step-columns { padding-left: 50px; + padding-top: 10px; } .step-columns .step-id-col, .steps-tree .tree-id-col { @@ -57,4 +59,24 @@ white-space: normal; text-align: center; width: 680px; -} \ No newline at end of file +} + +.steps-header > .steps-icon { + height: 25px; + padding-right: 10px; + display: inline; + vertical-align: middle; +} + +.vs-dark .steps-header > .steps-icon, +.hc-black .steps-header > .steps-icon { + content: url("../common/media/step_inverse.svg"); +} + +.steps-header > .steps-icon { + content: url("../common/media/step.svg"); +} + +jobstepsview-component { + padding-top: 10px; +} diff --git a/src/sql/parts/jobManagement/views/jobStepsViewTree.ts b/src/sql/parts/jobManagement/views/jobStepsViewTree.ts index 71230ddac8..3b69eb0ae3 100644 --- a/src/sql/parts/jobManagement/views/jobStepsViewTree.ts +++ b/src/sql/parts/jobManagement/views/jobStepsViewTree.ts @@ -164,7 +164,7 @@ export class JobStepsViewRenderer implements tree.IRenderer { private createStatusIcon(): HTMLElement { let statusIcon: HTMLElement = DOM.$('div'); - statusIcon.className += ' status-icon'; + statusIcon.className += 'status-icon'; return statusIcon; } } diff --git a/src/sql/parts/jobManagement/views/jobsView.component.html b/src/sql/parts/jobManagement/views/jobsView.component.html index c3f793205c..2a2491873d 100644 --- a/src/sql/parts/jobManagement/views/jobsView.component.html +++ b/src/sql/parts/jobManagement/views/jobsView.component.html @@ -4,6 +4,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ --> -

Jobs

+
+

Jobs

+
diff --git a/src/sql/parts/jobManagement/views/jobsView.component.ts b/src/sql/parts/jobManagement/views/jobsView.component.ts index f37436b6bf..4c02bb0772 100644 --- a/src/sql/parts/jobManagement/views/jobsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobsView.component.ts @@ -32,6 +32,7 @@ import { JobHistoryComponent } from './jobHistory.component'; import { AgentViewComponent } from '../agent/agentView.component'; import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowdetailview'; import { JobCacheObject } from 'sql/parts/jobManagement/common/jobManagementService'; +import { AgentJobUtilities } from '../common/agentJobUtilities'; export const JOBSVIEW_SELECTOR: string = 'jobsview-component'; @@ -49,16 +50,15 @@ export class JobsViewComponent implements AfterContentChecked { private _disposables = new Array(); private columns: Array> = [ - { name: 'Name', field: 'name', formatter: this.renderName, width: 200, }, - { name: 'Last Run', field: 'lastRun' }, - { name: 'Next Run', field: 'nextRun' }, - { name: 'Enabled', field: 'enabled' }, - { name: 'Status', field: 'currentExecutionStatus' }, - { name: 'Category', field: 'category' }, - { name: 'Runnable', field: 'runnable' }, - { name: 'Schedule', field: 'hasSchedule' }, - { name: 'Category ID', field: 'categoryId' }, - { name: 'Last Run Outcome', field: 'lastRunOutcome' }, + { name: nls.localize('jobColumns.name','Name'), field: 'name', formatter: this.renderName, width: 200 }, + { name: nls.localize('jobColumns.lastRun','Last Run'), field: 'lastRun', minWidth: 150 }, + { name: nls.localize('jobColumns.nextRun','Next Run'), field: 'nextRun', minWidth: 150 }, + { name: nls.localize('jobColumns.enabled','Enabled'), field: 'enabled', minWidth: 70 }, + { name: nls.localize('jobColumns.status','Status'), field: 'currentExecutionStatus', minWidth: 60 }, + { name: nls.localize('jobColumns.category','Category'), field: 'category', minWidth: 150 }, + { name: nls.localize('jobColumns.runnable','Runnable'), field: 'runnable', minWidth: 50 }, + { name: nls.localize('jobColumns.schedule','Schedule'), field: 'hasSchedule', minWidth: 50 }, + { name: nls.localize('jobColumns.lastRunOutcome', 'Last Run Outcome'), field: 'lastRunOutcome', minWidth: 150 }, ]; private rowDetail: any; @@ -125,24 +125,14 @@ export class JobsViewComponent implements AfterContentChecked { syncColumnCellResize: true, enableColumnReorder: false, rowHeight: 45, - enableCellNavigation: true + enableCellNavigation: true, + autoHeight: false, + forceFitColumns: false }; this.dataView = new Slick.Data.DataView({ inlineFilters: false }); - let rowDetail = new RowDetailView({ - cssClass: 'detailView-toggle', - preTemplate: this.loadingTemplate, - process: (job) => { - (rowDetail).onAsyncResponse.notify({ - 'itemDetail': job, - }, undefined, null); - }, - panelRows: 2, - postTemplate: () => '' - }); - - this.rowDetail = rowDetail; + this.rowDetail = new RowDetailView({}); columns.unshift(this.rowDetail.getColumnDefinition()); this._table = new Table(this._gridEl.nativeElement, undefined, columns, options); this._table.grid.setData(this.dataView, true); @@ -174,27 +164,17 @@ export class JobsViewComponent implements AfterContentChecked { id: job.jobId, jobId: job.jobId, name: job.name, - lastRun: job.lastRun, - nextRun: job.nextRun, - enabled: job.enabled, - currentExecutionStatus: job.currentExecutionStatus, + lastRun: AgentJobUtilities.convertToLastRun(job.lastRun), + nextRun: AgentJobUtilities.convertToNextRun(job.nextRun), + enabled: AgentJobUtilities.convertToResponse(job.enabled), + currentExecutionStatus: AgentJobUtilities.convertToExecutionStatusString(job.currentExecutionStatus), category: job.category, - runnable: job.runnable, - hasSchedule: job.hasSchedule, - categoryId: job.categoryId, - lastRunOutcome: job.lastRunOutcome + runnable: AgentJobUtilities.convertToResponse(job.runnable), + hasSchedule: AgentJobUtilities.convertToResponse(job.hasSchedule), + lastRunOutcome: AgentJobUtilities.convertToStatusString(job.lastRunOutcome) }; }); - this._table.registerPlugin(this.rowDetail); - - this.rowDetail.onBeforeRowDetailToggle.subscribe(function(e, args) { - }); - this.rowDetail.onAfterRowDetailToggle.subscribe(function(e, args) { - }); - this.rowDetail.onAsyncEndUpdate.subscribe(function(e, args) { - }); - this.dataView.beginUpdate(); this.dataView.setItems(jobViews); this.dataView.endUpdate(); @@ -209,8 +189,27 @@ export class JobsViewComponent implements AfterContentChecked { } renderName(row, cell, value, columnDef, dataContext) { + let resultIndicatorClass: string; + switch (dataContext.lastRunOutcome) { + case ('Succeeded'): + resultIndicatorClass = 'jobview-jobnameindicatorsuccess'; + break; + case ('Failed'): + resultIndicatorClass = 'jobview-jobnameindicatorfailure'; + break; + case ('Canceled'): + resultIndicatorClass = 'jobview-jobnameindicatorcancel'; + break; + case ('Status Unknown'): + resultIndicatorClass = 'jobview-jobnameindicatorunknown'; + break; + default: + resultIndicatorClass = 'jobview-jobnameindicatorunknown'; + break; + } + return '' + - '' + + '' + '' + '
' + dataContext.name + '
'; } diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index e6c68537f6..aa296760dc 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1065,6 +1065,7 @@ declare module 'sqlops' { stepName: string; message: string; runDate: string; + runStatus: number; } export interface AgentJobHistoryInfo {