Browse Source

web-ui: improvements on block details view (#19)

transactions for the block are now retrieved separately, the
transactions table is paginated and shows more info and
finally the view now is mobile friendly
prometheus-integration
Mario Mejia 7 years ago
committed by Alexis Hernandez
parent
commit
b6a98d1ffa
  1. 510
      web-ui/src/app/components/block-details/block-details.component.html
  2. 35
      web-ui/src/app/components/block-details/block-details.component.ts
  3. 7
      web-ui/src/app/services/blocks.service.ts

510
web-ui/src/app/components/block-details/block-details.component.html

@ -4,260 +4,288 @@
</div>
<div *ngIf="blockDetails != null">
<div class="row">
<h2 class="col-xs-12 col-sm-12 col-md-12 col-lg-12">{{'label.block' | translate}} #{{blockDetails.block.height}}</h2>
</div>
<h2 class="col-xs-12">{{'label.block' | translate}} #{{blockDetails.block.height}}</h2>
<div class="col-xs-12 col-sm-12 col-md-7 col-lg-7">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-4 col-sm-4 col-md-3 col-lg-9">{{'label.summary' | translate}}</th>
<th class="col-xs-8 col-sm-8 col-md-3 col-lg-9"></th>
</tr>
</thead>
<div class="col-xs-12 col-md-7">
<div class="table-responsive">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-4 col-md-3 col-lg-9">{{'label.summary' | translate}}</th>
<th class="col-xs-8 col-md-3 col-lg-9"></th>
</tr>
</thead>
<tbody>
<tr>
<!-- TODO: probably it would be better to just get the block type from the server-->
<td>{{'label.blockType' | translate}}</td>
<td>{{getBlockType(blockDetails)}}</td>
</tr>
<tr *ngIf="isTPoS(blockDetails)">
<td>{{'label.tposContract' | translate}}</td>
<td><a routerLink="/transactions/{{blockDetails.block.tposContract}}">{{blockDetails.block.tposContract}}</a></td>
</tr>
<tr>
<td>{{'label.blockhash' | translate}}</td>
<td>{{blockDetails.block.hash}}</td>
</tr>
<tr>
<td>{{'label.merkleRoot' | translate}}</td>
<td>{{blockDetails.block.merkleRoot}}</td>
</tr>
<tr>
<td>{{'label.confirmations' | translate}}</td>
<td>{{blockDetails.block.confirmations}}</td>
</tr>
<tr>
<td>{{'label.size' | translate}}</td>
<td>{{blockDetails.block.size}} bytes</td>
</tr>
<tr>
<td>{{'label.version' | translate}}</td>
<td>{{blockDetails.block.version}}</td>
</tr>
<tr>
<td>{{'label.nonce' | translate}}</td>
<td>{{blockDetails.block.nonce}}</td>
</tr>
<tr>
<td>{{'label.bits' | translate}}</td>
<td>{{blockDetails.block.bits}}</td>
</tr>
<tr>
<td>{{'label.chainwork' | translate}}</td>
<td>{{blockDetails.block.chainwork}}</td>
</tr>
<tr>
<td>{{'label.difficulty' | translate}}</td>
<td>{{blockDetails.block.difficulty}}</td>
</tr>
<tr>
<td>{{'label.blocktime' | translate}}</td>
<td>{{blockDetails.block.time * 1000 | explorerDatetime}}</td>
</tr>
<tr>
<td>{{'label.medianTime' | translate}}</td>
<td>{{blockDetails.block.medianTime * 1000 | explorerDatetime}}</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<a *ngIf="blockDetails.block.previousBlockhash != null" routerLink="/blocks/{{blockDetails.block.previousBlockhash}}">
{{'label.previous' | translate}} ({{blockDetails.block.height - 1}})
</a>
</td>
<td>
<a *ngIf="blockDetails.block.nextBlockhash != null" routerLink="/blocks/{{blockDetails.block.nextBlockhash}}">
{{'label.next' | translate}} ({{blockDetails.block.height + 1}})
</a>
</td>
</tr>
</tbody>
</table>
</div>
<tbody>
<tr>
<!-- TODO: probably it would be better to just get the block type from the server-->
<td>{{'label.blockType' | translate}}</td>
<td>{{getBlockType(blockDetails)}}</td>
</tr>
<tr *ngIf="isTPoS(blockDetails)">
<td>{{'label.tposContract' | translate}}</td>
<td>
<a routerLink="/transactions/{{blockDetails.block.tposContract}}">{{blockDetails.block.tposContract}}</a>
</td>
</tr>
<tr>
<td>{{'label.blockhash' | translate}}</td>
<td>{{blockDetails.block.hash}}</td>
</tr>
<tr>
<td>{{'label.merkleRoot' | translate}}</td>
<td>{{blockDetails.block.merkleRoot}}</td>
</tr>
<tr>
<td>{{'label.confirmations' | translate}}</td>
<td>{{blockDetails.block.confirmations}}</td>
</tr>
<tr>
<td>{{'label.size' | translate}}</td>
<td>{{blockDetails.block.size}} bytes</td>
</tr>
<tr>
<td>{{'label.version' | translate}}</td>
<td>{{blockDetails.block.version}}</td>
</tr>
<tr>
<td>{{'label.nonce' | translate}}</td>
<td>{{blockDetails.block.nonce}}</td>
</tr>
<tr>
<td>{{'label.bits' | translate}}</td>
<td>{{blockDetails.block.bits}}</td>
</tr>
<tr>
<td>{{'label.chainwork' | translate}}</td>
<td>{{blockDetails.block.chainwork}}</td>
</tr>
<tr>
<td>{{'label.difficulty' | translate}}</td>
<td>{{blockDetails.block.difficulty}}</td>
</tr>
<tr>
<td>{{'label.blocktime' | translate}}</td>
<td>{{blockDetails.block.time * 1000 | explorerDatetime}}</td>
</tr>
<tr>
<td>{{'label.medianTime' | translate}}</td>
<td>{{blockDetails.block.medianTime * 1000 | explorerDatetime}}</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<a *ngIf="blockDetails.block.previousBlockhash != null" routerLink="/blocks/{{blockDetails.block.previousBlockhash}}">
{{'label.previous' | translate}} ({{blockDetails.block.height - 1}})
</a>
</td>
<td>
<a *ngIf="blockDetails.block.nextBlockhash != null" routerLink="/blocks/{{blockDetails.block.nextBlockhash}}">
{{'label.next' | translate}} ({{blockDetails.block.height + 1}})
</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- rewards -->
<div class="col-xs-12 col-sm-12 col-md-offset-1 col-md-4 col-lg-offset-1 col-lg-4">
<!-- rewards -->
<div class="col-xs-12 col-md-offset-1 col-md-4">
<div class="table-responsive">
<!-- PoW -->
<div *ngIf="isPoW(blockDetails)">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-md-1">{{'label.blockReward' | translate}}</th>
<th class="col-xs-2 col-md-1">{{blockDetails.rewards.reward.value}} {{'label.coinName' | translate}}</th>
</tr>
</thead>
<!-- PoW -->
<div *ngIf="isPoW(blockDetails)">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{'label.blockReward' | translate}}</th>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{blockDetails.rewards.reward.value}} {{'label.coinName' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.reward.address}}">{{blockDetails.rewards.reward.address}}</a>
</td>
</tr>
</tbody>
</table>
</div>
<tbody>
<tr>
<td>{{'label.address' | translate}}</td>
<td><a routerLink="/addresses/{{blockDetails.rewards.reward.address}}">{{blockDetails.rewards.reward.address}}</a></td>
</tr>
</tbody>
</table>
</div>
<!-- PoS -->
<div *ngIf="isPoS(blockDetails)">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-md-1">{{'label.rewards' | translate}}</th>
<th class="col-xs-2 col-md-1">{{getPoSTotalReward(blockDetails)}} {{'label.coinName' | translate}}</th>
</tr>
</thead>
<!-- PoS -->
<div *ngIf="isPoS(blockDetails)">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{'label.rewards' | translate}}</th>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{getPoSTotalReward(blockDetails)}} {{'label.coinName' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<strong>{{'label.coinstake' | translate}}</strong>
</td>
<td></td>
</tr>
<tr>
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.coinstake.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.coinstake.address}}">{{blockDetails.rewards.coinstake.address}}</a>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>
<strong>{{'label.masternode' | translate}}</strong>
</td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.masternode.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.masternode.address}}">{{blockDetails.rewards.masternode.address}}</a>
</td>
</tr>
</tbody>
</table>
</div>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td><strong>{{'label.coinstake' | translate}}</strong></td>
<td></td>
</tr>
<tr>
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.coinstake.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.coinstake.address}}">{{blockDetails.rewards.coinstake.address}}</a>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td><strong>{{'label.masternode' | translate}}</strong></td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.masternode.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.masternode.address}}">{{blockDetails.rewards.masternode.address}}</a>
</td>
</tr>
</tbody>
</table>
</div>
<!-- TPoS -->
<div *ngIf="isTPoS(blockDetails)">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-md-1">{{'label.rewards' | translate}}</th>
<th class="col-xs-2 col-md-1">{{getTPoSTotalReward(blockDetails)}} {{'label.coinName' | translate}}</th>
</tr>
</thead>
<!-- TPoS -->
<div *ngIf="isTPoS(blockDetails)">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{'label.rewards' | translate}}</th>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{getTPoSTotalReward(blockDetails)}} {{'label.coinName' | translate}}</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<strong>{{'label.owner' | translate}}</strong>
</td>
<td></td>
</tr>
<tr>
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.owner.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.owner.address}}">{{blockDetails.rewards.owner.address}}</a>
</td>
</tr>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<strong>{{'label.owner' | translate}}</strong>
</td>
<td></td>
</tr>
<tr>
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.owner.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.owner.address}}">{{blockDetails.rewards.owner.address}}</a>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<strong>{{'label.merchant' | translate}}</strong>
</td>
<td></td>
</tr>
<tr>
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.merchant.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.merchant.address}}">{{blockDetails.rewards.merchant.address}}</a>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td>
<strong>{{'label.merchant' | translate}}</strong>
</td>
<td></td>
</tr>
<tr>
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.merchant.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr>
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.merchant.address}}">{{blockDetails.rewards.merchant.address}}</a>
</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>
<strong>{{'label.masternode' | translate}}</strong>
</td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.masternode.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.masternode.address}}">{{blockDetails.rewards.masternode.address}}</a>
</td>
</tr>
</tbody>
</table>
<tr>
<td></td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>
<strong>{{'label.masternode' | translate}}</strong>
</td>
<td></td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.amount' | translate}}</td>
<td>{{blockDetails.rewards.masternode.value}} {{'label.coinName' | translate}}</td>
</tr>
<tr *ngIf="blockDetails.rewards.masternode != null">
<td>{{'label.address' | translate}}</td>
<td>
<a routerLink="/addresses/{{blockDetails.rewards.masternode.address}}">{{blockDetails.rewards.masternode.address}}</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- transactions -->
<div class="col-xs-12 col-sm-12 col-md-offset-2 col-md-8 col-lg-offset-2 col-lg-8">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-2 col-sm-2 col-md-1 col-lg-1">{{'label.transactions' | translate}}</th>
</tr>
</thead>
<!-- transactions -->
<div class="col-xs-12 col-md-offset-2 col-md-8">
<div class="table-responsive">
<table class="table table-condensed table-bordered table-striped table-hover">
<thead>
<tr>
<th class="col-xs-1">#</th>
<th class="col-xs-4">{{'label.transaction' | translate}}</th>
<th class="col-xs-3">{{'label.date' | translate}}</th>
<th class="col-xs-2">{{'label.value' | translate}}</th>
<th class="col-xs-2">{{'label.size' | translate}}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of blockDetails.block.transactions">
<td>
<a routerLink="/transactions/{{item}}">{{item}}</a>
</td>
</tr>
</tbody>
</table>
<tbody>
<tr *ngFor="let index = index; let item of asyncItems | async | paginate: { id: 'transactions', itemsPerPage: pageSize, currentPage: currentPage, totalItems: total }">
<td>{{(currentPage - 1) * pageSize + index + 1}}</td>
<td>
<a routerLink="/transactions/{{item.id}}">{{item.id | slice:0:35}}...</a>
</td>
<td>{{item.time * 1000 | explorerDatetime}}</td>
<td>{{item.sent}} {{'label.coinName' | translate}}</td>
<td>{{item.size}} bytes</td>
</tr>
</tbody>
</table>
</div>
<div class="row">
<div class="col-xs-11 col-xs-offset-1 col-sm-5 col-sm-offset-4">
<pagination-controls (pageChange)="getPage($event)" id="transactions" previousLabel="" nextLabel="">
</pagination-controls>
</div>
</div>
</div>
</div>
</div>
</div>

35
web-ui/src/app/components/block-details/block-details.component.ts

@ -1,9 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { TranslateService } from '@ngx-translate/core';
import { Observable } from 'rxjs/Observable';
import { BlockDetails } from '../../models/block';
import { Transaction } from '../../models/transaction';
import { BlocksService } from '../../services/blocks.service';
import { ErrorService } from '../../services/error.service';
@ -16,8 +17,15 @@ import { NavigatorService } from '../../services/navigator.service';
})
export class BlockDetailsComponent implements OnInit {
blockhash: string;
blockDetails: BlockDetails;
// pagination
total = 0;
currentPage = 1;
pageSize = 10;
asyncItems: Observable<Transaction[]>;
constructor(
private route: ActivatedRoute,
private router: Router,
@ -30,17 +38,40 @@ export class BlockDetailsComponent implements OnInit {
}
private onBlockhash(blockhash: string) {
this.blockDetails = null;
this.clearCurrentValues();
this.blockhash = blockhash;
this.blocksService.get(blockhash).subscribe(
response => this.onBlockRetrieved(response),
response => this.onError(response)
);
this.getPage(this.currentPage);
}
private clearCurrentValues() {
this.blockhash = null;
this.blockDetails = null;
this.total = 0;
this.currentPage = 1;
this.pageSize = 10;
this.asyncItems = null;
}
private onBlockRetrieved(response: BlockDetails) {
this.blockDetails = response;
}
getPage(page: number) {
const offset = (page - 1) * this.pageSize;
const limit = this.pageSize;
const order = 'time:desc';
this.asyncItems = this.blocksService
.getTransactions(this.blockhash, offset, limit, order)
.do(response => this.total = response.total)
.do(response => this.currentPage = 1 + (response.offset / this.pageSize))
.map(response => response.data)
}
private onError(response: any) {
this.errorService.renderServerErrors(null, response);
}

7
web-ui/src/app/services/blocks.service.ts

@ -5,6 +5,8 @@ import { Observable } from 'rxjs/Observable';
import { environment } from '../../environments/environment';
import { Block, BlockDetails } from '../models/block';
import { PaginatedResult } from '../models/paginated-result';
import { Transaction } from '../models/transaction';
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
@ -27,6 +29,11 @@ export class BlocksService {
return this.http.get<any>(url);
}
getTransactions(hash: string, offset: number = 0, limit: number = 10, orderBy: string = ''): Observable<PaginatedResult<Transaction>> {
const url = `${this.baseUrl}/${hash}/transactions?offset=${offset}&limit=${limit}&orderBy=${orderBy}`;
return this.http.get<PaginatedResult<Transaction>>(url);
}
getLatest(): Observable<Block[]> {
return this.http.get<Block[]>(this.baseUrl);
}

Loading…
Cancel
Save