charting, routing and stats
This commit is contained in:
Generated
+30
@@ -17,6 +17,8 @@
|
|||||||
"@angular/platform-browser-dynamic": "15.1.5",
|
"@angular/platform-browser-dynamic": "15.1.5",
|
||||||
"@angular/router": "15.1.5",
|
"@angular/router": "15.1.5",
|
||||||
"chart.js": "^4.3.0",
|
"chart.js": "^4.3.0",
|
||||||
|
"chartjs-adapter-moment": "^1.0.1",
|
||||||
|
"moment": "^2.29.4",
|
||||||
"primeflex": "^3.3.0",
|
"primeflex": "^3.3.0",
|
||||||
"primeng": "15.2.0",
|
"primeng": "15.2.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
@@ -4797,6 +4799,15 @@
|
|||||||
"pnpm": ">=7"
|
"pnpm": ">=7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chartjs-adapter-moment": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": ">=3.0.0",
|
||||||
|
"moment": "^2.10.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
@@ -8573,6 +8584,14 @@
|
|||||||
"mkdirp": "bin/cmd.js"
|
"mkdirp": "bin/cmd.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/moment": {
|
||||||
|
"version": "2.29.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
@@ -15527,6 +15546,12 @@
|
|||||||
"@kurkle/color": "^0.3.0"
|
"@kurkle/color": "^0.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"chartjs-adapter-moment": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"chokidar": {
|
"chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
@@ -18417,6 +18442,11 @@
|
|||||||
"minimist": "^1.2.6"
|
"minimist": "^1.2.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"moment": {
|
||||||
|
"version": "2.29.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
|
||||||
|
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
|||||||
+3
-1
@@ -19,6 +19,8 @@
|
|||||||
"@angular/platform-browser-dynamic": "15.1.5",
|
"@angular/platform-browser-dynamic": "15.1.5",
|
||||||
"@angular/router": "15.1.5",
|
"@angular/router": "15.1.5",
|
||||||
"chart.js": "^4.3.0",
|
"chart.js": "^4.3.0",
|
||||||
|
"chartjs-adapter-moment": "^1.0.1",
|
||||||
|
"moment": "^2.29.4",
|
||||||
"primeflex": "^3.3.0",
|
"primeflex": "^3.3.0",
|
||||||
"primeng": "15.2.0",
|
"primeng": "15.2.0",
|
||||||
"rxjs": "~7.8.0",
|
"rxjs": "~7.8.0",
|
||||||
@@ -38,4 +40,4 @@
|
|||||||
"karma-jasmine-html-reporter": "~2.0.0",
|
"karma-jasmine-html-reporter": "~2.0.0",
|
||||||
"typescript": "~4.8.2"
|
"typescript": "~4.8.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router';
|
|||||||
|
|
||||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
|
import { DashboardComponent } from './components/dashboard/dashboard.component';
|
||||||
import { SplashComponent } from './components/splash/splash.component';
|
import { SplashComponent } from './components/splash/splash.component';
|
||||||
|
import { WorkerComponent } from './components/worker/worker.component';
|
||||||
import { AppLayoutComponent } from './layout/app.layout.component';
|
import { AppLayoutComponent } from './layout/app.layout.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
@@ -16,7 +17,16 @@ const routes: Routes = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: ':address',
|
path: ':address',
|
||||||
component: DashboardComponent
|
children: [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: DashboardComponent,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':workerId',
|
||||||
|
component: WorkerComponent
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'chartjs-adapter-moment';
|
||||||
|
|
||||||
import { CommonModule, HashLocationStrategy, LocationStrategy } from '@angular/common';
|
import { CommonModule, HashLocationStrategy, LocationStrategy } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||||
@@ -8,13 +10,19 @@ import { AppRoutingModule } from './app-routing.module';
|
|||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { DashboardComponent } from './components/dashboard/dashboard.component';
|
import { DashboardComponent } from './components/dashboard/dashboard.component';
|
||||||
import { SplashComponent } from './components/splash/splash.component';
|
import { SplashComponent } from './components/splash/splash.component';
|
||||||
|
import { WorkerComponent } from './components/worker/worker.component';
|
||||||
import { AppLayoutModule } from './layout/app.layout.module';
|
import { AppLayoutModule } from './layout/app.layout.module';
|
||||||
|
import { NumberSuffixPipe } from './pipes/number-suffix.pipe';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
SplashComponent,
|
SplashComponent,
|
||||||
DashboardComponent
|
DashboardComponent,
|
||||||
|
WorkerComponent,
|
||||||
|
NumberSuffixPipe
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
|||||||
@@ -1,5 +1,32 @@
|
|||||||
<div class="card">
|
<ng-container *ngIf="clientInfo$ | async as clientInfo">
|
||||||
<pre>
|
<div class="card">
|
||||||
{{clientInfo$ | async | json}}
|
|
||||||
</pre>
|
|
||||||
</div>
|
<p-table [rowHover]="true" [value]="clientInfo.workers" [tableStyle]="{ 'min-width': '50rem' }">
|
||||||
|
<ng-template pTemplate="header">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Hash Rate</th>
|
||||||
|
<th>Best Luck</th>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
<ng-template pTemplate="body" let-worker>
|
||||||
|
<tr [routerLink]="[worker.id]">
|
||||||
|
<td>{{worker.name}}</td>
|
||||||
|
<td>{{worker.id}}</td>
|
||||||
|
<td>{{worker.hashRate}}</td>
|
||||||
|
<td>{{worker.bestDifficulty}}</td>
|
||||||
|
</tr>
|
||||||
|
</ng-template>
|
||||||
|
</p-table>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</ng-container>
|
||||||
@@ -11,9 +11,13 @@ import { ClientService } from '../../services/client.service';
|
|||||||
})
|
})
|
||||||
export class DashboardComponent {
|
export class DashboardComponent {
|
||||||
|
|
||||||
|
|
||||||
public clientInfo$: Observable<any>;
|
public clientInfo$: Observable<any>;
|
||||||
|
|
||||||
constructor(private clientService: ClientService, private route: ActivatedRoute) {
|
constructor(private clientService: ClientService, private route: ActivatedRoute) {
|
||||||
this.clientInfo$ = this.clientService.getClientInfo(this.route.snapshot.params['address']);
|
this.clientInfo$ = this.clientService.getClientInfo(this.route.snapshot.params['address']);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<div class="grid">
|
||||||
|
<ng-container *ngIf="workerInfo$ | async as workerInfo">
|
||||||
|
<div class="col-12 lg:col-6 xl:col-3">
|
||||||
|
<div class="card mb-0">
|
||||||
|
<div class="flex justify-content-between mb-3">
|
||||||
|
<div>
|
||||||
|
<span class="block text-500 font-medium mb-3">ID</span>
|
||||||
|
<div class="text-900 font-medium text-xl">{{workerInfo.name}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex align-items-center justify-content-center bg-blue-100 border-round"
|
||||||
|
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||||
|
<i class="pi pi-id-card text-blue-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <span class="text-green-500 font-medium">24 new </span> -->
|
||||||
|
<span class="text-500">{{workerInfo.id}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 lg:col-6 xl:col-3">
|
||||||
|
<div class="card mb-0">
|
||||||
|
<div class="flex justify-content-between mb-3">
|
||||||
|
<div>
|
||||||
|
<span class="block text-500 font-medium mb-3">Best Luck</span>
|
||||||
|
<div class="text-900 font-medium text-xl">{{workerInfo.bestDifficulty | numberSuffix}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex align-items-center justify-content-center bg-orange-100 border-round"
|
||||||
|
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||||
|
<i class="pi pi-star text-orange-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-green-500 font-medium">{{workerInfo.bestDifficulty | number}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="col-12 lg:col-6 xl:col-3">
|
||||||
|
<div class="card mb-0">
|
||||||
|
<div class="flex justify-content-between mb-3">
|
||||||
|
<div>
|
||||||
|
<span class="block text-500 font-medium mb-3">Customers</span>
|
||||||
|
<div class="text-900 font-medium text-xl">28441</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex align-items-center justify-content-center bg-cyan-100 border-round"
|
||||||
|
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||||
|
<i class="pi pi-inbox text-cyan-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-green-500 font-medium">520 </span>
|
||||||
|
<span class="text-500">newly registered</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 lg:col-6 xl:col-3">
|
||||||
|
<div class="card mb-0">
|
||||||
|
<div class="flex justify-content-between mb-3">
|
||||||
|
<div>
|
||||||
|
<span class="block text-500 font-medium mb-3">Comments</span>
|
||||||
|
<div class="text-900 font-medium text-xl">152 Unread</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex align-items-center justify-content-center bg-purple-100 border-round"
|
||||||
|
[ngStyle]="{width: '2.5rem', height: '2.5rem'}">
|
||||||
|
<i class="pi pi-comment text-purple-500 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span class="text-green-500 font-medium">85 </span>
|
||||||
|
<span class="text-500">responded</span>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-12" *ngIf="chartData$ | async as chartData">
|
||||||
|
<div class="card">
|
||||||
|
<p-chart type="line" [data]="chartData" [options]="chartOptions"></p-chart>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { WorkerComponent } from './worker.component';
|
||||||
|
|
||||||
|
describe('WorkerComponent', () => {
|
||||||
|
let component: WorkerComponent;
|
||||||
|
let fixture: ComponentFixture<WorkerComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ WorkerComponent ]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(WorkerComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { ActivatedRoute } from '@angular/router';
|
||||||
|
import { map, Observable, shareReplay } from 'rxjs';
|
||||||
|
|
||||||
|
import { WorkerService } from '../../services/worker.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-worker',
|
||||||
|
templateUrl: './worker.component.html',
|
||||||
|
styleUrls: ['./worker.component.scss']
|
||||||
|
})
|
||||||
|
export class WorkerComponent {
|
||||||
|
|
||||||
|
public workerInfo$: Observable<any>;
|
||||||
|
|
||||||
|
public chartData$: Observable<any>;
|
||||||
|
|
||||||
|
public chartOptions: any;
|
||||||
|
|
||||||
|
constructor(private workerService: WorkerService, private route: ActivatedRoute) {
|
||||||
|
const documentStyle = getComputedStyle(document.documentElement);
|
||||||
|
const textColor = documentStyle.getPropertyValue('--text-color');
|
||||||
|
const textColorSecondary = documentStyle.getPropertyValue('--text-color-secondary');
|
||||||
|
const surfaceBorder = documentStyle.getPropertyValue('--surface-border');
|
||||||
|
|
||||||
|
this.workerInfo$ = this.workerService.getWorkerInfo(this.route.snapshot.params['address'], this.route.snapshot.params['workerId']).pipe(
|
||||||
|
shareReplay({ bufferSize: 1, refCount: true })
|
||||||
|
)
|
||||||
|
|
||||||
|
this.chartData$ = this.workerInfo$.pipe(
|
||||||
|
map((workerInfo: any) => {
|
||||||
|
|
||||||
|
const GROUP_SIZE = 20;
|
||||||
|
const slice = workerInfo.hashData.length % GROUP_SIZE == 0 ? workerInfo.hashData : workerInfo.hashData.slice(0, workerInfo.hashData.length - workerInfo.hashData.length % GROUP_SIZE)
|
||||||
|
const reducedData = ((slice as any[])
|
||||||
|
.reduce((pre, cur, idx, arr) => {
|
||||||
|
if (idx % GROUP_SIZE != 0) {
|
||||||
|
pre[pre.length - 1].difficulty += cur.difficulty;
|
||||||
|
} else {
|
||||||
|
pre.push(cur);
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
|
}, []) as any[]);
|
||||||
|
|
||||||
|
const labels = reducedData.reduce((pre, cur, idx, arr) => {
|
||||||
|
if (idx == 0) {
|
||||||
|
return pre;
|
||||||
|
}
|
||||||
|
pre.push(new Date(cur.time));
|
||||||
|
return pre;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const data = reducedData
|
||||||
|
.reduce((pre, cur, idx, arr) => {
|
||||||
|
if (idx == 0) {
|
||||||
|
return pre;
|
||||||
|
}
|
||||||
|
//hashrate = (nonce_difficulty * 2^32) / time_to_find
|
||||||
|
pre.push((cur.difficulty * 4294967296 / (new Date(cur.time).getTime() - new Date(arr[idx - 1].time).getTime())) / 1000000);
|
||||||
|
return pre;
|
||||||
|
|
||||||
|
}, []);
|
||||||
|
return {
|
||||||
|
labels,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: workerInfo.name,
|
||||||
|
data,
|
||||||
|
fill: false,
|
||||||
|
backgroundColor: documentStyle.getPropertyValue('--bluegray-700'),
|
||||||
|
borderColor: documentStyle.getPropertyValue('--bluegray-700'),
|
||||||
|
tension: .4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.chartOptions = {
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: textColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
type: 'time',
|
||||||
|
time: {
|
||||||
|
unit: 'minute', // Set the unit to 'minute'
|
||||||
|
stepSize: 10, // Set the desired interval between labels in minutes
|
||||||
|
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: textColorSecondary
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: surfaceBorder,
|
||||||
|
drawBorder: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
ticks: {
|
||||||
|
color: textColorSecondary,
|
||||||
|
callback: (value: number) => value + 'GH/s',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
color: surfaceBorder,
|
||||||
|
drawBorder: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<ul class="layout-menu">
|
<ul class="layout-menu">
|
||||||
<ng-container *ngFor="let item of model; let i = index;">
|
<ng-container *ngFor="let item of model$ | async; let i = index;">
|
||||||
<li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
|
<li app-menuitem *ngIf="!item.separator" [item]="item" [index]="i" [root]="true"></li>
|
||||||
<li *ngIf="item.separator" class="menu-separator"></li>
|
<li *ngIf="item.separator" class="menu-separator"></li>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { ActivatedRoute, ActivatedRouteSnapshot, NavigationEnd, Router } from '@angular/router';
|
||||||
|
import { filter, map, mergeMap, Observable, startWith } from 'rxjs';
|
||||||
|
|
||||||
import { LayoutService } from './service/app.layout.service';
|
import { LayoutService } from './service/app.layout.service';
|
||||||
|
|
||||||
@@ -8,19 +10,62 @@ import { LayoutService } from './service/app.layout.service';
|
|||||||
})
|
})
|
||||||
export class AppMenuComponent implements OnInit {
|
export class AppMenuComponent implements OnInit {
|
||||||
|
|
||||||
model: any[] = [];
|
private params$!: Observable<any>;
|
||||||
|
public model$!: Observable<any[]>;
|
||||||
|
|
||||||
constructor(public layoutService: LayoutService) { }
|
constructor(public layoutService: LayoutService, private router: Router, private activatedRoute: ActivatedRoute) { }
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.model = [
|
|
||||||
{
|
|
||||||
label: 'Home',
|
|
||||||
items: [
|
|
||||||
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: ['/'] }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
|
|
||||||
];
|
const rootRoute = this.activatedRoute.root;
|
||||||
|
const initialParams = this.extractRouteParams(rootRoute.snapshot);
|
||||||
|
|
||||||
|
this.params$ = this.router.events
|
||||||
|
.pipe(
|
||||||
|
filter((event) => event instanceof NavigationEnd),
|
||||||
|
map(() => this.activatedRoute),
|
||||||
|
map((route) => {
|
||||||
|
while (route.firstChild) {
|
||||||
|
route = route.firstChild;
|
||||||
|
}
|
||||||
|
return route;
|
||||||
|
}),
|
||||||
|
filter((route) => route.outlet === 'primary'), // Adjust the outlet name if necessary
|
||||||
|
mergeMap((route) => route.params),
|
||||||
|
startWith(initialParams)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.model$ = this.params$.pipe(map(params => {
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: 'Home',
|
||||||
|
items: [
|
||||||
|
{ label: 'Dashboard', icon: 'pi pi-fw pi-home', routerLink: [params.address] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
}))
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extractRouteParams(routeSnapshot: ActivatedRouteSnapshot): any {
|
||||||
|
let route = routeSnapshot;
|
||||||
|
let params = {};
|
||||||
|
|
||||||
|
while (route.firstChild) {
|
||||||
|
route = route.firstChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route && route.params) {
|
||||||
|
params = route.params;
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { NumberSuffixPipe } from './number-suffix.pipe';
|
||||||
|
|
||||||
|
describe('NumberSuffixPipe', () => {
|
||||||
|
it('create an instance', () => {
|
||||||
|
const pipe = new NumberSuffixPipe();
|
||||||
|
expect(pipe).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
|
@Pipe({
|
||||||
|
name: 'numberSuffix',
|
||||||
|
pure: true
|
||||||
|
})
|
||||||
|
export class NumberSuffixPipe implements PipeTransform {
|
||||||
|
|
||||||
|
transform(value: number): string {
|
||||||
|
|
||||||
|
const suffixes = ['', 'k', 'M', 'B', 'T', 'P', 'E'];
|
||||||
|
|
||||||
|
if (value === 0) {
|
||||||
|
return '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
const power = Math.floor(Math.log10(value) / 3);
|
||||||
|
const scaledValue = value / Math.pow(1000, power);
|
||||||
|
const suffix = suffixes[power];
|
||||||
|
|
||||||
|
return scaledValue.toFixed(1) + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { WorkerService } from './worker.service';
|
||||||
|
|
||||||
|
describe('WorkerService', () => {
|
||||||
|
let service: WorkerService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(WorkerService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class WorkerService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private httpClient: HttpClient
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public getWorkerInfo(address: string, workerId: string): Observable<any> {
|
||||||
|
return this.httpClient.get(`${environment.API_URL}/api/client/${address}/${workerId}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
height: 100%;
|
|
||||||
font-size: $scale;
|
font-size: $scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
API_URL: ''
|
API_URL: 'http://localhost:3334'
|
||||||
};
|
};
|
||||||
|
|||||||
+9
-1
@@ -1,4 +1,12 @@
|
|||||||
@import "assets/layout/styles/layout/layout.scss";
|
@import "assets/layout/styles/layout/layout.scss";
|
||||||
@import "../node_modules/primeng/resources/primeng.min.css";
|
@import "../node_modules/primeng/resources/primeng.min.css";
|
||||||
@import "../node_modules/primeicons/primeicons.css";
|
@import "../node_modules/primeicons/primeicons.css";
|
||||||
@import "../node_modules/primeflex/primeflex.css";
|
@import "../node_modules/primeflex/primeflex.css";
|
||||||
|
|
||||||
|
.p-datatable .p-datatable-thead>tr>th {
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.p-datatable.p-datatable-hoverable-rows .p-datatable-tbody>tr:not(.p-highlight) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user