diff --git a/extensions/query-store/src/common/constants.ts b/extensions/query-store/src/common/constants.ts index d519f6d5a0..d974ebf0a7 100644 --- a/extensions/query-store/src/common/constants.ts +++ b/extensions/query-store/src/common/constants.ts @@ -21,3 +21,64 @@ export function plan(queryId: string): string { return localize('plan', "Plan {0 export function topResourceConsumingQueriesToolbarLabel(databaseName: string): string { return localize('topResourceConsumingQueriesToolbarLabel', "Top 25 resource consumers for database {0}", databaseName); } export const configure = localize('configure', "Configure"); +export const okButtonText = localize('okButtonText', "Ok"); +export const cancelButtonText = localize('cancelButtonText', "Cancel"); +export const applyButtonText = localize('applyButtonText', "Apply"); +export const criteria = localize('criteria', "Criteria"); +export const executionCountLabel = localize('executionCountLabel', "Execution Count"); +export const durationLabel = localize('durationLabel', "Duration (ms)"); +export const CPUTimeLabel = localize('CPUTimeLabel', "CPU Time (ms)"); +export const logicalReadsLabel = localize('logicalReadsLabel', "Logical Reads (KB)"); +export const logicalWritesLabel = localize('logicalWritesLabel', "Logical Writes (KB)"); +export const physicalReadsLabel = localize('physicalReadsLabel', "Physical Reads (KB)"); +export const CLRTimeLabel = localize('CLRTimeLabel', "CLR Time (ms)"); +export const DOPLabel = localize('DOPLabel', "DOP"); +export const memoryConsumptionLabel = localize('memoryConsumptionLabel', "Memory Consumption (KB)"); +export const rowCountLabel = localize('rowCountLabel', "Row Count"); +export const logMemoryUsedLabel = localize('logMemoryUsedLabel', "Log Memory Used (KB)"); +export const tempDBMermoryUsedLabel = localize('tempDBMermoryUsedLabel', "Temp DB Memory Used (KB)"); +export const waitTimeLabel = localize('waitTimeLabel', "Wait Time (ms)"); +export const topConsumersRadioButtonsLabel = localize('topConsumersRadioButtonsLabel', "Check for top consumers of:"); +export const resourceConsumptionCriteriaTitle = localize('resourceConsumptionCriteriaTitle', "Resource Consumption Criteria") +export const showChartTitle = localize('showChartTitle', "Show Chart for") + +export const last5MinsLabel = localize('last5MinsLabel', "Last 5 minutes"); +export const last15MinsLabel = localize('last15MinsLabel', "Last 15 minutes"); +export const last30MinsLabel = localize('last30MinsLabel', "Last 30 minutes"); +export const lastHourLabel = localize('lastHourLabel', "Last hour"); +export const last12HoursLabel = localize('last12HoursLabel', "Last 12 hours"); +export const lastDayLabel = localize('lastDayLabel', "Last day"); +export const last2DaysLabel = localize('last2DaysLabel', "Last 2 days"); +export const lastWeekLabel = localize('lastWeekLabel', "Last week"); +export const last2WeeksLabel = localize('last2WeeksLabel', "Last 2 weeks"); +export const lastMonthLabel = localize('lastMonthLabel', "Last month"); +export const last3MonthsLabel = localize('last3MonthsLabel', "Last 3 months"); +export const last6MonthsLabel = localize('last6MonthsLabel', "Last 6 months"); +export const lastYearLabel = localize('lastYearLabel', "Last year"); +export const customLabel = localize('customLabel', "Custom"); +export const fromLabel = localize('fromLabel', "From"); +export const toLabel = localize('toLabel', "To"); +export const localLabel = localize('localLabel', "Local"); +export const UTCLabel = localize('UTCLabel', "UTC"); +export const timeFormatLabel = localize('timeFormatLabel', "Time Format"); +export const timeSettingsLabel = localize('timeSettingsLabel', "Time Settings"); +export const timeIntervalLabel = localize('timeIntervalLabel', "Time Interval"); +export const aggregationSizeLabel = localize('aggregationSizeLabel', "Aggregation Size"); +export const minuteLabel = localize('minuteLabel', "Minute"); +export const hourLabel = localize('hourLabel', "Hour"); +export const dayLabel = localize('dayLabel', "Day"); +export const automaticLabel = localize('automaticLabel', "Automatic"); + +export const basedOnLabel = localize('basedOnLabel', "Based on:"); +export const avgLabel = localize('avgLabel', "Avg"); +export const maxLabel = localize('maxLabel', "Max"); +export const minLabel = localize('minLabel', "Min"); +export const stdDevLabel = localize('stdDevLabel', "Std Dev"); +export const totalLabel = localize('totalLabel', "Total"); + +export const returnLabel = localize('returnLabel', "Return"); +export const allLabel = localize('allLabel', "All"); +export const topLabel = localize('topLabel', "Top"); + +export const filterLabel = localize('filterLabel', "Filters"); +export const filterMinPlanLabel = localize('filterMinPlanLabel', "Min number of plans"); diff --git a/extensions/query-store/src/common/uiConstants.ts b/extensions/query-store/src/common/uiConstants.ts new file mode 100644 index 0000000000..56098813e0 --- /dev/null +++ b/extensions/query-store/src/common/uiConstants.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// CSS Styles +export namespace cssStyles { + export const configureDialogLabelWidth = '140px'; + export const configureDialogObjectWidth = '180px'; +} diff --git a/extensions/query-store/src/common/utils.ts b/extensions/query-store/src/common/utils.ts index 9454a3bd1a..7d02f1d4e7 100644 --- a/extensions/query-store/src/common/utils.ts +++ b/extensions/query-store/src/common/utils.ts @@ -74,3 +74,15 @@ export function createSplitView(view: azdata.ModelView, firstComponent: azdata.C return splitview; } + +/** + * These are the available components that can be added to a config dialog. + */ +export enum ConfigComponentsInfo { + consumptionCriteriaComponentTopResource, + chartComponent, + timeIntervalComponentOverallResource, + timeIntervalComponent, + returnComponent, + filterComponent +} diff --git a/extensions/query-store/src/reports/baseQueryStoreReport.ts b/extensions/query-store/src/reports/baseQueryStoreReport.ts index 160ef8ae74..be109a769e 100644 --- a/extensions/query-store/src/reports/baseQueryStoreReport.ts +++ b/extensions/query-store/src/reports/baseQueryStoreReport.ts @@ -8,10 +8,13 @@ import * as azdata from 'azdata'; import * as path from 'path'; import * as utils from '../common/utils'; import * as constants from '../common/constants'; +import { ConfigureDialog } from '../settings/configureDialog'; export abstract class BaseQueryStoreReport { protected editor: azdata.workspace.ModelViewEditor; protected flexModel?: azdata.FlexContainer; + protected configureDialog?: ConfigureDialog; + protected configureButton?: azdata.ButtonComponent; constructor(reportName: string, private reportTitle: string, protected resizeable: boolean, private extensionContext: vscode.ExtensionContext) { this.editor = azdata.workspace.createModelViewEditor(reportName, { retainContextWhenHidden: true, supportsSave: false }, reportName); @@ -103,7 +106,7 @@ export abstract class BaseQueryStoreReport { CSSStyles: { 'margin-top': '5px', 'margin-bottom': '5px', 'margin-right': '15px' } }).component(); - const configureButton = view.modelBuilder.button().withProps({ + this.configureButton = view.modelBuilder.button().withProps({ label: constants.configure, title: constants.configure, iconPath: { @@ -111,16 +114,14 @@ export abstract class BaseQueryStoreReport { dark: path.join(this.extensionContext.extensionPath, 'images', 'dark', 'gear.svg') } }).component(); + this.configureButton.enabled = true; - // TODO: enable after the configuration dialog is implemented - configureButton.enabled = false; - - configureButton.onDidClick(() => { - // TODO: implement configuration dialog - console.error('configuration dialog not implemented') + this.configureButton.onDidClick(async () => { + this.configureDialog = new ConfigureDialog(); + await this.configureButtonClick(this.configureDialog); }); - await configureButton.updateCssStyles({ 'margin-top': '5px' }); + await this.configureButton.updateCssStyles({ 'margin-top': '5px' }); toolBar.addToolbarItems([ { @@ -132,7 +133,7 @@ export abstract class BaseQueryStoreReport { toolbarSeparatorAfter: true }, { - component: configureButton + component: this.configureButton } ]); @@ -140,5 +141,6 @@ export abstract class BaseQueryStoreReport { } protected abstract createViews(_view: azdata.ModelView): Promise; + protected abstract configureButtonClick(configureDialog: ConfigureDialog): Promise; } diff --git a/extensions/query-store/src/reports/overallResourceConsumption.ts b/extensions/query-store/src/reports/overallResourceConsumption.ts index 9d67ef4c69..3454bbe3f0 100644 --- a/extensions/query-store/src/reports/overallResourceConsumption.ts +++ b/extensions/query-store/src/reports/overallResourceConsumption.ts @@ -8,6 +8,8 @@ import * as vscode from 'vscode'; import * as constants from '../common/constants'; import { BaseQueryStoreReport } from './baseQueryStoreReport'; import { QueryStoreView } from './queryStoreView'; +import { ConfigureDialog } from '../settings/configureDialog'; +import { ConfigComponentsInfo } from '../common/utils'; export class OverallResourceConsumption extends BaseQueryStoreReport { @@ -32,4 +34,9 @@ export class OverallResourceConsumption extends BaseQueryStoreReport { return [durationContainer, executionCountContainer, cpuTimeContainer, logicalReadsContainer]; } + + protected override async configureButtonClick(configureDialog: ConfigureDialog): Promise { + const configComponentsInfo: ConfigComponentsInfo[] = [ConfigComponentsInfo.chartComponent, ConfigComponentsInfo.timeIntervalComponentOverallResource]; + await configureDialog.openDialog(configComponentsInfo); + } } diff --git a/extensions/query-store/src/reports/topResourceConsumingQueries.ts b/extensions/query-store/src/reports/topResourceConsumingQueries.ts index 59b17f3c34..31d20ed7a9 100644 --- a/extensions/query-store/src/reports/topResourceConsumingQueries.ts +++ b/extensions/query-store/src/reports/topResourceConsumingQueries.ts @@ -8,6 +8,8 @@ import * as vscode from 'vscode'; import * as constants from '../common/constants'; import { BaseQueryStoreReport } from './baseQueryStoreReport'; import { QueryStoreView } from './queryStoreView'; +import { ConfigureDialog } from '../settings/configureDialog'; +import { ConfigComponentsInfo } from '../common/utils'; export class TopResourceConsumingQueries extends BaseQueryStoreReport { private queries: QueryStoreView; @@ -28,4 +30,10 @@ export class TopResourceConsumingQueries extends BaseQueryStoreReport { return [queriesContainer, planSummaryContainer, planContainer]; } + + protected override async configureButtonClick(configureDialog: ConfigureDialog): Promise { + const configComponentsInfo: ConfigComponentsInfo[] = [ConfigComponentsInfo.consumptionCriteriaComponentTopResource, ConfigComponentsInfo.timeIntervalComponent, ConfigComponentsInfo.returnComponent, + ConfigComponentsInfo.filterComponent]; + await configureDialog.openDialog(configComponentsInfo); + } } diff --git a/extensions/query-store/src/settings/configureDialog.ts b/extensions/query-store/src/settings/configureDialog.ts new file mode 100644 index 0000000000..55220adc85 --- /dev/null +++ b/extensions/query-store/src/settings/configureDialog.ts @@ -0,0 +1,669 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as vscode from 'vscode'; +import * as constants from '../common/constants'; +import { cssStyles } from '../common/uiConstants'; +import { ConfigComponentsInfo } from '../common/utils'; + +interface Deferred { + resolve: (result: T | Promise) => void; + reject: (reason: any) => void; +} + +export class ConfigureDialog { + private _view!: azdata.ModelView + public dialog: azdata.window.Dialog; + private configureTab: azdata.window.DialogTab; + private toDispose: vscode.Disposable[] = []; + private executionCountRadioButton?: azdata.RadioButtonComponent; + private durationRadioButton?: azdata.RadioButtonComponent; + private CPUTimeRadioButton?: azdata.RadioButtonComponent; + private logicalReadsRadioButton?: azdata.RadioButtonComponent; + private logicalWritesRadioButton?: azdata.RadioButtonComponent; + private physicalReadsRadioButton?: azdata.RadioButtonComponent; + private CLRTimeRadioButton?: azdata.RadioButtonComponent; + private DOPRadioButton?: azdata.RadioButtonComponent; + private memoryConsumptionRadioButton?: azdata.RadioButtonComponent; + private rowCountRadioButton?: azdata.RadioButtonComponent; + private logMemoryUsedRadioButton?: azdata.RadioButtonComponent; + private tempDBMermoryUsedRadioButton?: azdata.RadioButtonComponent; + private waitTimeRadioButton?: azdata.RadioButtonComponent; + private criteriaRadioButtons?: azdata.RadioButtonComponent[]; + private formBuilder?: azdata.FormBuilder; + private formModel?: azdata.FormContainer; + private criteriaBasisAvgRadioButton?: azdata.RadioButtonComponent; + private criteriaBasisMaxRadioButton?: azdata.RadioButtonComponent; + private criteriaBasisMinRadioButton?: azdata.RadioButtonComponent; + private criteriaBasisStdDevRadioButton?: azdata.RadioButtonComponent; + private criteriaBasisTotalRadioButton?: azdata.RadioButtonComponent; + private executionCountCheckBox?: azdata.CheckBoxComponent; + private durationCheckBox?: azdata.CheckBoxComponent; + private CPUTimeCheckBox?: azdata.CheckBoxComponent; + private logicalReadsCheckBox?: azdata.CheckBoxComponent; + private logicalWritesCheckBox?: azdata.CheckBoxComponent; + private physicalReadsCheckBox?: azdata.CheckBoxComponent; + private CLRTimeCheckBox?: azdata.CheckBoxComponent; + private DOPCheckBox?: azdata.CheckBoxComponent; + private memoryConsumptionCheckBox?: azdata.CheckBoxComponent; + private rowCountCheckBox?: azdata.CheckBoxComponent; + private showChartComponent?: azdata.FormComponent; + private timeIntervalOptionsDropdown?: azdata.DropDownComponent; + private customTimeFromTextBox?: azdata.InputBoxComponent; + private customTimeToTextBox?: azdata.InputBoxComponent; + private localTimeFormatRadioButton?: azdata.RadioButtonComponent; + private UTCTimeFormatRadioButton?: azdata.RadioButtonComponent; + private timeIntervalComponent?: azdata.FormComponent; + private aggregationSizeComponent?: azdata.FlexContainer; + private timeIntervalOptions?: string[]; + private timeFormatRadioButtons?: azdata.RadioButtonComponent[]; + private returnDataAllRadioButton?: azdata.RadioButtonComponent; + private returnDataTopRadioButton?: azdata.RadioButtonComponent; + private returnDataTopInputBox?: azdata.InputBoxComponent; + private returnComponent?: azdata.FormComponent; + private filtersInputBox?: azdata.InputBoxComponent; + private filterComponent?: azdata.FormComponent; + private initDialogComplete?: Deferred; + private initDialogPromise: Promise = new Promise((resolve, reject) => this.initDialogComplete = { resolve, reject }); + + constructor() { + this.dialog = azdata.window.createModelViewDialog(constants.configure); + this.configureTab = azdata.window.createTab(constants.configure); + this.dialog.registerCloseValidator(async () => { + return this.validate(); + }); + } + + async validate(): Promise { + return true; // TODO: Add validation criteria + } + + /** + * Method to add components to the report. + * @param configComponentsInfo components to be added to the report. The sequence of the components in this array determines their placement in the report. + */ + public async openDialog(configComponentsInfo: ConfigComponentsInfo[]): Promise { + await this.initializeDialog(configComponentsInfo); + + this.dialog.okButton.label = constants.okButtonText; + this.dialog.okButton.enabled = false; + this.toDispose.push(this.dialog.okButton.onClick(async () => { this.dispose(); })); // TODO: Add return of settings change and functionality to enable OK Button + + this.dialog.cancelButton.label = constants.cancelButtonText; + this.toDispose.push(this.dialog.cancelButton.onClick(async () => await this.cancel())); + + azdata.window.openDialog(this.dialog); + await this.initDialogPromise; + } + + protected async initializeDialog(configComponentsInfo: ConfigComponentsInfo[]): Promise { + await this.initializeConfigureTab(configComponentsInfo); + this.dialog.content = [this.configureTab]; + } + + private async initializeConfigureTab(configComponentsInfo: ConfigComponentsInfo[]): Promise { + this.configureTab.registerContent(async view => { + this._view = view; + let componentGroups: azdata.GroupContainer[] = []; + + for (let config of configComponentsInfo) { + if (config === ConfigComponentsInfo.consumptionCriteriaComponentTopResource) { + const consumptionCriteriaComponent = await this.createCriteriaComponent(true); + const basedOnCriteriaComponent = this.createCriteriaBasedOnComponent(); + const typeGroup = this.createGroup(constants.topConsumersRadioButtonsLabel, [consumptionCriteriaComponent.component, basedOnCriteriaComponent.component]); + componentGroups.push(typeGroup); + } + + if (config === ConfigComponentsInfo.chartComponent) { + this.showChartComponent = this.createShowChartComponent(); + const typeGroup = this.createGroup(constants.showChartTitle, [this.showChartComponent.component]); + componentGroups.push(typeGroup); + } + + if (config === ConfigComponentsInfo.timeIntervalComponentOverallResource) { + this.timeIntervalComponent = this.createTimeIntervalComponent(config); + const typeGroup = this.createGroup(constants.timeSettingsLabel, [this.timeIntervalComponent.component]); + componentGroups.push(typeGroup); + } + + if (config === ConfigComponentsInfo.timeIntervalComponent) { + this.timeIntervalComponent = this.createTimeIntervalComponent(config); + const typeGroup = this.createGroup(constants.timeIntervalLabel, [this.timeIntervalComponent.component]); + componentGroups.push(typeGroup); + } + + if (config === ConfigComponentsInfo.returnComponent) { + this.returnComponent = this.createReturnComponent(); + const typeGroup = this.createGroup(constants.returnLabel, [this.returnComponent.component]); + componentGroups.push(typeGroup); + } + + if (config === ConfigComponentsInfo.filterComponent) { + this.filterComponent = this.createFilterComponent(); + const typeGroup = this.createGroup(constants.filterLabel, [this.filterComponent.component]); + componentGroups.push(typeGroup); + } + } + + const divContainer = this._view.modelBuilder.divContainer().withLayout({ width: 'calc(100% - 20px)', height: 'calc(100% - 20px)' }).withProps({ + CSSStyles: { 'padding': '10px' } + }).withItems(componentGroups).component(); + + this.formBuilder = this._view.modelBuilder.flexContainer() + .withItems([divContainer]) + .withLayout({ + width: '100%', + flexFlow: 'column' + }); + + this.toDispose.push(divContainer); + + this.formModel = this.formBuilder!.component(); + await this._view.initializeModel(this.formModel!); + this.initDialogComplete!.resolve(); + }); + } + + protected async cancel(): Promise { + this.dispose(); + } + + private dispose(): void { + this.toDispose.forEach(disposable => disposable.dispose()); + } + + protected createGroup(header: string, items: azdata.Component[], collapsible: boolean = true, collapsed: boolean = false): azdata.GroupContainer { + return this._view.modelBuilder.groupContainer() + .withLayout({ + header: header, + collapsible: collapsible, + collapsed: collapsed + }).withItems(items).component(); + } + + private async createCriteriaComponent(isTopResourceReport: boolean = false): Promise { + this.executionCountRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.executionCountLabel + }).component(); + + this.toDispose.push(this.executionCountRadioButton.onDidChangeCheckedState((checked) => { + if (checked) { + this.criteriaBasisAvgRadioButton!.enabled = false; + this.criteriaBasisMaxRadioButton!.enabled = false; + this.criteriaBasisMinRadioButton!.enabled = false; + this.criteriaBasisStdDevRadioButton!.enabled = false; + this.criteriaBasisTotalRadioButton!.enabled = false; + } else { + this.criteriaBasisAvgRadioButton!.enabled = true; + this.criteriaBasisMaxRadioButton!.enabled = true; + this.criteriaBasisMinRadioButton!.enabled = true; + this.criteriaBasisStdDevRadioButton!.enabled = true; + this.criteriaBasisTotalRadioButton!.enabled = true; + } + })); + + this.durationRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.durationLabel + }).component(); + + this.CPUTimeRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.CPUTimeLabel + }).component(); + + this.logicalReadsRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.logicalReadsLabel + }).component(); + + this.logicalWritesRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.logicalWritesLabel + }).component(); + + this.physicalReadsRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.physicalReadsLabel + }).component(); + + this.CLRTimeRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.CLRTimeLabel + }).component(); + + this.DOPRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.DOPLabel + }).component(); + + this.memoryConsumptionRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.memoryConsumptionLabel + }).component(); + + this.rowCountRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.rowCountLabel + }).component(); + + this.logMemoryUsedRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.logMemoryUsedLabel + }).component(); + + this.tempDBMermoryUsedRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.tempDBMermoryUsedLabel + }).component(); + + this.waitTimeRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.criteria, + label: constants.waitTimeLabel + }).component(); + + this.durationRadioButton.checked = true; + await this.durationRadioButton.focus(); + + this.criteriaRadioButtons = [this.executionCountRadioButton, this.durationRadioButton, this.CPUTimeRadioButton, this.logicalReadsRadioButton, this.logicalWritesRadioButton, this.physicalReadsRadioButton, + this.CLRTimeRadioButton, this.DOPRadioButton, this.memoryConsumptionRadioButton, this.rowCountRadioButton, this.logMemoryUsedRadioButton, this.tempDBMermoryUsedRadioButton, this.waitTimeRadioButton]; + + if (isTopResourceReport) { + this.criteriaRadioButtons.unshift(this.executionCountRadioButton); + } + + let flexRadioButtonsModel = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .withItems(this.criteriaRadioButtons) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + return { + component: flexRadioButtonsModel + }; + } + + private createCriteriaBasedOnComponent(): azdata.FormComponent { + this.criteriaBasisAvgRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.basedOnLabel, + label: constants.avgLabel + }).component(); + + this.criteriaBasisMaxRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.basedOnLabel, + label: constants.maxLabel + }).component(); + + this.criteriaBasisMinRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.basedOnLabel, + label: constants.minLabel + }).component(); + + this.criteriaBasisStdDevRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.basedOnLabel, + label: constants.stdDevLabel + }).component(); + + this.criteriaBasisTotalRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.basedOnLabel, + label: constants.totalLabel + }).component(); + + const basedOnLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.basedOnLabel + }).component(); + + this.criteriaBasisTotalRadioButton.checked = true; + + let flexRadioButtonsModel = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .withItems([basedOnLabel, this.criteriaBasisAvgRadioButton, this.criteriaBasisMaxRadioButton, this.criteriaBasisMinRadioButton, this.criteriaBasisStdDevRadioButton, this.criteriaBasisTotalRadioButton]) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + return { + component: flexRadioButtonsModel + }; + } + + private createShowChartComponent(): azdata.FormComponent { + this.executionCountCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.executionCountLabel + }).component(); + + this.durationCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.durationLabel + }).component(); + + this.CPUTimeCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.CPUTimeLabel + }).component(); + + this.logicalReadsCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.logicalReadsLabel + }).component(); + + this.logicalWritesCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.logicalWritesLabel + }).component(); + + this.physicalReadsCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.physicalReadsLabel + }).component(); + + this.CLRTimeCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.CLRTimeLabel + }).component(); + + this.DOPCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.DOPLabel + }).component(); + + this.memoryConsumptionCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.memoryConsumptionLabel + }).component(); + + this.rowCountCheckBox = this._view.modelBuilder.checkBox() + .withProps({ + label: constants.rowCountLabel + }).component(); + + this.executionCountCheckBox.checked = true; + this.durationCheckBox.checked = true; + this.CPUTimeCheckBox.checked = true; + this.logicalReadsCheckBox.checked = true; + + const showChartCheckBoxes = [this.executionCountCheckBox, this.durationCheckBox, this.CPUTimeCheckBox, this.logicalReadsCheckBox, this.logicalWritesCheckBox, this.physicalReadsCheckBox, + this.CLRTimeCheckBox, this.DOPCheckBox, this.memoryConsumptionCheckBox, this.rowCountCheckBox]; + + const flexCheckBoxesModel = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .withItems(showChartCheckBoxes) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + return { + component: flexCheckBoxesModel, + title: constants.showChartTitle + }; + } + + private createTimeIntervalComponent(config: ConfigComponentsInfo): azdata.FormComponent { + let value; + if (config === ConfigComponentsInfo.timeIntervalComponentOverallResource) { + this.timeIntervalOptions = [constants.lastHourLabel, constants.lastDayLabel, constants.last2DaysLabel, constants.lastWeekLabel, constants.lastMonthLabel, constants.last6MonthsLabel, constants.lastYearLabel, constants.customLabel]; + value = constants.lastMonthLabel; + } else { + this.timeIntervalOptions = [constants.last5MinsLabel, constants.last15MinsLabel, constants.last30MinsLabel, constants.lastHourLabel, constants.last12HoursLabel, constants.lastDayLabel, constants.last2DaysLabel, + constants.lastWeekLabel, constants.last2WeeksLabel, constants.lastMonthLabel, constants.last3MonthsLabel, constants.last6MonthsLabel, constants.lastYearLabel, constants.customLabel]; + value = constants.last5MinsLabel; + } + + const timeIntervalLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.timeIntervalLabel, + width: cssStyles.configureDialogLabelWidth + }).component(); + + this.timeIntervalOptionsDropdown = this._view.modelBuilder.dropDown() + .withProps({ + width: cssStyles.configureDialogObjectWidth, + editable: false, + fireOnTextChange: true, + values: this.timeIntervalOptions, + value: value + }).component(); + + this.toDispose.push(this.timeIntervalOptionsDropdown.onValueChanged(async () => { + if (this.timeIntervalOptionsDropdown?.value === constants.customLabel) { + this.customTimeFromTextBox!.enabled = true; + await this.customTimeFromTextBox?.updateProperties({ + value: '5/15/2023 11:58 AM' // TODO: Remove the hardcoded value + }); + + this.customTimeToTextBox!.enabled = true; + await this.customTimeToTextBox?.updateProperties({ + value: '5/23/2023 11:58 AM' // TODO: Removed the hardcode value + }); + } else { + this.customTimeFromTextBox!.enabled = false; + await this.customTimeFromTextBox?.updateProperties({ + value: '' + }); + + this.customTimeToTextBox!.enabled = false; + await this.customTimeToTextBox?.updateProperties({ + value: '' + }); + } + })); + + const customTimeFromLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.fromLabel, + width: cssStyles.configureDialogLabelWidth + }).component(); + + this.customTimeFromTextBox = this._view.modelBuilder.inputBox() + .withProps({ + ariaLabel: constants.fromLabel, + width: cssStyles.configureDialogObjectWidth, + enabled: false + }).component(); + + const customTimeToLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.toLabel, + width: cssStyles.configureDialogLabelWidth + }).component(); + + this.customTimeToTextBox = this._view.modelBuilder.inputBox() + .withProps({ + ariaLabel: constants.toLabel, + width: cssStyles.configureDialogObjectWidth, + enabled: false + }).component(); + + this.localTimeFormatRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.timeFormatLabel, + label: constants.localLabel + }).component(); + + this.UTCTimeFormatRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.timeFormatLabel, + label: constants.UTCLabel + }).component(); + + const timeFormatLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.timeFormatLabel, + width: cssStyles.configureDialogLabelWidth + }).component(); + + this.localTimeFormatRadioButton.checked = true; + + const aggregationSizeDropdown = this._view.modelBuilder.dropDown() + .withProps({ + editable: false, + fireOnTextChange: true, + values: [constants.minuteLabel, constants.hourLabel, constants.dayLabel, constants.automaticLabel], + value: constants.automaticLabel, + width: cssStyles.configureDialogObjectWidth + }).component(); + + const aggregationSizeLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.aggregationSizeLabel, + width: cssStyles.configureDialogLabelWidth + }).component(); + + const timeIntervalFromRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([customTimeFromLabel, this.customTimeFromTextBox], { CSSStyles: { flex: '0 0 auto' } }) + .component(); + + const timeIntervalToRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([customTimeToLabel, this.customTimeToTextBox], { CSSStyles: { flex: '0 0 auto' } }) + .component(); + + const timeIntervalRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([timeIntervalLabel, this.timeIntervalOptionsDropdown], { CSSStyles: { flex: '0 0 auto' } }) + .component(); + + const timeIntervalComponent = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .withItems([timeIntervalRow, timeIntervalFromRow, timeIntervalToRow]) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + this.timeFormatRadioButtons = [this.localTimeFormatRadioButton, this.UTCTimeFormatRadioButton]; + const timeFormatRadioButtonRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems(this.timeFormatRadioButtons, { CSSStyles: { flex: '0 0 auto', padding: '0 10px 0 0' } }) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + const timeFormatRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([timeFormatLabel, timeFormatRadioButtonRow], { CSSStyles: { flex: '0 0 auto' } }) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + this.aggregationSizeComponent = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([aggregationSizeLabel, aggregationSizeDropdown], { CSSStyles: { flex: '0 0 auto' } }) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + let items; + if (config === ConfigComponentsInfo.timeIntervalComponentOverallResource) { + items = [timeIntervalComponent, this.aggregationSizeComponent, timeFormatRow]; + } else { + items = [timeIntervalComponent, timeFormatRow]; + } + + const timeIntervalModel = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .withItems(items) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + return { + component: timeIntervalModel, + title: constants.timeSettingsLabel + }; + } + + private createReturnComponent(): azdata.FormComponent { + this.returnDataAllRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.returnLabel, + label: constants.allLabel, + width: cssStyles.configureDialogObjectWidth + }).component(); + + this.returnDataTopRadioButton = this._view.modelBuilder.radioButton() + .withProps({ + name: constants.returnLabel, + label: constants.topLabel, + width: cssStyles.configureDialogObjectWidth + }).component(); + + this.returnDataTopRadioButton.checked = true; + + this.toDispose.push(this.returnDataAllRadioButton.onDidChangeCheckedState((checked) => { + if (checked) { + this.returnDataTopInputBox!.enabled = false; + } else { + this.returnDataTopInputBox!.enabled = true; + } + })); + + this.returnDataTopInputBox = this._view.modelBuilder.inputBox() + .withProps({ + value: '25', + ariaLabel: constants.returnLabel, + width: cssStyles.configureDialogObjectWidth + }).component(); + + const returnTopRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([this.returnDataTopRadioButton], { CSSStyles: { flex: '0 0 auto' } }) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + returnTopRow.addItem(this.returnDataTopInputBox, { CSSStyles: { 'margin-left': '105px' } }); + + let flexRadioButtonsModel = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'column' }) + .withItems([this.returnDataAllRadioButton, returnTopRow]) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + return { + component: flexRadioButtonsModel, + title: constants.returnLabel + }; + } + + private createFilterComponent(): azdata.FormComponent { + const filterMinPlanLabel = this._view.modelBuilder.text() + .withProps({ + value: constants.filterMinPlanLabel, + width: cssStyles.configureDialogLabelWidth + }).component(); + + this.filtersInputBox = this._view.modelBuilder.inputBox() + .withProps({ + value: '1', + ariaLabel: constants.returnLabel, + width: cssStyles.configureDialogObjectWidth, + }).component(); + + const filterRow = this._view.modelBuilder.flexContainer() + .withLayout({ flexFlow: 'row', alignItems: 'baseline' }) + .withItems([filterMinPlanLabel, this.filtersInputBox], { CSSStyles: { flex: '0 0 auto' } }) + .withProps({ ariaRole: 'radiogroup' }) + .component(); + + return { + component: filterRow, + title: constants.filterLabel + }; + } +}