mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-18 09:35:39 -05:00
* Remove unnecessary awaits * fix ignore * revert eslintignore * try * increase size * Increase sql lint size
269 lines
9.6 KiB
TypeScript
269 lines
9.6 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import type * as azdataType from 'azdata';
|
|
import { IDashboardColumnInfo, IDashboardTable, IProjectAction, IProjectActionGroup, IProjectProvider, WorkspaceTreeItem } from 'dataworkspace';
|
|
import * as path from 'path';
|
|
import * as vscode from 'vscode';
|
|
import * as constants from '../common/constants';
|
|
import { IconPathHelper } from '../common/iconHelper';
|
|
import { IWorkspaceService } from '../common/interfaces';
|
|
import { fileExist, getAzdataApi } from '../common/utils';
|
|
|
|
export class ProjectDashboard {
|
|
|
|
private dashboard: azdataType.window.ModelViewDashboard | undefined;
|
|
private modelView: azdataType.ModelView | undefined;
|
|
private projectProvider: IProjectProvider | undefined;
|
|
private overviewTab: azdataType.DashboardTab | undefined;
|
|
private rootContainer: azdataType.FlexContainer | undefined;
|
|
private tableContainer: azdataType.Component | undefined;
|
|
|
|
constructor(private _workspaceService: IWorkspaceService, private _treeItem: WorkspaceTreeItem) {
|
|
}
|
|
|
|
public async showDashboard(): Promise<void> {
|
|
const project = this._treeItem.element.project;
|
|
|
|
if (!(await fileExist(project.projectFilePath))) {
|
|
throw new Error(constants.fileDoesNotExist(project.projectFilePath));
|
|
}
|
|
|
|
if (!project.projectFileName) {
|
|
throw new Error(constants.projectNameNull);
|
|
}
|
|
|
|
this.projectProvider = await this._workspaceService.getProjectProvider(vscode.Uri.file(project.projectFilePath));
|
|
if (!this.projectProvider) {
|
|
throw new Error(constants.ProviderNotFoundForProjectTypeError(project.projectFilePath));
|
|
}
|
|
|
|
await this.createDashboard(project.projectFileName, project.projectFilePath);
|
|
await this.dashboard!.open();
|
|
}
|
|
|
|
private async createDashboard(title: string, projectFilePath: string): Promise<void> {
|
|
this.dashboard = getAzdataApi()!.window.createModelViewDashboard(title, 'ProjectDashboard', { alwaysShowTabs: false });
|
|
this.dashboard.registerTabs(async (modelView: azdataType.ModelView) => {
|
|
this.modelView = modelView;
|
|
|
|
this.overviewTab = {
|
|
title: '',
|
|
id: 'overview-tab',
|
|
content: this.createContainer(title, projectFilePath),
|
|
toolbar: this.createToolbarContainer(projectFilePath)
|
|
};
|
|
return [
|
|
this.overviewTab
|
|
];
|
|
});
|
|
}
|
|
|
|
private createToolbarContainer(projectFilePath: string): azdataType.ToolbarContainer {
|
|
const projectActions: (IProjectAction | IProjectActionGroup)[] = this.projectProvider!.projectToolbarActions;
|
|
|
|
// Add actions as buttons
|
|
const buttons: azdataType.ToolbarComponent[] = [];
|
|
|
|
const projectActionsLength = projectActions.length;
|
|
|
|
projectActions.forEach((action, actionIndex) => {
|
|
if (this.isProjectAction(action)) {
|
|
const button = this.createButton(action);
|
|
buttons.push({ component: button, toolbarSeparatorAfter: (projectActionsLength - 1 === actionIndex) });
|
|
} else {
|
|
const groupLength = action.actions.length;
|
|
|
|
action.actions.forEach((groupAction, index) => {
|
|
const button = this.createButton(groupAction);
|
|
buttons.push({ component: button, toolbarSeparatorAfter: ((groupLength - 1 === index) || (projectActionsLength - 1 === actionIndex)) }); // Add toolbar separator at the end of the group
|
|
});
|
|
}
|
|
});
|
|
|
|
const refreshButton = this.modelView!.modelBuilder.button()
|
|
.withProps({
|
|
label: constants.Refresh,
|
|
iconPath: IconPathHelper.refresh,
|
|
height: '20px'
|
|
}).component();
|
|
|
|
refreshButton.onDidClick(() => {
|
|
this.rootContainer?.removeItem(this.tableContainer!);
|
|
this.tableContainer = this.createTables(projectFilePath);
|
|
this.rootContainer?.addItem(this.tableContainer);
|
|
});
|
|
|
|
buttons.push({ component: refreshButton });
|
|
|
|
return this.modelView!.modelBuilder.toolbarContainer()
|
|
.withToolbarItems(
|
|
buttons
|
|
).component();
|
|
}
|
|
|
|
private isProjectAction(obj: any): obj is IProjectAction {
|
|
return obj.id !== undefined;
|
|
}
|
|
|
|
private createButton(projectAction: IProjectAction): azdataType.ButtonComponent {
|
|
let button = this.modelView!.modelBuilder.button()
|
|
.withProps({
|
|
label: projectAction.id,
|
|
iconPath: projectAction.icon,
|
|
height: '20px'
|
|
}).component();
|
|
|
|
button.onDidClick(() => {
|
|
projectAction.run(this._treeItem);
|
|
});
|
|
|
|
return button;
|
|
}
|
|
|
|
private createContainer(title: string, projectFilePath: string): azdataType.FlexContainer {
|
|
this.rootContainer = this.modelView!.modelBuilder.flexContainer().withLayout(
|
|
{
|
|
flexFlow: 'column',
|
|
width: '100%',
|
|
height: '100%'
|
|
}).component();
|
|
|
|
const headerContainer = this.createHeader(title, projectFilePath);
|
|
this.tableContainer = this.createTables(projectFilePath);
|
|
|
|
this.rootContainer.addItem(headerContainer);
|
|
this.rootContainer.addItem(this.tableContainer);
|
|
|
|
return this.rootContainer;
|
|
}
|
|
|
|
/**
|
|
* Create header with title, location and background
|
|
*/
|
|
private createHeader(title: string, location: string): azdataType.Component {
|
|
const headerContainer = this.modelView!.modelBuilder.flexContainer().withLayout(
|
|
{
|
|
flexFlow: 'column',
|
|
width: '100%',
|
|
height: '30%'
|
|
}).component();
|
|
|
|
const header = this.modelView!.modelBuilder.flexContainer().withLayout(
|
|
{
|
|
flexFlow: 'column',
|
|
width: '100%',
|
|
height: '30%'
|
|
}).component();
|
|
|
|
const titleLabel = this.modelView!.modelBuilder.text()
|
|
.withProps({ value: title, CSSStyles: { 'margin-block-start': '20px', 'margin-block-end': '0px' } })
|
|
.component();
|
|
header.addItem(titleLabel, { CSSStyles: { 'padding-left': '34px', 'padding-top': '15px', 'font-size': '36px', 'font-weight': '400' } });
|
|
|
|
const projectFolderPath = path.dirname(location);
|
|
const locationLabel = this.modelView!.modelBuilder.text()
|
|
.withProps({ value: projectFolderPath, CSSStyles: { 'margin-block-start': '20px', 'margin-block-end': '0px' } })
|
|
.component();
|
|
header.addItem(locationLabel, { CSSStyles: { 'padding-left': '34px', 'padding-top': '15px', 'padding-bottom': '50px', 'font-size': '16px' } });
|
|
|
|
const backgroundImage = this.projectProvider!.image; // background image added at the bottom right of the header
|
|
// Files need to have the vscode-file scheme to be loaded by ADS
|
|
const backgroundUri = vscode.Uri.file(backgroundImage!.light.toString()).with({ scheme: 'vscode-file' });
|
|
headerContainer.addItem(header, {
|
|
CSSStyles: {
|
|
'background-image': `url(${backgroundUri})`,
|
|
'background-repeat': 'no-repeat',
|
|
'background-position': '85% bottom',
|
|
'background-size': '10%',
|
|
'border-bottom': 'solid 1px #ccc',
|
|
'width': '100%',
|
|
'height': '100%'
|
|
}
|
|
});
|
|
|
|
return headerContainer;
|
|
}
|
|
|
|
/**
|
|
* Adds all the tables to the container
|
|
*/
|
|
private createTables(projectFile: string): azdataType.Component {
|
|
const dashboardData: IDashboardTable[] = this.projectProvider!.getDashboardComponents(projectFile);
|
|
|
|
const tableContainer = this.modelView!.modelBuilder.flexContainer().withLayout(
|
|
{
|
|
flexFlow: 'column',
|
|
width: '100%',
|
|
height: 'auto'
|
|
}).component();
|
|
|
|
dashboardData.forEach(info => {
|
|
const tableNameLabel = this.modelView!.modelBuilder.text()
|
|
.withProps({ value: info.name, CSSStyles: { 'margin-block-start': '30px', 'margin-block-end': '0px' } })
|
|
.component();
|
|
tableContainer.addItem(tableNameLabel, { CSSStyles: { 'padding-left': '25px', 'padding-bottom': '20px', ...constants.cssStyles.title } });
|
|
|
|
if (info.data.length === 0) {
|
|
const noDataText = constants.noPreviousData(info.name.toLocaleLowerCase());
|
|
const noDataLabel = this.modelView!.modelBuilder.text()
|
|
.withProps({ value: noDataText })
|
|
.component();
|
|
tableContainer.addItem(noDataLabel, { CSSStyles: { 'padding-left': '25px', 'padding-bottom': '20px' } });
|
|
} else {
|
|
const columns: azdataType.DeclarativeTableColumn[] = [];
|
|
info.columns.forEach((column: IDashboardColumnInfo) => {
|
|
let col = {
|
|
displayName: column.displayName,
|
|
valueType: column.type === 'icon' ? getAzdataApi()!.DeclarativeDataType.component : getAzdataApi()!.DeclarativeDataType.string,
|
|
isReadOnly: true,
|
|
width: column.width,
|
|
headerCssStyles: {
|
|
'border': 'none',
|
|
...constants.cssStyles.tableHeader
|
|
},
|
|
rowCssStyles: {
|
|
...constants.cssStyles.tableRow
|
|
},
|
|
};
|
|
columns.push(col);
|
|
});
|
|
|
|
const data: azdataType.DeclarativeTableCellValue[][] = [];
|
|
info.data.forEach(values => {
|
|
const columnValue: azdataType.DeclarativeTableCellValue[] = [];
|
|
values.forEach(val => {
|
|
if (typeof val === 'string') {
|
|
columnValue.push({ value: val });
|
|
} else {
|
|
const iconComponent = this.modelView!.modelBuilder.image().withProps({
|
|
iconPath: val.icon,
|
|
width: '15px',
|
|
height: '15px',
|
|
iconHeight: '15px',
|
|
iconWidth: '15px'
|
|
}).component();
|
|
const stringComponent = this.modelView!.modelBuilder.text().withProps({
|
|
value: val.text,
|
|
CSSStyles: { 'margin-block-start': 'auto', 'block-size': 'auto', 'margin-block-end': '0px' }
|
|
}).component();
|
|
|
|
const columnData = this.modelView!.modelBuilder.flexContainer().withItems([iconComponent, stringComponent], { flex: '0 0 auto', CSSStyles: { 'margin-right': '10px' } }).withLayout({ flexFlow: 'row' }).component();
|
|
columnValue.push({ value: columnData });
|
|
}
|
|
});
|
|
data.push(columnValue);
|
|
});
|
|
|
|
const table = this.modelView!.modelBuilder.declarativeTable()
|
|
.withProps({ columns: columns, dataValues: data, ariaLabel: info.name, CSSStyles: { 'margin-left': '30px' } }).component();
|
|
|
|
tableContainer.addItem(table);
|
|
}
|
|
});
|
|
return tableContainer;
|
|
}
|
|
}
|