import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DataSource } from '@angular/cdk/collections';
import { BehaviorSubject, fromEvent, merge, Observable } from 'rxjs';
import { map, debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { MatDialog } from '@angular/material/dialog';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import { WorkspaceService } from '@workspace-manager/services/workspace.service';
import {
Workspace,
WorkspaceAdapter
} from '@workspace-manager/models/workspace';
import { AddWSDialogComponent } from '@app/features/workspace-manager/components/dialogs/add-ws-dialog/add-ws-dialog.component';
import { EditWSDialogComponent } from '@app/features/workspace-manager/components/dialogs/edit-ws-dialog/edit-ws-dialog.component';
import { DeleteWSDialogComponent } from '@app/features/workspace-manager/components/dialogs/delete-ws-dialog/delete-ws-dialog.component';
@Component({
selector: 'mev-workspace-list',
templateUrl: './workspace-list.component.html',
styleUrls: ['./workspace-list.component.scss']
})
export class WorkspaceListComponent implements OnInit {
displayedColumns = [
'workspace_name',
'created',
'accessed',
'file_number',
'actions'
];
exampleDatabase: WorkspaceService | null;
dataSource: ExampleDataSource | null;
id: string;
constructor(
public httpClient: HttpClient,
public dialog: MatDialog,
public workspaceService: WorkspaceService,
private adapter: WorkspaceAdapter
) {}
@ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
@ViewChild(MatSort, { static: true }) sort: MatSort;
@ViewChild('filter', { static: true }) filter: ElementRef;
ngOnInit() {
this.loadData();
}
refresh() {
this.loadData();
}
addItem() {
const dialogRef = this.dialog.open(AddWSDialogComponent, {
data: { workspace: Workspace }
});
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
// After dialog is closed we're doing frontend updates
// For add we're just pushing a new row inside WorkspaceService
this.exampleDatabase.dataChange.value.push(
this.workspaceService.getDialogData()
);
this.refresh();
}
});
}
editItem(i: number, id: string, workspace_name: string) {
this.id = id;
const dialogRef = this.dialog.open(EditWSDialogComponent, {
data: { id: id, workspace_name: workspace_name }
});
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
// When using an edit things are little different, firstly we find record inside WorkspaceService by id
const foundIndex = this.exampleDatabase.dataChange.value.findIndex(
x => x.id === this.id
);
// Then you update that record using data from dialogData (values you entered)
this.exampleDatabase.dataChange.value[
foundIndex
] = this.workspaceService.getDialogData();
// And lastly refresh table
this.refresh();
}
});
}
deleteItem(i: number, id: string, workspace_name: string) {
this.id = id;
const dialogRef = this.dialog.open(DeleteWSDialogComponent, {
data: { id: id, workspace_name: workspace_name }
});
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
const foundIndex = this.exampleDatabase.dataChange.value.findIndex(
x => x.id === this.id
);
// for delete we use splice in order to remove single object from WorkspaceService
this.exampleDatabase.dataChange.value.splice(foundIndex, 1);
this.refresh();
}
});
}
public loadData() {
this.exampleDatabase = new WorkspaceService(this.httpClient, this.adapter);
this.dataSource = new ExampleDataSource(
this.exampleDatabase,
this.paginator,
this.sort
);
fromEvent(this.filter.nativeElement, 'keyup')
.pipe(debounceTime(150), distinctUntilChanged())
.subscribe(() => {
if (!this.dataSource) {
return;
}
this.dataSource.filter = this.filter.nativeElement.value;
});
}
}
export class ExampleDataSource extends DataSource<Workspace> {
_filterChange = new BehaviorSubject('');
get filter(): string {
return this._filterChange.value;
}
set filter(filter: string) {
this._filterChange.next(filter);
}
filteredData: Workspace[] = [];
renderedData: Workspace[] = [];
constructor(
public _exampleDatabase: WorkspaceService,
public _paginator: MatPaginator,
public _sort: MatSort
) {
super();
// Reset to the first page when the user changes the filter.
this._filterChange.subscribe(() => (this._paginator.pageIndex = 0));
}
/** Connect function called by the table to retrieve one stream containing the data to render. */
connect(): Observable<Workspace[]> {
// Listen for any changes in the base data, sorting, filtering, or pagination
const displayDataChanges = [
this._exampleDatabase.dataChange,
this._sort.sortChange,
this._filterChange,
this._paginator.page
];
this._exampleDatabase.getAllWorkspaces();
return merge(...displayDataChanges).pipe(
map(() => {
// Filter data
this.filteredData = this._exampleDatabase.data
.slice()
.filter((workspace: Workspace) => {
const searchStr = (
workspace.id + workspace.workspace_name
).toLowerCase();
return searchStr.indexOf(this.filter.toLowerCase()) !== -1;
});
// Sort filtered data
const sortedData = this.sortData(this.filteredData.slice());
// Grab the page's slice of the filtered sorted data.
const startIndex = this._paginator.pageIndex * this._paginator.pageSize;
this.renderedData = sortedData.splice(
startIndex,
this._paginator.pageSize
);
return this.renderedData;
})
);
}
disconnect() {}
/** Returns a sorted copy of the database data. */
sortData(data: Workspace[]): Workspace[] {
if (!this._sort.active || this._sort.direction === '') {
return data;
}
return data.sort((a, b) => {
let propertyA: number | string | Date = '';
let propertyB: number | string | Date = '';
switch (this._sort.active) {
case 'id':
[propertyA, propertyB] = [a.id, b.id];
break;
case 'workspace_name':
[propertyA, propertyB] = [a.workspace_name, b.workspace_name];
break;
case 'url':
[propertyA, propertyB] = [a.url, b.url];
break;
case 'created':
[propertyA, propertyB] = [a.created, b.created];
break;
}
const valueA = isNaN(+propertyA) ? propertyA : +propertyA;
const valueB = isNaN(+propertyB) ? propertyB : +propertyB;
return (
(valueA < valueB ? -1 : 1) * (this._sort.direction === 'asc' ? 1 : -1)
);
});
}
}
<div class="instruction">
To keep your different projects separate, we require that all your analyses are run in the
context of a ‘workspace.’ Navigate to your workspaces below, or create a new one to get
started.
</div>
<div class="workspace-btn-group">
<button mat-raised-button color="accent" (click)="addItem()">
<mat-icon aria-label="Create a workspace">add</mat-icon> Create a workspace
</button>
<button mat-raised-button color="accent" (click)="refresh()">
<mat-icon aria-label="Refresh">refresh</mat-icon> Refresh
</button>
</div>
<div class="workspace-list-container">
<div class="form">
<mat-form-field floatPlaceholder="never" color="accent">
<input matInput #filter placeholder="Filter workspaces">
</mat-form-field>
</div>
<mat-table #table [dataSource]="dataSource" matSort matSortActive="accessed" matSortDirection="desc" class="mat-cell">
<ng-container matColumnDef="id">
<mat-header-cell *matHeaderCellDef mat-sort-header>Id</mat-header-cell>
<mat-cell *matCellDef="let row">{{ row.id }}</mat-cell>
</ng-container>
<ng-container matColumnDef="workspace_name">
<mat-header-cell *matHeaderCellDef mat-sort-header class="workspace-table-header">Workspace name</mat-header-cell>
<mat-cell *matCellDef="let row">
<a [routerLink]="['/workspace', row.id]">
{{ row.workspace_name }}
</a>
</mat-cell>
</ng-container>
<ng-container matColumnDef="created">
<mat-header-cell *matHeaderCellDef mat-sort-header class="workspace-table-header">Created date</mat-header-cell>
<mat-cell *matCellDef="let row"> {{ row.created | date :'medium' }}</mat-cell>
</ng-container>
<ng-container matColumnDef="accessed">
<mat-header-cell *matHeaderCellDef mat-sort-header class="workspace-table-header">Last accessed date
</mat-header-cell>
<mat-cell *matCellDef="let row"> {{ row.accessed | date :'medium' }}</mat-cell>
</ng-container>
<ng-container matColumnDef="file_number">
<mat-header-cell *matHeaderCellDef mat-sort-header class="workspace-table-header">Number of files
</mat-header-cell>
<mat-cell *matCellDef="let row"> {{ row.file_number }}</mat-cell>
</ng-container>
<!-- actions -->
<ng-container matColumnDef="actions">
<mat-header-cell *matHeaderCellDef class="workspace-table-header">Actions</mat-header-cell>
<mat-cell *matCellDef="let row; let i=index;">
<button mat-icon-button color="accent" title="Edit workspace" (click)="editItem(i, row.id, row.workspace_name)">
<mat-icon aria-label="Edit">edit</mat-icon>
</button>
<button mat-icon-button color="accent" title="Delete workspace"
(click)="deleteItem(i, row.id, row.workspace_name)">
<mat-icon aria-label="Delete">delete</mat-icon>
</button>
<button mat-icon-button color="accent" title="Go to workspace" [routerLink]="['/workspace', row.id]">
<mat-icon aria-label="Go to workspace">launch</mat-icon>
</button>
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
<mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row>
</mat-table>
<div class="no-results" [style.display]="dataSource.renderedData.length == 0 ? '' : 'none'">
No results are available
</div>
<mat-paginator #paginator [length]="dataSource.filteredData.length" [pageIndex]="0" [pageSize]="10"
[pageSizeOptions]="[10, 25, 100]">
</mat-paginator>
</div>