move code from parts to contrib (#8319)
@@ -0,0 +1,56 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div id="agentViewDiv" class="fullsize">
|
||||
<panel class="dashboard-panel" [options]="panelOpt">
|
||||
<tab [title]="jobsComponentTitle" class="fullsize" [identifier]="jobsTabIdentifier"
|
||||
[iconClass]="jobsIconClass">
|
||||
<ng-template>
|
||||
<div id="jobsDiv" class="fullsize" *ngIf="showHistory === false">
|
||||
<jobsview-component></jobsview-component>
|
||||
</div>
|
||||
<div id="historyDiv" class="fullsize" *ngIf="showHistory === true">
|
||||
<jobhistory-component></jobhistory-component>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
<tab [title]="notebooksComponentTitle" class="fullsize" [identifier]="notebooksTabIdentifier"
|
||||
[iconClass]="notebooksIconClass">
|
||||
<ng-template>
|
||||
<div id="notebooksDiv" class="fullsize" *ngIf="showNotebookHistory === false">
|
||||
<notebooksview-component></notebooksview-component>
|
||||
</div>
|
||||
<div id="historyDiv" class="fullsize" *ngIf="showNotebookHistory === true">
|
||||
<notebookhistory-component></notebookhistory-component>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
<tab [title]="alertsComponentTitle" class="fullsize" [identifier]="alertsTabIdentifier"
|
||||
[iconClass]="alertsIconClass">
|
||||
<ng-template>
|
||||
<div id="alertsDiv" class="fullsize">
|
||||
<jobalertsview-component></jobalertsview-component>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
<tab [title]="operatorsComponentTitle" class="fullsize" [identifier]="operatorsTabIdentifier"
|
||||
[iconClass]="operatorsIconClass">
|
||||
<ng-template>
|
||||
<div id="operatorsDiv" class="fullsize">
|
||||
<joboperatorsview-component></joboperatorsview-component>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
<tab [title]="proxiesComponentTitle" class="fullsize" [identifier]="proxiesTabIdentifier"
|
||||
[iconClass]="proxiesIconClass">
|
||||
<ng-template>
|
||||
<div id="proxiesDiv" class="fullsize">
|
||||
<jobproxiesview-component></jobproxiesview-component>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
</panel>
|
||||
</div>
|
||||
@@ -0,0 +1,159 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobs';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Component, Inject, forwardRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
|
||||
import { AgentJobInfo, AgentNotebookInfo } from 'azdata';
|
||||
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
|
||||
|
||||
export const DASHBOARD_SELECTOR: string = 'agentview-component';
|
||||
|
||||
@Component({
|
||||
selector: DASHBOARD_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./agentView.component.html'))
|
||||
})
|
||||
@Injectable()
|
||||
export class AgentViewComponent {
|
||||
|
||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||
|
||||
private _showHistory: boolean = false;
|
||||
private _showNotebookHistory: boolean = false;
|
||||
private _jobId: string = null;
|
||||
private _notebookId: string = null;
|
||||
private _agentJobInfo: AgentJobInfo = null;
|
||||
private _agentNotebookInfo: AgentNotebookInfo = null;
|
||||
private _refresh: boolean = undefined;
|
||||
private _expanded: Map<string, string>;
|
||||
private _expandedNotebook: Map<string, string>;
|
||||
|
||||
public jobsIconClass: string = 'jobsview-icon';
|
||||
public notebooksIconClass: string = 'notebooksview-icon';
|
||||
public alertsIconClass: string = 'alertsview-icon';
|
||||
public proxiesIconClass: string = 'proxiesview-icon';
|
||||
public operatorsIconClass: string = 'operatorsview-icon';
|
||||
|
||||
public readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
|
||||
public readonly notebooksComponentTitle: string = nls.localize('jobview.Notebooks', "Notebooks");
|
||||
public readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts");
|
||||
public readonly proxiesComponentTitle: string = nls.localize('jobview.Proxies', "Proxies");
|
||||
public readonly operatorsComponentTitle: string = nls.localize('jobview.Operators', "Operators");
|
||||
|
||||
public readonly panelOpt: IPanelOptions = {
|
||||
showTabsWhenOne: true,
|
||||
layout: NavigationBarLayout.vertical,
|
||||
showIcon: true
|
||||
};
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(IJobManagementService) jobManagementService: IJobManagementService) {
|
||||
this._expanded = new Map<string, string>();
|
||||
|
||||
let self = this;
|
||||
jobManagementService.onDidChange((args) => {
|
||||
self.refresh = true;
|
||||
self._cd.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Public Getters
|
||||
*/
|
||||
public get jobId(): string {
|
||||
return this._jobId;
|
||||
}
|
||||
|
||||
public get notebookId(): string {
|
||||
return this._notebookId;
|
||||
}
|
||||
|
||||
public get showHistory(): boolean {
|
||||
return this._showHistory;
|
||||
}
|
||||
|
||||
public get showNotebookHistory(): boolean {
|
||||
return this._showNotebookHistory;
|
||||
}
|
||||
|
||||
public get agentJobInfo(): AgentJobInfo {
|
||||
return this._agentJobInfo;
|
||||
}
|
||||
|
||||
public get agentNotebookInfo(): AgentNotebookInfo {
|
||||
return this._agentNotebookInfo;
|
||||
}
|
||||
|
||||
public get refresh(): boolean {
|
||||
return this._refresh;
|
||||
}
|
||||
|
||||
public get expanded(): Map<string, string> {
|
||||
return this._expanded;
|
||||
}
|
||||
|
||||
public get expandedNotebook(): Map<string, string> {
|
||||
return this._expandedNotebook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public Setters
|
||||
*/
|
||||
|
||||
public set jobId(value: string) {
|
||||
this._jobId = value;
|
||||
}
|
||||
|
||||
public set notebookId(value: string) {
|
||||
this._notebookId = value;
|
||||
}
|
||||
|
||||
public set showHistory(value: boolean) {
|
||||
this._showHistory = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
public set showNotebookHistory(value: boolean) {
|
||||
this._showNotebookHistory = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
public set agentJobInfo(value: AgentJobInfo) {
|
||||
this._agentJobInfo = value;
|
||||
}
|
||||
|
||||
public set agentNotebookInfo(value: AgentNotebookInfo) {
|
||||
this._agentNotebookInfo = value;
|
||||
}
|
||||
|
||||
public set refresh(value: boolean) {
|
||||
this._refresh = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
public setExpanded(jobId: string, errorMessage: string) {
|
||||
this._expanded.set(jobId, errorMessage);
|
||||
}
|
||||
|
||||
public setExpandedNotebook(jobId: string, errorMessage: string) {
|
||||
this._expandedNotebook.set(jobId, errorMessage);
|
||||
}
|
||||
|
||||
public set expanded(value: Map<string, string>) {
|
||||
this._expanded = value;
|
||||
}
|
||||
|
||||
public set expandedNotebook(value: Map<string, string>) {
|
||||
this._expandedNotebook = value;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
this._panel.layout();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="job-heading-container">
|
||||
<h1 class="job-heading" *ngIf="_isCloud === false">Alerts</h1>
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Alerts Available</h1>
|
||||
<div class="codicon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<div #jobalertsgrid class="jobalertsview-grid"></div>
|
||||
@@ -0,0 +1,228 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobs';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as azdata from 'azdata';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { EditAlertAction, DeleteAlertAction, NewAlertAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { AlertsCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
|
||||
export const VIEW_SELECTOR: string = 'jobalertsview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
|
||||
@Component({
|
||||
selector: VIEW_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./alertsView.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => AlertsViewComponent) }],
|
||||
})
|
||||
export class AlertsViewComponent extends JobManagementView implements OnInit, OnDestroy {
|
||||
|
||||
private columns: Array<Slick.Column<any>> = [
|
||||
{
|
||||
name: nls.localize('jobAlertColumns.name', "Name"),
|
||||
field: 'name',
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
||||
width: 500,
|
||||
id: 'name'
|
||||
},
|
||||
{ name: nls.localize('jobAlertColumns.lastOccurrenceDate', "Last Occurrence"), field: 'lastOccurrenceDate', width: 150, id: 'lastOccurrenceDate' },
|
||||
{ name: nls.localize('jobAlertColumns.enabled', "Enabled"), field: 'enabled', width: 80, id: 'enabled' },
|
||||
{ name: nls.localize('jobAlertColumns.delayBetweenResponses', "Delay Between Responses (in secs)"), field: 'delayBetweenResponses', width: 200, id: 'delayBetweenResponses' },
|
||||
{ name: nls.localize('jobAlertColumns.categoryName', "Category Name"), field: 'categoryName', width: 250, id: 'categoryName' },
|
||||
];
|
||||
|
||||
private options: Slick.GridOptions<any> = {
|
||||
syncColumnCellResize: true,
|
||||
enableColumnReorder: false,
|
||||
rowHeight: ROW_HEIGHT,
|
||||
enableCellNavigation: true,
|
||||
editable: false
|
||||
};
|
||||
|
||||
private dataView: any;
|
||||
public _isCloud: boolean;
|
||||
private _alertsCacheObject: AlertsCacheObject;
|
||||
|
||||
private _didTabChange: boolean;
|
||||
@ViewChild('jobalertsgrid') _gridEl: ElementRef;
|
||||
|
||||
public alerts: azdata.AgentAlertInfo[];
|
||||
public contextAction = NewAlertAction;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService) {
|
||||
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
this._didTabChange = false;
|
||||
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
||||
let alertsCacheObjectMap = this._jobManagementService.alertsCacheObjectMap;
|
||||
let alertsCache = alertsCacheObjectMap[this._serverName];
|
||||
if (alertsCache) {
|
||||
this._alertsCacheObject = alertsCache;
|
||||
} else {
|
||||
this._alertsCacheObject = new AlertsCacheObject();
|
||||
this._alertsCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._alertsCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._gridEl;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._didTabChange = true;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
|
||||
if (height < 0) {
|
||||
height = 0;
|
||||
}
|
||||
|
||||
if (this._table) {
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._gridEl.nativeElement),
|
||||
height));
|
||||
}
|
||||
}
|
||||
|
||||
onFirstVisible() {
|
||||
let self = this;
|
||||
let cached: boolean = false;
|
||||
if (this._alertsCacheObject.serverName === this._serverName) {
|
||||
if (this._alertsCacheObject.alerts && this._alertsCacheObject.alerts.length > 0) {
|
||||
cached = true;
|
||||
this.alerts = this._alertsCacheObject.alerts;
|
||||
}
|
||||
}
|
||||
|
||||
let columns = this.columns.map((column) => {
|
||||
column.rerenderOnResize = true;
|
||||
return column;
|
||||
});
|
||||
|
||||
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
||||
let rowDetail = new RowDetailView({
|
||||
cssClass: '_detail_selector',
|
||||
useRowClick: false,
|
||||
panelRows: 1,
|
||||
postTemplate: () => '', // I'm assuming these code paths are just never hit...
|
||||
preTemplate: () => '',
|
||||
process: () => { }
|
||||
});
|
||||
columns.unshift(rowDetail.getColumnDefinition());
|
||||
jQuery(this._gridEl.nativeElement).empty();
|
||||
jQuery(this.actionBarContainer.nativeElement).empty();
|
||||
this.initActionBar();
|
||||
|
||||
this._table = new Table(this._gridEl.nativeElement, { columns }, this.options);
|
||||
this._table.grid.setData(this.dataView, true);
|
||||
this._register(this._table.onContextMenu(e => {
|
||||
self.openContextMenu(e);
|
||||
}));
|
||||
|
||||
// check for cached state
|
||||
if (cached && this._agentViewComponent.refresh !== true) {
|
||||
self.onAlertsAvailable(this.alerts);
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
} else {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._jobManagementService.getAlerts(ownerUri).then((result) => {
|
||||
if (result && result.alerts) {
|
||||
self.alerts = result.alerts;
|
||||
self._alertsCacheObject.alerts = result.alerts;
|
||||
self.onAlertsAvailable(result.alerts);
|
||||
} else {
|
||||
// TODO: handle error
|
||||
}
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible && !this._didTabChange) {
|
||||
this._cd.detectChanges();
|
||||
} else if (this._didTabChange) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onAlertsAvailable(alerts: azdata.AgentAlertInfo[]) {
|
||||
let items: any = alerts.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
lastOccurrenceDate: item.lastOccurrenceDate,
|
||||
enabled: item.isEnabled,
|
||||
delayBetweenResponses: item.delayBetweenResponses,
|
||||
categoryName: item.categoryName
|
||||
};
|
||||
});
|
||||
|
||||
this.dataView.beginUpdate();
|
||||
this.dataView.setItems(items);
|
||||
this.dataView.endUpdate();
|
||||
this._alertsCacheObject.dataview = this.dataView;
|
||||
this._table.autosizeColumns();
|
||||
this._table.resizeCanvas();
|
||||
}
|
||||
|
||||
protected getTableActions(): IAction[] {
|
||||
return [
|
||||
this._instantiationService.createInstance(EditAlertAction),
|
||||
this._instantiationService.createInstance(DeleteAlertAction)
|
||||
];
|
||||
}
|
||||
|
||||
protected getCurrentTableObject(rowIndex: number): any {
|
||||
let targetObject = {
|
||||
alertInfo: this.alerts && this.alerts.length >= rowIndex ? this.alerts[rowIndex] : undefined
|
||||
};
|
||||
return targetObject;
|
||||
}
|
||||
|
||||
|
||||
private renderName(row, cell, value, columnDef, dataContext) {
|
||||
let resultIndicatorClass = dataContext.enabled ? 'alertview-alertnameindicatorenabled' :
|
||||
'alertview-alertnameindicatordisabled';
|
||||
|
||||
return '<table class="alertview-alertnametable"><tr class="alertview-alertnamerow">' +
|
||||
'<td nowrap class=' + resultIndicatorClass + '></td>' +
|
||||
'<td nowrap class="alertview-alertnametext">' + dataContext.name + '</td>' +
|
||||
'</tr></table>';
|
||||
}
|
||||
|
||||
public async openCreateAlertDialog() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
await this._commandService.executeCommand('agent.openAlertDialog', ownerUri, null, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="jobhistory-heading-container">
|
||||
<h1 class="job-heading">Jobs | {{this._agentJobInfo?.name}} </h1>
|
||||
<div class="codicon in-progress" *ngIf="showProgressWheel()"></div>
|
||||
</div>
|
||||
|
||||
<!-- Back -->
|
||||
<div class="all-jobs" tabindex="0" (click)="goToJobs()" (keyup.enter)="goToJobs()">
|
||||
<div class="back-button-icon" (click)="goToJobs()" (keyup.enter)="goToJobs()"></div>All Jobs
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<!-- Overview -->
|
||||
<div class="overview-container">
|
||||
<div class="overview-tab" (click)='toggleCollapse()' tabindex="0">
|
||||
<input id="accordion" type="checkbox">
|
||||
<label for="accordion">
|
||||
<div class="resultsViewCollapsible collapsed" (click)='toggleCollapse()'></div>
|
||||
Overview
|
||||
</label>
|
||||
<div class="accordion-content">
|
||||
<table align='left'>
|
||||
<tr>
|
||||
<td id='col1'>
|
||||
Category:
|
||||
</td>
|
||||
<td id='col2'>
|
||||
{{this._agentJobInfo?.category}}
|
||||
</td>
|
||||
<td id='col3'>
|
||||
Enabled:
|
||||
</td>
|
||||
<td id='col4'>
|
||||
{{this._agentJobInfo?.enabled}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id='col1'>
|
||||
Has Alert:
|
||||
</td>
|
||||
<td id='col2'>
|
||||
{{this._agentJobInfo?.hasTarget}}
|
||||
</td>
|
||||
<td id='col3'>
|
||||
Has Schedule:
|
||||
</td>
|
||||
<td id='col4'>
|
||||
{{this._agentJobInfo?.hasSchedule}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id='col1'>
|
||||
Last Run:
|
||||
</td>
|
||||
<td id='col2'>
|
||||
{{this._agentJobInfo?.lastRun}}
|
||||
</td>
|
||||
<td id='col3'>
|
||||
Next Run:
|
||||
</td>
|
||||
<td id='col4'>
|
||||
{{this._agentJobInfo?.nextRun}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Job History details -->
|
||||
<div class='history-details'>
|
||||
<!-- Previous run list -->
|
||||
<div class="prev-run-list-container" style="min-width: 270px">
|
||||
<table *ngIf="_showPreviousRuns === true">
|
||||
<tr>
|
||||
<td class="date-column">
|
||||
<b>Date</b>
|
||||
</td>
|
||||
<td>
|
||||
<b>Status</b>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<h3 *ngIf="_showPreviousRuns === false" style="text-align: center">No Previous Runs Available</h3>
|
||||
<div class="step-table prev-run-list" style="position: relative; width: 100%">
|
||||
<div #table style="position: absolute; width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Job Steps -->
|
||||
<div class="job-steps" id="job-steps">
|
||||
<h1 class="job-heading">
|
||||
{{agentJobHistoryInfo?.runDate}}
|
||||
</h1>
|
||||
<table class="step-list">
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
<h3>Status:</h3>
|
||||
</td>
|
||||
<td height="20">
|
||||
<h3>{{_runStatus}}</h3>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
Job ID:
|
||||
</td>
|
||||
<td height="20" style="user-select: initial">
|
||||
{{agentJobHistoryInfo?.jobId || agentJobInfo?.jobId}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
Message:
|
||||
</td>
|
||||
<td height="20" style="user-select: initial">
|
||||
{{agentJobHistoryInfo?.message}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
Duration:
|
||||
</td>
|
||||
<td height="20">
|
||||
{{agentJobHistoryInfo?.runDuration}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
Server:
|
||||
</td>
|
||||
<td>
|
||||
{{agentJobHistoryInfo?.server}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
SQL message ID:
|
||||
</td>
|
||||
<td height="20">
|
||||
{{agentJobHistoryInfo?.sqlMessageId}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="step-row">
|
||||
<td height="20">
|
||||
Retries Attempted:
|
||||
</td>
|
||||
<td height="20">
|
||||
{{agentJobHistoryInfo?.retriesAttempted}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div #jobsteps *ngIf="showSteps === true" style="flex: 1 1 auto; position: relative">
|
||||
<jobstepsview-component *ngIf="showSteps === true"></jobstepsview-component>
|
||||
</div>
|
||||
<h3 *ngIf="showSteps === false">No Steps Available</h3>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,399 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobHistory';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { RunJobAction, StopJobAction, EditJobAction, JobsRefreshAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { JobCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { JobManagementUtilities } from 'sql/platform/jobManagement/browser/jobManagementUtilities';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import {
|
||||
JobHistoryController, JobHistoryDataSource,
|
||||
JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow
|
||||
} from 'sql/workbench/contrib/jobManagement/browser/jobHistoryTree';
|
||||
import { JobStepsViewRow } from 'sql/workbench/contrib/jobManagement/browser/jobStepsViewTree';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { JobManagementView, JobActionContext } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
export const DASHBOARD_SELECTOR: string = 'jobhistory-component';
|
||||
|
||||
@Component({
|
||||
selector: DASHBOARD_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./jobHistory.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => JobHistoryComponent) }],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
@Injectable()
|
||||
export class JobHistoryComponent extends JobManagementView implements OnInit {
|
||||
|
||||
private _tree: Tree;
|
||||
private _treeController: JobHistoryController;
|
||||
private _treeDataSource: JobHistoryDataSource;
|
||||
private _treeRenderer: JobHistoryRenderer;
|
||||
private _treeFilter: JobHistoryFilter;
|
||||
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
|
||||
@Input() public agentJobInfo: azdata.AgentJobInfo = undefined;
|
||||
@Input() public agentJobHistories: azdata.AgentJobHistoryInfo[] = undefined;
|
||||
public agentJobHistoryInfo: azdata.AgentJobHistoryInfo = undefined;
|
||||
|
||||
private _stepRows: JobStepsViewRow[] = [];
|
||||
private _showSteps: boolean = undefined;
|
||||
private _showPreviousRuns: boolean = undefined;
|
||||
public _runStatus: string = undefined;
|
||||
private _jobCacheObject: JobCacheObject;
|
||||
private _agentJobInfo: azdata.AgentJobInfo;
|
||||
private _noJobsAvailable: boolean = false;
|
||||
|
||||
// Job Actions
|
||||
private _editJobAction: EditJobAction;
|
||||
private _runJobAction: RunJobAction;
|
||||
private _stopJobAction: StopJobAction;
|
||||
private _refreshAction: JobsRefreshAction;
|
||||
|
||||
private static readonly HEADING_HEIGHT: number = 24;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
this._treeController = new JobHistoryController();
|
||||
this._treeDataSource = new JobHistoryDataSource();
|
||||
this._treeRenderer = new JobHistoryRenderer();
|
||||
this._treeFilter = new JobHistoryFilter();
|
||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||
this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
let jobCache = jobCacheObjectMap[this._serverName];
|
||||
if (jobCache) {
|
||||
this._jobCacheObject = jobCache;
|
||||
} else {
|
||||
this._jobCacheObject = new JobCacheObject();
|
||||
this._jobCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._tableContainer;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
|
||||
this.initActionBar();
|
||||
const self = this;
|
||||
this._treeController.onClick = (tree, element, event, origin = 'mouse') => {
|
||||
const payload = { origin: origin };
|
||||
const isDoubleClick = (origin === 'mouse' && event.detail === 2);
|
||||
// Cancel Event
|
||||
const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown';
|
||||
if (!isMouseDown) {
|
||||
event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise
|
||||
}
|
||||
event.stopPropagation();
|
||||
tree.setFocus(element, payload);
|
||||
if (element && isDoubleClick) {
|
||||
event.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
} else {
|
||||
tree.setFocus(element, payload);
|
||||
tree.setSelection([element], payload);
|
||||
if (element.rowID) {
|
||||
self.setStepsTree(element);
|
||||
} else {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
this._treeController.onKeyDown = (tree, event) => {
|
||||
this._treeController.onKeyDownWrapper(tree, event);
|
||||
let element = tree.getFocus();
|
||||
if (element) {
|
||||
self.setStepsTree(element);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
this._tree = new Tree(this._tableContainer.nativeElement, {
|
||||
controller: this._treeController,
|
||||
dataSource: this._treeDataSource,
|
||||
filter: this._treeFilter,
|
||||
renderer: this._treeRenderer
|
||||
}, { verticalScrollMode: ScrollbarVisibility.Visible });
|
||||
this._register(attachListStyler(this._tree, this.themeService));
|
||||
this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement));
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobHistoryView);
|
||||
}
|
||||
|
||||
private loadHistory() {
|
||||
const self = this;
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let jobName = this._agentViewComponent.agentJobInfo.name;
|
||||
let jobId = this._agentViewComponent.jobId;
|
||||
this._jobManagementService.getJobHistory(ownerUri, jobId, jobName).then((result) => {
|
||||
if (result && result.histories) {
|
||||
self._jobCacheObject.setJobHistory(jobId, result.histories);
|
||||
self._jobCacheObject.setJobAlerts(jobId, result.alerts);
|
||||
self._jobCacheObject.setJobSchedules(jobId, result.schedules);
|
||||
self._jobCacheObject.setJobSteps(jobId, result.steps);
|
||||
this._agentViewComponent.agentJobInfo.jobSteps = this._jobCacheObject.getJobSteps(jobId);
|
||||
this._agentViewComponent.agentJobInfo.jobSchedules = this._jobCacheObject.getJobSchedules(jobId);
|
||||
this._agentViewComponent.agentJobInfo.alerts = this._jobCacheObject.getJobAlerts(jobId);
|
||||
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
|
||||
if (result.histories.length > 0) {
|
||||
self._noJobsAvailable = false;
|
||||
self._showPreviousRuns = true;
|
||||
self.buildHistoryTree(self, result.histories);
|
||||
} else {
|
||||
self._jobCacheObject.setJobHistory(self._agentViewComponent.jobId, result.histories);
|
||||
self._noJobsAvailable = true;
|
||||
self._showPreviousRuns = false;
|
||||
}
|
||||
} else {
|
||||
self._noJobsAvailable = true;
|
||||
self._showPreviousRuns = false;
|
||||
self._showSteps = false;
|
||||
}
|
||||
this._actionBar.context = { targetObject: { canEdit: true, job: this._agentJobInfo }, ownerUri: this.ownerUri, component: this };
|
||||
this._editJobAction.enabled = true;
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editJobAction }
|
||||
]);
|
||||
if (self._agentViewComponent.showHistory) {
|
||||
self._cd.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setStepsTree(element: JobHistoryRow) {
|
||||
const self = this;
|
||||
let cachedHistory = self._jobCacheObject.getJobHistory(element.jobID);
|
||||
if (cachedHistory) {
|
||||
self.agentJobHistoryInfo = find(cachedHistory,
|
||||
history => self.formatTime(history.runDate) === self.formatTime(element.runDate));
|
||||
} else {
|
||||
self.agentJobHistoryInfo = find(self._treeController.jobHistories,
|
||||
history => self.formatTime(history.runDate) === self.formatTime(element.runDate));
|
||||
}
|
||||
if (self.agentJobHistoryInfo) {
|
||||
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
|
||||
if (self.agentJobHistoryInfo.steps) {
|
||||
let jobStepStatus = this.didJobFail(self.agentJobHistoryInfo);
|
||||
self._stepRows = self.agentJobHistoryInfo.steps.map(step => {
|
||||
let stepViewRow = new JobStepsViewRow();
|
||||
stepViewRow.message = step.message;
|
||||
stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
|
||||
JobManagementUtilities.convertToStatusString(step.runStatus);
|
||||
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentJobHistoryInfo.runStatus);
|
||||
stepViewRow.stepName = step.stepDetails.stepName;
|
||||
stepViewRow.stepId = step.stepDetails.id.toString();
|
||||
return stepViewRow;
|
||||
});
|
||||
self._stepRows.unshift(new JobStepsViewRow());
|
||||
self._stepRows[0].rowID = 'stepsColumn' + self._agentJobInfo.jobId;
|
||||
self._stepRows[0].stepId = nls.localize('stepRow.stepID', "Step ID");
|
||||
self._stepRows[0].stepName = nls.localize('stepRow.stepName', "Step Name");
|
||||
self._stepRows[0].message = nls.localize('stepRow.message', "Message");
|
||||
this._showSteps = self._stepRows.length > 1;
|
||||
} else {
|
||||
self._showSteps = false;
|
||||
}
|
||||
if (self._agentViewComponent.showHistory) {
|
||||
self._cd.detectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private didJobFail(job: azdata.AgentJobHistoryInfo): boolean {
|
||||
for (let i = 0; i < job.steps.length; i++) {
|
||||
if (job.steps[i].runStatus === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private buildHistoryTree(self: any, jobHistories: azdata.AgentJobHistoryInfo[]) {
|
||||
self._treeController.jobHistories = jobHistories;
|
||||
let jobHistoryRows: JobHistoryRow[] = this._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job));
|
||||
let sortedRows = jobHistoryRows.sort((row1, row2) => {
|
||||
let date1 = new Date(row1.runDate).getTime();
|
||||
let date2 = new Date(row2.runDate).getTime();
|
||||
return date2 - date1;
|
||||
});
|
||||
self._treeDataSource.data = sortedRows;
|
||||
self._tree.setInput(new JobHistoryModel());
|
||||
self.agentJobHistoryInfo = self._treeController.jobHistories[0];
|
||||
if (self.agentJobHistoryInfo) {
|
||||
self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate);
|
||||
}
|
||||
const payload = { origin: 'origin' };
|
||||
let element = this._treeDataSource.getFirstElement();
|
||||
this._tree.setFocus(element, payload);
|
||||
this._tree.setSelection([element], payload);
|
||||
if (element.rowID) {
|
||||
self.setStepsTree(element);
|
||||
}
|
||||
}
|
||||
|
||||
public toggleCollapse(): void {
|
||||
let arrow: HTMLElement = jQuery('.resultsViewCollapsible').get(0);
|
||||
let checkbox: any = document.getElementById('accordion');
|
||||
if (arrow.className === 'resultsViewCollapsible' && checkbox.checked === false) {
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
} else if (arrow.className === 'resultsViewCollapsible collapsed' && checkbox.checked === true) {
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
}
|
||||
}
|
||||
|
||||
public goToJobs(): void {
|
||||
this._agentViewComponent.showHistory = false;
|
||||
}
|
||||
|
||||
private formatTime(time: string): string {
|
||||
return time.replace('T', ' ');
|
||||
}
|
||||
|
||||
private showProgressWheel(): boolean {
|
||||
return this._showPreviousRuns !== true && this._noJobsAvailable === false;
|
||||
}
|
||||
|
||||
public onFirstVisible() {
|
||||
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
|
||||
if (!this.agentJobInfo) {
|
||||
this.agentJobInfo = this._agentJobInfo;
|
||||
}
|
||||
|
||||
if (this.isRefreshing) {
|
||||
this.loadHistory();
|
||||
return;
|
||||
}
|
||||
|
||||
let jobHistories = this._jobCacheObject.jobHistories[this._agentViewComponent.jobId];
|
||||
if (jobHistories) {
|
||||
if (jobHistories.length > 0) {
|
||||
const self = this;
|
||||
this._noJobsAvailable = false;
|
||||
if (this._jobCacheObject.prevJobID === this._agentViewComponent.jobId || jobHistories[0].jobId === this._agentViewComponent.jobId) {
|
||||
this._showPreviousRuns = true;
|
||||
this._agentViewComponent.agentJobInfo.jobSteps = this._jobCacheObject.getJobSteps(this._agentJobInfo.jobId);
|
||||
this._agentViewComponent.agentJobInfo.jobSchedules = this._jobCacheObject.getJobSchedules(this._agentJobInfo.jobId);
|
||||
this._agentViewComponent.agentJobInfo.alerts = this._jobCacheObject.getJobAlerts(this._agentJobInfo.jobId);
|
||||
this._agentJobInfo = this._agentViewComponent.agentJobInfo;
|
||||
this.buildHistoryTree(self, jobHistories);
|
||||
}
|
||||
} else if (jobHistories.length === 0) {
|
||||
this._showPreviousRuns = false;
|
||||
this._showSteps = false;
|
||||
this._noJobsAvailable = true;
|
||||
}
|
||||
this._editJobAction.enabled = true;
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editJobAction }
|
||||
]);
|
||||
this._cd.detectChanges();
|
||||
|
||||
} else {
|
||||
this.loadHistory();
|
||||
}
|
||||
this._jobCacheObject.prevJobID = this._agentViewComponent.jobId;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let historyDetails = jQuery('.overview-container').get(0);
|
||||
let statusBar = jQuery('.part.statusbar').get(0);
|
||||
if (historyDetails && statusBar) {
|
||||
let historyBottom = historyDetails.getBoundingClientRect().bottom;
|
||||
let statusTop = statusBar.getBoundingClientRect().top;
|
||||
|
||||
let height: number = statusTop - historyBottom - JobHistoryComponent.HEADING_HEIGHT;
|
||||
|
||||
if (this._table) {
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._tableContainer.nativeElement),
|
||||
height));
|
||||
}
|
||||
|
||||
if (this._tree) {
|
||||
this._tree.layout(dom.getContentHeight(this._tableContainer.nativeElement));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
this._runJobAction = this.instantiationService.createInstance(RunJobAction);
|
||||
this._stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
||||
this._editJobAction = this.instantiationService.createInstance(EditJobAction);
|
||||
this._refreshAction = this.instantiationService.createInstance(JobsRefreshAction);
|
||||
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._editJobAction.enabled = !this.showProgressWheel();
|
||||
let targetObject: JobActionContext = { canEdit: !this.showProgressWheel(), job: this._agentJobInfo };
|
||||
this._actionBar.context = { targetObject: targetObject, ownerUri: this.ownerUri, component: this };
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editJobAction }
|
||||
]);
|
||||
}
|
||||
|
||||
/** GETTERS */
|
||||
|
||||
public get showSteps(): boolean {
|
||||
return this._showSteps;
|
||||
}
|
||||
|
||||
public get stepRows() {
|
||||
return this._stepRows;
|
||||
}
|
||||
|
||||
public get ownerUri(): string {
|
||||
return this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
}
|
||||
|
||||
public get serverName(): string {
|
||||
return this._serverName;
|
||||
}
|
||||
|
||||
/** SETTERS */
|
||||
|
||||
public set showSteps(value: boolean) {
|
||||
this._showSteps = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as tree from 'vs/base/parts/tree/browser/tree';
|
||||
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { AgentJobHistoryInfo } from 'azdata';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
|
||||
export class JobHistoryRow {
|
||||
runDate: string;
|
||||
runStatus: string;
|
||||
instanceID: number;
|
||||
rowID: string = generateUuid();
|
||||
jobID: string;
|
||||
}
|
||||
|
||||
// Empty class just for tree input
|
||||
export class JobHistoryModel {
|
||||
public static readonly id = generateUuid();
|
||||
}
|
||||
|
||||
export class JobHistoryController extends TreeDefaults.DefaultController {
|
||||
private _jobHistories: AgentJobHistoryInfo[];
|
||||
|
||||
protected onLeftClick(tree: tree.ITree, element: JobHistoryRow, event: IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public set jobHistories(value: AgentJobHistoryInfo[]) {
|
||||
this._jobHistories = value;
|
||||
}
|
||||
|
||||
public get jobHistories(): AgentJobHistoryInfo[] {
|
||||
return this._jobHistories;
|
||||
}
|
||||
|
||||
public onKeyDownWrapper(tree: tree.ITree, event: IKeyboardEvent): boolean {
|
||||
if (event.code === 'ArrowDown' || event.keyCode === 40) {
|
||||
super.onDown(tree, event);
|
||||
return super.onEnter(tree, event);
|
||||
} else if (event.code === 'ArrowUp' || event.keyCode === 38) {
|
||||
super.onUp(tree, event);
|
||||
return super.onEnter(tree, event);
|
||||
} else if (event.code !== 'Tab' && event.keyCode !== 2) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class JobHistoryDataSource implements tree.IDataSource {
|
||||
private _data: JobHistoryRow[];
|
||||
|
||||
public getId(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): string {
|
||||
if (element instanceof JobHistoryModel) {
|
||||
return JobHistoryModel.id;
|
||||
} else {
|
||||
return (element as JobHistoryRow).rowID;
|
||||
}
|
||||
}
|
||||
|
||||
public hasChildren(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): boolean {
|
||||
if (element instanceof JobHistoryModel) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public getChildren(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): Promise<JobHistoryRow[]> {
|
||||
if (element instanceof JobHistoryModel) {
|
||||
return Promise.resolve(this._data);
|
||||
} else {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public getParent(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): Promise<JobHistoryModel> {
|
||||
if (element instanceof JobHistoryModel) {
|
||||
return Promise.resolve(undefined);
|
||||
} else {
|
||||
return Promise.resolve(new JobHistoryModel());
|
||||
}
|
||||
}
|
||||
|
||||
public set data(data: JobHistoryRow[]) {
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
public getFirstElement() {
|
||||
return this._data[0];
|
||||
}
|
||||
}
|
||||
|
||||
export interface IListTemplate {
|
||||
statusIcon: HTMLElement;
|
||||
label: HTMLElement;
|
||||
}
|
||||
|
||||
export class JobHistoryRenderer implements tree.IRenderer {
|
||||
|
||||
public getHeight(tree: tree.ITree, element: JobHistoryRow): number {
|
||||
return 30;
|
||||
}
|
||||
|
||||
public getTemplateId(tree: tree.ITree, element: JobHistoryRow | JobHistoryModel): string {
|
||||
if (element instanceof JobHistoryModel) {
|
||||
return 'jobHistoryModel';
|
||||
} else {
|
||||
return 'jobHistoryInfo';
|
||||
}
|
||||
}
|
||||
|
||||
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
|
||||
let row = DOM.$('.list-row');
|
||||
let label = DOM.$('.label');
|
||||
let statusIcon = this.createStatusIcon();
|
||||
row.appendChild(statusIcon);
|
||||
row.appendChild(label);
|
||||
container.appendChild(row);
|
||||
return { statusIcon, label };
|
||||
}
|
||||
|
||||
public renderElement(tree: tree.ITree, element: JobHistoryRow, templateId: string, templateData: IListTemplate): void {
|
||||
templateData.label.innerHTML = element.runDate + ' ' + element.runStatus;
|
||||
let statusClass: string;
|
||||
if (element.runStatus === 'Succeeded') {
|
||||
statusClass = 'status-icon job-passed';
|
||||
} else if (element.runStatus === 'Failed') {
|
||||
statusClass = 'status-icon job-failed';
|
||||
} else {
|
||||
statusClass = 'status-icon job-unknown';
|
||||
}
|
||||
templateData.statusIcon.className = statusClass;
|
||||
}
|
||||
|
||||
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void {
|
||||
// no op
|
||||
}
|
||||
|
||||
private createStatusIcon(): HTMLElement {
|
||||
let statusIcon: HTMLElement = DOM.$('div');
|
||||
return statusIcon;
|
||||
}
|
||||
}
|
||||
|
||||
export class JobHistoryFilter implements tree.IFilter {
|
||||
|
||||
public isVisible(tree: tree.ITree, element: JobHistoryRow): boolean {
|
||||
return this._isJobVisible();
|
||||
}
|
||||
|
||||
private _isJobVisible(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public set filterString(val: string) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import { ElementRef, AfterContentChecked, ViewChild } from '@angular/core';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { IAction, Action } from 'vs/base/common/actions';
|
||||
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { JobsRefreshAction, IJobActionInfo } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
|
||||
|
||||
export abstract class JobManagementView extends TabChild implements AfterContentChecked {
|
||||
protected isVisible: boolean = false;
|
||||
protected isInitialized: boolean = false;
|
||||
protected isRefreshing: boolean = false;
|
||||
protected _showProgressWheel: boolean;
|
||||
protected _visibilityElement: ElementRef;
|
||||
protected _parentComponent: AgentViewComponent;
|
||||
protected _table: Table<any>;
|
||||
protected _actionBar: Taskbar;
|
||||
protected _serverName: string;
|
||||
public contextAction: any;
|
||||
|
||||
@ViewChild('actionbarContainer') protected actionBarContainer: ElementRef;
|
||||
|
||||
constructor(
|
||||
protected _commonService: CommonServiceInterface,
|
||||
protected _dashboardService: IDashboardService,
|
||||
protected _contextMenuService: IContextMenuService,
|
||||
protected _keybindingService: IKeybindingService,
|
||||
protected _instantiationService: IInstantiationService,
|
||||
protected _agentViewComponent: AgentViewComponent) {
|
||||
super();
|
||||
|
||||
let self = this;
|
||||
this._serverName = this._commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
this._dashboardService.onLayout((d) => {
|
||||
self.layout();
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
if (this._visibilityElement && this._parentComponent) {
|
||||
if (this.isVisible === false && this._visibilityElement.nativeElement.offsetParent !== null) {
|
||||
this.isVisible = true;
|
||||
if (!this.isInitialized) {
|
||||
this._showProgressWheel = true;
|
||||
this.onFirstVisible();
|
||||
this.layout();
|
||||
this.isInitialized = true;
|
||||
}
|
||||
} else if (this.isVisible === true && this._parentComponent.refresh === true) {
|
||||
this._showProgressWheel = true;
|
||||
this.isRefreshing = true;
|
||||
this.onFirstVisible();
|
||||
this.layout();
|
||||
this._parentComponent.refresh = false;
|
||||
} else if (this.isVisible === true && this._visibilityElement.nativeElement.offsetParent === null) {
|
||||
this.isVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract onFirstVisible();
|
||||
|
||||
protected openContextMenu(event: ITableMouseEvent): void {
|
||||
const rowIndex = event.cell.row;
|
||||
|
||||
const targetObject = this.getCurrentTableObject(rowIndex);
|
||||
const actions = this.getTableActions(targetObject);
|
||||
if (actions) {
|
||||
const ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
const actionContext: IJobActionInfo = {
|
||||
ownerUri: ownerUri,
|
||||
targetObject: targetObject,
|
||||
component: this
|
||||
};
|
||||
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => event.anchor,
|
||||
getActions: () => actions,
|
||||
getKeyBinding: (action) => this._keybindingFor(action),
|
||||
getActionsContext: () => (actionContext)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected _keybindingFor(action: IAction): ResolvedKeybinding {
|
||||
let [kb] = this._keybindingService.lookupKeybindings(action.id);
|
||||
return kb;
|
||||
}
|
||||
|
||||
protected getTableActions(targetObject?: JobActionContext): IAction[] {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected getCurrentTableObject(rowIndex: number): JobActionContext {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
let refreshAction = this._instantiationService.createInstance(JobsRefreshAction);
|
||||
let newAction: Action = this._instantiationService.createInstance(this.contextAction);
|
||||
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._actionBar.setContent([
|
||||
{ action: refreshAction },
|
||||
{ action: newAction }
|
||||
]);
|
||||
let context: IJobActionInfo = { component: this, ownerUri: this._commonService.connectionManagementService.connectionInfo.ownerUri };
|
||||
this._actionBar.context = context;
|
||||
}
|
||||
|
||||
public refreshJobs() {
|
||||
this._agentViewComponent.refresh = true;
|
||||
}
|
||||
|
||||
public openLastNRun(notebook: azdata.AgentNotebookInfo, n: number, maxVisibleElements: number) {
|
||||
}
|
||||
}
|
||||
|
||||
export interface JobActionContext {
|
||||
canEdit: boolean;
|
||||
job: azdata.AgentJobInfo;
|
||||
}
|
||||
|
||||
export interface NotebookActionContext {
|
||||
canEdit: boolean;
|
||||
notebook: azdata.AgentNotebookInfo;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
|
||||
<div class="steps-header">
|
||||
<div class="steps-icon" title="Steps"></div>
|
||||
<h1 style="display: inline">Steps</h1>
|
||||
</div>
|
||||
<div class='steps-tree' style="flex: 1 1 auto; position: relative">
|
||||
<div #table style="position: absolute; height: 100%; width: 100%" ></div>
|
||||
</div>
|
||||
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobStepsView';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { OnInit, Component, Inject, forwardRef, ElementRef, ViewChild, AfterContentChecked } from '@angular/core';
|
||||
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import {
|
||||
JobStepsViewController, JobStepsViewDataSource, JobStepsViewFilter,
|
||||
JobStepsViewRenderer, JobStepsViewModel
|
||||
} from 'sql/workbench/contrib/jobManagement/browser/jobStepsViewTree';
|
||||
import { JobHistoryComponent } from 'sql/workbench/contrib/jobManagement/browser/jobHistory.component';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
|
||||
export const JOBSTEPSVIEW_SELECTOR: string = 'jobstepsview-component';
|
||||
|
||||
@Component({
|
||||
selector: JOBSTEPSVIEW_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./jobStepsView.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => JobStepsViewComponent) }],
|
||||
})
|
||||
export class JobStepsViewComponent extends JobManagementView implements OnInit, AfterContentChecked {
|
||||
|
||||
private _tree: Tree;
|
||||
private _treeController = new JobStepsViewController();
|
||||
private _treeDataSource = new JobStepsViewDataSource();
|
||||
private _treeRenderer = new JobStepsViewRenderer();
|
||||
private _treeFilter = new JobStepsViewFilter();
|
||||
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => JobHistoryComponent)) private _jobHistoryComponent: JobHistoryComponent,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService, undefined);
|
||||
}
|
||||
|
||||
ngAfterContentChecked() {
|
||||
jQuery('.steps-tree .step-column-heading').closest('.monaco-tree-row').addClass('step-column-row');
|
||||
this.layout();
|
||||
this._tree.onDidScroll(() => {
|
||||
jQuery('.steps-tree .step-column-heading').closest('.monaco-tree-row').addClass('step-column-row');
|
||||
});
|
||||
this._treeController.onClick = (tree, element, event, origin = 'mouse') => {
|
||||
const payload = { origin: origin };
|
||||
const isDoubleClick = (origin === 'mouse' && event.detail === 2);
|
||||
// Cancel Event
|
||||
const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown';
|
||||
if (!isMouseDown) {
|
||||
event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise
|
||||
}
|
||||
event.stopPropagation();
|
||||
tree.setFocus(element, payload);
|
||||
if (element && isDoubleClick) {
|
||||
event.preventDefault(); // focus moves to editor, we need to prevent default
|
||||
} else {
|
||||
tree.setFocus(element, payload);
|
||||
tree.setSelection([element], payload);
|
||||
}
|
||||
jQuery('.steps-tree .step-column-heading').closest('.monaco-tree-row').addClass('step-column-row');
|
||||
return true;
|
||||
};
|
||||
this._treeController.onKeyDown = (tree, event) => {
|
||||
this._treeController.onKeyDownWrapper(tree, event);
|
||||
jQuery('.steps-tree .step-column-heading').closest('.monaco-tree-row').addClass('step-column-row');
|
||||
return true;
|
||||
};
|
||||
this._tree.onDidFocus(() => {
|
||||
this._tree.focusNth(1);
|
||||
let element = this._tree.getFocus();
|
||||
this._tree.select(element);
|
||||
});
|
||||
this._tree.setInput(new JobStepsViewModel());
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._treeDataSource.data = this._jobHistoryComponent.stepRows;
|
||||
this._tree = new Tree(this._tableContainer.nativeElement, {
|
||||
controller: this._treeController,
|
||||
dataSource: this._treeDataSource,
|
||||
filter: this._treeFilter,
|
||||
renderer: this._treeRenderer
|
||||
}, { verticalScrollMode: ScrollbarVisibility.Visible, horizontalScrollMode: ScrollbarVisibility.Visible });
|
||||
this._register(attachListStyler(this._tree, this.themeService));
|
||||
const stepsTooltip = nls.localize('agent.steps', "Steps");
|
||||
jQuery('.steps-header > .steps-icon').attr('title', stepsTooltip);
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobStepsView);
|
||||
}
|
||||
|
||||
public onFirstVisible() {
|
||||
}
|
||||
|
||||
public layout() {
|
||||
if (this._tree) {
|
||||
let treeheight = dom.getContentHeight(this._tableContainer.nativeElement);
|
||||
this._tree.layout(treeheight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as tree from 'vs/base/parts/tree/browser/tree';
|
||||
import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
|
||||
export class JobStepsViewRow {
|
||||
public stepId: string;
|
||||
public stepName: string;
|
||||
public message: string;
|
||||
public rowID: string = generateUuid();
|
||||
public runStatus: string;
|
||||
}
|
||||
|
||||
// Empty class just for tree input
|
||||
export class JobStepsViewModel {
|
||||
public static readonly id = generateUuid();
|
||||
}
|
||||
|
||||
export class JobStepsViewController extends TreeDefaults.DefaultController {
|
||||
|
||||
protected onLeftClick(tree: tree.ITree, element: JobStepsViewRow, event: IMouseEvent, origin: string = 'mouse'): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public onContextMenu(tree: tree.ITree, element: JobStepsViewRow, event: tree.ContextMenuEvent): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public onKeyDownWrapper(tree: tree.ITree, event: IKeyboardEvent): boolean {
|
||||
if (event.code === 'ArrowDown' || event.keyCode === 40) {
|
||||
super.onDown(tree, event);
|
||||
return super.onEnter(tree, event);
|
||||
} else if (event.code === 'ArrowUp' || event.keyCode === 38) {
|
||||
super.onUp(tree, event);
|
||||
return super.onEnter(tree, event);
|
||||
} else if (event.code !== 'Tab' && event.keyCode !== 2) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class JobStepsViewDataSource implements tree.IDataSource {
|
||||
private _data: JobStepsViewRow[];
|
||||
|
||||
public getId(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): string {
|
||||
if (element instanceof JobStepsViewModel) {
|
||||
return JobStepsViewModel.id;
|
||||
} else {
|
||||
return (element as JobStepsViewRow).rowID;
|
||||
}
|
||||
}
|
||||
|
||||
public hasChildren(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): boolean {
|
||||
if (element instanceof JobStepsViewModel) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public getChildren(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): Promise<JobStepsViewRow[]> {
|
||||
if (element instanceof JobStepsViewModel) {
|
||||
return Promise.resolve(this._data);
|
||||
} else {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public getParent(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): Promise<JobStepsViewModel> {
|
||||
if (element instanceof JobStepsViewModel) {
|
||||
return Promise.resolve(undefined);
|
||||
} else {
|
||||
return Promise.resolve(new JobStepsViewModel());
|
||||
}
|
||||
}
|
||||
|
||||
public set data(data: JobStepsViewRow[]) {
|
||||
this._data = data;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IListTemplate {
|
||||
statusIcon: HTMLElement;
|
||||
label: HTMLElement;
|
||||
}
|
||||
|
||||
export class JobStepsViewRenderer implements tree.IRenderer {
|
||||
|
||||
public getHeight(tree: tree.ITree, element: JobStepsViewRow): number {
|
||||
return 40;
|
||||
}
|
||||
|
||||
public getTemplateId(tree: tree.ITree, element: JobStepsViewRow | JobStepsViewModel): string {
|
||||
if (element instanceof JobStepsViewModel) {
|
||||
return 'jobStepsViewModel';
|
||||
} else {
|
||||
return 'jobStepsViewRow';
|
||||
}
|
||||
}
|
||||
|
||||
public renderTemplate(tree: tree.ITree, templateId: string, container: HTMLElement): IListTemplate {
|
||||
let row = DOM.$('.list-row');
|
||||
let label = DOM.$('.label');
|
||||
let statusIcon = this.createStatusIcon();
|
||||
row.appendChild(statusIcon);
|
||||
row.appendChild(label);
|
||||
container.appendChild(row);
|
||||
return { statusIcon, label };
|
||||
}
|
||||
|
||||
public renderElement(tree: tree.ITree, element: JobStepsViewRow, templateId: string, templateData: IListTemplate): void {
|
||||
let stepIdCol: HTMLElement = DOM.$('div');
|
||||
stepIdCol.className = 'tree-id-col';
|
||||
stepIdCol.innerText = element.stepId;
|
||||
let stepNameCol: HTMLElement = DOM.$('div');
|
||||
stepNameCol.className = 'tree-name-col';
|
||||
stepNameCol.innerText = element.stepName;
|
||||
let stepMessageCol: HTMLElement = DOM.$('div');
|
||||
stepMessageCol.className = 'tree-message-col';
|
||||
stepMessageCol.innerText = element.message;
|
||||
if (element.rowID.indexOf('stepsColumn') !== -1) {
|
||||
stepNameCol.className += ' step-column-heading';
|
||||
stepIdCol.className += ' step-column-heading';
|
||||
stepMessageCol.className += ' step-column-heading';
|
||||
}
|
||||
DOM.clearNode(templateData.label);
|
||||
templateData.label.appendChild(stepIdCol);
|
||||
templateData.label.appendChild(stepNameCol);
|
||||
templateData.label.appendChild(stepMessageCol);
|
||||
if (element.runStatus) {
|
||||
if (element.runStatus === 'Succeeded') {
|
||||
templateData.statusIcon.className = 'status-icon step-passed';
|
||||
} else if (element.runStatus === 'Failed') {
|
||||
templateData.statusIcon.className = 'status-icon step-failed';
|
||||
} else {
|
||||
templateData.statusIcon.className = 'status-icon step-unknown';
|
||||
}
|
||||
} else {
|
||||
templateData.statusIcon.className = '';
|
||||
}
|
||||
}
|
||||
|
||||
public disposeTemplate(tree: tree.ITree, templateId: string, templateData: IListTemplate): void {
|
||||
// no op
|
||||
}
|
||||
|
||||
private createStatusIcon(): HTMLElement {
|
||||
let statusIcon: HTMLElement = DOM.$('div');
|
||||
return statusIcon;
|
||||
}
|
||||
}
|
||||
|
||||
export class JobStepsViewFilter implements tree.IFilter {
|
||||
|
||||
public isVisible(tree: tree.ITree, element: JobStepsViewRow): boolean {
|
||||
return this._isJobVisible();
|
||||
}
|
||||
|
||||
private _isJobVisible(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public set filterString(val: string) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="job-heading-container">
|
||||
<h1 class="job-heading" *ngIf="_isCloud === false">Jobs</h1>
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Jobs Available</h1>
|
||||
<div class="codicon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<div #jobsgrid class="jobview-grid"></div>
|
||||
@@ -0,0 +1,961 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobs';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
import { JobCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { EditJobAction, DeleteJobAction, NewJobAction, RunJobAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { JobManagementUtilities } from 'sql/platform/jobManagement/browser/jobManagementUtilities';
|
||||
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { JobManagementView, JobActionContext } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { tableBackground, cellBackground, cellBorderColor } from 'sql/platform/theme/common/colors';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { find, fill } from 'vs/base/common/arrays';
|
||||
|
||||
export const JOBSVIEW_SELECTOR: string = 'jobsview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
export const ACTIONBAR_PADDING: number = 10;
|
||||
|
||||
interface IItem extends Slick.SlickData {
|
||||
jobId?: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: JOBSVIEW_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./jobsView.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => JobsViewComponent) }],
|
||||
})
|
||||
|
||||
export class JobsViewComponent extends JobManagementView implements OnInit, OnDestroy {
|
||||
|
||||
private columns: Array<Slick.Column<any>> = [
|
||||
{
|
||||
name: nls.localize('jobColumns.name', "Name"),
|
||||
field: 'name',
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
||||
width: 150,
|
||||
id: 'name'
|
||||
},
|
||||
{ name: nls.localize('jobColumns.lastRun', "Last Run"), field: 'lastRun', width: 80, id: 'lastRun' },
|
||||
{ name: nls.localize('jobColumns.nextRun', "Next Run"), field: 'nextRun', width: 80, id: 'nextRun' },
|
||||
{ name: nls.localize('jobColumns.enabled', "Enabled"), field: 'enabled', width: 60, id: 'enabled' },
|
||||
{ name: nls.localize('jobColumns.status', "Status"), field: 'currentExecutionStatus', width: 50, id: 'currentExecutionStatus' },
|
||||
{ name: nls.localize('jobColumns.category', "Category"), field: 'category', width: 100, id: 'category' },
|
||||
{ name: nls.localize('jobColumns.runnable', "Runnable"), field: 'runnable', width: 70, id: 'runnable' },
|
||||
{ name: nls.localize('jobColumns.schedule', "Schedule"), field: 'hasSchedule', width: 60, id: 'hasSchedule' },
|
||||
{ name: nls.localize('jobColumns.lastRunOutcome', "Last Run Outcome"), field: 'lastRunOutcome', width: 100, id: 'lastRunOutcome' },
|
||||
{
|
||||
name: nls.localize('jobColumns.previousRuns', "Previous Runs"),
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderChartsPostHistory(row, cell, value, columnDef, dataContext),
|
||||
field: 'previousRuns',
|
||||
width: 100,
|
||||
id: 'previousRuns'
|
||||
}
|
||||
];
|
||||
|
||||
private _jobCacheObject: JobCacheObject;
|
||||
private rowDetail: RowDetailView<IItem>;
|
||||
private filterPlugin: any;
|
||||
private dataView: any;
|
||||
public _isCloud: boolean;
|
||||
private filterStylingMap: { [columnName: string]: [any]; } = {};
|
||||
private filterStack = ['start'];
|
||||
private filterValueMap: { [columnName: string]: string[]; } = {};
|
||||
private sortingStylingMap: { [columnName: string]: any; } = {};
|
||||
|
||||
public jobs: azdata.AgentJobInfo[];
|
||||
private jobHistories: { [jobId: string]: azdata.AgentJobHistoryInfo[]; } = Object.create(null);
|
||||
private jobSteps: { [jobId: string]: azdata.AgentJobStepInfo[]; } = Object.create(null);
|
||||
private jobAlerts: { [jobId: string]: azdata.AgentAlertInfo[]; } = Object.create(null);
|
||||
private jobSchedules: { [jobId: string]: azdata.AgentJobScheduleInfo[]; } = Object.create(null);
|
||||
public contextAction = NewJobAction;
|
||||
|
||||
@ViewChild('jobsgrid') _gridEl: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(IWorkbenchThemeService) private _themeService: IWorkbenchThemeService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
let jobCacheObjectMap = this._jobManagementService.jobCacheObjectMap;
|
||||
let jobCache = jobCacheObjectMap[this._serverName];
|
||||
if (jobCache) {
|
||||
this._jobCacheObject = jobCache;
|
||||
} else {
|
||||
this._jobCacheObject = new JobCacheObject();
|
||||
this._jobCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._jobCacheObject);
|
||||
}
|
||||
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._gridEl;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e)));
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobsView);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let jobsViewToolbar = jQuery('jobsview-component .agent-actionbar-container').get(0);
|
||||
let statusBar = jQuery('.part.statusbar').get(0);
|
||||
if (jobsViewToolbar && statusBar) {
|
||||
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom + ACTIONBAR_PADDING;
|
||||
let statusTop = statusBar.getBoundingClientRect().top;
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._gridEl.nativeElement),
|
||||
statusTop - toolbarBottom));
|
||||
}
|
||||
}
|
||||
|
||||
onFirstVisible() {
|
||||
let self = this;
|
||||
let cached: boolean = false;
|
||||
if (this._jobCacheObject.serverName === this._serverName && this._jobCacheObject.jobs.length > 0) {
|
||||
cached = true;
|
||||
this.jobs = this._jobCacheObject.jobs;
|
||||
}
|
||||
|
||||
let columns = this.columns.map((column) => {
|
||||
column.rerenderOnResize = true;
|
||||
return column;
|
||||
});
|
||||
let options = <Slick.GridOptions<any>>{
|
||||
syncColumnCellResize: true,
|
||||
enableColumnReorder: false,
|
||||
rowHeight: ROW_HEIGHT,
|
||||
enableCellNavigation: true,
|
||||
forceFitColumns: false
|
||||
};
|
||||
|
||||
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
||||
|
||||
let rowDetail = new RowDetailView<IItem>({
|
||||
cssClass: '_detail_selector',
|
||||
process: (job) => {
|
||||
(<any>rowDetail).onAsyncResponse.notify({
|
||||
'itemDetail': job
|
||||
}, undefined, this);
|
||||
},
|
||||
useRowClick: false,
|
||||
panelRows: 1,
|
||||
postTemplate: () => '', // I'm assuming these code paths are just never hit...
|
||||
preTemplate: () => '',
|
||||
});
|
||||
this.rowDetail = rowDetail;
|
||||
columns.unshift(this.rowDetail.getColumnDefinition());
|
||||
let filterPlugin = new HeaderFilter<{ inlineFilters: false }>();
|
||||
this._register(attachButtonStyler(filterPlugin, this._themeService));
|
||||
this.filterPlugin = filterPlugin;
|
||||
jQuery(this._gridEl.nativeElement).empty();
|
||||
jQuery(this.actionBarContainer.nativeElement).empty();
|
||||
this.initActionBar();
|
||||
this._table = new Table(this._gridEl.nativeElement, { columns }, options);
|
||||
this._table.grid.setData(this.dataView, true);
|
||||
this._table.grid.onClick.subscribe((e, args) => {
|
||||
let job = self.getJob(args);
|
||||
self._agentViewComponent.jobId = job.jobId;
|
||||
self._agentViewComponent.agentJobInfo = job;
|
||||
self._agentViewComponent.showHistory = true;
|
||||
});
|
||||
this._register(this._table.onContextMenu(e => {
|
||||
self.openContextMenu(e);
|
||||
}));
|
||||
|
||||
if (cached && this._agentViewComponent.refresh !== true) {
|
||||
this.onJobsAvailable(null);
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
} else {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._jobManagementService.getJobs(ownerUri).then((result) => {
|
||||
if (result && result.jobs) {
|
||||
self.jobs = result.jobs;
|
||||
self._jobCacheObject.jobs = self.jobs;
|
||||
self.onJobsAvailable(result.jobs);
|
||||
} else {
|
||||
// TODO: handle error
|
||||
}
|
||||
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onJobsAvailable(jobs: azdata.AgentJobInfo[]) {
|
||||
let jobViews: any;
|
||||
let start: boolean = true;
|
||||
if (!jobs) {
|
||||
let dataView = this._jobCacheObject.dataView;
|
||||
jobViews = dataView.getItems();
|
||||
start = false;
|
||||
} else {
|
||||
jobViews = jobs.map((job) => {
|
||||
return {
|
||||
id: job.jobId,
|
||||
jobId: job.jobId,
|
||||
name: job.name,
|
||||
lastRun: JobManagementUtilities.convertToLastRun(job.lastRun),
|
||||
nextRun: JobManagementUtilities.convertToNextRun(job.nextRun),
|
||||
enabled: JobManagementUtilities.convertToResponse(job.enabled),
|
||||
currentExecutionStatus: JobManagementUtilities.convertToExecutionStatusString(job.currentExecutionStatus),
|
||||
category: job.category,
|
||||
runnable: JobManagementUtilities.convertToResponse(job.runnable),
|
||||
hasSchedule: JobManagementUtilities.convertToResponse(job.hasSchedule),
|
||||
lastRunOutcome: JobManagementUtilities.convertToStatusString(job.lastRunOutcome)
|
||||
};
|
||||
});
|
||||
}
|
||||
this._table.registerPlugin(<any>this.rowDetail);
|
||||
this.filterPlugin.onFilterApplied.subscribe((e, args) => {
|
||||
this.dataView.refresh();
|
||||
this._table.grid.resetActiveCell();
|
||||
let filterValues = args.column.filterValues;
|
||||
if (filterValues) {
|
||||
if (filterValues.length === 0) {
|
||||
// if an associated styling exists with the current filters
|
||||
if (this.filterStylingMap[args.column.name]) {
|
||||
let filterLength = this.filterStylingMap[args.column.name].length;
|
||||
// then remove the filtered styling
|
||||
for (let i = 0; i < filterLength; i++) {
|
||||
let lastAppliedStyle = this.filterStylingMap[args.column.name].pop();
|
||||
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
|
||||
}
|
||||
delete this.filterStylingMap[args.column.name];
|
||||
let index = this.filterStack.indexOf(args.column.name, 0);
|
||||
if (index > -1) {
|
||||
this.filterStack.splice(index, 1);
|
||||
delete this.filterValueMap[args.column.name];
|
||||
}
|
||||
// apply the previous filter styling
|
||||
let currentItems = this.dataView.getFilteredItems();
|
||||
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length - 1]][1];
|
||||
if (styledItems === currentItems) {
|
||||
let lastColStyle = this.filterStylingMap[this.filterStack[this.filterStack.length - 1]];
|
||||
for (let i = 0; i < lastColStyle.length; i++) {
|
||||
this._table.grid.setCellCssStyles(lastColStyle[i][0], lastColStyle[i][1]);
|
||||
}
|
||||
} else {
|
||||
// style it all over again
|
||||
let seenJobs = 0;
|
||||
for (let i = 0; i < currentItems.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
let item = this.dataView.getFilteredItems()[i];
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||
if (this.filterStack.indexOf(args.column.name) < 0) {
|
||||
this.filterStack.push(args.column.name);
|
||||
this.filterValueMap[args.column.name] = [filterValues];
|
||||
}
|
||||
// one expansion for the row and one for
|
||||
// the error detail
|
||||
seenJobs++;
|
||||
i++;
|
||||
}
|
||||
seenJobs++;
|
||||
}
|
||||
this.dataView.refresh();
|
||||
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
|
||||
this._table.grid.resetActiveCell();
|
||||
}
|
||||
if (this.filterStack.length === 0) {
|
||||
this.filterStack = ['start'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let seenJobs = 0;
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
let item = this.dataView.getItemByIdx(i);
|
||||
// current filter
|
||||
if (find(filterValues, x => x === item[args.column.field])) {
|
||||
// check all previous filters
|
||||
if (this.checkPreviousFilters(item)) {
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||
if (this.filterStack.indexOf(args.column.name) < 0) {
|
||||
this.filterStack.push(args.column.name);
|
||||
this.filterValueMap[args.column.name] = [filterValues];
|
||||
}
|
||||
// one expansion for the row and one for
|
||||
// the error detail
|
||||
seenJobs++;
|
||||
i++;
|
||||
}
|
||||
seenJobs++;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dataView.refresh();
|
||||
if (this.filterValueMap[args.column.name]) {
|
||||
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
|
||||
} else {
|
||||
this.filterValueMap[args.column.name] = this.dataView.getFilteredItems();
|
||||
}
|
||||
|
||||
this._table.grid.resetActiveCell();
|
||||
}
|
||||
} else {
|
||||
this.expandJobs(false);
|
||||
}
|
||||
});
|
||||
|
||||
this.filterPlugin.onCommand.subscribe((e, args: any) => {
|
||||
this.columnSort(args.column.name, args.command === 'sort-asc');
|
||||
});
|
||||
this._table.registerPlugin(this.filterPlugin);
|
||||
|
||||
this.dataView.beginUpdate();
|
||||
this.dataView.setItems(jobViews);
|
||||
this.dataView.setFilter((item) => this.filter(item));
|
||||
this.dataView.endUpdate();
|
||||
this._table.autosizeColumns();
|
||||
this._table.resizeCanvas();
|
||||
|
||||
this.expandJobs(start);
|
||||
// tooltip for job name
|
||||
jQuery('.jobview-jobnamerow').hover(e => {
|
||||
let currentTarget = e.currentTarget;
|
||||
currentTarget.title = currentTarget.innerText;
|
||||
});
|
||||
|
||||
const self = this;
|
||||
this._table.grid.onColumnsResized.subscribe((e, data: any) => {
|
||||
let nameWidth: number = data.grid.getColumns()[1].width;
|
||||
// adjust job name when resized
|
||||
jQuery('#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext').css('width', `${nameWidth - 10}px`);
|
||||
// adjust error message when resized
|
||||
jQuery('#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext').css('width', '100%');
|
||||
|
||||
// generate job charts again
|
||||
self.jobs.forEach(job => {
|
||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||
if (jobHistories) {
|
||||
let previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
||||
self.createJobChart(job.jobId, previousRuns);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
jQuery('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
||||
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
||||
|
||||
this._table.grid.onScroll.subscribe((e) => {
|
||||
jQuery('#jobsDiv .jobview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
||||
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
||||
});
|
||||
|
||||
// cache the dataview for future use
|
||||
this._jobCacheObject.dataView = this.dataView;
|
||||
this.filterValueMap['start'] = [[], this.dataView.getItems()];
|
||||
this.loadJobHistories();
|
||||
}
|
||||
|
||||
private highlightErrorRows(e) {
|
||||
// highlight the error row as well if a failing job row is hovered
|
||||
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||
let target = jQuery(e.currentTarget);
|
||||
let targetChildren = jQuery(e.currentTarget.children);
|
||||
let siblings = target.nextAll().toArray();
|
||||
let top = parseInt(target.css('top'), 10);
|
||||
for (let i = 0; i < siblings.length; i++) {
|
||||
let sibling = siblings[i];
|
||||
let siblingTop = parseInt(jQuery(sibling).css('top'), 10);
|
||||
if (siblingTop === top + ROW_HEIGHT) {
|
||||
jQuery(sibling.children).addClass('hovered');
|
||||
sibling.onmouseenter = (e) => {
|
||||
targetChildren.addClass('hovered');
|
||||
};
|
||||
sibling.onmouseleave = (e) => {
|
||||
targetChildren.removeClass('hovered');
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private hightlightNonErrorRows(e) {
|
||||
// switch back to original background
|
||||
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||
let target = jQuery(e.currentTarget);
|
||||
let siblings = target.nextAll().toArray();
|
||||
let top = parseInt(target.css('top'), 10);
|
||||
for (let i = 0; i < siblings.length; i++) {
|
||||
let sibling = siblings[i];
|
||||
let siblingTop = parseInt(jQuery(sibling).css('top'), 10);
|
||||
if (siblingTop === top + ROW_HEIGHT) {
|
||||
jQuery(sibling.children).removeClass('hovered');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setRowWithErrorClass(hash: { [index: number]: { [id: string]: string; } }, row: number, errorClass: string) {
|
||||
hash[row] = {
|
||||
'_detail_selector': errorClass,
|
||||
'id': errorClass,
|
||||
'jobId': errorClass,
|
||||
'name': errorClass,
|
||||
'lastRun': errorClass,
|
||||
'nextRun': errorClass,
|
||||
'enabled': errorClass,
|
||||
'currentExecutionStatus': errorClass,
|
||||
'category': errorClass,
|
||||
'runnable': errorClass,
|
||||
'hasSchedule': errorClass,
|
||||
'lastRunOutcome': errorClass,
|
||||
'previousRuns': errorClass
|
||||
};
|
||||
return hash;
|
||||
}
|
||||
|
||||
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
||||
let hash: {
|
||||
[index: number]: {
|
||||
[id: string]: string;
|
||||
}
|
||||
} = {};
|
||||
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||
hash = this.setRowWithErrorClass(hash, row + 1, 'error-row');
|
||||
if (start) {
|
||||
if (map['start']) {
|
||||
map['start'].push(['error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map['start'] = [['error-row' + row.toString(), hash]];
|
||||
}
|
||||
} else {
|
||||
if (map[columnName]) {
|
||||
map[columnName].push(['error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map[columnName] = [['error-row' + row.toString(), hash]];
|
||||
}
|
||||
}
|
||||
this._table.grid.setCellCssStyles('error-row' + row.toString(), hash);
|
||||
}
|
||||
|
||||
private 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 ('Cancelled'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorcancel';
|
||||
break;
|
||||
case ('Status Unknown'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorunknown';
|
||||
break;
|
||||
default:
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorfailure';
|
||||
break;
|
||||
}
|
||||
|
||||
return '<table class="jobview-jobnametable"><tr class="jobview-jobnamerow">' +
|
||||
'<td nowrap class=' + resultIndicatorClass + '></td>' +
|
||||
'<td nowrap class="jobview-jobnametext">' + escape(dataContext.name) + '</td>' +
|
||||
'</tr></table>';
|
||||
}
|
||||
|
||||
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
||||
let runChart = this._jobCacheObject.getRunChart(dataContext.id);
|
||||
if (runChart && runChart.length > 0) {
|
||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||
<tr>
|
||||
<td>${runChart[0] ? runChart[0] : '<div class="bar0"></div>'}</td>
|
||||
<td>${runChart[1] ? runChart[1] : '<div class="bar1"></div>'}</td>
|
||||
<td>${runChart[2] ? runChart[2] : '<div class="bar2"></div>'}</td>
|
||||
<td>${runChart[3] ? runChart[3] : '<div class="bar3"></div>'}</td>
|
||||
<td>${runChart[4] ? runChart[4] : '<div class="bar4"></div>'}</td>
|
||||
</tr>
|
||||
</table>`;
|
||||
} else {
|
||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||
<tr>
|
||||
<td><div class="bar0"></div></td>
|
||||
<td><div class="bar1"></div></td>
|
||||
<td><div class="bar2"></div></td>
|
||||
<td><div class="bar3"></div></td>
|
||||
<td><div class="bar4"></div></td>
|
||||
</tr>
|
||||
</table>`;
|
||||
}
|
||||
}
|
||||
|
||||
private expandJobRowDetails(rowIdx: number, message?: string): void {
|
||||
let item = this.dataView.getItemByIdx(rowIdx);
|
||||
item.message = this._agentViewComponent.expanded.get(item.jobId);
|
||||
this.rowDetail.applyTemplateNewLineHeight(item, true);
|
||||
}
|
||||
|
||||
private async loadJobHistories() {
|
||||
if (this.jobs) {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let separatedJobs = this.separateFailingJobs();
|
||||
// grab histories of the failing jobs first
|
||||
// so they can be expanded quicker
|
||||
let failing = separatedJobs[0];
|
||||
let passing = separatedJobs[1];
|
||||
Promise.all([this.curateJobHistory(failing, ownerUri), this.curateJobHistory(passing, ownerUri)]);
|
||||
}
|
||||
}
|
||||
|
||||
private separateFailingJobs(): azdata.AgentJobInfo[][] {
|
||||
let failing = [];
|
||||
let nonFailing = [];
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
if (this.jobs[i].lastRunOutcome === 0) {
|
||||
failing.push(this.jobs[i]);
|
||||
} else {
|
||||
nonFailing.push(this.jobs[i]);
|
||||
}
|
||||
}
|
||||
return [failing, nonFailing];
|
||||
}
|
||||
|
||||
private checkPreviousFilters(item): boolean {
|
||||
for (let column in this.filterValueMap) {
|
||||
if (column !== 'start' && this.filterValueMap[column][0].length > 0) {
|
||||
if (!find(this.filterValueMap[column][0], x => x === item[JobManagementUtilities.convertColNameToField(column)])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private isErrorRow(cell: HTMLElement) {
|
||||
return cell.classList.contains('error-row');
|
||||
}
|
||||
|
||||
private getJob(args: Slick.OnClickEventArgs<any>): azdata.AgentJobInfo {
|
||||
let row = args.row;
|
||||
let jobName: string;
|
||||
let cell = args.grid.getCellNode(row, 1);
|
||||
if (this.isErrorRow(cell)) {
|
||||
jobName = args.grid.getCellNode(row - 1, 1).innerText.trim();
|
||||
} else {
|
||||
jobName = cell.innerText.trim();
|
||||
}
|
||||
let job = this.jobs.filter(job => job.name === jobName)[0];
|
||||
return job;
|
||||
}
|
||||
|
||||
private async curateJobHistory(jobs: azdata.AgentJobInfo[], ownerUri: string) {
|
||||
const self = this;
|
||||
for (let job of jobs) {
|
||||
let result = await this._jobManagementService.getJobHistory(ownerUri, job.jobId, job.name);
|
||||
if (result) {
|
||||
self.jobSteps[job.jobId] = result.steps ? result.steps : [];
|
||||
self.jobAlerts[job.jobId] = result.alerts ? result.alerts : [];
|
||||
self.jobSchedules[job.jobId] = result.schedules ? result.schedules : [];
|
||||
self.jobHistories[job.jobId] = result.histories ? result.histories : [];
|
||||
self._jobCacheObject.setJobSteps(job.jobId, self.jobSteps[job.jobId]);
|
||||
self._jobCacheObject.setJobHistory(job.jobId, self.jobHistories[job.jobId]);
|
||||
self._jobCacheObject.setJobAlerts(job.jobId, self.jobAlerts[job.jobId]);
|
||||
self._jobCacheObject.setJobSchedules(job.jobId, self.jobSchedules[job.jobId]);
|
||||
let jobHistories = self._jobCacheObject.getJobHistory(job.jobId);
|
||||
let previousRuns: azdata.AgentJobHistoryInfo[];
|
||||
if (jobHistories.length >= 5) {
|
||||
previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
||||
} else {
|
||||
previousRuns = jobHistories;
|
||||
}
|
||||
self.createJobChart(job.jobId, previousRuns);
|
||||
if (self._agentViewComponent.expanded.has(job.jobId)) {
|
||||
let lastJobHistory = jobHistories[jobHistories.length - 1];
|
||||
let item = self.dataView.getItemById(job.jobId + '.error');
|
||||
let noStepsMessage = nls.localize('jobsView.noSteps', "No Steps available for this job.");
|
||||
let errorMessage = lastJobHistory ? lastJobHistory.message : noStepsMessage;
|
||||
if (item) {
|
||||
item['name'] = nls.localize('jobsView.error', "Error: ") + errorMessage;
|
||||
self._agentViewComponent.setExpanded(job.jobId, item['name']);
|
||||
self.dataView.updateItem(job.jobId + '.error', item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createJobChart(jobId: string, jobHistories: azdata.AgentJobHistoryInfo[]): void {
|
||||
let chartHeights = this.getChartHeights(jobHistories);
|
||||
let runCharts = [];
|
||||
for (let i = 0; i < chartHeights.length; i++) {
|
||||
let runGraph = jQuery(`table.jobprevruns#${jobId} > tbody > tr > td > div.bar${i}`);
|
||||
if (runGraph.length > 0) {
|
||||
runGraph.css('height', chartHeights[i]);
|
||||
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
|
||||
runGraph.css('background', bgColor);
|
||||
runGraph.hover((e) => {
|
||||
let currentTarget = e.currentTarget;
|
||||
currentTarget.title = jobHistories[i].runDuration;
|
||||
});
|
||||
runCharts.push(runGraph.get(0).outerHTML);
|
||||
}
|
||||
}
|
||||
if (runCharts.length > 0) {
|
||||
this._jobCacheObject.setRunChart(jobId, runCharts);
|
||||
}
|
||||
}
|
||||
|
||||
// chart height normalization logic
|
||||
private getChartHeights(jobHistories: azdata.AgentJobHistoryInfo[]): string[] {
|
||||
if (!jobHistories || jobHistories.length === 0) {
|
||||
return [];
|
||||
}
|
||||
let maxDuration: number = 0;
|
||||
jobHistories.forEach(history => {
|
||||
let historyDuration = JobManagementUtilities.convertDurationToSeconds(history.runDuration);
|
||||
if (historyDuration > maxDuration) {
|
||||
maxDuration = historyDuration;
|
||||
}
|
||||
});
|
||||
maxDuration = maxDuration === 0 ? 1 : maxDuration;
|
||||
let maxBarHeight: number = 24;
|
||||
let chartHeights = [];
|
||||
let zeroDurationJobCount = 0;
|
||||
for (let i = 0; i < jobHistories.length; i++) {
|
||||
let duration = jobHistories[i].runDuration;
|
||||
let chartHeight = (maxBarHeight * JobManagementUtilities.convertDurationToSeconds(duration)) / maxDuration;
|
||||
chartHeights.push(`${chartHeight}px`);
|
||||
if (chartHeight === 0) {
|
||||
zeroDurationJobCount++;
|
||||
}
|
||||
}
|
||||
// if the durations are all 0 secs, show minimal chart
|
||||
// instead of nothing
|
||||
if (zeroDurationJobCount === jobHistories.length) {
|
||||
return fill(jobHistories.length, '5px');
|
||||
} else {
|
||||
return chartHeights;
|
||||
}
|
||||
}
|
||||
|
||||
private expandJobs(start: boolean): void {
|
||||
if (start) {
|
||||
this._agentViewComponent.expanded = new Map<string, string>();
|
||||
}
|
||||
let expandedJobs = this._agentViewComponent.expanded;
|
||||
let expansions = 0;
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
let job = this.jobs[i];
|
||||
if (job.lastRunOutcome === 0 && !expandedJobs.get(job.jobId)) {
|
||||
this.expandJobRowDetails(i + expandedJobs.size);
|
||||
this.addToStyleHash(i + expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||
this._agentViewComponent.setExpanded(job.jobId, 'Loading Error...');
|
||||
} else if (job.lastRunOutcome === 0 && expandedJobs.get(job.jobId)) {
|
||||
this.addToStyleHash(i + expansions, start, this.filterStylingMap, undefined);
|
||||
expansions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private filter(item: any) {
|
||||
let columns = this._table.grid.getColumns();
|
||||
let value = true;
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
let col: any = columns[i];
|
||||
let filterValues = col.filterValues;
|
||||
if (filterValues && filterValues.length > 0) {
|
||||
if (item._parent) {
|
||||
value = value && find(filterValues, x => x === item._parent[col.field]);
|
||||
} else {
|
||||
value = value && find(filterValues, x => x === item[col.field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private columnSort(column: string, isAscending: boolean) {
|
||||
let items = this.dataView.getItems();
|
||||
// get error items here and remove them
|
||||
let jobItems = items.filter(x => x._parent === undefined);
|
||||
let errorItems = items.filter(x => x._parent !== undefined);
|
||||
this.sortingStylingMap[column] = items;
|
||||
switch (column) {
|
||||
case ('Name'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.name.localeCompare(item2.name);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Last Run'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, true), isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Next Run'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Enabled'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.enabled.localeCompare(item2.enabled);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Status'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.currentExecutionStatus.localeCompare(item2.currentExecutionStatus);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Category'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.category.localeCompare(item2.category);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Runnable'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.runnable.localeCompare(item2.runnable);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Schedule'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.hasSchedule.localeCompare(item2.hasSchedule);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Last Run Outcome'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.lastRunOutcome.localeCompare(item2.lastRunOutcome);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// insert the errors back again
|
||||
let jobItemsLength = jobItems.length;
|
||||
for (let i = 0; i < jobItemsLength; i++) {
|
||||
let item = jobItems[i];
|
||||
if (item._child) {
|
||||
let child = errorItems.find(error => error === item._child);
|
||||
jobItems.splice(i + 1, 0, child);
|
||||
jobItemsLength++;
|
||||
}
|
||||
}
|
||||
this.dataView.setItems(jobItems);
|
||||
// remove old style
|
||||
if (this.filterStylingMap[column]) {
|
||||
let filterLength = this.filterStylingMap[column].length;
|
||||
for (let i = 0; i < filterLength; i++) {
|
||||
let lastAppliedStyle = this.filterStylingMap[column].pop();
|
||||
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.jobs.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
}
|
||||
}
|
||||
// add new style to the items back again
|
||||
items = this.filterStack.length > 1 ? this.dataView.getFilteredItems() : this.dataView.getItems();
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(i, false, this.sortingStylingMap, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private dateCompare(item1: any, item2: any, lastRun: boolean): number {
|
||||
let exceptionString = lastRun ? 'Never Run' : 'Not Scheduled';
|
||||
if (item2.lastRun === exceptionString && item1.lastRun !== exceptionString) {
|
||||
return -1;
|
||||
} else if (item1.lastRun === exceptionString && item2.lastRun !== exceptionString) {
|
||||
return 1;
|
||||
} else if (item1.lastRun === exceptionString && item2.lastRun === exceptionString) {
|
||||
return 0;
|
||||
} else {
|
||||
let date1 = new Date(item1.lastRun);
|
||||
let date2 = new Date(item2.lastRun);
|
||||
if (date1 > date2) {
|
||||
return 1;
|
||||
} else if (date1 === date2) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme) {
|
||||
let bgColor = theme.getColor(tableBackground);
|
||||
let cellColor = theme.getColor(cellBackground);
|
||||
let borderColor = theme.getColor(cellBorderColor);
|
||||
let headerColumns = jQuery('#agentViewDiv .slick-header-column');
|
||||
let cells = jQuery('.grid-canvas .ui-widget-content.slick-row .slick-cell');
|
||||
let cellDetails = jQuery('#jobsDiv .dynamic-cell-detail');
|
||||
headerColumns.toArray().forEach(col => {
|
||||
col.style.background = bgColor.toString();
|
||||
});
|
||||
cells.toArray().forEach(cell => {
|
||||
cell.style.background = bgColor.toString();
|
||||
cell.style.border = borderColor ? '1px solid ' + borderColor.toString() : null;
|
||||
});
|
||||
cellDetails.toArray().forEach(cellDetail => {
|
||||
cellDetail.style.background = cellColor.toString();
|
||||
});
|
||||
}
|
||||
|
||||
protected getTableActions(targetObject: JobActionContext): IAction[] {
|
||||
const editAction = this._instantiationService.createInstance(EditJobAction);
|
||||
const runJobAction = this._instantiationService.createInstance(RunJobAction);
|
||||
if (!targetObject.canEdit) {
|
||||
editAction.enabled = false;
|
||||
}
|
||||
return [
|
||||
runJobAction,
|
||||
editAction,
|
||||
this._instantiationService.createInstance(DeleteJobAction)
|
||||
];
|
||||
}
|
||||
|
||||
protected convertStepsToStepInfos(steps: azdata.AgentJobStep[], job: azdata.AgentJobInfo): azdata.AgentJobStepInfo[] {
|
||||
let result = [];
|
||||
steps.forEach(step => {
|
||||
let stepInfo: azdata.AgentJobStepInfo = {
|
||||
jobId: job.jobId,
|
||||
jobName: job.name,
|
||||
script: null,
|
||||
scriptName: null,
|
||||
stepName: step.stepName,
|
||||
subSystem: null,
|
||||
id: +step.stepId,
|
||||
failureAction: null,
|
||||
successAction: null,
|
||||
failStepId: null,
|
||||
successStepId: null,
|
||||
command: null,
|
||||
commandExecutionSuccessCode: null,
|
||||
databaseName: null,
|
||||
databaseUserName: null,
|
||||
server: null,
|
||||
outputFileName: null,
|
||||
appendToLogFile: null,
|
||||
appendToStepHist: null,
|
||||
writeLogToTable: null,
|
||||
appendLogToTable: null,
|
||||
retryAttempts: null,
|
||||
retryInterval: null,
|
||||
proxyName: null
|
||||
};
|
||||
result.push(stepInfo);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
protected getCurrentTableObject(rowIndex: number): JobActionContext {
|
||||
let data = this._table.grid.getData() as Slick.DataProvider<IItem>;
|
||||
if (!data || rowIndex >= data.getLength()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let jobId = data.getItem(rowIndex).jobId;
|
||||
if (!jobId) {
|
||||
// if we couldn't find the ID, check if it's an
|
||||
// error row
|
||||
let isErrorRow: boolean = data.getItem(rowIndex).id.indexOf('error') >= 0;
|
||||
if (isErrorRow) {
|
||||
jobId = data.getItem(rowIndex - 1).jobId;
|
||||
}
|
||||
}
|
||||
|
||||
let job: azdata.AgentJobInfo[] = this.jobs.filter(job => {
|
||||
return job.jobId === jobId;
|
||||
});
|
||||
|
||||
if (job && job.length > 0) {
|
||||
// add steps
|
||||
if (this.jobSteps && this.jobSteps[jobId]) {
|
||||
let steps = this.jobSteps[jobId];
|
||||
job[0].jobSteps = steps;
|
||||
}
|
||||
|
||||
// add schedules
|
||||
if (this.jobSchedules && this.jobSchedules[jobId]) {
|
||||
let schedules = this.jobSchedules[jobId];
|
||||
job[0].jobSchedules = schedules;
|
||||
}
|
||||
// add alerts
|
||||
if (this.jobAlerts && this.jobAlerts[jobId]) {
|
||||
let alerts = this.jobAlerts[jobId];
|
||||
job[0].alerts = alerts;
|
||||
}
|
||||
|
||||
if (job[0].jobSteps && job[0].jobSchedules && job[0].alerts) {
|
||||
return { job: job[0], canEdit: true };
|
||||
}
|
||||
return { job: job[0], canEdit: false };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async openCreateJobDialog() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
await this._commandService.executeCommand('agent.openJobDialog', ownerUri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 240</title>
|
||||
<g>
|
||||
<path d="M11.5,7,7,16h9Zm.5,8H11V14h1Zm-1-1.5V11h1v2.5Z" fill="#db7500"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z"/>
|
||||
<polygon points="5.882 16 6.382 15 2 15 2 13 3 13 3 12 2 12 2 10.023 3 10.023 3 9.023 2 9.023 2 7.039 3 7.039 3 6.039 2 6.039 2 4 3 4 3 3 2 3 2 1 13 1 13 7.764 14 9.764 14 0 1 0 1 3 0 3 0 4 1 4 1 6.039 0 6.039 0 7.039 1 7.039 1 9.023 0 9.023 0 10.023 1 10.023 1 12 0 12 0 13 1 13 1 16 5.882 16"/>
|
||||
<path d="M11,15h1V14H11Zm0-4v2.5h1V11Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 610 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 210</title>
|
||||
<g>
|
||||
<path d="M11.5,7,7,16h9Zm.5,8H11V14h1Zm-1-1.5V11h1v2.5Z" fill="#db7500"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z" fill="#fff"/>
|
||||
<polygon points="5.882 16 6.382 15 2 15 2 13 3 13 3 12 2 12 2 10.023 3 10.023 3 9.023 2 9.023 2 7.039 3 7.039 3 6.039 2 6.039 2 4 3 4 3 3 2 3 2 1 13 1 13 7.764 14 9.764 14 0 1 0 1 3 0 3 0 4 1 4 1 6.039 0 6.039 0 7.039 1 7.039 1 9.023 0 9.023 0 10.023 1 10.023 1 12 0 12 0 13 1 13 1 16 5.882 16" fill="#fff"/>
|
||||
<path d="M11,15h1V14H11Zm0-4v2.5h1V11Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 634 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 260</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#e00b1c"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z"/>
|
||||
<polygon points="12.648 9.649 11.5 10.797 10.352 9.649 9.648 10.352 10.797 11.5 9.648 12.649 10.352 13.352 11.5 12.204 12.648 13.352 13.352 12.649 12.203 11.5 13.352 10.352 12.648 9.649" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 847 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 230</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#e00b1c"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z" fill="#fff"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z" fill="#fff"/>
|
||||
<polygon points="12.648 9.649 11.5 10.797 10.352 9.649 9.648 10.352 10.797 11.5 9.648 12.649 10.352 13.352 11.5 12.204 12.648 13.352 13.352 12.649 12.203 11.5 13.352 10.352 12.648 9.649" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 871 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 250</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#57a300"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z"/>
|
||||
<path d="M13.224,9.794l.7.7-3,3L9.073,11.648l.7-.7,1.148,1.149Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 725 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 220</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#57a300"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z" fill="#fff"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z" fill="#fff"/>
|
||||
<path d="M13.224,9.794l.7.7-3,3L9.073,11.648l.7-.7,1.148,1.149Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>jobalert</title><path d="M16,2.24V7.58A5.38,5.38,0,0,0,15,6.5V4.3L12.8,5.39l-.64-.11a5,5,0,0,0-.65,0l-.36,0-.36,0,4.11-2.05H1.12L7.87,6.61,7.49,7a4.7,4.7,0,0,0-.34.39L1,4.3v6.94H6a4.64,4.64,0,0,0,.07.5c0,.17.07.33.11.5H0v-10Zm-4.5,4a4.35,4.35,0,0,1,1.75.36A4.53,4.53,0,0,1,15.64,9a4.49,4.49,0,0,1,0,3.5,4.53,4.53,0,0,1-2.39,2.39,4.49,4.49,0,0,1-3.5,0,4.53,4.53,0,0,1-2.39-2.39,4.49,4.49,0,0,1,0-3.5A4.53,4.53,0,0,1,9.75,6.59,4.35,4.35,0,0,1,11.5,6.24Zm0,8A3.38,3.38,0,0,0,12.86,14a3.53,3.53,0,0,0,1.86-1.86,3.49,3.49,0,0,0,0-2.73,3.53,3.53,0,0,0-1.86-1.86,3.49,3.49,0,0,0-2.73,0A3.53,3.53,0,0,0,8.28,9.37a3.49,3.49,0,0,0,0,2.73A3.53,3.53,0,0,0,10.14,14,3.38,3.38,0,0,0,11.5,14.24Zm-.5-6h1v3H11Zm0,4h1v1H11Z"/></svg>
|
||||
|
After Width: | Height: | Size: 815 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>jobalert_inverse</title><path class="cls-1" d="M16,2.24V7.58A5.38,5.38,0,0,0,15,6.5V4.3L12.8,5.39l-.64-.11a5,5,0,0,0-.65,0l-.36,0-.36,0,4.11-2.05H1.12L7.87,6.61,7.49,7a4.7,4.7,0,0,0-.34.39L1,4.3v6.94H6a4.64,4.64,0,0,0,.07.5c0,.17.07.33.11.5H0v-10Zm-4.5,4a4.35,4.35,0,0,1,1.75.36A4.53,4.53,0,0,1,15.64,9a4.49,4.49,0,0,1,0,3.5,4.53,4.53,0,0,1-2.39,2.39,4.49,4.49,0,0,1-3.5,0,4.53,4.53,0,0,1-2.39-2.39,4.49,4.49,0,0,1,0-3.5A4.53,4.53,0,0,1,9.75,6.59,4.35,4.35,0,0,1,11.5,6.24Zm0,8A3.38,3.38,0,0,0,12.86,14a3.53,3.53,0,0,0,1.86-1.86,3.49,3.49,0,0,0,0-2.73,3.53,3.53,0,0,0-1.86-1.86,3.49,3.49,0,0,0-2.73,0A3.53,3.53,0,0,0,8.28,9.37a3.49,3.49,0,0,0,0,2.73A3.53,3.53,0,0,0,10.14,14,3.38,3.38,0,0,0,11.5,14.24Zm-.5-6h1v3H11Zm0,4h1v1H11Z"/></svg>
|
||||
|
After Width: | Height: | Size: 883 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#212121;}</style></defs><title>back_16x16</title><path class="cls-1" d="M16.15,8.5H2.1l6.15,6.15-.7.7L.19,8,7.55.65l.7.7L2.1,7.5h14Z"/></svg>
|
||||
|
After Width: | Height: | Size: 259 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>back_inverse_16x16</title><path class="cls-1" d="M16.15,8.5H2.1l6.15,6.15-.7.7L.19,8,7.55.65l.7.7L2.1,7.5h14Z"/></svg>
|
||||
|
After Width: | Height: | Size: 264 B |
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.detailView-toggle
|
||||
{
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.detailView-toggle.expand
|
||||
{
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background: url(../images/arrow-right.gif) no-repeat center center;
|
||||
}
|
||||
|
||||
.detailView-toggle.collapse
|
||||
{
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
background: url(../images/sort-desc.gif) no-repeat center center;
|
||||
}
|
||||
.dynamic-cell-detail
|
||||
{
|
||||
z-index: 10000;
|
||||
position: absolute;
|
||||
background-color: #dae5e8;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dynamic-cell-detail > :first-child
|
||||
{
|
||||
vertical-align: middle;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.dynamic-cell-detail > .detail-container {
|
||||
overflow: auto;
|
||||
display: block !important;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 4.28l-11.673 11.72h-4.327v-4.406l11.477-11.594h.308l4.215 4.237v.043z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M14.598 4.25l-1.688 1.75-3-3 1.688-1.75 3 3zm-5.688-.25l-7 7 3 3 7-7-3-3zm-7.91 8.09v2.91h2.91l-2.91-2.91z" id="iconBg"/></svg>
|
||||
|
After Width: | Height: | Size: 571 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#2d2d30;} .icon-vs-out{fill:#2d2d30;} .icon-vs-bg{fill:#c5c5c5;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 4.28l-11.673 11.72h-4.327v-4.406l11.477-11.594h.308l4.215 4.237v.043z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M14.598 4.25l-1.688 1.75-3-3 1.688-1.75 3 3zm-5.688-.25l-7 7 3 3 7-7-3-3zm-7.91 8.09v2.91h2.91l-2.91-2.91z" id="iconBg"/></svg>
|
||||
|
After Width: | Height: | Size: 571 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1{fill:none;clip-rule:evenodd;}.cls-2{clip-path:url(#clip-path);}.cls-3{fill:#d02e00;}</style><clipPath id="clip-path"><path class="cls-1" d="M8.88,8l2.67-2.67-.88-.88L8,7.12,5.33,4.45l-.88.88L7.12,8,4.45,10.67l.88.88L8,8.88l2.67,2.67.88-.88ZM8,0a7.92,7.92,0,0,1,4,1.09A8.15,8.15,0,0,1,14.91,4a8,8,0,0,1,0,8.07A8.15,8.15,0,0,1,12,14.91a8,8,0,0,1-8.07,0A8.15,8.15,0,0,1,1.09,12,8,8,0,0,1,1.09,4,8.15,8.15,0,0,1,4,1.09,7.92,7.92,0,0,1,8,0Z"/></clipPath></defs><title>failed</title><g class="cls-2"><rect class="cls-3" x="-5" y="-5" width="26" height="26"/></g></svg>
|
||||
|
After Width: | Height: | Size: 718 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>job</title><path d="M11,4h5v9H0V4H5V2h6Zm4,1H1V7H4V6H5V7h6V6h1V7h3ZM1,12H15V8H12V9H11V8H5V9H4V8H1ZM6,3V4h4V3Z"/></svg>
|
||||
|
After Width: | Height: | Size: 218 B |
@@ -0,0 +1,477 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
jobhistory-component .all-jobs {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
width: 10%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
notebookhistory-component .all-jobs {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.overview-container .overview-tab .resultsViewCollapsible,
|
||||
.overview-container .overview-tab .notebooksgridViewCollapsible {
|
||||
padding: 15px;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.job-heading {
|
||||
text-align: left;
|
||||
padding-left: 13px;
|
||||
font-size: 1.5vw;
|
||||
}
|
||||
|
||||
.overview-container {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.history-container {
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.vs-dark .overview-container .overview-tab {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hc-black .overview-container .overview-tab {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.overview-container > .overview-tab {
|
||||
position: relative;
|
||||
margin-bottom: 1px;
|
||||
width: 100%;
|
||||
color: #4a4a4a;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input#accordion{
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.grid-arrow{
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.vs-dark .overview-container .overview-tab label {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
.hc-black .overview-container .overview-tab label {
|
||||
background: #000000;
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
|
||||
.overview-container .overview-tab label {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0 0 0 1em;
|
||||
background: #f4f4f4;
|
||||
font-weight: bold;
|
||||
line-height: 3;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vs-dark .overview-tab .accordion-content {
|
||||
background: #333333;
|
||||
}
|
||||
|
||||
.hc-black .overview-tab .accordion-content,
|
||||
.grid-arrow {
|
||||
background: #000000;
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
|
||||
.overview-tab .accordion-content {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
background: #eaeaea;
|
||||
-webkit-transition: max-height 0.35s;
|
||||
-o-transition: max-height 0.35s;
|
||||
transition: max-height 0.35s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.overview-tab .accordion-content p {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
/* :checked */
|
||||
input#accordion:checked ~ .accordion-content,
|
||||
.grid-arrow:checked {
|
||||
max-height: 10em;
|
||||
}
|
||||
|
||||
input#accordion:checked ~ .accordion-content,
|
||||
.grid-arrow:checked {
|
||||
max-height: 10em;
|
||||
}
|
||||
|
||||
/* Icon */
|
||||
.overview-container .overview-tab label::after {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
line-height: 3;
|
||||
text-align: center;
|
||||
-webkit-transition: all 0.3s;
|
||||
-o-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.all-jobs > .back-button-icon {
|
||||
content: url("back.svg");
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding-left: 13px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.vs-dark .all-jobs > .back-button-icon,
|
||||
.hc-black .all-jobs > .back-button-icon {
|
||||
content: url("back_inverse.svg");
|
||||
}
|
||||
|
||||
.vs .action-label.codicon.newStepIcon {
|
||||
background-image: url("new.svg");
|
||||
}
|
||||
|
||||
.vs-dark .action-label.codicon.newStepIcon,
|
||||
.hc-black .action-label.codicon.newStepIcon {
|
||||
background-image: url("new_inverse.svg");
|
||||
}
|
||||
|
||||
jobhistory-component .hc-black .codicon.edit,
|
||||
jobhistory-component .vs-dark .codicon.edit,
|
||||
notebookhistory-component .hc-black .codicon.edit,
|
||||
notebookhistory-component .vs-dark .codicon.edit {
|
||||
background-image: url("edit_inverse.svg");
|
||||
}
|
||||
|
||||
jobhistory-component .vs .codicon.edit,
|
||||
notebookhistory-component .vs .codicon.edit {
|
||||
background-image: url("edit.svg");
|
||||
}
|
||||
|
||||
jobhistory-component .actions-container .codicon.edit,
|
||||
notebookhistory-component .actions-container .codicon.edit {
|
||||
background-position: 0% 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 12px;
|
||||
}
|
||||
|
||||
a.action-label.codicon.runJobIcon.non-runnable {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
a.action-label.codicon.stopJobIcon.non-runnable {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.accordion-content #col1,
|
||||
.accordion-content #col2,
|
||||
.accordion-content #col3,
|
||||
.accordion-content #col4 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.accordion-content #col1,
|
||||
.accordion-content #col3 {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.accordion-content #col2 {
|
||||
padding-right: 300px;
|
||||
}
|
||||
|
||||
table.step-list tr.step-row td {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.history-details {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.history-details > .job-steps {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
border-left: 3px solid #f4f4f4;
|
||||
padding-left: 10px;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.vs-dark .history-details > .job-steps {
|
||||
border-left: 3px solid #444444;
|
||||
}
|
||||
|
||||
.hc-black .history-details > .job-steps {
|
||||
border-left: 3px solid #2b56f2;
|
||||
}
|
||||
|
||||
.history-details > .job-steps > table.step-list {
|
||||
padding-bottom: 10px;
|
||||
display: flex;
|
||||
flex: 1 1;
|
||||
overflow: scroll;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.step-table
|
||||
.monaco-tree
|
||||
.monaco-tree-rows.show-twisties
|
||||
> .monaco-tree-row.has-children
|
||||
> .content:before {
|
||||
background: none;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.step-table
|
||||
.monaco-tree.focused
|
||||
.monaco-tree-rows.show-twisties
|
||||
> .monaco-tree-row.has-children.selected:not(.loading)
|
||||
> .content:before {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.step-table .list-row .status-icon {
|
||||
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 {
|
||||
background: green;
|
||||
}
|
||||
|
||||
.job-failed {
|
||||
background: red;
|
||||
}
|
||||
|
||||
.job-unknown {
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
.date-column {
|
||||
padding-left: 50px;
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.step-table {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.prev-run-list-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
jobhistory-component,
|
||||
notebookhistory-component {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
jobhistory-component > .jobhistory-heading-container,
|
||||
notebookhistory-component > .jobhistory-heading-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
jobhistory-component > .jobhistory-heading-container > .codicon.in-progress,
|
||||
notebookhistory-component > .jobhistory-heading-container > .codicon.in-progress {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding-top: 16px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
jobhistory-component > .agent-actionbar-container,
|
||||
notebookhistory-component > .agent-actionbar-container {
|
||||
border-top: 3px solid #f4f4f4;
|
||||
}
|
||||
|
||||
.vs-dark jobhistory-component > .agent-actionbar-container,
|
||||
.vs-dark notebookhistory-component > .agent-actionbar-container {
|
||||
border-top: 3px solid #444444;
|
||||
}
|
||||
|
||||
.hc-black jobhistory-component > .agent-actionbar-container,
|
||||
.hc-black notebookhistory-component > .agent-actionbar-container {
|
||||
border-top: 3px solid #2b56f2;
|
||||
}
|
||||
|
||||
jobhistory-component
|
||||
.step-table.prev-run-list
|
||||
.monaco-tree-wrapper
|
||||
.monaco-tree-row,
|
||||
notebookhistory-component
|
||||
.step-table.prev-run-list
|
||||
.monaco-tree-wrapper
|
||||
.monaco-tree-row {
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
jobhistory-component
|
||||
.agent-actionbar-container
|
||||
> .monaco-toolbar.carbon-taskbar,
|
||||
notebookhistory-component
|
||||
.agent-actionbar-container
|
||||
> .monaco-toolbar.carbon-taskbar {
|
||||
margin: 10px 0px 5px 0px;
|
||||
}
|
||||
.notebook-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
grid-gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.notebook-grid-item {
|
||||
border-radius: 5px;
|
||||
padding-bottom: 5px;
|
||||
height: 95px;
|
||||
display: block;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.notebook-grid-item > img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.notebook-grid-item:hover {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item:hover,
|
||||
.hc-black .notebook-grid-item:hover {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.notebook-grid-item > .img-success {
|
||||
background-image: url(./NotebookSuccess_16x.svg);
|
||||
background-position: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: 40px 40px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item > .img-success,
|
||||
.hc-black .notebook-grid-item > .img-success {
|
||||
background-image: url(./NotebookSuccess_16x_white.svg);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.notebook-grid-item > .img-failure {
|
||||
background-image: url(./NotebookFail_16x.svg);
|
||||
background-position: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: 40px 40px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item > .img-failure,
|
||||
.hc-black .notebook-grid-item > .img-failure {
|
||||
background-image: url(./NotebookFail_16x_white.svg);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.notebook-grid-item > .img-error {
|
||||
background-image: url(./NotebookError_16x.svg);
|
||||
background-position: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: 40px 40px;
|
||||
background-repeat: no-repeat;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item > .img-error,
|
||||
.hc-black .notebook-grid-item > .img-error {
|
||||
background-image: url(./NotebookError_16x_white.svg);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.notebook-grid-item > p {
|
||||
display: absolute;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notebook-history-container {
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.grid-title {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0 0 0 1em;
|
||||
background: #f4f4f4;
|
||||
font-weight: bold;
|
||||
line-height: 3;
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.vs-dark .grid-title {
|
||||
background: #333333;
|
||||
}
|
||||
|
||||
.hc-black .grid-title {
|
||||
background: #000000;
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.steps-tree .list-row .status-icon.step-passed {
|
||||
content: url("success_complete.svg");
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.steps-tree .list-row .status-icon.step-failed {
|
||||
content: url("failed.svg");
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.steps-tree .list-row .label {
|
||||
padding-left: 10px;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
user-select: initial;
|
||||
}
|
||||
|
||||
.steps-tree .list-row {
|
||||
display: inline-flex;
|
||||
height: 20px
|
||||
}
|
||||
|
||||
.step-columns {
|
||||
padding-left: 50px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.steps-tree .tree-id-col {
|
||||
padding-left: 10px;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.steps-tree .tree-name-col {
|
||||
padding-right: 10px;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.steps-tree .tree-message-col {
|
||||
padding-right: 10px;
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
.steps-tree .step-column-heading {
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.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("step_inverse.svg");
|
||||
}
|
||||
|
||||
.steps-header > .steps-icon {
|
||||
content: url("step.svg");
|
||||
}
|
||||
|
||||
jobstepsview-component {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row {
|
||||
width: 99.2%;
|
||||
}
|
||||
|
||||
.vs-dark jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row,
|
||||
.vs-dark jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.focused,
|
||||
.vs-dark jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.selected,
|
||||
.vs-dark jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.focused.selected {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.hc-black jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row,
|
||||
.hc-black jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.selected,
|
||||
.hc-black jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.focused,
|
||||
.hc-black jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.focused.selected {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row,
|
||||
jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.focused,
|
||||
jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.selected,
|
||||
jobstepsview-component .steps-tree .monaco-tree-wrapper .monaco-tree-row.step-column-row.focused.selected {
|
||||
background: #dcdcdc !important;
|
||||
cursor: none;
|
||||
padding-left: 0px;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>job_inverse</title><path class="cls-1" d="M11,4h5v9H0V4H5V2h6Zm4,1H1V7H4V6H5V7h6V6h1V7h3ZM1,12H15V8H12V9H11V8H5V9H4V8H1ZM6,3V4h4V3Z"/></svg>
|
||||
|
After Width: | Height: | Size: 286 B |
940
src/sql/workbench/contrib/jobManagement/browser/media/jobs.css
Normal file
@@ -0,0 +1,940 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
agentview-component {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
jobsview-component,
|
||||
notebooksview-component {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.job-heading-container {
|
||||
height: 50px;
|
||||
border-bottom: 3px solid #f4f4f4;
|
||||
display: -webkit-box;
|
||||
}
|
||||
|
||||
.vs-dark .job-heading-container {
|
||||
border-bottom: 3px solid #444444;
|
||||
}
|
||||
|
||||
.hc-black .job-heading-container {
|
||||
border-bottom: 3px solid #2b56f2;
|
||||
}
|
||||
|
||||
.jobview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.jobnotebooksview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.vs-dark #agentViewDiv .slick-header-column {
|
||||
background: #333333 !important;
|
||||
}
|
||||
|
||||
#agentViewDiv .slick-header-column {
|
||||
background-color: transparent !important;
|
||||
border: 0px !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hc-black
|
||||
#jobsDiv
|
||||
jobsview-component
|
||||
.jobview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell,
|
||||
.hc-black
|
||||
#notebooksDiv
|
||||
notebooks-component
|
||||
.jobnotebooksview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell {
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
|
||||
#jobsDiv
|
||||
jobsview-component
|
||||
.jobview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell,
|
||||
#notebooksDiv
|
||||
notebooksview-component
|
||||
.jobnotebooksview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-joblist,
|
||||
#notebooksDiv .jobview-joblist {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobnametable,
|
||||
#notebooksDiv .jobview-jobnametable {
|
||||
border: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobnameindicatorsuccess,
|
||||
#notebooksDiv .jobview-jobnameindicatorsuccess {
|
||||
width: 5px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
#jobsDiv .slick-cell.l1.r1 .jobview-jobnameindicatorfailure,
|
||||
#notebooksDiv .slick-cell.l1.r1 .jobview-jobnameindicatorfailure {
|
||||
width: 5px;
|
||||
background: red;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobnameindicatorcancel,
|
||||
#notebooksDiv .jobview-jobnameindicatorcancel {
|
||||
width: 5px;
|
||||
background: orange;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid .jobview-jobnameindicatorunknown,
|
||||
#notebooks .jobnotebooksview-grid .jobview-jobnameindicatorunknown {
|
||||
width: 5px;
|
||||
background: grey;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
.slick-cell.l1.r1.error-row
|
||||
.jobview-jobnametext,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
.slick-cell.l1.r1.notebook-error-row
|
||||
.jobview-jobnametext {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext,
|
||||
#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1 .jobview-jobnametext {
|
||||
text-overflow: ellipsis;
|
||||
width: 250px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
.slick-cell.l1.r1
|
||||
.operatorview-operatornametext,
|
||||
#alertsDiv .jobalertsview-grid .slick-cell.l1.r1 .alertview-alertnametext,
|
||||
#proxiesDiv .jobproxiesview-grid .slick-cell.l1.r1 .proxyview-proxynametext {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#jobsDiv .job-with-error,
|
||||
#notebooksDiv .job-with-error {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
.slick-cell.l1.r1.error-row,
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
.slick-cell.l1.r1.error-row {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
font-weight: 700;
|
||||
text-overflow: ellipsis;
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
.slick-cell.l1.r1.notebook-error-row {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
.slick-cell._detail_selector.error-row,
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
.slick-cell._detail_selector.error-row,
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
.slick-cell._detail_selector.notebook-error-row {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-splitter,
|
||||
#notebooksDiv .jobview-splitter {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobitem,
|
||||
#notebooksDiv .jobview-jobitem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
flex-flow: row wrap;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-label,
|
||||
#notebooksDiv .jobview-label {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-highlight-none,
|
||||
#notebooksDiv .jobview-highlight-none {
|
||||
width: 5px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#jobsDiv .detail-container,
|
||||
#notebooksDiv .detail-container {
|
||||
max-height: 100px !important;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#jobsDiv .detail,
|
||||
#notebooksDiv .detail {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#jobsDiv .preload,
|
||||
#notebooksDiv .preload {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#jobsDiv .dynamic-cell-detail > :first-child,
|
||||
#notebooksDiv .dynamic-cell-detail > :first-child {
|
||||
vertical-align: middle;
|
||||
line-height: 13px;
|
||||
padding: 10px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.jobsview-icon {
|
||||
background-image: url("./job.svg");
|
||||
}
|
||||
|
||||
.vs-dark .jobsview-icon,
|
||||
.hc-black .jobsview-icon {
|
||||
background-image: url("./job_inverse.svg");
|
||||
}
|
||||
|
||||
.notebooksview-icon {
|
||||
background-image: url("./notebook.svg");
|
||||
}
|
||||
|
||||
.vs-dark .notebooksview-icon,
|
||||
.hc-black .notebooksview-icon {
|
||||
background-image: url("./notebook_inverse.svg");
|
||||
}
|
||||
|
||||
.alertsview-icon {
|
||||
background-image: url("./alert.svg");
|
||||
}
|
||||
|
||||
.vs-dark .alertsview-icon,
|
||||
.hc-black .alertsview-icon {
|
||||
background-image: url("./alert_inverse.svg");
|
||||
}
|
||||
|
||||
.proxiesview-icon {
|
||||
background-image: url("./proxy.svg");
|
||||
}
|
||||
|
||||
.vs-dark .proxiesview-icon,
|
||||
.hc-black .proxiesview-icon {
|
||||
background-image: url("./proxy_inverse.svg");
|
||||
}
|
||||
|
||||
.operatorsview-icon {
|
||||
background-image: url("./operator.svg");
|
||||
}
|
||||
|
||||
.vs-dark .operatorsview-icon,
|
||||
.hc-black .operatorsview-icon {
|
||||
background-image: url("./operator_inverse.svg");
|
||||
}
|
||||
|
||||
agentview-component
|
||||
.jobview-grid
|
||||
.grid-canvas
|
||||
> .ui-widget-content.slick-row.even
|
||||
> .slick-cell,
|
||||
agentview-component
|
||||
.jobnotebooksview-grid
|
||||
.grid-canvas
|
||||
> .ui-widget-content.slick-row.odd
|
||||
> .slick-cell {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted white;
|
||||
}
|
||||
|
||||
.hc-black
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted #2b56f2;
|
||||
}
|
||||
|
||||
.jobview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
.job-heading-container > .codicon.in-progress {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding-top: 16px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
#jobsDiv
|
||||
jobsview-component
|
||||
.jobview-grid
|
||||
.slick-cell.l1.r1.error-row
|
||||
td.jobview-jobnameindicatorfailure,
|
||||
#notebooksDiv
|
||||
notebooksview-component
|
||||
.jobnotebooksview-grid
|
||||
.slick-cell.l1.r1.error-row
|
||||
td.jobview-jobnameindicatorfailure,
|
||||
#notebooksDiv
|
||||
notebooksview-component
|
||||
.jobnotebooksview-grid
|
||||
.slick-cell.l1.r1.notebook-error-row
|
||||
td.jobview-jobnameindicatorfailure {
|
||||
width: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.vs-dark
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.vs-dark
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.hc-black
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.hc-black
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.hc-black
|
||||
#jobsDiv
|
||||
.jobview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
.hc-black
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.hc-black
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.hc-black
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
table.jobprevruns div.bar,
|
||||
table.jobprevruns div.bar0,
|
||||
table.jobprevruns div.bar1,
|
||||
table.jobprevruns div.bar2,
|
||||
table.jobprevruns div.bar3,
|
||||
table.jobprevruns div.bar4,
|
||||
table.jobprevruns div.bar5 {
|
||||
padding-top: 3px;
|
||||
padding-left: 5px;
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
table.jobprevruns div.bar {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jobview-grid .slick-cell.l10.r10,
|
||||
.jobnotebooksview-grid .slick-cell.l10.r10 {
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
table.jobprevruns {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
table.jobprevruns > tbody {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
#notebooksDiv .jobnotebooksview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#alertsDiv .jobalertsview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#operatorsDiv .joboperatorsview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width: 100%;
|
||||
display: block;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
#proxiesDiv .jobproxiesview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.vs .action-label.codicon.refreshIcon {
|
||||
background-image: url("refresh.svg");
|
||||
}
|
||||
|
||||
.vs-dark .action-label.codicon.refreshIcon,
|
||||
.hc-black .action-label.codicon.refreshIcon {
|
||||
background-image: url("refresh_inverse.svg");
|
||||
}
|
||||
|
||||
.vs .action-label.codicon.openNotebook {
|
||||
background-image: url("open_notebook.svg");
|
||||
}
|
||||
|
||||
.vs-dark .action-label.codicon.openNotebook,
|
||||
.hc-black .action-label.codicon.openNotebook {
|
||||
background-image: url("open_notebook_inverse.svg");
|
||||
}
|
||||
|
||||
.agent-actionbar-container
|
||||
.monaco-action-bar
|
||||
> ul.actions-container
|
||||
> li.action-item {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
jobsview-component .jobview-grid .slick-cell.error-row,
|
||||
notebooksview-component .jobnotebooksview-grid .slick-cell.error-row,
|
||||
notebooksview-component .jobnotebooksview-grid .slick-cell.notebook-error-row {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#notebooksDiv
|
||||
notebooksview-component
|
||||
.jobnotebooksview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.vs-dark
|
||||
#notebooksDiv
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted white;
|
||||
}
|
||||
|
||||
.jobnotebooksview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
#alertsDiv
|
||||
jobalertsview-component
|
||||
.jobalertsview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#alertsDiv
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
#alertsDiv
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
#alertsDiv
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
#alertsDiv
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#alertsDiv
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.vs-dark
|
||||
#alertsDiv
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted white;
|
||||
}
|
||||
|
||||
.jobalertsview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
#operatorsDiv
|
||||
joboperatorsview-component
|
||||
.joboperatorsview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.vs-dark
|
||||
#operatorsDiv
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted white;
|
||||
}
|
||||
|
||||
.joboperatorsview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
#proxiesDiv
|
||||
jobproxiesview-component
|
||||
.jobproxiesview-grid
|
||||
.grid-canvas
|
||||
.ui-widget-content.slick-row
|
||||
.slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#proxiesDiv
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
#proxiesDiv
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell,
|
||||
#proxiesDiv
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
#proxiesDiv
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row:hover
|
||||
> .slick-cell,
|
||||
.vs-dark
|
||||
#proxiesDiv
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row
|
||||
> .slick-cell.hovered,
|
||||
.vs-dark
|
||||
#proxiesDiv
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-viewport
|
||||
> .grid-canvas
|
||||
> .ui-widget-content.slick-row.hovered
|
||||
> .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.vs-dark
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted white;
|
||||
}
|
||||
|
||||
.jobproxiesview-grid
|
||||
> .monaco-table
|
||||
.slick-header-columns
|
||||
.slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
.overview-container > .overview-tab > label {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
#operatorsDiv .operatorview-operatornameindicatorenabled,
|
||||
#alertsDiv .alertview-alertnameindicatorenabled,
|
||||
#proxiesDiv .proxyview-proxynameindicatorenabled {
|
||||
width: 5px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
#operatorsDiv .operatorview-operatornameindicatordisabled,
|
||||
#alertsDiv .alertview-alertnameindicatordisabled,
|
||||
#proxiesDiv .proxyview-proxynameindicatordisabled {
|
||||
width: 5px;
|
||||
background: red;
|
||||
}
|
||||
|
||||
#jobsDiv jobsview-component .monaco-toolbar.carbon-taskbar,
|
||||
#notebooksDiv notebooksview-component .monaco-toolbar.carbon-taskbar,
|
||||
#operatorsDiv joboperatorsview-component .monaco-toolbar.carbon-taskbar,
|
||||
#alertsDiv jobalertsview-component .monaco-toolbar.carbon-taskbar,
|
||||
#proxiesDiv jobproxiesview-component .monaco-toolbar.carbon-taskbar {
|
||||
margin: 10px 0px 10px 0px;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#231f20;}.cls-2{fill:#212121;}</style></defs><title>new_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-2" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 336 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>new_inverse_16x16</title><polygon class="cls-1" points="13.59 2.21 13.58 2.22 13.58 2.2 13.59 2.21"/><path class="cls-1" d="M16,7.5v1H8.5V16h-1V8.5H0v-1H7.5V0h1V7.5Z"/></svg>
|
||||
|
After Width: | Height: | Size: 320 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>notebook</title><path d="M15.5,2V15H.5V2h2V1h3a4.19,4.19,0,0,1,1.32.21A3.87,3.87,0,0,1,8,1.84a3.87,3.87,0,0,1,1.18-.63A4.19,4.19,0,0,1,10.5,1h3V2ZM1.5,14H7.8a4.43,4.43,0,0,0-.51-.43,3.41,3.41,0,0,0-.54-.31,2.74,2.74,0,0,0-.59-.2A3.2,3.2,0,0,0,5.5,13h-3V3h-1Zm2-2h2a4.18,4.18,0,0,1,1,.13,4,4,0,0,1,1,.39V2.72a3,3,0,0,0-.94-.54A3.15,3.15,0,0,0,5.5,2h-2Zm11-9h-1V13h-3a3.2,3.2,0,0,0-.67.07,2.74,2.74,0,0,0-.59.2,3.41,3.41,0,0,0-.54.31A4.43,4.43,0,0,0,8.2,14h6.3Zm-4-1a3.15,3.15,0,0,0-1.06.18,3,3,0,0,0-.94.54v9.8a4,4,0,0,1,1-.39,4.18,4.18,0,0,1,1-.13h2V2Z"/></svg>
|
||||
|
After Width: | Height: | Size: 661 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>notebook_inverse</title><path class="cls-1" d="M15.46,2V15H.46V2h2V1h3a4.19,4.19,0,0,1,1.32.21A3.87,3.87,0,0,1,8,1.84a3.87,3.87,0,0,1,1.18-.63A4.19,4.19,0,0,1,10.46,1h3V2Zm-14,12h6.3a4.43,4.43,0,0,0-.51-.43,3.41,3.41,0,0,0-.54-.31,2.74,2.74,0,0,0-.59-.2A3.2,3.2,0,0,0,5.46,13h-3V3h-1Zm2-2h2a4.18,4.18,0,0,1,1,.13,4,4,0,0,1,1,.39V2.72a3,3,0,0,0-.94-.54A3.15,3.15,0,0,0,5.46,2h-2Zm11-9h-1V13h-3a3.2,3.2,0,0,0-.67.07,2.74,2.74,0,0,0-.59.2,3.41,3.41,0,0,0-.54.31,4.43,4.43,0,0,0-.51.43h6.3Zm-4-1a3.15,3.15,0,0,0-1.06.18,3,3,0,0,0-.94.54v9.8a4,4,0,0,1,1-.39,4.18,4.18,0,0,1,1-.13h2V2Z"/></svg>
|
||||
|
After Width: | Height: | Size: 734 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#00539c;}</style></defs><title>open_notebook</title><path d="M12.4,4.21l-.08-.11h-.56l-.69.06a1.54,1.54,0,0,0-.23.29v8.69H9a3.32,3.32,0,0,0-.93.13,3.34,3.34,0,0,0-.87.34V4.76a2.88,2.88,0,0,1,.43-.31A5.58,5.58,0,0,1,8.14,3.3a2.63,2.63,0,0,0-.3.09A3.62,3.62,0,0,0,6.78,4a3.68,3.68,0,0,0-1.07-.57A3.58,3.58,0,0,0,4.52,3.2H1.81v.9H0V15.85H13.57V5.48ZM2.71,4.1H4.52a2.61,2.61,0,0,1,1,.17,2.32,2.32,0,0,1,.86.49v8.85a3.27,3.27,0,0,0-.88-.34,3.22,3.22,0,0,0-.93-.13H2.71ZM.9,15V5h.91v9H4.52a3.94,3.94,0,0,1,.61.06,3.2,3.2,0,0,1,.52.18,4.19,4.19,0,0,1,.49.29,2.28,2.28,0,0,1,.45.39Zm11.75,0H7a2.7,2.7,0,0,1,.47-.39,2.83,2.83,0,0,1,.47-.29,3.42,3.42,0,0,1,.54-.18A3.81,3.81,0,0,1,9,14h2.73V5h.89Z"/><polygon class="cls-1" points="13.05 3.56 13.05 3.58 13.04 3.57 13.05 3.56"/><path class="cls-1" d="M13,3.57h0v0Z"/><polygon class="cls-1" points="13.05 3.56 13.05 3.58 13.04 3.57 13.05 3.56"/><polygon class="cls-1" points="14.06 1.65 14.04 1.65 14.04 1.63 14.06 1.65"/><path class="cls-1" d="M15.76,2.1,14,3.81l-.38.38L13,3.58v0l1-1H12.64a3.35,3.35,0,0,0-1.09.26h0a3.94,3.94,0,0,0-.86.52l-.24.21s0,0,0,0a3.3,3.3,0,0,0-.51.67,3.1,3.1,0,0,0-.26.47,3.41,3.41,0,0,0-.27,1.39h-.9a4.68,4.68,0,0,1,.16-1.19,4.74,4.74,0,0,1,.25-.66,2.21,2.21,0,0,1,.2-.41,4.66,4.66,0,0,1,.36-.51c.1-.13.22-.26.34-.39a4.14,4.14,0,0,1,.66-.53,1.19,1.19,0,0,1,.23-.16A2.79,2.79,0,0,1,11,2.08l.31-.13.42-.14a4.32,4.32,0,0,1,1.19-.16h1.15l-1-1L13.67,0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#0095d7;}</style></defs><title>open_notebook_inverse</title><path class="cls-1" d="M12.55,4.21l-.08-.11h-.56l-.69.06a1.54,1.54,0,0,0-.23.29v8.69H9.18a3.32,3.32,0,0,0-.93.13,3.34,3.34,0,0,0-.87.34V4.76a2.88,2.88,0,0,1,.43-.31A5.58,5.58,0,0,1,8.29,3.3a2.63,2.63,0,0,0-.3.09A3.62,3.62,0,0,0,6.93,4a3.68,3.68,0,0,0-1.07-.57A3.58,3.58,0,0,0,4.67,3.2H2v.9H.15V15.85H13.72V5.48ZM2.86,4.1H4.67a2.61,2.61,0,0,1,1,.17,2.32,2.32,0,0,1,.86.49v8.85a3.27,3.27,0,0,0-.88-.34,3.22,3.22,0,0,0-.93-.13H2.86ZM1,15V5H2v9H4.67a3.94,3.94,0,0,1,.61.06,3.2,3.2,0,0,1,.52.18,4.19,4.19,0,0,1,.49.29,2.28,2.28,0,0,1,.45.39ZM12.8,15H7.11a2.7,2.7,0,0,1,.47-.39A2.83,2.83,0,0,1,8,14.28a3.42,3.42,0,0,1,.54-.18A3.81,3.81,0,0,1,9.18,14h2.73V5h.89Z"/><polygon class="cls-2" points="13.2 3.56 13.2 3.58 13.19 3.57 13.2 3.56"/><path class="cls-2" d="M13.19,3.57h0v0Z"/><polygon class="cls-2" points="13.2 3.56 13.2 3.58 13.19 3.57 13.2 3.56"/><polygon class="cls-2" points="14.21 1.65 14.19 1.65 14.19 1.63 14.21 1.65"/><path class="cls-2" d="M15.91,2.1,14.2,3.81l-.38.38-.62-.61v0l1-1H12.79a3.35,3.35,0,0,0-1.09.26h0a3.94,3.94,0,0,0-.86.52l-.24.21s0,0,0,0a3.3,3.3,0,0,0-.51.67,3.1,3.1,0,0,0-.26.47A3.41,3.41,0,0,0,9.5,6.11H8.6a4.68,4.68,0,0,1,.16-1.19A4.74,4.74,0,0,1,9,4.26a2.21,2.21,0,0,1,.2-.41,4.66,4.66,0,0,1,.36-.51c.1-.13.22-.26.34-.39a4.14,4.14,0,0,1,.66-.53,1.19,1.19,0,0,1,.23-.16,2.79,2.79,0,0,1,.34-.18l.31-.13.42-.14a4.32,4.32,0,0,1,1.19-.16h1.15l-1-1L13.82,0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>operator</title><path d="M10.36,9.41a7.54,7.54,0,0,1,1.9,1.05A7,7,0,0,1,13.73,12,6.9,6.9,0,0,1,15,16H14a5.79,5.79,0,0,0-.47-2.33,6.07,6.07,0,0,0-3.2-3.2A5.79,5.79,0,0,0,8,10a5.79,5.79,0,0,0-2.33.47,6.07,6.07,0,0,0-3.2,3.2A5.79,5.79,0,0,0,2,16H1a6.89,6.89,0,0,1,2.74-5.54,7.54,7.54,0,0,1,1.9-1.05A5.07,5.07,0,0,1,4,8a4.24,4.24,0,0,1-.73-.06,1.92,1.92,0,0,1-.64-.23,1.25,1.25,0,0,1-.45-.46A1.48,1.48,0,0,1,2,6.5v-3a1.47,1.47,0,0,1,.12-.59,1.49,1.49,0,0,1,.8-.8A1.47,1.47,0,0,1,3.5,2a1.4,1.4,0,0,1,.45.07,4.88,4.88,0,0,1,.8-.87,5.21,5.21,0,0,1,1-.65A4.95,4.95,0,0,1,8,0,4.88,4.88,0,0,1,9.95.39a5,5,0,0,1,2.66,2.66A4.88,4.88,0,0,1,13,5a4.93,4.93,0,0,1-.18,1.34,5,5,0,0,1-.53,1.23,5.12,5.12,0,0,1-.83,1A4.73,4.73,0,0,1,10.36,9.41ZM3,6.5a.51.51,0,0,0,.5.5H4V3.5a.5.5,0,0,0-.85-.35A.48.48,0,0,0,3,3.5ZM5.35,8a3.92,3.92,0,0,0,1.23.74A4,4,0,0,0,8,9a3.85,3.85,0,0,0,1.55-.32,4.05,4.05,0,0,0,2.13-2.13A3.85,3.85,0,0,0,12,5a3.85,3.85,0,0,0-.32-1.55A4.05,4.05,0,0,0,9.55,1.32,3.85,3.85,0,0,0,8,1a4,4,0,0,0-1.83.45A4,4,0,0,0,4.75,2.67a1.47,1.47,0,0,1,.18.51A5.75,5.75,0,0,1,5,3.91q0,.41,0,.85t0,.87q0,.42,0,.78T5,7H7.5a.5.5,0,0,1,0,1Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>operator_inverse</title><path class="cls-1" d="M10.36,9.41a7.54,7.54,0,0,1,1.9,1.05A7,7,0,0,1,13.73,12,6.9,6.9,0,0,1,15,16H14a5.79,5.79,0,0,0-.47-2.33,6.07,6.07,0,0,0-3.2-3.2A5.79,5.79,0,0,0,8,10a5.79,5.79,0,0,0-2.33.47,6.07,6.07,0,0,0-3.2,3.2A5.79,5.79,0,0,0,2,16H1a6.89,6.89,0,0,1,2.74-5.54,7.54,7.54,0,0,1,1.9-1.05A5.07,5.07,0,0,1,4,8a4.24,4.24,0,0,1-.73-.06,1.92,1.92,0,0,1-.64-.23,1.25,1.25,0,0,1-.45-.46A1.48,1.48,0,0,1,2,6.5v-3a1.47,1.47,0,0,1,.12-.59,1.49,1.49,0,0,1,.8-.8A1.47,1.47,0,0,1,3.5,2a1.4,1.4,0,0,1,.45.07,4.88,4.88,0,0,1,.8-.87,5.21,5.21,0,0,1,1-.65A4.95,4.95,0,0,1,8,0,4.88,4.88,0,0,1,9.95.39a5,5,0,0,1,2.66,2.66A4.88,4.88,0,0,1,13,5a4.93,4.93,0,0,1-.18,1.34,5,5,0,0,1-.53,1.23,5.12,5.12,0,0,1-.83,1A4.73,4.73,0,0,1,10.36,9.41ZM3,6.5a.51.51,0,0,0,.5.5H4V3.5a.5.5,0,0,0-.85-.35A.48.48,0,0,0,3,3.5ZM5.35,8a3.92,3.92,0,0,0,1.23.74A4,4,0,0,0,8,9a3.85,3.85,0,0,0,1.55-.32,4.05,4.05,0,0,0,2.13-2.13A3.85,3.85,0,0,0,12,5a3.85,3.85,0,0,0-.32-1.55A4.05,4.05,0,0,0,9.55,1.32,3.85,3.85,0,0,0,8,1a4,4,0,0,0-1.83.45A4,4,0,0,0,4.75,2.67a1.47,1.47,0,0,1,.18.51A5.75,5.75,0,0,1,5,3.91q0,.41,0,.85t0,.87q0,.42,0,.78T5,7H7.5a.5.5,0,0,1,0,1Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>proxy_account</title><path d="M12.23,7a3,3,0,0,0-3.39-.77,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,7,9a3,3,0,0,1-1.23,2.41,4.31,4.31,0,0,1,.66.42,4.2,4.2,0,0,1,.57.53V15a2.93,2.93,0,0,0-.23-1.16,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,1,15H0a3.92,3.92,0,0,1,.16-1.11,4.11,4.11,0,0,1,.45-1,3.87,3.87,0,0,1,.7-.84,4.2,4.2,0,0,1,.92-.63,3,3,0,0,1-1-3.58,3,3,0,0,1,1.6-1.6A2.92,2.92,0,0,1,4,6,3,3,0,0,1,6.41,7.23,4.12,4.12,0,0,1,8.23,5.41,3,3,0,0,1,7,3a2.93,2.93,0,0,1,.23-1.16A3,3,0,0,1,8.83.23a3,3,0,0,1,2.33,0,3,3,0,0,1,1.6,1.6,3,3,0,0,1-.09,2.52,2.94,2.94,0,0,1-.9,1.06,4.07,4.07,0,0,1,1,.67,4,4,0,0,1,.73.92ZM4,11a1.94,1.94,0,0,0,.78-.16A2,2,0,0,0,5.84,9.78a2,2,0,0,0,0-1.55A2,2,0,0,0,4.78,7.16a2,2,0,0,0-1.55,0A2,2,0,0,0,2.16,8.22a2,2,0,0,0,0,1.55,2,2,0,0,0,1.07,1.07A1.94,1.94,0,0,0,4,11ZM8,3a1.94,1.94,0,0,0,.16.78A2,2,0,0,0,9.22,4.84a2,2,0,0,0,1.55,0,2,2,0,0,0,1.07-1.07,2,2,0,0,0,0-1.55,2,2,0,0,0-1.07-1.07,2,2,0,0,0-1.55,0A2,2,0,0,0,8.16,2.22,1.94,1.94,0,0,0,8,3Zm8,7v6H8V10h2V8h4v2Zm-1,1H9v1h6Zm0,2H14v1H13V13H11v1H10V13H9v2h6Zm-4-3h2V9H11Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>proxy_account_inverse</title><path class="cls-1" d="M12.23,7a3,3,0,0,0-3.39-.77,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,7,9a3,3,0,0,1-1.23,2.41,4.31,4.31,0,0,1,.66.42,4.2,4.2,0,0,1,.57.53V15a2.93,2.93,0,0,0-.23-1.16,3,3,0,0,0-1.6-1.6,3,3,0,0,0-2.33,0,3,3,0,0,0-1.6,1.6A2.93,2.93,0,0,0,1,15H0a3.92,3.92,0,0,1,.16-1.11,4.11,4.11,0,0,1,.45-1A3.87,3.87,0,0,1,1.3,12a4.2,4.2,0,0,1,.92-.63,3,3,0,0,1-1-3.58,3,3,0,0,1,1.6-1.6A2.92,2.92,0,0,1,4,6,3,3,0,0,1,6.41,7.23,4.12,4.12,0,0,1,8.23,5.41,3,3,0,0,1,7,3a2.93,2.93,0,0,1,.23-1.16A3,3,0,0,1,8.83.23a3,3,0,0,1,2.33,0,3,3,0,0,1,1.6,1.6,3,3,0,0,1-.09,2.52,2.94,2.94,0,0,1-.9,1.06,4.07,4.07,0,0,1,1,.67,4,4,0,0,1,.73.92ZM4,11a1.94,1.94,0,0,0,.78-.16A2,2,0,0,0,5.84,9.78a2,2,0,0,0,0-1.55A2,2,0,0,0,4.78,7.16a2,2,0,0,0-1.55,0A2,2,0,0,0,2.16,8.22a2,2,0,0,0,0,1.55,2,2,0,0,0,1.07,1.07A1.94,1.94,0,0,0,4,11ZM8,3a1.94,1.94,0,0,0,.16.78A2,2,0,0,0,9.22,4.84a2,2,0,0,0,1.55,0,2,2,0,0,0,1.07-1.07,2,2,0,0,0,0-1.55,2,2,0,0,0-1.07-1.07,2,2,0,0,0-1.55,0A2,2,0,0,0,8.16,2.22,1.94,1.94,0,0,0,8,3Zm8,7v6H8V10h2V8h4v2Zm-1,1H9v1h6Zm0,2H14v1H13V13H11v1H10V13H9v2h6Zm-4-3h2V9H11Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#212121;}</style></defs><title>refresh</title><path class="cls-1" d="M12.51,1.59a8.06,8.06,0,0,1,3.06,4A7.83,7.83,0,0,1,16,8.2a7.91,7.91,0,0,1-.29,2.12,8.13,8.13,0,0,1-.8,1.91A8,8,0,0,1,12,15.11a8.1,8.1,0,0,1-1.91.8,8.06,8.06,0,0,1-4.25,0A8.08,8.08,0,0,1,4,15.11a8,8,0,0,1-2.87-2.87,8.07,8.07,0,0,1-.8-1.91,8,8,0,0,1,0-4.25,8.11,8.11,0,0,1,.82-1.94,7.86,7.86,0,0,1,1.3-1.66A8,8,0,0,1,4.14,1.2H2V.2H6v4H5V1.88A7,7,0,0,0,1.28,6.24a7,7,0,0,0,0,3.82,7,7,0,0,0,1.8,3.09A7,7,0,0,0,6.14,15a7,7,0,0,0,3.71,0,7,7,0,0,0,1.67-.71,7,7,0,0,0,3.22-4.18,7,7,0,0,0-.13-4.12,7.07,7.07,0,0,0-2.68-3.52,6.78,6.78,0,0,0-2.07-1l.27-1A7.67,7.67,0,0,1,12.51,1.59Z"/></svg>
|
||||
|
After Width: | Height: | Size: 767 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#fff;}</style></defs><title>refresh_inverse</title><path class="cls-1" d="M12.51,1.59a8.06,8.06,0,0,1,3.06,4A7.83,7.83,0,0,1,16,8.2a7.91,7.91,0,0,1-.29,2.12,8.13,8.13,0,0,1-.8,1.91A8,8,0,0,1,12,15.11a8.1,8.1,0,0,1-1.91.8,8.06,8.06,0,0,1-4.25,0A8.08,8.08,0,0,1,4,15.11a8,8,0,0,1-2.87-2.87,8.07,8.07,0,0,1-.8-1.91,8,8,0,0,1,0-4.25,8.11,8.11,0,0,1,.82-1.94,7.86,7.86,0,0,1,1.3-1.66A8,8,0,0,1,4.14,1.2H2V.2H6v4H5V1.88A7,7,0,0,0,1.28,6.24a7,7,0,0,0,0,3.82,7,7,0,0,0,1.8,3.09A7,7,0,0,0,6.14,15a7,7,0,0,0,3.71,0,7,7,0,0,0,1.67-.71,7,7,0,0,0,3.22-4.18,7,7,0,0,0-.13-4.12,7.07,7.07,0,0,0-2.68-3.52,6.78,6.78,0,0,0-2.07-1l.27-1A7.67,7.67,0,0,1,12.51,1.59Z"/></svg>
|
||||
|
After Width: | Height: | Size: 772 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#d02e00;}.cls-2{fill:#fff;}</style></defs><title>error_16x16</title><circle class="cls-1" cx="8.07" cy="8.07" r="7.93"/><polygon class="cls-2" points="8.82 8.07 11.53 10.78 10.83 11.48 8.12 8.78 5.41 11.48 4.7 10.78 7.42 8.07 4.65 5.31 5.36 4.61 8.12 7.37 10.83 4.67 11.53 5.36 8.82 8.07"/></svg>
|
||||
|
After Width: | Height: | Size: 414 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-1{fill:#3bb44a;}</style></defs><title>success_16x16</title><path class="cls-1" d="M16,3.16,5.48,13.69,0,8.2l.89-.89,4.6,4.59,9.63-9.62Z"/></svg>
|
||||
|
After Width: | Height: | Size: 255 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1{fill:none;clip-rule:evenodd;}.cls-2{clip-path:url(#clip-path);}</style><clipPath id="clip-path"><path class="cls-1" d="M1.14,10.29H3.43V8H1.14ZM4.57,6.86H6.86V4.57H4.57ZM1.14,3.43H3.43V1.14H1.14ZM8,3.43h2.29V1.14H8Zm-6.86,8H4.57V8H8V4.57h3.43V1.14h3.43V14.86H1.14ZM6.86,0V3.43H4.57V0H0V4.57H3.43V6.86H0V16H16V0Z"/></clipPath></defs><title>step</title><g class="cls-2"><rect x="-5.71" y="-5.71" width="27.43" height="27.43"/></g></svg>
|
||||
|
After Width: | Height: | Size: 590 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1,.cls-2,.cls-4{fill:#fff;}.cls-1{clip-rule:evenodd;}.cls-2{fill-rule:evenodd;}.cls-3{clip-path:url(#clip-path);}</style><clipPath id="clip-path"><path class="cls-1" d="M1.14,10.29H3.43V8H1.14ZM4.57,6.86H6.86V4.57H4.57ZM1.14,3.43H3.43V1.14H1.14ZM8,3.43h2.29V1.14H8Zm-6.86,8H4.57V8H8V4.57h3.43V1.14h3.43V14.86H1.14ZM6.86,0V3.43H4.57V0H0V4.57H3.43V6.86H0V16H16V0Z"/></clipPath></defs><title>step_inverse</title><path class="cls-2" d="M1.14,10.29H3.43V8H1.14ZM4.57,6.86H6.86V4.57H4.57ZM1.14,3.43H3.43V1.14H1.14ZM8,3.43h2.29V1.14H8Zm-6.86,8H4.57V8H8V4.57h3.43V1.14h3.43V14.86H1.14ZM6.86,0V3.43H4.57V0H0V4.57H3.43V6.86H0V16H16V0Z"/><g class="cls-3"><rect class="cls-4" x="-5.71" y="-5.71" width="27.43" height="27.43"/></g></svg>
|
||||
|
After Width: | Height: | Size: 878 B |
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16"><defs><style>.cls-1{fill:none;clip-rule:evenodd;}.cls-2{clip-path:url(#clip-path);}.cls-3{fill:#3bb44a;}.cls-4{clip-path:url(#clip-path-2);}.cls-5{fill:#fff;}</style><clipPath id="clip-path"><path class="cls-1" d="M16,8a7.92,7.92,0,0,1-1.09,4A8.15,8.15,0,0,1,12,14.91a8,8,0,0,1-8.07,0A8.15,8.15,0,0,1,1.09,12,8,8,0,0,1,1.09,4,8.15,8.15,0,0,1,4,1.09a8,8,0,0,1,8.07,0A8.15,8.15,0,0,1,14.91,4,7.92,7.92,0,0,1,16,8Z"/></clipPath><clipPath id="clip-path-2"><polygon class="cls-1" points="10.9 4.9 11.6 5.6 6.5 10.71 3.65 7.85 4.35 7.15 6.5 9.29 10.9 4.9"/></clipPath></defs><title>success_complete</title><g class="cls-2"><rect class="cls-3" x="-5" y="-5" width="26" height="26"/></g><g class="cls-4"><rect class="cls-5" x="-1.35" y="-0.1" width="17.95" height="15.81"/></g></svg>
|
||||
|
After Width: | Height: | Size: 911 B |
@@ -0,0 +1,164 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="jobhistory-heading-container">
|
||||
<h1 class="job-heading">Notebook Jobs| {{ this._agentNotebookInfo?.name }}</h1>
|
||||
<div class="codicon in-progress" *ngIf="showProgressWheel()"></div>
|
||||
</div>
|
||||
|
||||
<!-- Back -->
|
||||
<div
|
||||
class="all-jobs"
|
||||
tabindex="0"
|
||||
(click)="goToJobs()"
|
||||
(keyup.enter)="goToJobs()"
|
||||
>
|
||||
<div
|
||||
class="back-button-icon"
|
||||
(click)="goToJobs()"
|
||||
(keyup.enter)="goToJobs()"
|
||||
></div>
|
||||
All Notebooks
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<!-- Overview -->
|
||||
<div class="overview-container">
|
||||
<div class="overview-tab" (click)="toggleCollapse()">
|
||||
<input id="accordion" type="checkbox" />
|
||||
<label for="accordion" tabindex="0">
|
||||
<div
|
||||
class="resultsViewCollapsible collapsed"
|
||||
(click)="toggleCollapse()"
|
||||
></div>
|
||||
Overview
|
||||
</label>
|
||||
<div class="accordion-content">
|
||||
<table align="left">
|
||||
<tr>
|
||||
<td id="col1">
|
||||
TargetDatabase:
|
||||
</td>
|
||||
<td id="col2">
|
||||
{{ this._agentNotebookInfo?.targetDatabase }}
|
||||
</td>
|
||||
<td id="col3">
|
||||
Has Schedule:
|
||||
</td>
|
||||
<td id="col4">
|
||||
{{ this._agentNotebookInfo?.hasSchedule }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="col1">
|
||||
Last Run:
|
||||
</td>
|
||||
<td id="col2">
|
||||
{{ this._agentNotebookInfo?.lastRun }}
|
||||
</td>
|
||||
<td id="col3">
|
||||
Next Run:
|
||||
</td>
|
||||
<td id="col4">
|
||||
{{ this._agentNotebookInfo?.nextRun }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Overview -->
|
||||
<div class="history-container">
|
||||
<div
|
||||
class="overview-container"
|
||||
*ngFor="let grid of this._grids; let i = index"
|
||||
>
|
||||
<div
|
||||
class="overview-tab"
|
||||
(click)="toggleGridCollapse(i)"
|
||||
*ngIf="grid.histories?.length"
|
||||
>
|
||||
<input id="accordion{{ i }}" type="checkbox" class="grid-arrow"/>
|
||||
<label for="accordion{{ i }}" tabindex="0">
|
||||
<div id= "history-grid-icon{{i}}" (click)="toggleGridCollapse(i)"
|
||||
class="resultsViewCollapsible"></div>
|
||||
{{ grid.title }}
|
||||
</label>
|
||||
<div id="notebook-grid{{ i }}" class="notebook-grid {{ i }}" >
|
||||
<div
|
||||
*ngFor="let history of grid.histories"
|
||||
class="notebook-grid-item"
|
||||
tabindex="0"
|
||||
(dblclick)="openNotebook(history)"
|
||||
title="{{ createdTooltip(history) }}"
|
||||
(contextmenu)="
|
||||
openHistoryContextMenu($event, history, grid.contextMenuType)
|
||||
"
|
||||
(keydown.enter)="openNotebook(history)"
|
||||
>
|
||||
<div
|
||||
*ngIf="history.materializedNotebookErrorInfo"
|
||||
class="img-error"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
history.runStatus === 1 && !history.materializedNotebookErrorInfo
|
||||
"
|
||||
class="img-success"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
history.runStatus === 0 && !history.materializedNotebookErrorInfo
|
||||
"
|
||||
class="img-failure"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
!history.materializedNotebookName || history.materializedNotebookName === '';
|
||||
else notebookRunName
|
||||
"
|
||||
>
|
||||
<p style="text-align: center;">
|
||||
{{ formatDateTimetoLocaleDate(history.runDate) }}
|
||||
<br />
|
||||
{{ formatDateTimetoLocaleTime(history.runDate) }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="
|
||||
history.materializedNotebookName !== '';
|
||||
else notebookRunName
|
||||
"
|
||||
>
|
||||
<p
|
||||
style="text-align: center; text-overflow: ellipsis; overflow:hidden; white-space: nowrap"
|
||||
>
|
||||
{{ history.materializedNotebookName }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Job History details -->
|
||||
<div class="history-details">
|
||||
<!-- Previous run list -->
|
||||
<div class="prev-run-list-container" style="min-width: 270px">
|
||||
<h3 *ngIf="_showPreviousRuns === false" style="text-align: center">
|
||||
No Previous Runs Available
|
||||
</h3>
|
||||
<div
|
||||
class="step-table prev-run-list"
|
||||
style="position: relative; width: 100%"
|
||||
>
|
||||
<div #table style="position: absolute; width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,526 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobHistory';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable } from '@angular/core';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { RunJobAction, StopJobAction, JobsRefreshAction, EditNotebookJobAction, OpenMaterializedNotebookAction, OpenTemplateNotebookAction, RenameNotebookMaterializedAction, PinNotebookMaterializedAction, UnpinNotebookMaterializedAction, DeleteMaterializedNotebookAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { NotebookCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { JobStepsViewRow } from 'sql/workbench/contrib/jobManagement/browser/jobStepsViewTree';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { JobManagementView, JobActionContext } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
export const DASHBOARD_SELECTOR: string = 'notebookhistory-component';
|
||||
export class GridSection {
|
||||
title: string;
|
||||
histories: azdata.AgentNotebookHistoryInfo[];
|
||||
contextMenuType: number;
|
||||
style: string;
|
||||
}
|
||||
@Component({
|
||||
selector: DASHBOARD_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./notebookHistory.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => NotebookHistoryComponent) }],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
@Injectable()
|
||||
export class NotebookHistoryComponent extends JobManagementView implements OnInit {
|
||||
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
|
||||
@Input() public agentNotebookInfo: azdata.AgentNotebookInfo = undefined;
|
||||
@Input() public agentJobHistories: azdata.AgentJobHistoryInfo[] = undefined;
|
||||
public notebookHistories: azdata.AgentNotebookHistoryInfo[] = undefined;
|
||||
public agentNotebookHistoryInfo: azdata.AgentNotebookHistoryInfo = undefined;
|
||||
|
||||
private _stepRows: JobStepsViewRow[] = [];
|
||||
private _showSteps: boolean = undefined;
|
||||
private _showPreviousRuns: boolean = undefined;
|
||||
private _notebookCacheObject: NotebookCacheObject;
|
||||
private _agentNotebookInfo: azdata.AgentNotebookInfo;
|
||||
private _noJobsAvailable: boolean = false;
|
||||
protected _notebookHistoryActionBar: Taskbar;
|
||||
|
||||
// Job Actions
|
||||
private _editNotebookJobAction: EditNotebookJobAction;
|
||||
private _runJobAction: RunJobAction;
|
||||
private _stopJobAction: StopJobAction;
|
||||
private _refreshAction: JobsRefreshAction;
|
||||
private _openNotebookTemplateAction: OpenTemplateNotebookAction;
|
||||
|
||||
private static readonly HEADING_HEIGHT: number = 24;
|
||||
|
||||
private _grids: GridSection[] = [];
|
||||
|
||||
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService,
|
||||
@Inject(IQuickInputService) private _quickInputService: IQuickInputService
|
||||
) {
|
||||
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
let notebookCacheObjectMap = this._jobManagementService.notebookCacheObjectMap;
|
||||
this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
let notebookCache = notebookCacheObjectMap[this._serverName];
|
||||
if (notebookCache) {
|
||||
this._notebookCacheObject = notebookCache;
|
||||
} else {
|
||||
this._notebookCacheObject = new NotebookCacheObject();
|
||||
this._notebookCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._notebookCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._tableContainer;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
this.initActionBar();
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobHistoryView);
|
||||
}
|
||||
|
||||
private loadHistory() {
|
||||
const self = this;
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let jobName = this._agentViewComponent.agentNotebookInfo.name;
|
||||
let jobId = this._agentViewComponent.notebookId;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.getNotebookHistory(ownerUri, jobId, jobName, targetDatabase).then((result) => {
|
||||
if (result && result.histories) {
|
||||
this.notebookHistories = result.histories;
|
||||
self._notebookCacheObject.setNotebookHistory(jobId, this.notebookHistories);
|
||||
self._notebookCacheObject.setJobSchedules(jobId, result.schedules);
|
||||
self._notebookCacheObject.setJobSteps(jobId, result.steps);
|
||||
this._agentViewComponent.agentNotebookInfo.jobSteps = this._notebookCacheObject.getJobSteps(jobId);
|
||||
this._agentViewComponent.agentNotebookInfo.jobSchedules = this._notebookCacheObject.getJobSchedules(jobId);
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
if (result.histories.length > 0) {
|
||||
self._noJobsAvailable = false;
|
||||
self._showPreviousRuns = true;
|
||||
} else {
|
||||
self._notebookCacheObject.setNotebookHistory(self._agentViewComponent.notebookId, result.histories);
|
||||
self._noJobsAvailable = true;
|
||||
self._showPreviousRuns = false;
|
||||
}
|
||||
} else {
|
||||
self._noJobsAvailable = true;
|
||||
self._showPreviousRuns = false;
|
||||
self._showSteps = false;
|
||||
}
|
||||
this._actionBar.context = { targetObject: { canEdit: true, notebook: this._agentNotebookInfo, job: this._agentNotebookInfo }, ownerUri: this.ownerUri, component: this };
|
||||
this._editNotebookJobAction.enabled = true;
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editNotebookJobAction },
|
||||
{ action: this._openNotebookTemplateAction }
|
||||
]);
|
||||
|
||||
this.createGrid();
|
||||
if (self._agentViewComponent.showNotebookHistory) {
|
||||
self._cd.detectChanges();
|
||||
this.collapseGrid();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public toggleCollapse(): void {
|
||||
let arrow: HTMLElement = jQuery('.resultsViewCollapsible').get(0);
|
||||
let checkbox: any = document.getElementById('accordion');
|
||||
if (arrow.className === 'resultsViewCollapsible' && checkbox.checked === false) {
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
} else if (arrow.className === 'resultsViewCollapsible collapsed' && checkbox.checked === true) {
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public toggleGridCollapse(i): void {
|
||||
let notebookGrid = document.getElementById('notebook-grid' + i);
|
||||
let checkbox: any = document.getElementById('accordion' + i);
|
||||
let arrow = document.getElementById('history-grid-icon' + i);
|
||||
if (notebookGrid.className === 'notebook-grid ' + i && checkbox.checked === true) {
|
||||
notebookGrid.className = 'notebook-grid ' + i + ' collapsed';
|
||||
notebookGrid.style.display = 'none';
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
} else if (notebookGrid.className === 'notebook-grid ' + i + ' collapsed' && checkbox.checked === false) {
|
||||
notebookGrid.className = 'notebook-grid ' + i;
|
||||
notebookGrid.style.display = 'grid';
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public goToJobs(): void {
|
||||
this._agentViewComponent.showNotebookHistory = false;
|
||||
}
|
||||
|
||||
public formatDateTimetoLocaleDate(time: string) {
|
||||
let dateInstance = new Date(time);
|
||||
return dateInstance.toLocaleDateString();
|
||||
}
|
||||
|
||||
public formatDateTimetoLocaleTime(time: string) {
|
||||
let dateInstance = new Date(time);
|
||||
return dateInstance.toLocaleTimeString();
|
||||
}
|
||||
|
||||
|
||||
private showProgressWheel(): boolean {
|
||||
return this._showPreviousRuns !== true && this._noJobsAvailable === false;
|
||||
}
|
||||
|
||||
public onFirstVisible() {
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
|
||||
if (!this.agentNotebookInfo) {
|
||||
this.agentNotebookInfo = this._agentNotebookInfo;
|
||||
}
|
||||
|
||||
if (this.isRefreshing) {
|
||||
this.loadHistory();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.createGrid();
|
||||
}
|
||||
let notebookHistories = this._notebookCacheObject.notebookHistories[this._agentViewComponent.notebookId];
|
||||
if (notebookHistories) {
|
||||
if (notebookHistories.length > 0) {
|
||||
this._noJobsAvailable = false;
|
||||
if (this._notebookCacheObject.prevJobID === this._agentViewComponent.notebookId || notebookHistories[0].jobId === this._agentViewComponent.notebookId) {
|
||||
this._showPreviousRuns = true;
|
||||
this._agentViewComponent.agentNotebookInfo.jobSteps = this._notebookCacheObject.getJobSteps(this._agentNotebookInfo.jobId);
|
||||
this._agentViewComponent.agentNotebookInfo.jobSchedules = this._notebookCacheObject.getJobSchedules(this._agentNotebookInfo.jobId);
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
}
|
||||
} else if (notebookHistories.length === 0) {
|
||||
this._showPreviousRuns = false;
|
||||
this._showSteps = false;
|
||||
this._noJobsAvailable = true;
|
||||
}
|
||||
this._editNotebookJobAction.enabled = true;
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editNotebookJobAction },
|
||||
{ action: this._openNotebookTemplateAction }
|
||||
]);
|
||||
this._cd.detectChanges();
|
||||
this.collapseGrid();
|
||||
} else {
|
||||
this.loadHistory();
|
||||
}
|
||||
this._notebookCacheObject.prevJobID = this._agentViewComponent.notebookId;
|
||||
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let historyDetails = jQuery('.overview-container').get(0);
|
||||
let statusBar = jQuery('.part.statusbar').get(0);
|
||||
if (historyDetails && statusBar) {
|
||||
let historyBottom = historyDetails.getBoundingClientRect().bottom;
|
||||
let statusTop = statusBar.getBoundingClientRect().top;
|
||||
|
||||
let height: number = statusTop - historyBottom - NotebookHistoryComponent.HEADING_HEIGHT;
|
||||
|
||||
if (this._table) {
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._tableContainer.nativeElement),
|
||||
height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
this._runJobAction = this.instantiationService.createInstance(RunJobAction);
|
||||
this._stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
||||
this._editNotebookJobAction = this.instantiationService.createInstance(EditNotebookJobAction);
|
||||
this._refreshAction = this.instantiationService.createInstance(JobsRefreshAction);
|
||||
this._openNotebookTemplateAction = this.instantiationService.createInstance(OpenTemplateNotebookAction);
|
||||
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._editNotebookJobAction.enabled = !this.showProgressWheel();
|
||||
let targetObject: JobActionContext = { canEdit: !this.showProgressWheel(), job: this._agentNotebookInfo };
|
||||
this._actionBar.context = { targetObject: targetObject, ownerUri: this.ownerUri, component: this };
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editNotebookJobAction },
|
||||
{ action: this._openNotebookTemplateAction }
|
||||
]);
|
||||
}
|
||||
|
||||
public openNotebook(history: azdata.AgentNotebookHistoryInfo) {
|
||||
if (history.runStatus === 0) {
|
||||
return;
|
||||
}
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.getMaterialziedNotebook(ownerUri, targetDatabase, history.materializedNotebookId).then(async (result) => {
|
||||
if (result) {
|
||||
let regex = /:|-/gi;
|
||||
let readableDataTimeString = history.runDate.replace(regex, '').replace(' ', '');
|
||||
let tempNotebookFileName = this._agentViewComponent.agentNotebookInfo.name + '_' + readableDataTimeString;
|
||||
await this._commandService.executeCommand('agent.openNotebookEditorFromJsonString', tempNotebookFileName, result.notebookMaterialized);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public deleteMaterializedNotebook(history: azdata.AgentNotebookHistoryInfo) {
|
||||
//TODO: Implement deletenotebook context menu action
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.deleteMaterializedNotebook(ownerUri, history, targetDatabase).then(async (result) => {
|
||||
if (result) {
|
||||
this.loadHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openTemplateNotebook() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
let jobId = this._agentViewComponent.agentNotebookInfo.jobId;
|
||||
|
||||
this._jobManagementService.getTemplateNotebook(ownerUri, targetDatabase, jobId).then(async (result) => {
|
||||
if (result) {
|
||||
await this._commandService.executeCommand('agent.openNotebookEditorFromJsonString', this._agentViewComponent.agentNotebookInfo.name, result.notebookTemplate, this.agentNotebookInfo, ownerUri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public renameNotebook(history: azdata.AgentNotebookHistoryInfo) {
|
||||
const defaultDateTime = new Date(history.runDate).toLocaleDateString() + ' ' + new Date(history.runDate).toLocaleTimeString();
|
||||
let notebookRunName = (history.materializedNotebookName === '') ? defaultDateTime : history.materializedNotebookName;
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._quickInputService.input({ placeHolder: notebookRunName }).then(async (value) => {
|
||||
if (value) {
|
||||
if (!/\S/.test(value)) {
|
||||
value = '';
|
||||
}
|
||||
await this._jobManagementService.updateNotebookMaterializedName(ownerUri, history, targetDatabase, value).then(async (result) => {
|
||||
if (result) {
|
||||
history.materializedNotebookName = value;
|
||||
this.loadHistory();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public toggleNotebookPin(history: azdata.AgentNotebookHistoryInfo, pin: boolean) {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.updateNotebookMaterializedPin(ownerUri, history, targetDatabase, pin).then(async (result) => {
|
||||
if (result) {
|
||||
history.materializedNotebookPin = pin;
|
||||
this.loadHistory();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public openHistoryContextMenu(event: MouseEvent, history: azdata.AgentNotebookHistoryInfo, contextMenuType: number) {
|
||||
let anchor = {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
};
|
||||
let actionContext = {
|
||||
component: this,
|
||||
history: history
|
||||
};
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => (contextMenuType === 1) ? this.getPinnedGridActions() : this.getGridActions(),
|
||||
getKeyBinding: (action) => this._keybindingFor(action),
|
||||
getActionsContext: () => (actionContext)
|
||||
});
|
||||
}
|
||||
|
||||
protected getGridActions(): IAction[] {
|
||||
const openNotebookAction = this._instantiationService.createInstance(OpenMaterializedNotebookAction);
|
||||
const renameNotebookAction = this._instantiationService.createInstance(RenameNotebookMaterializedAction);
|
||||
const pinNotebookAction = this._instantiationService.createInstance(PinNotebookMaterializedAction);
|
||||
const deleteMaterializedNotebookAction = this._instantiationService.createInstance(DeleteMaterializedNotebookAction);
|
||||
return [
|
||||
openNotebookAction,
|
||||
renameNotebookAction,
|
||||
pinNotebookAction,
|
||||
deleteMaterializedNotebookAction
|
||||
];
|
||||
}
|
||||
|
||||
protected getPinnedGridActions(): IAction[] {
|
||||
const openNotebookAction = this._instantiationService.createInstance(OpenMaterializedNotebookAction);
|
||||
const renameNotebookAction = this._instantiationService.createInstance(RenameNotebookMaterializedAction);
|
||||
const unpinNotebookAction = this._instantiationService.createInstance(UnpinNotebookMaterializedAction);
|
||||
const deleteMaterializedNotebookAction = this._instantiationService.createInstance(DeleteMaterializedNotebookAction);
|
||||
return [
|
||||
openNotebookAction,
|
||||
renameNotebookAction,
|
||||
unpinNotebookAction,
|
||||
deleteMaterializedNotebookAction
|
||||
];
|
||||
}
|
||||
|
||||
public createdTooltip(history: azdata.AgentNotebookHistoryInfo) {
|
||||
let tooltipString: string = '';
|
||||
if (history.materializedNotebookName && history.materializedNotebookName !== '') {
|
||||
tooltipString = history.materializedNotebookName;
|
||||
}
|
||||
let dateOptions = {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
};
|
||||
|
||||
tooltipString += '\n' + nls.localize('notebookHistory.dateCreatedTooltip', "Date Created: ") + new Date(history.runDate).toLocaleDateString(undefined, dateOptions);
|
||||
if (history.materializedNotebookErrorInfo && /\S/.test(history.materializedNotebookErrorInfo)) {
|
||||
tooltipString += '\n' + nls.localize('notebookHistory.notebookErrorTooltip', "Notebook Error: ") + history.materializedNotebookErrorInfo;
|
||||
}
|
||||
if (history.runStatus === 0 && history.message && /\S/.test(history.message)) {
|
||||
tooltipString += '\n' + nls.localize('notebookHistory.ErrorTooltip', "Job Error: ") + history.message;
|
||||
}
|
||||
return tooltipString;
|
||||
}
|
||||
|
||||
public createGrid() {
|
||||
let histories = this._notebookCacheObject.getNotebookHistory(this._agentViewComponent.notebookId);
|
||||
histories = histories.sort((h1, h2) => {
|
||||
return new Date(h2.runDate).getTime() - new Date(h1.runDate).getTime();
|
||||
});
|
||||
this._grids = [];
|
||||
let tempHistory: azdata.AgentNotebookHistoryInfo[] = [];
|
||||
for (let i = 0; i < histories.length; i++) {
|
||||
if (histories[i].materializedNotebookPin) {
|
||||
tempHistory.push(histories[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this._grids.push({
|
||||
title: nls.localize('notebookHistory.pinnedTitle', "Pinned"),
|
||||
histories: tempHistory,
|
||||
contextMenuType: 1,
|
||||
style: 'grid'
|
||||
});
|
||||
// Pushing the pinned notebooks grid
|
||||
tempHistory = [];
|
||||
let count = 0;
|
||||
let i = 0;
|
||||
for (; i < histories.length; i++) {
|
||||
if (!histories[i].materializedNotebookPin && count < 10) {
|
||||
tempHistory.push(histories[i]);
|
||||
count++;
|
||||
}
|
||||
if (count === 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._grids.push({
|
||||
title: nls.localize('notebookHistory.recentRunsTitle', "Recent Runs"),
|
||||
histories: tempHistory,
|
||||
contextMenuType: 0,
|
||||
style: 'grid'
|
||||
});
|
||||
tempHistory = [];
|
||||
for (i += 1; i < histories.length; i++) {
|
||||
if (!histories[i].materializedNotebookPin) {
|
||||
tempHistory.push(histories[i]);
|
||||
}
|
||||
}
|
||||
this._grids.push({
|
||||
title: nls.localize('notebookHistory.pastRunsTitle', "Past Runs"),
|
||||
histories: tempHistory,
|
||||
contextMenuType: 0,
|
||||
style: 'none'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public collapseGrid() {
|
||||
for (let i = 0; i < this._grids.length; i++) {
|
||||
let notebookGrid = document.getElementById('notebook-grid' + i);
|
||||
let arrow = document.getElementById('history-grid-icon' + i);
|
||||
if (notebookGrid) {
|
||||
let checkbox: any = document.getElementById('accordion' + i);
|
||||
if (this._grids[i].style === 'none') {
|
||||
notebookGrid.className = 'notebook-grid ' + i + ' collapsed';
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
notebookGrid.style.display = 'none';
|
||||
checkbox.checked = true;
|
||||
}
|
||||
else {
|
||||
notebookGrid.className = 'notebook-grid ' + i;
|
||||
notebookGrid.style.display = 'grid';
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
checkbox.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public refreshJobs() {
|
||||
this._agentViewComponent.refresh = true;
|
||||
this.loadHistory();
|
||||
}
|
||||
|
||||
/** GETTERS */
|
||||
|
||||
public get showSteps(): boolean {
|
||||
return this._showSteps;
|
||||
}
|
||||
|
||||
public get stepRows() {
|
||||
return this._stepRows;
|
||||
}
|
||||
|
||||
public get ownerUri(): string {
|
||||
return this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
}
|
||||
|
||||
public get serverName(): string {
|
||||
return this._serverName;
|
||||
}
|
||||
|
||||
/** SETTERS */
|
||||
|
||||
public set showSteps(value: boolean) {
|
||||
this._showSteps = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="job-heading-container">
|
||||
<h1 class="job-heading" *ngIf="_isCloud === false">Notebook Jobs</h1>
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Notebooks Jobs Available</h1>
|
||||
<div class="codicon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<div #notebooksgrid class="jobnotebooksview-grid"></div>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="job-heading-container">
|
||||
<h1 class="job-heading" *ngIf="_isCloud === false">Operators</h1>
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Operators Available</h1>
|
||||
<div class="codicon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<div #operatorsgrid class="joboperatorsview-grid"></div>
|
||||
@@ -0,0 +1,224 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobs';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as azdata from 'azdata';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { EditOperatorAction, DeleteOperatorAction, NewOperatorAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { OperatorsCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
|
||||
export const VIEW_SELECTOR: string = 'joboperatorsview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
|
||||
@Component({
|
||||
selector: VIEW_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./operatorsView.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => OperatorsViewComponent) }],
|
||||
})
|
||||
|
||||
export class OperatorsViewComponent extends JobManagementView implements OnInit, OnDestroy {
|
||||
|
||||
private columns: Array<Slick.Column<any>> = [
|
||||
{
|
||||
name: nls.localize('jobOperatorsView.name', "Name"),
|
||||
field: 'name',
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
||||
width: 200,
|
||||
id: 'name'
|
||||
},
|
||||
{ name: nls.localize('jobOperatorsView.emailAddress', "Email Address"), field: 'emailAddress', width: 200, id: 'emailAddress' },
|
||||
{ name: nls.localize('jobOperatorsView.enabled', "Enabled"), field: 'enabled', width: 200, id: 'enabled' },
|
||||
];
|
||||
|
||||
private options: Slick.GridOptions<any> = {
|
||||
syncColumnCellResize: true,
|
||||
enableColumnReorder: false,
|
||||
rowHeight: ROW_HEIGHT,
|
||||
enableCellNavigation: true,
|
||||
editable: false
|
||||
};
|
||||
|
||||
private dataView: any;
|
||||
public _isCloud: boolean;
|
||||
private _operatorsCacheObject: OperatorsCacheObject;
|
||||
|
||||
private _didTabChange: boolean;
|
||||
@ViewChild('operatorsgrid') _gridEl: ElementRef;
|
||||
|
||||
public operators: azdata.AgentOperatorInfo[];
|
||||
public contextAction = NewOperatorAction;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService
|
||||
) {
|
||||
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
||||
let operatorsCacheObject = this._jobManagementService.operatorsCacheObjectMap;
|
||||
let operatorsCache = operatorsCacheObject[this._serverName];
|
||||
if (operatorsCache) {
|
||||
this._operatorsCacheObject = operatorsCache;
|
||||
} else {
|
||||
this._operatorsCacheObject = new OperatorsCacheObject();
|
||||
this._operatorsCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._operatorsCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._gridEl;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._didTabChange = true;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
|
||||
if (height < 0) {
|
||||
height = 0;
|
||||
}
|
||||
|
||||
if (this._table) {
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._gridEl.nativeElement),
|
||||
height));
|
||||
}
|
||||
}
|
||||
|
||||
onFirstVisible() {
|
||||
let self = this;
|
||||
|
||||
let cached: boolean = false;
|
||||
if (this._operatorsCacheObject.serverName === this._serverName) {
|
||||
if (this._operatorsCacheObject.operators && this._operatorsCacheObject.operators.length > 0) {
|
||||
cached = true;
|
||||
this.operators = this._operatorsCacheObject.operators;
|
||||
}
|
||||
}
|
||||
|
||||
let columns = this.columns.map((column) => {
|
||||
column.rerenderOnResize = true;
|
||||
return column;
|
||||
});
|
||||
|
||||
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
||||
let rowDetail = new RowDetailView({
|
||||
cssClass: '_detail_selector',
|
||||
useRowClick: false,
|
||||
panelRows: 1,
|
||||
postTemplate: () => '', // I'm assuming these code paths are just never hit...
|
||||
preTemplate: () => '',
|
||||
process: () => { }
|
||||
});
|
||||
columns.unshift(rowDetail.getColumnDefinition());
|
||||
|
||||
jQuery(this._gridEl.nativeElement).empty();
|
||||
jQuery(this.actionBarContainer.nativeElement).empty();
|
||||
this.initActionBar();
|
||||
this._table = new Table(this._gridEl.nativeElement, { columns }, this.options);
|
||||
this._table.grid.setData(this.dataView, true);
|
||||
|
||||
this._register(this._table.onContextMenu(e => {
|
||||
self.openContextMenu(e);
|
||||
}));
|
||||
|
||||
// check for cached state
|
||||
if (cached && this._agentViewComponent.refresh !== true) {
|
||||
this.onOperatorsAvailable(this.operators);
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
} else {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._jobManagementService.getOperators(ownerUri).then((result) => {
|
||||
if (result && result.operators) {
|
||||
self.operators = result.operators;
|
||||
self._operatorsCacheObject.operators = result.operators;
|
||||
self.onOperatorsAvailable(result.operators);
|
||||
} else {
|
||||
// TODO: handle error
|
||||
}
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible && !this._didTabChange) {
|
||||
this._cd.detectChanges();
|
||||
} else if (this._didTabChange) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onOperatorsAvailable(operators: azdata.AgentOperatorInfo[]) {
|
||||
let items: any = operators.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
emailAddress: item.emailAddress,
|
||||
enabled: item.enabled
|
||||
};
|
||||
});
|
||||
|
||||
this.dataView.beginUpdate();
|
||||
this.dataView.setItems(items);
|
||||
this.dataView.endUpdate();
|
||||
this._operatorsCacheObject.dataview = this.dataView;
|
||||
this._table.autosizeColumns();
|
||||
this._table.resizeCanvas();
|
||||
}
|
||||
|
||||
protected getTableActions(): IAction[] {
|
||||
return [
|
||||
this._instantiationService.createInstance(EditOperatorAction),
|
||||
this._instantiationService.createInstance(DeleteOperatorAction)
|
||||
];
|
||||
}
|
||||
|
||||
protected getCurrentTableObject(rowIndex: number): any {
|
||||
return (this.operators && this.operators.length >= rowIndex)
|
||||
? this.operators[rowIndex]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private renderName(row, cell, value, columnDef, dataContext) {
|
||||
let resultIndicatorClass = dataContext.enabled ? 'operatorview-operatornameindicatorenabled' :
|
||||
'operatorview-operatornameindicatordisabled';
|
||||
return '<table class="operatorview-operatornametable"><tr class="operatorview-operatornamerow">' +
|
||||
'<td nowrap class=' + resultIndicatorClass + '></td>' +
|
||||
'<td nowrap class="operatorview-operatornametext">' + dataContext.name + '</td>' +
|
||||
'</tr></table>';
|
||||
}
|
||||
|
||||
public async openCreateOperatorDialog() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
await this._commandService.executeCommand('agent.openOperatorDialog', ownerUri);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="job-heading-container">
|
||||
<h1 class="job-heading" *ngIf="_isCloud === false">Proxies</h1>
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Proxies Available</h1>
|
||||
<div class="codicon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<div #proxiesgrid class="jobproxiesview-grid"></div>
|
||||
@@ -0,0 +1,229 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobs';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/contrib/jobManagement/browser/agentView.component';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { EditProxyAction, DeleteProxyAction, NewProxyAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { CommonServiceInterface } from 'sql/workbench/services/bootstrap/browser/commonServiceInterface.service';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { JobManagementView } from 'sql/workbench/contrib/jobManagement/browser/jobManagementView';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { ProxiesCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
|
||||
export const VIEW_SELECTOR: string = 'jobproxiesview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
|
||||
@Component({
|
||||
selector: VIEW_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./proxiesView.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => ProxiesViewComponent) }],
|
||||
})
|
||||
|
||||
export class ProxiesViewComponent extends JobManagementView implements OnInit, OnDestroy {
|
||||
|
||||
private columns: Array<Slick.Column<any>> = [
|
||||
{
|
||||
name: nls.localize('jobProxiesView.accountName', "Account Name"),
|
||||
field: 'accountName',
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
||||
width: 200,
|
||||
id: 'accountName'
|
||||
},
|
||||
{ name: nls.localize('jobProxiesView.credentialName', "Credential Name"), field: 'credentialName', width: 200, id: 'credentialName' },
|
||||
{ name: nls.localize('jobProxiesView.description', "Description"), field: 'description', width: 200, id: 'description' },
|
||||
{ name: nls.localize('jobProxiesView.isEnabled', "Enabled"), field: 'isEnabled', width: 200, id: 'isEnabled' }
|
||||
];
|
||||
|
||||
private options: Slick.GridOptions<any> = {
|
||||
syncColumnCellResize: true,
|
||||
enableColumnReorder: false,
|
||||
rowHeight: ROW_HEIGHT,
|
||||
enableCellNavigation: true,
|
||||
editable: false
|
||||
};
|
||||
|
||||
private dataView: any;
|
||||
public _isCloud: boolean;
|
||||
private _proxiesCacheObject: ProxiesCacheObject;
|
||||
|
||||
public proxies: azdata.AgentProxyInfo[];
|
||||
public readonly contextAction = NewProxyAction;
|
||||
|
||||
private _didTabChange: boolean;
|
||||
@ViewChild('proxiesgrid') _gridEl: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService
|
||||
) {
|
||||
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
||||
let proxiesCacheObjectMap = this._jobManagementService.proxiesCacheObjectMap;
|
||||
let proxiesCacheObject = proxiesCacheObjectMap[this._serverName];
|
||||
if (proxiesCacheObject) {
|
||||
this._proxiesCacheObject = proxiesCacheObject;
|
||||
} else {
|
||||
this._proxiesCacheObject = new ProxiesCacheObject();
|
||||
this._proxiesCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._proxiesCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._gridEl;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._didTabChange = true;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let height = dom.getContentHeight(this._gridEl.nativeElement) - 10;
|
||||
if (height < 0) {
|
||||
height = 0;
|
||||
}
|
||||
|
||||
if (this._table) {
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._gridEl.nativeElement),
|
||||
height));
|
||||
}
|
||||
}
|
||||
|
||||
onFirstVisible() {
|
||||
let self = this;
|
||||
let cached: boolean = false;
|
||||
if (this._proxiesCacheObject.serverName === this._serverName) {
|
||||
if (this._proxiesCacheObject.proxies && this._proxiesCacheObject.proxies.length > 0) {
|
||||
cached = true;
|
||||
this.proxies = this._proxiesCacheObject.proxies;
|
||||
}
|
||||
}
|
||||
|
||||
let columns = this.columns.map((column) => {
|
||||
column.rerenderOnResize = true;
|
||||
return column;
|
||||
});
|
||||
|
||||
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
||||
let rowDetail = new RowDetailView({
|
||||
cssClass: '_detail_selector',
|
||||
useRowClick: false,
|
||||
panelRows: 1,
|
||||
postTemplate: () => '', // I'm assuming these code paths are just never hit...
|
||||
preTemplate: () => '',
|
||||
process: () => { }
|
||||
});
|
||||
columns.unshift(rowDetail.getColumnDefinition());
|
||||
|
||||
jQuery(this._gridEl.nativeElement).empty();
|
||||
jQuery(this.actionBarContainer.nativeElement).empty();
|
||||
this.initActionBar();
|
||||
this._table = new Table(this._gridEl.nativeElement, { columns }, this.options);
|
||||
this._table.grid.setData(this.dataView, true);
|
||||
|
||||
this._register(this._table.onContextMenu(e => {
|
||||
self.openContextMenu(e);
|
||||
}));
|
||||
|
||||
// checked for cached state
|
||||
if (cached && this._agentViewComponent.refresh !== true) {
|
||||
self.onProxiesAvailable(this.proxies);
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
} else {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._jobManagementService.getProxies(ownerUri).then((result) => {
|
||||
if (result && result.proxies) {
|
||||
self.proxies = result.proxies;
|
||||
self._proxiesCacheObject.proxies = result.proxies;
|
||||
self.onProxiesAvailable(result.proxies);
|
||||
} else {
|
||||
// TODO: handle error
|
||||
}
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible && !this._didTabChange) {
|
||||
this._cd.detectChanges();
|
||||
} else if (this._didTabChange) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onProxiesAvailable(proxies: azdata.AgentProxyInfo[]) {
|
||||
let items: any = proxies.map((item) => {
|
||||
return {
|
||||
id: item.accountName,
|
||||
accountName: item.accountName,
|
||||
credentialName: item.credentialName,
|
||||
description: item.description,
|
||||
isEnabled: item.isEnabled
|
||||
};
|
||||
});
|
||||
|
||||
this.dataView.beginUpdate();
|
||||
this.dataView.setItems(items);
|
||||
this.dataView.endUpdate();
|
||||
this._proxiesCacheObject.dataview = this.dataView;
|
||||
this._table.autosizeColumns();
|
||||
this._table.resizeCanvas();
|
||||
}
|
||||
|
||||
protected getTableActions(): IAction[] {
|
||||
return [
|
||||
this._instantiationService.createInstance(EditProxyAction),
|
||||
this._instantiationService.createInstance(DeleteProxyAction)
|
||||
];
|
||||
}
|
||||
|
||||
protected getCurrentTableObject(rowIndex: number): any {
|
||||
return (this.proxies && this.proxies.length >= rowIndex)
|
||||
? this.proxies[rowIndex]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private renderName(row, cell, value, columnDef, dataContext) {
|
||||
let resultIndicatorClass = dataContext.isEnabled ? 'proxyview-proxynameindicatorenabled' :
|
||||
'proxyview-proxynameindicatordisabled';
|
||||
return '<table class="proxyview-proxynametable"><tr class="proxyview-proxynamerow">' +
|
||||
'<td nowrap class=' + resultIndicatorClass + '></td>' +
|
||||
'<td nowrap class="proxyview-proxynametext">' + dataContext.accountName + '</td>' +
|
||||
'</tr></table>';
|
||||
}
|
||||
|
||||
public openCreateProxyDialog() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._jobManagementService.getCredentials(ownerUri).then(async (result) => {
|
||||
if (result && result.credentials) {
|
||||
await this._commandService.executeCommand('agent.openProxyDialog', ownerUri, undefined, result.credentials);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||