From 567c1be9fb492b323d304e37eec70642595c98f2 Mon Sep 17 00:00:00 2001 From: Charles Gagnon Date: Mon, 11 May 2020 09:21:03 -0700 Subject: [PATCH] Refactor BDC dashboard to use ModelView Dashboard (#10327) * WIP refactor * More changes * More changes * Remove unused styles * Uncomment code --- .../resources/status_circle_blank.svg | 2 + .../resources/status_circle_red.svg | 3 + .../src/bigDataCluster/constants.ts | 17 +- .../src/bigDataCluster/dialog/bdcDashboard.ts | 257 ++++-------------- .../dialog/bdcDashboardModel.ts | 3 +- .../dialog/bdcDashboardOverviewPage.ts | 119 ++++---- .../bigDataCluster/dialog/bdcDashboardPage.ts | 76 ++++-- .../dialog/bdcDashboardResourceStatusPage.ts | 4 +- .../dialog/bdcServiceStatusPage.ts | 133 ++------- .../dialog/intializingComponent.ts | 40 +++ .../src/bigDataCluster/localizedConstants.ts | 3 +- .../src/bigDataCluster/utils.ts | 8 +- extensions/big-data-cluster/src/extension.ts | 2 +- 13 files changed, 238 insertions(+), 429 deletions(-) create mode 100644 extensions/big-data-cluster/resources/status_circle_blank.svg create mode 100644 extensions/big-data-cluster/resources/status_circle_red.svg create mode 100644 extensions/big-data-cluster/src/bigDataCluster/dialog/intializingComponent.ts diff --git a/extensions/big-data-cluster/resources/status_circle_blank.svg b/extensions/big-data-cluster/resources/status_circle_blank.svg new file mode 100644 index 0000000000..af5badbac8 --- /dev/null +++ b/extensions/big-data-cluster/resources/status_circle_blank.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/extensions/big-data-cluster/resources/status_circle_red.svg b/extensions/big-data-cluster/resources/status_circle_red.svg new file mode 100644 index 0000000000..f73edde71c --- /dev/null +++ b/extensions/big-data-cluster/resources/status_circle_red.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/extensions/big-data-cluster/src/bigDataCluster/constants.ts b/extensions/big-data-cluster/src/bigDataCluster/constants.ts index dfe2569e2c..20d1f69f84 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/constants.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/constants.ts @@ -26,6 +26,8 @@ export class IconPathHelper { public static status_ok: IconPath; public static status_warning: IconPath; public static notebook: IconPath; + public static status_circle_red: IconPath; + public static status_circle_blank: IconPath; public static setExtensionContext(extensionContext: vscode.ExtensionContext) { IconPathHelper.extensionContext = extensionContext; @@ -53,6 +55,14 @@ export class IconPathHelper { light: IconPathHelper.extensionContext.asAbsolutePath('resources/light/notebook.svg'), dark: IconPathHelper.extensionContext.asAbsolutePath('resources/dark/notebook_inverse.svg') }; + IconPathHelper.status_circle_red = { + light: IconPathHelper.extensionContext.asAbsolutePath('resources/status_circle_red.svg'), + dark: IconPathHelper.extensionContext.asAbsolutePath('resources/status_circle_red.svg') + }; + IconPathHelper.status_circle_blank = { + light: IconPathHelper.extensionContext.asAbsolutePath('resources/status_circle_blank.svg'), + dark: IconPathHelper.extensionContext.asAbsolutePath('resources/status_circle_blank.svg') + }; } } @@ -60,13 +70,6 @@ export namespace cssStyles { export const title = { 'font-size': '14px', 'font-weight': '600' }; export const tableHeader = { 'text-align': 'left', 'font-weight': 'bold', 'text-transform': 'uppercase', 'font-size': '10px', 'user-select': 'text' }; export const text = { 'margin-block-start': '0px', 'margin-block-end': '0px' }; - export const overflowEllipsisText = { ...text, 'overflow': 'hidden', 'text-overflow': 'ellipsis' }; - export const nonSelectableText = { ...cssStyles.text, 'user-select': 'none' }; - export const tabHeaderText = { 'margin-block-start': '2px', 'margin-block-end': '0px', 'user-select': 'none' }; - export const selectedResourceHeaderTab = { 'font-weight': 'bold', 'color': '' }; - export const unselectedResourceHeaderTab = { 'font-weight': '', 'color': '#0078d4' }; - export const selectedTabDiv = { 'border-bottom': '2px solid #000' }; - export const unselectedTabDiv = { 'border-bottom': '1px solid #ccc' }; export const lastUpdatedText = { ...text, 'color': '#595959' }; export const errorText = { ...text, 'color': 'red' }; } diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts index 0d6d80154a..ea4a94c5c1 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboard.ts @@ -4,176 +4,62 @@ *--------------------------------------------------------------------------------------------*/ import * as azdata from 'azdata'; -import * as vscode from 'vscode'; -import { BdcDashboardModel, getTroubleshootNotebookUrl, BdcErrorEvent } from './bdcDashboardModel'; -import { IconPathHelper, cssStyles } from '../constants'; +import { BdcDashboardModel, BdcErrorEvent } from './bdcDashboardModel'; import { BdcServiceStatusPage } from './bdcServiceStatusPage'; import { BdcDashboardOverviewPage } from './bdcDashboardOverviewPage'; import { BdcStatusModel, ServiceStatusModel } from '../controller/apiGenerated'; -import { getHealthStatusDot, getServiceNameDisplayText, showErrorMessage } from '../utils'; +import { getServiceNameDisplayText, showErrorMessage, getHealthStatusDotIcon } from '../utils'; import { HdfsDialogCancelledError } from './hdfsDialogBase'; -import { BdcDashboardPage } from './bdcDashboardPage'; +import { InitializingComponent } from './intializingComponent'; import * as loc from '../localizedConstants'; -const navWidth = '200px'; +export class BdcDashboard extends InitializingComponent { -const selectedTabCss = { 'font-weight': 'bold' }; -const unselectedTabCss = { 'font-weight': '' }; - -type NavTab = { serviceName: string, div: azdata.DivContainer, dot: azdata.TextComponent, text: azdata.TextComponent }; - -export class BdcDashboard extends BdcDashboardPage { - - private dashboard: azdata.workspace.ModelViewEditor; + private dashboard: azdata.window.ModelViewDashboard; private modelView: azdata.ModelView; - private mainAreaContainer: azdata.FlexContainer; - private navContainer: azdata.FlexContainer; - private overviewPage: BdcDashboardOverviewPage; - private currentTab: NavTab; - private currentPageContainer: azdata.FlexContainer; - - private refreshButton: azdata.ButtonComponent; - - private serviceTabPageMapping = new Map(); + private createdServicePages: Map = new Map(); + private overviewTab: azdata.DashboardTab; constructor(private title: string, private model: BdcDashboardModel) { super(); - this.model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus))); - this.model.onBdcError(errorEvent => this.eventuallyRunOnInitialized(() => this.handleError(errorEvent))); + model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus))); + model.onBdcError(errorEvent => this.eventuallyRunOnInitialized(() => this.handleError(errorEvent))); } - public showDashboard(): void { - this.createDashboard(); - this.dashboard.openEditor(); + public async showDashboard(): Promise { + await this.createDashboard(); + await this.dashboard.open(); } - private createDashboard(): void { - this.dashboard = azdata.workspace.createModelViewEditor(this.title, { retainContextWhenHidden: true, supportsSave: false }); - this.dashboard.registerContent(async (modelView: azdata.ModelView) => { + private async createDashboard(): Promise { + this.dashboard = azdata.window.createModelViewDashboard(this.title, { alwaysShowTabs: true }); + this.dashboard.registerTabs(async (modelView: azdata.ModelView) => { this.modelView = modelView; - const rootContainer = modelView.modelBuilder.flexContainer().withLayout( - { - flexFlow: 'column', - width: '100%', - height: '100%' - }).component(); - // ########### - // # TOOLBAR # - // ########### - - // Refresh button - this.refreshButton = modelView.modelBuilder.button() - .withProperties({ - label: loc.refresh, - iconPath: IconPathHelper.refresh - }).component(); - - this.refreshButton.onDidClick(async () => { - this.overviewPage.onRefreshStarted(); - await this.doRefresh(); - }); - - const openTroubleshootNotebookButton = modelView.modelBuilder.button() - .withProperties({ - label: loc.troubleshoot, - iconPath: IconPathHelper.notebook - }).component(); - - openTroubleshootNotebookButton.onDidClick(() => { - vscode.commands.executeCommand('books.sqlserver2019', getTroubleshootNotebookUrl(this.currentTab.serviceName)); - }); - - const toolbarContainer = modelView.modelBuilder.toolbarContainer() - .withToolbarItems( - [ - { component: this.refreshButton }, - { component: openTroubleshootNotebookButton } - ] - ).component(); - - rootContainer.addItem(toolbarContainer, { flex: '0 0 auto' }); - - // ############# - // # MAIN AREA # - // ############# - - this.mainAreaContainer = modelView.modelBuilder.flexContainer().withLayout( - { - flexFlow: 'row', - width: '100%', - height: '100%' - }).component(); - - rootContainer.addItem(this.mainAreaContainer, { flex: '0 0 100%' }); - - // ################# - // # NAV CONTAINER # - // ################# - - this.navContainer = modelView.modelBuilder.flexContainer().withLayout( - { - flexFlow: 'column', - width: navWidth, - height: '100%' - } - ).withProperties({ - ariaRole: 'tablist' - }).component(); - - this.mainAreaContainer.addItem(this.navContainer, { flex: `0 0 ${navWidth}`, CSSStyles: { 'padding': '0 20px 0 20px', 'border-right': 'solid 1px #ccc' } }); - - // Overview nav item - this will be the initial page - const overviewNavItemDiv = modelView.modelBuilder - .divContainer() - .withLayout({ width: navWidth, height: '30px' }) - .withProperties({ - clickable: true, - ariaRole: 'tab', - ariaSelected: true - }).component(); - const overviewNavItemText = modelView.modelBuilder.text().withProperties({ value: loc.bdcOverview }).component(); - overviewNavItemText.updateCssStyles(selectedTabCss); - overviewNavItemDiv.addItem(overviewNavItemText, { CSSStyles: { 'user-select': 'text' } }); - this.overviewPage = new BdcDashboardOverviewPage(this, this.model); - const overviewContainer: azdata.FlexContainer = this.overviewPage.create(modelView); - this.currentPageContainer = overviewContainer; - this.currentTab = { serviceName: undefined, div: overviewNavItemDiv, dot: undefined, text: overviewNavItemText }; - this.mainAreaContainer.addItem(overviewContainer, { flex: '0 0 100%', CSSStyles: { 'margin': '0 20px 0 20px' } }); - - overviewNavItemDiv.onDidClick(() => { - if (this.currentTab) { - this.currentTab.text.updateCssStyles(unselectedTabCss); - this.currentTab.div.ariaSelected = false; - } - this.mainAreaContainer.removeItem(this.currentPageContainer); - this.mainAreaContainer.addItem(overviewContainer, { flex: '0 0 100%', CSSStyles: { 'margin': '0 20px 0 20px' } }); - this.currentPageContainer = overviewContainer; - this.currentTab = { serviceName: undefined, div: overviewNavItemDiv, dot: undefined, text: overviewNavItemText }; - this.currentTab.text.updateCssStyles(selectedTabCss); - this.currentTab.div.ariaSelected = true; - }); - this.navContainer.addItem(overviewNavItemDiv, { flex: '0 0 auto' }); - - const clusterDetailsHeader = modelView.modelBuilder.text().withProperties({ value: loc.clusterDetails, CSSStyles: { 'margin-block-end': '0px' } }).component(); - this.navContainer.addItem(clusterDetailsHeader, { CSSStyles: { 'user-select': 'none', 'font-weight': 'bold', 'border-bottom': 'solid 1px #ccc', 'margin-bottom': '10px' } }); - - await modelView.initializeModel(rootContainer); - - this.initialized = true; - - // Now that we've created the UI load data from the model in case it already had data - this.handleBdcStatusUpdate(this.model.bdcStatus); + const overviewPage = new BdcDashboardOverviewPage(this.model, modelView); + this.overviewTab = { + title: loc.bdcOverview, + id: 'overview-tab', + content: overviewPage.container, + toolbar: overviewPage.toolbarContainer + }; + return [ + this.overviewTab + ]; }); + this.initialized = true; + + // Now that we've created the UI load data from the model in case it already had data + this.handleBdcStatusUpdate(this.model.bdcStatus); } private handleBdcStatusUpdate(bdcStatus?: BdcStatusModel): void { if (!bdcStatus) { return; } - this.updateServiceNavTabs(bdcStatus.services); + this.updateServicePages(bdcStatus.services); } private handleError(errorEvent: BdcErrorEvent): void { @@ -187,77 +73,34 @@ export class BdcDashboard extends BdcDashboardPage { } } - private async doRefresh(): Promise { - try { - this.refreshButton.enabled = false; - await this.model.refresh(); - } finally { - this.refreshButton.enabled = true; - } - } - /** - * Switches the current navigation tab to the one corresponding to the specified service - * @param serviceName The name of the service to switch to the tab of + * Update the service tab pages, creating any new ones as necessary */ - public switchToServiceTab(serviceName: string): void { - const tabPageMapping = this.serviceTabPageMapping.get(serviceName); - if (!tabPageMapping) { - return; - } - if (this.currentTab) { - this.currentTab.text.updateCssStyles(unselectedTabCss); - this.currentTab.div.ariaSelected = false; - } - this.mainAreaContainer.removeItem(this.currentPageContainer); - this.mainAreaContainer.addItem(tabPageMapping.servicePage.container, { CSSStyles: { 'margin': '0 20px 0 20px' } }); - this.currentPageContainer = tabPageMapping.servicePage.container; - this.currentTab = tabPageMapping.navTab; - this.currentTab.text.updateCssStyles(selectedTabCss); - this.currentTab.div.ariaSelected = true; - } - - /** - * Helper to update the navigation tabs for the services when we get a status update - */ - private updateServiceNavTabs(services?: ServiceStatusModel[]): void { + private updateServicePages(services?: ServiceStatusModel[]): void { if (services) { - // Add a nav item for each service + // Create a service page for each new service. We currently don't support services being removed. services.forEach(s => { - const existingTabPage = this.serviceTabPageMapping.get(s.serviceName); - if (existingTabPage) { - // We've already created the tab and page for this service, just update the tab health status dot - existingTabPage.navTab.dot.value = getHealthStatusDot(s.healthStatus); + const existingPage = this.createdServicePages.get(s.serviceName); + if (existingPage) { + existingPage.icon = getHealthStatusDotIcon(s.healthStatus); } else { - // New service - create the page and tab - const navItem = createServiceNavTab(this.modelView.modelBuilder, s); const serviceStatusPage = new BdcServiceStatusPage(s.serviceName, this.model, this.modelView); - this.serviceTabPageMapping.set(s.serviceName, { navTab: navItem, servicePage: serviceStatusPage }); - navItem.div.onDidClick(() => { - this.switchToServiceTab(s.serviceName); - }); - this.navContainer.addItem(navItem.div, { flex: '0 0 auto' }); + const newTab = { + title: getServiceNameDisplayText(s.serviceName), + id: s.serviceName, + icon: getHealthStatusDotIcon(s.healthStatus), + content: serviceStatusPage.container, + toolbar: serviceStatusPage.toolbarContainer + }; + this.createdServicePages.set(s.serviceName, newTab); } }); + this.dashboard.updateTabs([ + this.overviewTab, + { + title: loc.clusterDetails, + tabs: Array.from(this.createdServicePages.values()) + }]); } } } - -function createServiceNavTab(modelBuilder: azdata.ModelBuilder, serviceStatus: ServiceStatusModel): NavTab { - const div = modelBuilder.divContainer() - .withLayout({ - width: navWidth, - height: '30px', - }) - .withProperties({ - clickable: true, - ariaRole: 'tab' - }).component(); - const innerContainer = modelBuilder.flexContainer().withLayout({ width: navWidth, height: '30px', flexFlow: 'row' }).component(); - const dot = modelBuilder.text().withProperties({ value: getHealthStatusDot(serviceStatus.healthStatus), CSSStyles: { 'color': 'red', 'font-size': '40px', 'width': '20px', ...cssStyles.nonSelectableText } }).component(); - innerContainer.addItem(dot, { flex: '0 0 auto' }); - const text = modelBuilder.text().withProperties({ value: getServiceNameDisplayText(serviceStatus.serviceName), CSSStyles: { ...cssStyles.tabHeaderText } }).component(); - innerContainer.addItem(text, { flex: '0 0 auto' }); - div.addItem(innerContainer); - return { serviceName: serviceStatus.serviceName, div: div, dot: dot, text: text }; -} diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts index e4052cdc0e..bf9583cfb7 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardModel.ts @@ -34,8 +34,7 @@ export class BdcDashboardModel { constructor(private _options: BdcDashboardOptions, private _treeDataProvider: ControllerTreeDataProvider) { try { this._clusterController = new ClusterController(_options.url, _options.auth, _options.username, _options.password); - // tslint:disable-next-line:no-floating-promises - this.refresh(); + this.refresh().catch(e => console.log(`Unexpected error refreshing BdcModel ${e instanceof Error ? e.message : e}`)); } catch { this.promptReconnect().then(async () => { await this.refresh(); diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts index 6fdfe2d95e..df50b587e7 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardOverviewPage.ts @@ -9,26 +9,17 @@ import { BdcDashboardModel, BdcErrorEvent } from './bdcDashboardModel'; import { IconPathHelper, cssStyles } from '../constants'; import { getStateDisplayText, getHealthStatusDisplayText, getEndpointDisplayText, getHealthStatusIcon, getServiceNameDisplayText, Endpoint, getBdcStatusErrorMessage } from '../utils'; import { EndpointModel, BdcStatusModel } from '../controller/apiGenerated'; -import { BdcDashboard } from './bdcDashboard'; import { createViewDetailsButton } from './commonControls'; import { HdfsDialogCancelledError } from './hdfsDialogBase'; import { BdcDashboardPage } from './bdcDashboardPage'; import * as loc from '../localizedConstants'; -const clusterStateLabelColumnWidth = 100; -const clusterStateValueColumnWidth = 225; -const healthStatusColumnWidth = 125; - const hyperlinkedEndpoints = [Endpoint.metricsui, Endpoint.logsui, Endpoint.sparkHistory, Endpoint.yarnUi]; export class BdcDashboardOverviewPage extends BdcDashboardPage { - - private modelBuilder: azdata.ModelBuilder; - + private rootContainer: azdata.FlexContainer; private lastUpdatedLabel: azdata.TextComponent; - private propertiesContainer: azdata.DivContainer; - private clusterStateLoadingComponent: azdata.LoadingComponent; - private clusterHealthStatusLoadingComponent: azdata.LoadingComponent; + private propertiesContainerLoadingComponent: azdata.LoadingComponent; private serviceStatusTable: azdata.DeclarativeTableComponent; private endpointsTable: azdata.DeclarativeTableComponent; @@ -40,16 +31,23 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { private endpointsErrorMessage: azdata.TextComponent; private serviceStatusErrorMessage: azdata.TextComponent; - constructor(private dashboard: BdcDashboard, private model: BdcDashboardModel) { - super(); + constructor(model: BdcDashboardModel, modelView: azdata.ModelView) { + super(model, modelView); this.model.onDidUpdateEndpoints(endpoints => this.eventuallyRunOnInitialized(() => this.handleEndpointsUpdate(endpoints))); this.model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus))); this.model.onBdcError(error => this.eventuallyRunOnInitialized(() => this.handleBdcError(error))); } - public create(view: azdata.ModelView): azdata.FlexContainer { - this.modelBuilder = view.modelBuilder; - const rootContainer = view.modelBuilder.flexContainer().withLayout( + public get container(): azdata.FlexContainer { + // Lazily create the container only when needed + if (!this.rootContainer) { + this.rootContainer = this.createContainer(); + } + return this.rootContainer; + } + + public createContainer(): azdata.FlexContainer { + const rootContainer = this.modelView.modelBuilder.flexContainer().withLayout( { flexFlow: 'column', width: '100%', @@ -60,51 +58,24 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { // # PROPERTIES # // ############## - const propertiesLabel = view.modelBuilder.text() + const propertiesLabel = this.modelView.modelBuilder.text() .withProperties({ value: loc.clusterProperties, CSSStyles: { 'margin-block-start': '0px', 'margin-block-end': '10px' } }) .component(); rootContainer.addItem(propertiesLabel, { CSSStyles: { 'margin-top': '15px', 'padding-left': '10px', ...cssStyles.title } }); - this.propertiesErrorMessage = view.modelBuilder.text().withProperties({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component(); - rootContainer.addItem(this.propertiesErrorMessage, { flex: '0 0 auto' }); + const propertiesContainer = this.modelView.modelBuilder.propertiesContainer().component(); - this.propertiesContainer = view.modelBuilder.divContainer().withProperties({ clickable: false }).component(); - - // Row 1 - const row1 = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '30px', alignItems: 'center' }).component(); - - // Cluster State - const clusterStateLabel = view.modelBuilder.text().withProperties({ value: loc.clusterState }).component(); - const clusterStateValue = view.modelBuilder.text().component(); - this.clusterStateLoadingComponent = view.modelBuilder.loadingComponent() - .withItem(clusterStateValue) - .withProperties({ loadingCompletedText: loc.loadingClusterStateCompleted }) - .component(); - row1.addItem(clusterStateLabel, { CSSStyles: { 'width': `${clusterStateLabelColumnWidth}px`, 'min-width': `${clusterStateLabelColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } }); - row1.addItem(this.clusterStateLoadingComponent, { CSSStyles: { 'width': `${clusterStateValueColumnWidth}px`, 'min-width': `${clusterStateValueColumnWidth}px` } }); - - // Health Status - const healthStatusLabel = view.modelBuilder.text().withProperties({ value: loc.healthStatusWithColon }).component(); - const healthStatusValue = view.modelBuilder.text().component(); - this.clusterHealthStatusLoadingComponent = view.modelBuilder.loadingComponent() - .withItem(healthStatusValue) - .withProperties({ loadingCompletedText: loc.loadingHealthStatusCompleted }) - .component(); - row1.addItem(healthStatusLabel, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px`, 'user-select': 'none', 'font-weight': 'bold' } }); - row1.addItem(this.clusterHealthStatusLoadingComponent, { CSSStyles: { 'width': `${healthStatusColumnWidth}px`, 'min-width': `${healthStatusColumnWidth}px` } }); - - this.propertiesContainer.addItem(row1, { CSSStyles: { 'padding-left': '10px', 'border-bottom': 'solid 1px #ccc', 'box-sizing': 'border-box', 'user-select': 'text' } }); - - rootContainer.addItem(this.propertiesContainer, { flex: '0 0 auto' }); + this.propertiesContainerLoadingComponent = this.modelView.modelBuilder.loadingComponent().withItem(propertiesContainer).component(); + rootContainer.addItem(this.propertiesContainerLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'padding-left': '10px' } }); // ############ // # OVERVIEW # // ############ - const overviewHeaderContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '20px' }).component(); + const overviewHeaderContainer = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'row', height: '20px' }).component(); rootContainer.addItem(overviewHeaderContainer, { CSSStyles: { 'padding-left': '10px', 'padding-top': '15px' } }); - const overviewLabel = view.modelBuilder.text() + const overviewLabel = this.modelView.modelBuilder.text() .withProperties({ value: loc.clusterOverview, CSSStyles: { ...cssStyles.text } @@ -113,7 +84,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { overviewHeaderContainer.addItem(overviewLabel, { CSSStyles: { ...cssStyles.title } }); - this.lastUpdatedLabel = view.modelBuilder.text() + this.lastUpdatedLabel = this.modelView.modelBuilder.text() .withProperties({ value: loc.lastUpdated(), CSSStyles: { ...cssStyles.lastUpdatedText } @@ -121,9 +92,9 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { overviewHeaderContainer.addItem(this.lastUpdatedLabel, { CSSStyles: { 'margin-left': '45px' } }); - const overviewContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%' }).component(); + const overviewContainer = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%' }).component(); - this.serviceStatusTable = view.modelBuilder.declarativeTable() + this.serviceStatusTable = this.modelView.modelBuilder.declarativeTable() .withProperties( { columns: [ @@ -214,19 +185,19 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { }) .component(); - this.serviceStatusDisplayContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); + this.serviceStatusDisplayContainer = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); this.serviceStatusDisplayContainer.addItem(this.serviceStatusTable); // Note we don't make the table a child of the loading component since making the loading component align correctly // messes up the layout for the table that we display after loading is finished. Instead we'll just remove the loading // component once it's finished loading the content - this.serviceStatusLoadingComponent = view.modelBuilder.loadingComponent() + this.serviceStatusLoadingComponent = this.modelView.modelBuilder.loadingComponent() .withProperties({ CSSStyles: { 'padding-top': '0px', 'padding-bottom': '0px' } }) .component(); this.serviceStatusDisplayContainer.addItem(this.serviceStatusLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'padding-left': '150px', width: '30px' } }); - this.serviceStatusErrorMessage = view.modelBuilder.text().withProperties({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component(); + this.serviceStatusErrorMessage = this.modelView.modelBuilder.text().withProperties({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component(); overviewContainer.addItem(this.serviceStatusErrorMessage); overviewContainer.addItem(this.serviceStatusDisplayContainer); @@ -237,16 +208,16 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { // # SERVICE ENDPOINTS # // ##################### - const endpointsLabel = view.modelBuilder.text() + const endpointsLabel = this.modelView.modelBuilder.text() .withProperties({ value: loc.serviceEndpoints, CSSStyles: { 'margin-block-start': '20px', 'margin-block-end': '0px' } }) .component(); rootContainer.addItem(endpointsLabel, { CSSStyles: { 'padding-left': '10px', ...cssStyles.title } }); - this.endpointsErrorMessage = view.modelBuilder.text().withProperties({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component(); + this.endpointsErrorMessage = this.modelView.modelBuilder.text().withProperties({ display: 'none', CSSStyles: { ...cssStyles.errorText } }).component(); - const endpointsContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%' }).component(); + const endpointsContainer = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column', width: '100%', height: '100%' }).component(); - this.endpointsTable = view.modelBuilder.declarativeTable() + this.endpointsTable = this.modelView.modelBuilder.declarativeTable() .withProperties( { columns: [ @@ -305,13 +276,13 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { ariaLabel: loc.serviceEndpoints }).component(); - this.endpointsDisplayContainer = view.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); + this.endpointsDisplayContainer = this.modelView.modelBuilder.flexContainer().withLayout({ flexFlow: 'column' }).component(); this.endpointsDisplayContainer.addItem(this.endpointsTable); // Note we don't make the table a child of the loading component since making the loading component align correctly // messes up the layout for the table that we display after loading is finished. Instead we'll just remove the loading // component once it's finished loading the content - this.endpointsLoadingComponent = view.modelBuilder.loadingComponent() + this.endpointsLoadingComponent = this.modelView.modelBuilder.loadingComponent() .withProperties({ CSSStyles: { 'padding-top': '0px', 'padding-bottom': '0px' } }) .component(); this.endpointsDisplayContainer.addItem(this.endpointsLoadingComponent, { flex: '0 0 auto', CSSStyles: { 'padding-left': '150px', width: '30px' } }); @@ -335,7 +306,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { this.endpointsErrorMessage.display = 'none'; this.serviceStatusDisplayContainer.display = undefined; - this.propertiesContainer.display = undefined; + this.propertiesContainerLoadingComponent.display = undefined; this.endpointsDisplayContainer.display = undefined; } @@ -345,31 +316,33 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { } this.lastUpdatedLabel.value = loc.lastUpdated(this.model.bdcStatusLastUpdated); - this.clusterStateLoadingComponent.loading = false; - this.clusterHealthStatusLoadingComponent.loading = false; - (this.clusterStateLoadingComponent.component).value = getStateDisplayText(bdcStatus.state); - (this.clusterHealthStatusLoadingComponent.component).value = getHealthStatusDisplayText(bdcStatus.healthStatus); + this.propertiesContainerLoadingComponent.loading = false; + + (this.propertiesContainerLoadingComponent.component).propertyItems = [ + { displayName: loc.clusterState, value: getStateDisplayText(bdcStatus.state) }, + { displayName: loc.healthStatus, value: getHealthStatusDisplayText(bdcStatus.healthStatus) } + ]; if (bdcStatus.services) { this.serviceStatusTable.data = bdcStatus.services.map(serviceStatus => { - const statusIconCell = this.modelBuilder.text() + const statusIconCell = this.modelView.modelBuilder.text() .withProperties({ value: getHealthStatusIcon(serviceStatus.healthStatus), ariaRole: 'img', title: getHealthStatusDisplayText(serviceStatus.healthStatus), CSSStyles: { 'user-select': 'none', ...cssStyles.text } }).component(); - const nameCell = this.modelBuilder.hyperlink() + const nameCell = this.modelView.modelBuilder.hyperlink() .withProperties({ label: getServiceNameDisplayText(serviceStatus.serviceName), url: '', CSSStyles: { ...cssStyles.text } }).component(); nameCell.onDidClick(() => { - this.dashboard.switchToServiceTab(serviceStatus.serviceName); + //this.dashboard.switchToServiceTab(serviceStatus.serviceName); TODO: Enable direct link to tab page }); - const viewDetailsButton = serviceStatus.healthStatus !== 'healthy' && serviceStatus.details && serviceStatus.details.length > 0 ? createViewDetailsButton(this.modelBuilder, serviceStatus.details) : undefined; + const viewDetailsButton = serviceStatus.healthStatus !== 'healthy' && serviceStatus.details && serviceStatus.details.length > 0 ? createViewDetailsButton(this.modelView.modelBuilder, serviceStatus.details) : undefined; return [ statusIconCell, nameCell, @@ -397,7 +370,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { endpoints.unshift(...sqlServerMasterEndpoints); this.endpointsTable.data = endpoints.map(e => { - const copyValueCell = this.modelBuilder.button().withProperties({ title: loc.copy }).component(); + const copyValueCell = this.modelView.modelBuilder.button().withProperties({ title: loc.copy }).component(); copyValueCell.iconPath = IconPathHelper.copy; copyValueCell.onDidClick(() => { vscode.env.clipboard.writeText(e.endpoint); @@ -406,7 +379,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { copyValueCell.iconHeight = '14px'; copyValueCell.iconWidth = '14px'; return [getEndpointDisplayText(e.name, e.description), - createEndpointComponent(this.modelBuilder, e, this.model, hyperlinkedEndpoints.some(he => he === e.name)), + createEndpointComponent(this.modelView.modelBuilder, e, this.model, hyperlinkedEndpoints.some(he => he === e.name)), copyValueCell]; }); @@ -426,7 +399,7 @@ export class BdcDashboardOverviewPage extends BdcDashboardPage { private showBdcStatusError(errorMessage: string): void { this.serviceStatusDisplayContainer.display = 'none'; - this.propertiesContainer.display = 'none'; + this.propertiesContainerLoadingComponent.display = 'none'; this.serviceStatusErrorMessage.value = errorMessage; this.serviceStatusErrorMessage.display = undefined; this.propertiesErrorMessage.value = errorMessage; diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardPage.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardPage.ts index 22a09faf2b..382ed95a3b 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardPage.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardPage.ts @@ -3,37 +3,67 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Deferred } from '../../common/promise'; +import { IconPathHelper } from '../constants'; +import { BdcDashboardModel, getTroubleshootNotebookUrl } from './bdcDashboardModel'; +import * as loc from '../localizedConstants'; +import * as azdata from 'azdata'; +import * as vscode from 'vscode'; +import { InitializingComponent } from './intializingComponent'; -export abstract class BdcDashboardPage { +export abstract class BdcDashboardPage extends InitializingComponent { - private _initialized: boolean = false; + private _toolbarContainer: azdata.ToolbarContainer; + private _refreshButton: azdata.ButtonComponent; - private onInitializedPromise: Deferred = new Deferred(); - - constructor() { } - - protected get initialized(): boolean { - return this._initialized; + constructor(protected model: BdcDashboardModel, protected modelView: azdata.ModelView, protected serviceName?: string) { + super(); } - protected set initialized(value: boolean) { - if (!this._initialized && value) { - this._initialized = true; - this.onInitializedPromise.resolve(); + public get toolbarContainer(): azdata.ToolbarContainer { + // Lazily create the container only when needed + if (!this._toolbarContainer) { + this._toolbarContainer = this.createToolbarContainer(); } + return this._toolbarContainer; } - /** - * Runs the specified action when the component is initialized. If already initialized just runs - * the action immediately. - * @param action The action to be ran when the page is initialized - */ - protected eventuallyRunOnInitialized(action: () => void): void { - if (!this._initialized) { - this.onInitializedPromise.promise.then(() => action()).catch(error => console.error(`Unexpected error running onInitialized action for BDC Page : ${error}`)); - } else { - action(); + protected createToolbarContainer(): azdata.ToolbarContainer { + // Refresh button + this._refreshButton = this.modelView.modelBuilder.button() + .withProperties({ + label: loc.refresh, + iconPath: IconPathHelper.refresh + }).component(); + + this._refreshButton.onDidClick(async () => { + await this.doRefresh(); + }); + + const openTroubleshootNotebookButton = this.modelView.modelBuilder.button() + .withProperties({ + label: loc.troubleshoot, + iconPath: IconPathHelper.notebook + }).component(); + + openTroubleshootNotebookButton.onDidClick(() => { + vscode.commands.executeCommand('books.sqlserver2019', getTroubleshootNotebookUrl(this.serviceName)); + }); + + return this.modelView.modelBuilder.toolbarContainer() + .withToolbarItems( + [ + { component: this._refreshButton }, + { component: openTroubleshootNotebookButton } + ] + ).component(); + } + + private async doRefresh(): Promise { + try { + this._refreshButton.enabled = false; + await this.model.refresh(); + } finally { + this._refreshButton.enabled = true; } } } diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardResourceStatusPage.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardResourceStatusPage.ts index 736f735f3e..1edd813576 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardResourceStatusPage.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcDashboardResourceStatusPage.ts @@ -21,8 +21,8 @@ export class BdcDashboardResourceStatusPage extends BdcDashboardPage { private metricsAndLogsRowsTable: azdata.DeclarativeTableComponent; private lastUpdatedLabel: azdata.TextComponent; - constructor(private model: BdcDashboardModel, private modelView: azdata.ModelView, private serviceName: string, private resourceName: string) { - super(); + constructor(model: BdcDashboardModel, modelView: azdata.ModelView, serviceName: string, private resourceName: string) { + super(model, modelView, serviceName); this.model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus))); } diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcServiceStatusPage.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcServiceStatusPage.ts index 86f1effee1..118c2ab3de 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcServiceStatusPage.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/bdcServiceStatusPage.ts @@ -7,57 +7,35 @@ import * as azdata from 'azdata'; import { BdcStatusModel, ResourceStatusModel } from '../controller/apiGenerated'; import { BdcDashboardResourceStatusPage } from './bdcDashboardResourceStatusPage'; import { BdcDashboardModel } from './bdcDashboardModel'; -import { getHealthStatusDot } from '../utils'; -import { cssStyles } from '../constants'; import { BdcDashboardPage } from './bdcDashboardPage'; - -type ServiceTab = { div: azdata.DivContainer, dot: azdata.TextComponent, text: azdata.TextComponent }; +import { getHealthStatusDotIcon } from '../utils'; export class BdcServiceStatusPage extends BdcDashboardPage { - private currentTab: { tab: ServiceTab, index: number }; - private currentTabPage: BdcDashboardResourceStatusPage; - private rootContainer: azdata.FlexContainer; - private resourceHeader: azdata.FlexContainer; + private createdResourceTabs: Map = new Map(); + private tabbedPanel: azdata.TabbedPanelComponent; - private createdTabs: Map = new Map(); - - constructor(private serviceName: string, private model: BdcDashboardModel, private modelView: azdata.ModelView) { - super(); + constructor(serviceName: string, model: BdcDashboardModel, modelView: azdata.ModelView) { + super(model, modelView, serviceName); this.model.onDidUpdateBdcStatus(bdcStatus => this.eventuallyRunOnInitialized(() => this.handleBdcStatusUpdate(bdcStatus))); } - public get container(): azdata.FlexContainer { + public get container(): azdata.TabbedPanelComponent { // Lazily create the container only when needed - if (!this.rootContainer) { + if (!this.tabbedPanel) { this.createPage(); } - return this.rootContainer; + return this.tabbedPanel; } private createPage(): void { - this.rootContainer = this.modelView.modelBuilder.flexContainer().withLayout( - { - flexFlow: 'column', - width: '100%', - height: '100%' - }).component(); + this.tabbedPanel = this.modelView.modelBuilder.tabbedPanel() + .withLayout({ showIcon: true, alwaysShowTabs: true }).component(); - this.resourceHeader = this.modelView.modelBuilder.flexContainer().withLayout( - { - flexFlow: 'row', - width: '100%', - height: '25px' - } - ).withProperties({ - ariaRole: 'tablist' - }).component(); - - this.rootContainer.addItem(this.resourceHeader, { CSSStyles: { 'padding-top': '15px' } }); + // Initialize our set of tab pages + this.handleBdcStatusUpdate(this.model.bdcStatus); this.initialized = true; - - this.handleBdcStatusUpdate(this.model.bdcStatus); } private handleBdcStatusUpdate(bdcStatus: BdcStatusModel): void { @@ -66,90 +44,29 @@ export class BdcServiceStatusPage extends BdcDashboardPage { } const service = bdcStatus.services.find(s => s.serviceName === this.serviceName); if (service && service.resources) { - this.createResourceNavTabs(service.resources); + this.updateResourcePages(service.resources); } } - private changeSelectedTabPage(newPage: BdcDashboardResourceStatusPage): void { - if (this.currentTabPage) { - this.rootContainer.removeItem(this.currentTabPage.container); - } - this.rootContainer.addItem(newPage.container); - this.currentTabPage = newPage; - } - /** - * Helper to create the navigation tabs for the resources + * Update the resource tab pages, creating any new ones as necessary */ - private createResourceNavTabs(resources: ResourceStatusModel[]) { - let tabIndex = this.createdTabs.size; + private updateResourcePages(resources: ResourceStatusModel[]): void { resources.forEach(resource => { - const existingTab: ServiceTab = this.createdTabs.get(resource.resourceName); + const existingTab = this.createdResourceTabs.get(resource.resourceName); if (existingTab) { - // We already created this tab so just update the status - existingTab.dot.value = getHealthStatusDot(resource.healthStatus); + existingTab.icon = getHealthStatusDotIcon(resource.healthStatus); } else { - // New tab - create and add to the end of the container - const currentIndex = tabIndex++; - const resourceHeaderTab = createResourceHeaderTab(this.modelView.modelBuilder, resource); - this.createdTabs.set(resource.resourceName, resourceHeaderTab); const resourceStatusPage = new BdcDashboardResourceStatusPage(this.model, this.modelView, this.serviceName, resource.resourceName); - resourceHeaderTab.div.onDidClick(() => { - // Don't need to do anything if this is already the currently selected tab - if (this.currentTab.index === currentIndex) { - return; - } - if (this.currentTab) { - this.currentTab.tab.text.updateCssStyles(cssStyles.unselectedResourceHeaderTab); - this.currentTab.tab.div.ariaSelected = false; - this.resourceHeader.removeItem(this.currentTab.tab.div); - this.resourceHeader.insertItem(this.currentTab.tab.div, this.currentTab.index, { flex: '0 0 auto', CSSStyles: cssStyles.unselectedTabDiv }); - } - this.changeSelectedTabPage(resourceStatusPage); - this.currentTab = { tab: resourceHeaderTab, index: currentIndex }; - this.currentTab.tab.text.updateCssStyles(cssStyles.selectedResourceHeaderTab); - this.currentTab.tab.div.ariaSelected = true; - this.resourceHeader.removeItem(this.currentTab.tab.div); - this.resourceHeader.insertItem(this.currentTab.tab.div, this.currentTab.index, { flex: '0 0 auto', CSSStyles: cssStyles.selectedTabDiv }); - }); - // Set initial page - if (!this.currentTabPage) { - this.changeSelectedTabPage(resourceStatusPage); - this.currentTab = { tab: resourceHeaderTab, index: currentIndex }; - this.currentTab.tab.text.updateCssStyles(cssStyles.selectedResourceHeaderTab); - this.currentTab.tab.div.ariaSelected = true; - this.resourceHeader.addItem(resourceHeaderTab.div, { flex: '0 0 auto', CSSStyles: cssStyles.selectedTabDiv }); - } - else { - resourceHeaderTab.text.updateCssStyles(cssStyles.unselectedResourceHeaderTab); - this.resourceHeader.addItem(resourceHeaderTab.div, { flex: '0 0 auto', CSSStyles: cssStyles.unselectedTabDiv }); - } + const newTab: azdata.Tab = { + title: resource.resourceName, + id: resource.resourceName, + content: resourceStatusPage.container, + icon: getHealthStatusDotIcon(resource.healthStatus) + }; + this.createdResourceTabs.set(resource.resourceName, newTab); } }); + this.tabbedPanel.updateTabs(Array.from(this.createdResourceTabs.values())); } } - -/** - * Creates a single resource header tab - * @param modelBuilder The ModelBuilder used to construct the object - * @param resourceStatus The status of the resource we're creating - */ -function createResourceHeaderTab(modelBuilder: azdata.ModelBuilder, resourceStatus: ResourceStatusModel): ServiceTab { - const resourceHeaderTab = modelBuilder - .divContainer() - .withLayout({ - width: '100px', - height: '25px' - }) - .withProperties({ - clickable: true, - ariaRole: 'tab' - }).component(); - const innerContainer = modelBuilder.flexContainer().withLayout({ width: '100px', height: '25px', flexFlow: 'row' }).component(); - const statusDot = modelBuilder.text().withProperties({ value: getHealthStatusDot(resourceStatus.healthStatus), CSSStyles: { 'color': 'red', 'font-size': '40px', 'width': '20px', 'text-align': 'right', ...cssStyles.nonSelectableText } }).component(); - innerContainer.addItem(statusDot, { flex: '0 0 auto' }); - const resourceHeaderLabel = modelBuilder.text().withProperties({ value: resourceStatus.resourceName, CSSStyles: { 'text-align': 'left', ...cssStyles.tabHeaderText } }).component(); - innerContainer.addItem(resourceHeaderLabel); - resourceHeaderTab.addItem(innerContainer); - return { div: resourceHeaderTab, text: resourceHeaderLabel, dot: statusDot }; -} diff --git a/extensions/big-data-cluster/src/bigDataCluster/dialog/intializingComponent.ts b/extensions/big-data-cluster/src/bigDataCluster/dialog/intializingComponent.ts new file mode 100644 index 0000000000..046ebe7cf8 --- /dev/null +++ b/extensions/big-data-cluster/src/bigDataCluster/dialog/intializingComponent.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Deferred } from '../../common/promise'; + +export abstract class InitializingComponent { + + private _initialized: boolean = false; + + private _onInitializedPromise: Deferred = new Deferred(); + + constructor() { } + + protected get initialized(): boolean { + return this._initialized; + } + + protected set initialized(value: boolean) { + if (!this._initialized && value) { + this._initialized = true; + this._onInitializedPromise.resolve(); + } + } + + /** + * Runs the specified action when the component is initialized. If already initialized just runs + * the action immediately. + * @param action The action to be ran when the page is initialized + */ + protected eventuallyRunOnInitialized(action: () => void): void { + if (!this._initialized) { + this._onInitializedPromise.promise.then(() => action()).catch(error => console.error(`Unexpected error running onInitialized action for BDC Page : ${error}`)); + } else { + action(); + } + } +} + diff --git a/extensions/big-data-cluster/src/bigDataCluster/localizedConstants.ts b/extensions/big-data-cluster/src/bigDataCluster/localizedConstants.ts index 2d73827565..902764f285 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/localizedConstants.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/localizedConstants.ts @@ -48,8 +48,7 @@ export const clusterDetails = localize('bdc.dashboard.clusterDetails', "Cluster export const clusterOverview = localize('bdc.dashboard.clusterOverview', "Cluster Overview"); export const serviceEndpoints = localize('bdc.dashboard.serviceEndpoints', "Service Endpoints"); export const clusterProperties = localize('bdc.dashboard.clusterProperties', "Cluster Properties"); -export const clusterState = localize('bdc.dashboard.clusterState', "Cluster State :"); -export const healthStatusWithColon = localize('bdc.dashboard.healthStatusWithColon', "Health Status :"); +export const clusterState = localize('bdc.dashboard.clusterState', "Cluster State"); export const serviceName = localize('bdc.dashboard.serviceName', "Service Name"); export const service = localize('bdc.dashboard.service', "Service"); export const endpoint = localize('bdc.dashboard.endpoint', "Endpoint"); diff --git a/extensions/big-data-cluster/src/bigDataCluster/utils.ts b/extensions/big-data-cluster/src/bigDataCluster/utils.ts index de33e24295..21622650f2 100644 --- a/extensions/big-data-cluster/src/bigDataCluster/utils.ts +++ b/extensions/big-data-cluster/src/bigDataCluster/utils.ts @@ -210,17 +210,17 @@ export function getHealthStatusIcon(healthStatus?: string): string { } /** - * Returns the status dot string which will be a • for all non-healthy states + * Returns the status dot icon which will be a • for all non-healthy states * @param healthStatus The status to check */ -export function getHealthStatusDot(healthStatus?: string): string { +export function getHealthStatusDotIcon(healthStatus?: string): constants.IconPath { healthStatus = healthStatus || ''; switch (healthStatus.toLowerCase()) { case 'healthy': - return ''; + return constants.IconPathHelper.status_circle_blank; default: // Display status dot for all non-healthy status' - return '•'; + return constants.IconPathHelper.status_circle_red; } } diff --git a/extensions/big-data-cluster/src/extension.ts b/extensions/big-data-cluster/src/extension.ts index 9072190dd9..96390c5e6f 100644 --- a/extensions/big-data-cluster/src/extension.ts +++ b/extensions/big-data-cluster/src/extension.ts @@ -68,7 +68,7 @@ function registerCommands(context: vscode.ExtensionContext, treeDataProvider: Co await treeDataProvider.saveControllers(); } const dashboard: BdcDashboard = new BdcDashboard(title, new BdcDashboardModel(info, treeDataProvider)); - dashboard.showDashboard(); + await dashboard.showDashboard(); }); vscode.commands.registerCommand(commands.MountHdfsCommand, e => mountHdfs(e).catch(error => {