projects/web-mev/src/app/features/workspace-detail/components/workspace-detail/workspace-detail.component.ts
Workspace Detail Component
Used to display the list of files/resources included in the current workspace Also contains child components for Metadata, Analyses Flow, Tools and Analyses Result
| changeDetection | ChangeDetectionStrategy.Default |
| selector | mev-workspace-detail |
| styleUrls | ./workspace-detail.component.scss |
| templateUrl | ./workspace-detail.component.html |
Properties |
Methods |
constructor(route: ActivatedRoute, service: WorkspaceDetailService, dialog: MatDialog)
|
||||||||||||
|
Parameters :
|
| addItem |
addItem()
|
|
Open a modal dialog to add files to a specific workspace
Returns :
void
|
| applyFilter | ||||||
applyFilter(event: Event)
|
||||||
|
Parameters :
Returns :
void
|
| deleteItem | ||||
deleteItem(resource)
|
||||
|
Open a modal dialog to delete a workspace resource from the current workspace
Parameters :
Returns :
void
|
| editItem | ||||
editItem(resource)
|
||||
|
Open a modal dialog to edit workspace resource Users can re-name resources
Parameters :
Returns :
void
|
| goToAnalysesTab |
goToAnalysesTab()
|
|
Switch to Analyses tab when the user clicks the Run button on the Tools tab
Returns :
void
|
| Public loadData |
loadData()
|
|
Returns :
void
|
| ngOnInit |
ngOnInit()
|
|
Returns :
void
|
| onTabChanged | ||||
onTabChanged($event)
|
||||
|
Refresh data when user switching between tabs
Parameters :
Returns :
void
|
| previewItem | ||||
previewItem(resourceId)
|
||||
|
Open a modal dialog to preview workspace resource content
Parameters :
Returns :
void
|
| refresh |
refresh()
|
|
Returns :
void
|
| selectResource | ||||
selectResource(resource)
|
||||
|
Parameters :
Returns :
void
|
| Public showExecutedOperationResult | ||||||
showExecutedOperationResult(executedOperationId: string)
|
||||||
|
Method is triggered when the user clicks on a executed operation on the Analyses Flow Tab
Parameters :
Returns :
void
|
| Public dialog |
Type : MatDialog
|
| displayedColumns |
Type : string[]
|
Default value : [
'name',
'readable_resource_type',
'size',
'created',
'actions'
]
|
| execOperationId |
Type : string
|
| isWait |
Default value : false
|
| paginator |
Type : MatPaginator
|
Decorators :
@ViewChild(MatPaginator, {static: false})
|
| searchText |
| selectedTabIndex |
| sort |
Type : MatSort
|
Decorators :
@ViewChild(MatSort, {static: false})
|
| workspace$ |
Type : Observable<Workspace>
|
| workspaceId |
Type : string
|
| workspaceResources |
Type : WorkspaceResource[]
|
| workspaceResourcesDS |
import {
Component,
OnInit,
ChangeDetectionStrategy,
ViewChild
} from '@angular/core';
import { WorkspaceResource } from '@features/workspace-detail/models/workspace-resource';
import { Workspace } from '@workspace-manager/models/workspace';
import { Observable } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { MatDialog } from '@angular/material/dialog';
import { WorkspaceDetailService } from '@features/workspace-detail/services/workspace-detail.service';
import { AddDialogComponent } from '../dialogs/add-dialog/add-dialog.component';
import { PreviewDialogComponent } from '../dialogs/preview-dialog/preview-dialog.component';
import { DeleteDialogComponent } from '../dialogs/delete-dialog/delete-dialog.component';
import { EditDialogComponent } from '../dialogs/edit-dialog/edit-dialog/edit-dialog.component';
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
/**
* Workspace Detail Component
*
* Used to display the list of files/resources included in the current workspace
* Also contains child components for Metadata, Analyses Flow, Tools and Analyses Result
*/
@Component({
selector: 'mev-workspace-detail',
templateUrl: './workspace-detail.component.html',
styleUrls: ['./workspace-detail.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
})
export class WorkspaceDetailComponent implements OnInit {
workspaceResources: WorkspaceResource[];
workspaceId: string;
workspace$: Observable<Workspace>;
searchText;
selectedTabIndex;
execOperationId: string;
isWait = false;
workspaceResourcesDS;
displayedColumns: string[] = [
'name',
'readable_resource_type',
'size',
'created',
'actions'
];
@ViewChild(MatPaginator, { static: false }) paginator: MatPaginator;
@ViewChild(MatSort, { static: false }) sort: MatSort;
constructor(
private route: ActivatedRoute,
private service: WorkspaceDetailService,
public dialog: MatDialog
) {}
ngOnInit(): void {
this.loadData();
}
public loadData() {
this.workspaceId = this.route.snapshot.paramMap.get('workspaceId');
this.service.getConnectedResources(this.workspaceId).subscribe(data => {
this.workspaceResources = data;
this.workspaceResourcesDS = new MatTableDataSource(data);
this.workspaceResourcesDS.paginator = this.paginator;
this.workspaceResourcesDS.sort = this.sort;
});
this.workspace$ = this.service.getWorkspaceDetail(this.workspaceId);
}
refresh() {
this.loadData();
}
applyFilter(event: Event) {
const filterValue = (event.target as HTMLInputElement).value;
this.workspaceResourcesDS.filter = filterValue.trim().toLowerCase();
}
selectResource(resource) {
console.log(`The selected resource is:: ${resource.name}`);
}
/**
* Open a modal dialog to add files to a specific workspace
*
*/
addItem() {
const dialogRef = this.dialog.open(AddDialogComponent, {
data: { workspaceId: this.workspaceId }
});
dialogRef.afterClosed().subscribe(result => {
if (result === 1) {
this.refresh();
}
});
}
/**
* Open a modal dialog to preview workspace resource content
*
*/
previewItem(resourceId) {
this.isWait = true;
this.service.getResourcePreview(resourceId).subscribe(data => {
const previewData = {};
if (data?.results?.length && 'rowname' in data.results[0]) {
const minN = Math.min(data.results.length, 10);
let slicedData = data.results.slice(0, minN);
const columns = Object.keys(slicedData[0].values);
const rows = slicedData.map(elem => elem.rowname);
const values = slicedData.map(elem => {
let rowValues = [];
const elemValues = elem.values;
columns.forEach(col => rowValues.push(elemValues[col]));
return rowValues;
});
previewData['columns'] = columns;
previewData['rows'] = rows;
previewData['values'] = values;
}
setTimeout(() => {
this.isWait = false;
this.dialog.open(PreviewDialogComponent, {
data: {
previewData: previewData
}
});
}, 1000); // time-out for spinner
});
}
/**
* Open a modal dialog to edit workspace resource
* Users can re-name resources
*/
editItem(resource) {
const dialogRef = this.dialog.open(EditDialogComponent, {
data: {
id: resource.id,
name: resource.name,
resource_type: resource.resource_type
}
});
dialogRef.afterClosed().subscribe(result => {
this.refresh();
});
}
/**
* Open a modal dialog to delete a workspace resource from the current workspace
*
*/
deleteItem(resource) {
const dialogRef = this.dialog.open(DeleteDialogComponent, {
data: { workspaceId: this.workspaceId, resource: resource }
});
dialogRef.afterClosed().subscribe(result => {
this.refresh();
});
}
/**
* Switch to Analyses tab when the user clicks the Run button on the Tools tab
*
*/
goToAnalysesTab() {
this.selectedTabIndex = 4;
}
/**
* Refresh data when user switching between tabs
*
*/
onTabChanged($event) {
const clickedIndex = $event.index;
if (clickedIndex === 0) {
this.refresh();
}
}
/**
* Method is triggered when the user clicks on a executed operation on the Analyses Flow Tab
*
*/
public showExecutedOperationResult(executedOperationId: string) {
this.execOperationId = executedOperationId;
this.goToAnalysesTab();
}
}
<mev-spinner-overlay *ngIf="isWait"></mev-spinner-overlay>
<div class="workspace-header">
<a routerLink='/workarea' class="workspace-header__nav nav">
<mat-icon class="nav__icon" aria-label="Next step">reply</mat-icon>
<span class="nav__text">Back to File & Workspace list</span>
</a>
<span class="workspace-header__title" *ngIf="workspace$ | async as workspace">
Workspace
<b> {{ workspace.workspace_name }} </b>
<mat-icon class="workspace-header__icon" matTooltipClass="tooltip" fontSet="material-icons-outlined" matTooltip="Workspace: {{ workspace.workspace_name }}
Created: {{ workspace.created }}
Owner: {{ workspace.owner_email }}" aria-label="Info tooltip about the workspace">info
</mat-icon>
</span>
</div>
<mat-tab-group animationDuration="0ms" color="accent" [(selectedIndex)]="selectedTabIndex"
(selectedTabChange)="onTabChanged($event);">
<mat-tab label="Resources">
<ng-template matTabContent>
<div *ngIf="!workspaceResources?.length" class="instruction">
To perform analyses, you need to add or import data into the workspace. Select
from among the files you uploaded or import public data (if available)
</div>
<mat-toolbar>
<mat-form-field floatLabel='never' class='search-field'>
<input matInput [(ngModel)]="searchText" (keyup)="applyFilter($event)" autocomplete="off">
<mat-placeholder class="placeholder">Search</mat-placeholder>
</mat-form-field>
</mat-toolbar>
<div class="btn-panel">
<div class="btn-group">
<button mat-raised-button color="accent" (click)="addItem()">
<mat-icon aria-label="Add local data">add</mat-icon>
Add local data
</button>
<button mat-raised-button color="accent" (click)="addItem()">
<mat-icon aria-label="Add external data">add</mat-icon>
Add external data
</button>
</div>
<mat-button-toggle-group #viewMode="matButtonToggleGroup" value="tableMode">
<mat-button-toggle value="tableMode" aria-label="Text align left">
Table view
</mat-button-toggle>
<mat-button-toggle value="cardMode" aria-label="Text align center">
Card view
</mat-button-toggle>
</mat-button-toggle-group>
</div>
<div *ngIf="viewMode.value === 'cardMode'" class="cardList">
<a *ngFor="let resource of workspaceResources | filter : searchText" (click)="selectResource(resource);"
class="cardList__item card" matRipple matRippleColor="rgba(158, 158, 158, 0.22)">
<mat-card>
<mat-card-title class="card-title">
{{ resource.name }}
</mat-card-title>
<mat-card-content>
<ul>
<li>Resource type: {{ resource.readable_resource_type }}</li>
<li>Size: {{ resource.size | byteName}}</li>
<li>Date added: {{ resource.created | date :'medium' }}</li>
</ul>
</mat-card-content>
<mat-card-footer>
<mat-icon fontSet="material-icons-outlined" title="Edit resource" (click)="editItem(resource)">
edit</mat-icon>
<mat-icon fontSet="material-icons-outlined" title="Preview resource" (click)="previewItem(resource.id)">
search</mat-icon>
<mat-icon fontSet="material-icons-outlined" title="Delete resource" (click)="deleteItem(resource)">delete
</mat-icon>
</mat-card-footer>
</mat-card>
</a>
</div>
<div *ngIf="viewMode.value === 'tableMode'" class="resource-table-container mat-elevation-z8">
<table mat-table [dataSource]="workspaceResourcesDS" matSort matSortActive="date" matSortDirection="desc">
<ng-container matColumnDef="name">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Resource name </th>
<td mat-cell *matCellDef="let resource">
{{ resource.name }}
</td>
</ng-container>
<ng-container matColumnDef="readable_resource_type">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Resource type </th>
<td mat-cell *matCellDef="let resource">
{{ resource.readable_resource_type }}
</td>
</ng-container>
<ng-container matColumnDef="size">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Size </th>
<td mat-cell *matCellDef="let resource"> {{ resource.size | byteName }} </td>
</ng-container>
<ng-container matColumnDef="created">
<th mat-header-cell *matHeaderCellDef mat-sort-header> Creation date </th>
<td mat-cell *matCellDef="let resource"> {{ resource.created | date :'medium' }} </td>
</ng-container>
<ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>Actions</th>
<td mat-cell *matCellDef="let resource">
<button mat-icon-button color="accent" title="Preview" (click)="previewItem(resource.id)">
<mat-icon aria-label="View">visibility</mat-icon>
</button>
<button mat-icon-button color="accent" title="Delete" (click)="deleteItem(resource)">
<mat-icon aria-label="Delete">delete</mat-icon>
</button>
<button mat-icon-button color="accent" title="Edit" (click)="editItem(resource)">
<mat-icon aria-label="Edit">edit</mat-icon>
</button>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;">
</table>
<mat-paginator #paginator [pageSizeOptions]="[5, 10, 25]" [pageIndex]="0" [pageSize]="10">
</mat-paginator>
</div>
</ng-template>
</mat-tab>
<mat-tab label="Metadata">
<ng-template matTabContent>
<mev-metadata *ngIf='workspaceResources' [workspaceResources]="workspaceResources"></mev-metadata>
</ng-template>
</mat-tab>
<mat-tab label="Analysis Flow">
<ng-template matTabContent>
<mev-analysis-flow (executedOperationId)="showExecutedOperationResult($event)"></mev-analysis-flow>
</ng-template>
</mat-tab>
<mat-tab label="Tools">
<ng-template matTabContent>
<mev-analyses (executedOperationId)="showExecutedOperationResult($event)"></mev-analyses>
</ng-template>
</mat-tab>
<mat-tab label="Analyses Result">
<ng-template matTabContent>
<mev-executed-operation [execOperationId]="execOperationId"></mev-executed-operation>
</ng-template>
</mat-tab>
</mat-tab-group>
./workspace-detail.component.scss
.workspace-header {
display: flex;
justify-content: flex-start;
padding: 10px;
}
.workspace-header__nav {
color: #03a9f4;
font-size: 14px;
.nav__icon {
vertical-align: bottom;
}
}
.workspace-header__title {
position: relative;
margin: auto;
left: -100px;
}
.workspace-header__icon {
vertical-align: top;
}
.instruction {
font-size: 14px;
padding: 16px;
}
.mat-toolbar {
background-color: transparent;
}
.search-field {
min-width: 250px;
}
.search-field .placeholder {
font-size: 12px;
color: #666666;
}
.btn-panel {
display: flex;
justify-content: space-between;
padding: 16px;
}
button {
margin-right: 10px;
}
::ng-deep {
.tooltip {
white-space: pre-line !important;
}
.mat-button-toggle-group {
font-size: 14px;
}
.mat-button-toggle-label-content {
line-height: 36px !important;
}
.mat-button-toggle-checked {
background-color: #c8ebfa !important;
}
}
.resource-table-container {
margin: 15px;
table {
width: 100%;
}
.mat-header-cell {
background: #eeeeee;
}
.mat-column-name {
padding-right: 5px;
word-wrap: break-word !important;
white-space: unset !important;
flex: 0 0 40% !important;
width: 40% !important;
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
}
.cardList {
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: flex-start;
}
.cardList > * {
box-sizing: border-box;
}
.cardList__item {
margin: 15px;
flex: 0 1 calc(33.3% - 32px);
}
@media screen and (max-width: 959px) {
.cardListItem {
flex: 0 1 calc(50% - 32px);
}
}
@media screen and (max-width: 599px) {
.cardList {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
}
.card-title {
word-wrap: break-word;
}
.mat-card-footer {
padding: 7px;
text-align: right;
.mat-icon {
margin-right: 10px;
cursor: pointer;
&:hover {
color: #666;
}
}
}
.workspace-footer {
float: right;
padding: 20px;
}