From 7aeaf5510333f3b6640ef0e9b09324209e29dfc3 Mon Sep 17 00:00:00 2001
From: Artem Dychenko <s192441@student.pg.edu.pl>
Date: Mon, 3 Mar 2025 22:50:15 +0100
Subject: [PATCH] feat: add all API info on html, add time pipe, fix total_time
 type in mock

---
 src/app/api/dashboard.api.ts                  |  2 +-
 .../dashboard-statistics.component.html       | 37 +++++++-
 .../dashboard-statistics.component.scss       | 34 +++++++
 .../dashboard-statistics.component.ts         | 93 +++++++++++++++----
 .../mocks/dashboard-statistics.json           |  2 +-
 src/app/models/dashboard-statistics.ts        |  2 +-
 src/app/pipes/time.pipe.ts                    | 15 +++
 7 files changed, 161 insertions(+), 24 deletions(-)
 create mode 100644 src/app/pipes/time.pipe.ts

diff --git a/src/app/api/dashboard.api.ts b/src/app/api/dashboard.api.ts
index 498536d..c0378e5 100644
--- a/src/app/api/dashboard.api.ts
+++ b/src/app/api/dashboard.api.ts
@@ -17,7 +17,7 @@ export const DASHBOARD_API_URL = '/api/v1/dashboard/statistics';
 export class DashboardApi {
   private readonly httpClient = inject(HttpClient);
 
-  fetch(): Observable<DashboardStatistics> {
+  fetchStatistics(): Observable<DashboardStatistics> {
     return this.httpClient
       .get<ApiResponse<DashboardStatisticsDto>>(DASHBOARD_API_URL)
       .pipe(map(dto => dtoToDashboardStatistics(dto.data)));
diff --git a/src/app/components/dashboard-statistics/dashboard-statistics.component.html b/src/app/components/dashboard-statistics/dashboard-statistics.component.html
index c34d1b5..5a6fc81 100644
--- a/src/app/components/dashboard-statistics/dashboard-statistics.component.html
+++ b/src/app/components/dashboard-statistics/dashboard-statistics.component.html
@@ -1,5 +1,6 @@
 <div class="statistics">
-  @if (isLoading()) {
+  @let statistics = dashboardStatistics | async;
+  @if (!statistics) {
     <div class="statistics__loader">
       <div time class="statistics__total-time">
           <ngx-skeleton-loader count="7" />
@@ -24,21 +25,47 @@
   @else {
     <div class="statistics__content">
       <div time class="statistics__total-time">
-        <p>Total time {{ dashboardStatistic()?.total_time }} </p>
+        <p>Total time: {{ statistics.total_time | time }} </p>
       </div>
       <div eth class="statistics__eth">
         <div class="statistics__eth-content">
-<!--          <p>ETH Price: {{ statistics.ethPrice }} USD</p>-->
+          <ng-container *ngIf="getProtocolETH() | async as protocol">
+            {{ protocol.name }}
+            {{ protocol.total_packets }}
+            <ng-container *ngIf="getPacketsPerSecond(protocol.name) | async as packetsPerSecond">
+              {{ packetsPerSecond }}
+            </ng-container>
+            {{protocol.total_bytes}}
+            <ng-container *ngIf="getBytesPerSecond(protocol.name) | async as bytesPerSecond">
+              {{ bytesPerSecond }}
+            </ng-container>
+          </ng-container>
         </div>
       </div>
       <div protocols class="statistics__prot">
         <div class="statistics__prot-content">
-<!--          <p>Number of active protocols: {{ statistics.protocols }}</p>-->
+          <div *ngFor="let protocol of getProtocolOthers() | async">
+            {{ protocol.name }}
+            {{ protocol.total_packets }}
+            <ng-container *ngIf="getPacketsPerSecond(protocol.name) | async as packetsPerSecond">
+              {{ packetsPerSecond }}
+            </ng-container>
+            {{protocol.total_bytes}}
+            <ng-container *ngIf="getBytesPerSecond(protocol.name) | async as bytesPerSecond">
+              {{ bytesPerSecond }}
+            </ng-container>
+          </div>
         </div>
       </div>
       <div ir class="statistics__ir">
         <div class="statistics__ir-content">
-<!--        <p>Other data: {{ statistics.otherData }}</p>-->
+          <ng-container *ngIf="getIR() | async as information_rate">
+            Information Rate
+            {{information_rate.min}}
+            {{information_rate.max}}
+            {{information_rate.current}}
+            MB/s
+          </ng-container>
         </div>
       </div>
     </div>
diff --git a/src/app/components/dashboard-statistics/dashboard-statistics.component.scss b/src/app/components/dashboard-statistics/dashboard-statistics.component.scss
index e69de29..a52ca0a 100644
--- a/src/app/components/dashboard-statistics/dashboard-statistics.component.scss
+++ b/src/app/components/dashboard-statistics/dashboard-statistics.component.scss
@@ -0,0 +1,34 @@
+@use '../../../vars';
+@use 'sass:map';
+
+:host {
+  height: 100%;
+  width: 100%;
+
+  .statistics {
+      &content {
+        display: flex;
+        gap: map.get(vars.$spacing, 'xs');
+    }
+
+    &__eth {
+      display: flex;
+      justify-content: space-between;
+
+      &-title {
+        color: map.get(vars.$grey, 100);
+        font-size: map.get(vars.$text, 'lg');
+      }
+
+      &-content {
+        width: calc(100% - 200px);
+      }
+    }
+  }
+
+  // Configuration select
+  ::ng-deep .mdc-text-field--filled:not(.mdc-text-field--disabled) {
+    background-color: map.get(vars.$grey, 10);
+    border-radius: map.get(vars.$radius, 'xs');
+  }
+}
diff --git a/src/app/components/dashboard-statistics/dashboard-statistics.component.ts b/src/app/components/dashboard-statistics/dashboard-statistics.component.ts
index 56a83da..34ccbc3 100644
--- a/src/app/components/dashboard-statistics/dashboard-statistics.component.ts
+++ b/src/app/components/dashboard-statistics/dashboard-statistics.component.ts
@@ -6,15 +6,16 @@ import {
   signal,
 } from '@angular/core';
 import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
-import { NgTemplateOutlet } from '@angular/common';
+import { AsyncPipe, NgForOf, NgIf, NgTemplateOutlet } from '@angular/common';
 import { DashboardApi } from '../../api/dashboard.api';
-import { map, Observable, tap } from 'rxjs';
+import { interval, map, Observable, shareReplay, switchMap, tap } from 'rxjs';
 import { DashboardStatistics } from '../../models/dashboard-statistics';
 import { PillComponent } from '../pill/pill.component';
+import { TimePipe } from '../../pipes/time.pipe';
 
 @Component({
   selector: 'app-dashboard-statistics',
-  imports: [NgxSkeletonLoaderComponent, NgTemplateOutlet, PillComponent],
+  imports: [NgxSkeletonLoaderComponent, AsyncPipe, TimePipe, NgIf, NgForOf],
   templateUrl: './dashboard-statistics.component.html',
   styleUrl: './dashboard-statistics.component.scss',
 })
@@ -22,30 +23,90 @@ export class DashboardStatisticsComponent {
   dashboardApi = inject(DashboardApi);
 
   isLoading = signal<boolean>(false);
-  dashboardStatistic = signal<DashboardStatistics | undefined>(undefined);
+
+  dashboardStatistics = interval(1000).pipe(
+    switchMap(() => this.dashboardApi.fetchStatistics()),
+    map(dashboardStatistics => ({
+      ...dashboardStatistics,
+      total_time: new Date(dashboardStatistics.total_time * 1000),
+    })),
+    shareReplay(1)
+  );
 
   constructor() {
     effect(() => {
       this.isLoading.set(true);
 
-      this.dashboardApi.fetch().subscribe(data => {
+      this.dashboardApi.fetchStatistics().subscribe(data => {
         this.isLoading.set(false);
-        this.dashboardStatistic.set(data);
+        this.dashboardApi.fetchStatistics().subscribe(() => {
+          this.isLoading.set(false);
+        });
       });
     });
   }
 
-  private fetchConfigurationOptions(): Observable<DashboardStatistics> {
-    this.isLoading.set(true);
-    return this.dashboardApi.fetch().pipe(
+  getPacketsPerSecond(protocolName: string): Observable<number> {
+    return this.dashboardStatistics.pipe(
+      map(dashboardStatistics => {
+        const protocol = dashboardStatistics.protocols.find(
+          protocol => protocol.name === protocolName
+        );
+
+        if (!protocol) {
+          return 0;
+        }
+
+        const totalPackets = protocol.total_packets;
+        const totalSeconds = dashboardStatistics.total_time.getTime() / 1000;
+        return totalPackets / totalSeconds;
+      })
+    );
+  }
+
+  getBytesPerSecond(protocolName: string): Observable<number> {
+    return this.dashboardStatistics.pipe(
+      map(dashboardStatistics => {
+        const protocol = dashboardStatistics.protocols.find(
+          protocol => protocol.name === protocolName
+        );
+
+        if (!protocol) {
+          return 0;
+        }
+
+        const totalBytes = protocol.total_bytes;
+        const totalSeconds = dashboardStatistics.total_time.getTime() / 1000;
+        return totalBytes / totalSeconds;
+      })
+    );
+  }
+
+  getProtocolETH() {
+    return this.dashboardStatistics.pipe(
+      map(dashboardStatistics => {
+        return dashboardStatistics.protocols.find(
+          protocol => protocol.name === 'ETH'
+        );
+      })
+    );
+  }
+
+  getProtocolOthers() {
+    return this.dashboardStatistics.pipe(
+      map(dashboardStatistics => {
+        return dashboardStatistics.protocols.filter(
+          protocol => protocol.name !== 'ETH'
+        );
+      })
+    );
+  }
+
+  getIR() {
+    return this.dashboardStatistics.pipe(
       map(dashboardStatistics => {
-        return {
-          total_time: dashboardStatistics.total_time,
-          protocols: dashboardStatistics.protocols,
-          information_rate: dashboardStatistics.information_rate,
-        };
-      }),
-      tap(() => this.isLoading.set(false))
+        return dashboardStatistics.information_rate;
+      })
     );
   }
 }
diff --git a/src/app/interceptor/mocks/dashboard-statistics.json b/src/app/interceptor/mocks/dashboard-statistics.json
index 65334a5..f12cab4 100644
--- a/src/app/interceptor/mocks/dashboard-statistics.json
+++ b/src/app/interceptor/mocks/dashboard-statistics.json
@@ -1,7 +1,7 @@
 {
   "data": {
     "id": "statistics",
-    "total-time": "1740770761000",
+    "total-time": 160401,
     "protocols": [
         {
           "name": "ETH",
diff --git a/src/app/models/dashboard-statistics.ts b/src/app/models/dashboard-statistics.ts
index 8d07d36..2214b87 100644
--- a/src/app/models/dashboard-statistics.ts
+++ b/src/app/models/dashboard-statistics.ts
@@ -13,7 +13,7 @@ export interface InformationRate {
 }
 
 export interface DashboardStatisticsDto {
-  total_time: string;
+  total_time: number;
   protocols: ProtocolStatistics[];
   information_rate: InformationRate;
 }
diff --git a/src/app/pipes/time.pipe.ts b/src/app/pipes/time.pipe.ts
new file mode 100644
index 0000000..3213400
--- /dev/null
+++ b/src/app/pipes/time.pipe.ts
@@ -0,0 +1,15 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+  name: 'time',
+})
+export class TimePipe implements PipeTransform {
+  transform(date: Date): string {
+    let totalSeconds = Math.floor(date.getTime() / 1000);
+    const hours = Math.floor(totalSeconds / 3600);
+    totalSeconds %= 3600;
+    const minutes = Math.floor(totalSeconds / 60);
+    const seconds = totalSeconds % 60;
+    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
+  }
+}
-- 
GitLab