Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/components/base/base.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { BreadcrumbModule } from "./breadcrumbs";
import { ButtonsModule } from "./buttons";
import { ChartsModule } from "./charts";
import { ContextMenuModule } from "./context-menu";
import { DialogsModule } from "./dialogs";
import { DropdownModule } from "./dropdown";
import { EditorModule } from "./editor";
import { ElapsedTimeComponent } from "./elapsed-time";
Expand Down Expand Up @@ -43,6 +44,7 @@ const modules = [
BackgroundTaskModule,
ChartsModule,
ContextMenuModule,
DialogsModule,
DropdownModule,
EditorModule,
FocusSectionModule,
Expand Down Expand Up @@ -79,6 +81,7 @@ const components = [
declarations: components,
entryComponents: [
DeleteSelectedItemsDialogComponent,
SimpleDialogComponent,
],
exports: [...modules, ...components],
imports: [
Expand Down
29 changes: 29 additions & 0 deletions app/components/base/dialogs/confirmation-dialog.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Component } from "@angular/core";
import { MdDialogRef } from "@angular/material";
import { autobind } from "core-decorators";
import { AsyncSubject, Observable } from "rxjs";

@Component({
selector: "bl-confirmation-dialog",
templateUrl: "confirmation-dialog.html",
})
export class ConfirmationDialogComponent {
public title: string;
public description: string;
public execute: () => Observable<any>;

public response = new AsyncSubject<boolean>();

constructor(public dialogRef: MdDialogRef<ConfirmationDialogComponent>) {
this.response.next(false);
}

@autobind()
public submit() {
return this.execute();
}

public done() {
this.response.complete();
}
}
6 changes: 6 additions & 0 deletions app/components/base/dialogs/confirmation-dialog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<bl-simple-form [submit]="submit" [containerRef]="dialogRef" actionColor="warn" actionName="Yes" [multiUse]="false" size="small"
[title]="title">
<div class="message" *ngIf="description">
<p>{{description}}</p>
</div>
</bl-simple-form>
30 changes: 30 additions & 0 deletions app/components/base/dialogs/dialog.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injectable } from "@angular/core";
import { ComponentType, MdDialog, MdDialogConfig, MdDialogRef } from "@angular/material";
import { Observable } from "rxjs";
import { ConfirmationDialogComponent } from "./confirmation-dialog.component";

export interface ConfirmOptions {
description?: string;
yes: () => Observable<any>;
}

/**
* Dialog service is a service to help open commonly used dialog such as a confirmation dialog.
* It can also open a dialog the same way material does so you only need to inject this service and not the MdDialog
*/
@Injectable()
export class DialogService {
constructor(private mdDialog: MdDialog) { }

public confirm(title: string = "Are you sure?", options: ConfirmOptions) {
const ref = this.mdDialog.open(ConfirmationDialogComponent);
const component = ref.componentInstance;
component.title = title;
component.description = options.description;
component.execute = options.yes;
}

public open<T>(type: ComponentType<T>, config?: MdDialogConfig): MdDialogRef<T> {
return this.mdDialog.open(type, config);
}
}
31 changes: 31 additions & 0 deletions app/components/base/dialogs/dialogs.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { NgModule } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { MaterialModule } from "@angular/material";
import { BrowserModule } from "@angular/platform-browser";

import { FormModule } from "app/components/base/form";
import { ConfirmationDialogComponent } from "./confirmation-dialog.component";
import { DialogService } from "./dialog.service";

@NgModule({
declarations: [
ConfirmationDialogComponent,
],
exports: [
ConfirmationDialogComponent,
],
imports: [
BrowserModule,
FormsModule,
MaterialModule,
FormModule,
],
providers: [
DialogService,
],
entryComponents: [
ConfirmationDialogComponent,
],
})
export class DialogsModule {
}
3 changes: 3 additions & 0 deletions app/components/base/dialogs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from "./confirmation-dialog.component";
export * from "./dialog.service";
export * from "./dialogs.module";
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class SimpleDialogComponent {
public done = new EventEmitter();

@Input()
public title: string;
public title: string = "Are you sure?";

@Input()
public subtitle: string;
Expand Down
14 changes: 13 additions & 1 deletion app/components/node/details/node-details.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ActivatedRoute } from "@angular/router";
import { autobind } from "core-decorators";
import { Subscription } from "rxjs";

import { DialogService } from "app/components/base/dialogs";
import { SidebarManager } from "app/components/base/sidebar";
import { Node, Pool } from "app/models";
import { FileService, NodeParams, NodeService, PoolService } from "app/services";
Expand Down Expand Up @@ -33,8 +34,9 @@ export class NodeDetailsComponent implements OnInit, OnDestroy {

constructor(
private route: ActivatedRoute,
nodeService: NodeService,
private nodeService: NodeService,
private poolService: PoolService,
private dialog: DialogService,
fileService: FileService,
private sidebarManager: SidebarManager) {

Expand Down Expand Up @@ -92,4 +94,14 @@ export class NodeDetailsComponent implements OnInit, OnDestroy {
ref.component.node = this.node;
ref.component.pool = this.pool;
}

@autobind()
public delete() {
this.dialog.confirm("Are you sure you want to delete this node?", {
yes: () => {
return this.nodeService.delete(this.pool.id, this.nodeId)
.cascade(() => this.nodeService.getOnce(this.pool.id, this.node.id));
},
});
}
}
1 change: 1 addition & 0 deletions app/components/node/details/node-details.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<bl-button-group>
<bl-refresh-btn [refresh]="refresh"></bl-refresh-btn>
<bl-button color="light" [action]="connect" icon="fa fa-plug"></bl-button>
<bl-delete-button [entity]="node" [action]="delete"></bl-delete-button>
</bl-button-group>
</div>
<div class="content">
Expand Down
48 changes: 43 additions & 5 deletions app/components/pool/graphs/nodes-heatmap.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import { Router } from "@angular/router";
import * as d3 from "d3";
import * as elementResizeDetectorMaker from "element-resize-detector";
import { List } from "immutable";
import { BehaviorSubject } from "rxjs";
import { BehaviorSubject, Observable } from "rxjs";

import { ContextMenu, ContextMenuItem, ContextMenuService } from "app/components/base/context-menu";
import { NotificationService } from "app/components/base/notifications";
import { SidebarManager } from "app/components/base/sidebar";
import { NodeConnectComponent } from "app/components/node/connect";
import { Job, Node, NodeState, Pool } from "app/models";
import { Job, Node, NodeState, Pool, ServerError } from "app/models";
import { NodeService } from "app/services";
import { log } from "app/utils";
import { HeatmapColor } from "./heatmap-color";
Expand Down Expand Up @@ -117,6 +118,7 @@ export class NodesHeatmapComponent implements AfterViewInit, OnChanges, OnDestro
private contextMenuService: ContextMenuService,
private nodeService: NodeService,
private sidebarManager: SidebarManager,
private notificationService: NotificationService,
private router: Router,
) {
this.colors = new HeatmapColor(stateTree);
Expand Down Expand Up @@ -161,6 +163,19 @@ export class NodesHeatmapComponent implements AfterViewInit, OnChanges, OnDestro
.attr("width", this._width)
.attr("height", this._height);

const defs = this._svg.append("defs");
const pattern = defs.append("pattern")
.attr("id", "low-pri-stripes")
.attr("width", "10")
.attr("height", "10")
.attr("patternUnits", "userSpaceOnUse")
.attr("patternTransform", "rotate(45 50 50)");

pattern.append("line")
.attr("stroke", "#fff")
.attr("stroke-width", "2px")
.attr("y2", "10");

this._processNewData();
}

Expand Down Expand Up @@ -429,18 +444,41 @@ export class NodesHeatmapComponent implements AfterViewInit, OnChanges, OnDestro
private _buildContextMenu(node: Node) {
return new ContextMenu([
new ContextMenuItem({ label: "Go to", click: () => this._gotoNode(node) }),
new ContextMenuItem({ label: "Connect", click: () => this._connectTo(node) }),
new ContextMenuItem({ label: "Reboot", click: () => this._reboot(node) }),
new ContextMenuItem({ label: "Reimage", click: () => this._reimage(node) }),
new ContextMenuItem({ label: "Connect", click: () => this._connectTo(node) }),
new ContextMenuItem({ label: "Delete", click: () => this._delete(node) }),
]);
}

private _reboot(node: Node) {
this.nodeService.reboot(this.pool.id, node.id).cascade(() => this.nodeService.getOnce(this.pool.id, node.id));
this._nodeAction(node, this.nodeService.reboot(this.pool.id, node.id)).cascade(() => {
this.notificationService.success("Node reimaging!", `Node ${node.id} started reimaging`);
});
}

private _reimage(node: Node) {
this.nodeService.reimage(this.pool.id, node.id).cascade(() => this.nodeService.getOnce(this.pool.id, node.id));
this._nodeAction(node, this.nodeService.reimage(this.pool.id, node.id)).cascade(() => {
this.notificationService.success("Node rebooting!", `Node ${node.id} started rebooting`);
});
}

private _delete(node: Node) {
this._nodeAction(node, this.nodeService.delete(this.pool.id, node.id)).cascade(() => {
this.notificationService.success("Node deleting!", `Node ${node.id} is being removed from the pool.`);
});
}

private _nodeAction(node: Node, action: Observable<any>): Observable<any> {
action.subscribe({
next: () => {
this.nodeService.getOnce(this.pool.id, node.id);
},
error: (error: ServerError) => {
this.notificationService.error(error.body.code, error.body.message);
},
});
return action;
}

private _gotoNode(node: Node) {
Expand Down
4 changes: 2 additions & 2 deletions app/components/pool/graphs/nodes-heatmap.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
</div>
<div class="heatmap-container" #heatmap>
<svg #svg>
<defs>
<!-- <defs>
<pattern id="low-pri-stripes" width="10" height="10" patternUnits="userSpaceOnUse" patternTransform="rotate(45 50 50)">
<line stroke="#fff" stroke-width="2px" y2="10"></line>
</pattern>
</defs>
</defs> -->
</svg>

<div *ngIf="nodes.size === 0" class="no-nodes-container">
Expand Down
6 changes: 6 additions & 0 deletions app/services/node-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ export class NodeService extends ServiceBase {
return observable;
}

public delete(poolId: string, nodeId: string): Observable<any> {
return this.callBatchClient((client) => client.node.delete(poolId, nodeId, {}), (error) => {
log.error("Error deleting node: " + nodeId, Object.assign({}, error));
});
}

public listNodeAgentSkus(initialOptions: any = { pageSize: 1000 }): RxListProxy<{}, NodeAgentSku> {
return new RxBatchListProxy<{}, NodeAgentSku>(NodeAgentSku, this.batchService, {
cache: (params) => this._nodeAgentSkusCache,
Expand Down
10 changes: 8 additions & 2 deletions app/styles/partials/grid.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
.grow {
display: flex;
margin: 0 -5px;

> .gcol {
flex: 1;
margin: 0 5px;

&:first-child {
margin-left: 0;
}

&:last-child {
margin-right: 0;
}
}
}
52 changes: 52 additions & 0 deletions docs/dialog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# How to open a popup dialog

You can open a popup dialog in batchlabs to ask for confirmation to the user, fill a small form, etc.

First you need to import the `DialogService`

```ts
import { DialogService } from "app/components/base/dialogs";

constructor(private dialog: DialogService) {}
```

## Common dialogs

**1. Confirmation dialog**

Simplest confirmation dialog that do some action when user confirm.
```ts
this.dialog.confirm("Are you sure?", {
yes: () => this.otherService.doSomething(),
})
```

You can also have a description to detail a bit more what the user is about to confirm
```ts
this.dialog.confirm("Are you sure?", {
description: "This cannot be reverted.",
yes: () => this.otherService.doSomething(),
})
```


## Custom dialogs

You can also open any component as a dialog. For that you'll need to first add the component to the entryComponents list of the module

```ts
@NgModule({
...
entryComponents: [
MyDialogComponent
],
...
})
```

then just call open(Note that this is just a proxy function to material MdDialog open)

```ts
const ref = this.dialog.open(MyDialogComponent);
reg.componentInstance # If you want to set some variables.
```
1 change: 1 addition & 0 deletions docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

## How to
* [Context Menu](context-menu.md)
* [Open a dialog](dialog.md)
* [Store user data/Cache data](store-user-data.md)


Expand Down
10 changes: 10 additions & 0 deletions src/client/api/batch-client-proxy/nodeProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,16 @@ export default class NodeProxy {
return this.client.computeNodeOperations.reboot(poolId, nodeId, wrapOptions(options));
}

/**
* Reinstalls the operating system on the specified compute node.
* @param poolId: The id of the pool.
* @param nodeId: The id of the node to delete
* @param options: Optional Parameters.
*/
public delete(poolId: string, nodeId: string, options?: any): Promise<any> {
return this.client.pool.removeNodes(poolId, { nodeList: [nodeId] }, wrapOptions(options));
}

/**
* Reinstalls the operating system on the specified compute node.
* http://azure.github.io/azure-sdk-for-node/azure-batch/latest/ComputeNodeOperations.html#reimage
Expand Down
Loading