From 0cb8b029f64a4e0c6ede4926b4059636c5664740 Mon Sep 17 00:00:00 2001 From: Teofil Jolte Date: Wed, 20 Nov 2024 12:34:01 +0200 Subject: [PATCH] Dashboard redesign Redesign dashboard Redesigning - UX features Redesign Upgrade --- frontend/README.md | 12 +- frontend/httpd.conf | 5 +- frontend/package-lock.json | 12 +- frontend/package.json | 4 +- frontend/src/app/app.component.scss | 13 +- frontend/src/app/app.effects.ts | 2 +- ...-production-won-slots-epoch.component.html | 1 + ...-production-won-slots-epoch.component.scss | 0 ...ck-production-won-slots-epoch.component.ts | 46 ++++ .../block-production-won-slots.component.html | 5 +- .../block-production-won-slots.component.scss | 13 +- .../block-production-won-slots.component.ts | 1 + .../block-production-won-slots.effects.ts | 19 +- .../block-production-won-slots.module.ts | 2 + .../block-production-won-slots.reducer.ts | 4 +- .../block-production-won-slots.service.ts | 17 +- ...-production-won-slots-cards.component.html | 25 +- ...ck-production-won-slots-cards.component.ts | 39 ++- ...uction-won-slots-side-panel.component.html | 20 +- ...uction-won-slots-side-panel.component.scss | 15 +- ...oduction-won-slots-side-panel.component.ts | 4 +- ...-production-won-slots-table.component.html | 2 +- ...-production-won-slots-table.component.scss | 19 ++ ...ck-production-won-slots-table.component.ts | 10 +- .../dashboard-blocks-sync.component.html | 50 ++-- .../dashboard-blocks-sync.component.scss | 43 +-- .../dashboard-blocks-sync.component.ts | 4 +- .../dashboard-ledger.component.html | 18 +- .../dashboard-ledger.component.scss | 55 +--- .../dashboard-ledger.component.ts | 11 +- .../dashboard-network.component.html | 34 +-- .../dashboard-network.component.scss | 61 +---- .../dashboard-network.component.ts | 1 - ...shboard-peers-minimal-table.component.scss | 1 + .../dashboard/dashboard.component.html | 18 +- .../dashboard/dashboard.component.scss | 89 ++++++- .../features/dashboard/dashboard.component.ts | 108 +++++++- .../mina-card/mina-card.component.html | 10 - .../mina-card/mina-card.component.scss | 18 -- .../mina-card/mina-card.component.ts | 21 -- .../web-node-initialization.component.html | 18 +- .../web-node-initialization.component.scss | 4 - .../web-node-initialization.component.ts | 6 +- .../block-production-pill.component.scss | 23 +- .../layout/menu-tabs/menu-tabs.component.ts | 4 +- .../src/app/layout/menu/menu.component.scss | 1 - .../server-status.component.html | 6 +- .../server-status.component.scss | 2 +- .../server-status/server-status.component.ts | 3 +- .../src/app/layout/toolbar/loading.reducer.ts | 35 +-- .../app/layout/toolbar/toolbar.component.html | 10 +- .../app/layout/toolbar/toolbar.component.scss | 9 +- .../app/layout/toolbar/toolbar.component.ts | 5 +- .../mina-table-rust-wrapper.class.ts | 2 +- .../base-classes/mina-table-wrapper.class.ts | 33 +++ .../mina-card/mina-card.component.ts | 2 +- .../mina-table/mina-table.component.html | 57 ++++ .../mina-table/mina-table.component.scss | 251 ++++++++++++++++++ .../mina-table/mina-table.component.ts | 156 +++++++++++ frontend/src/assets/styles/openmina.scss | 176 +----------- frontend/src/environments/environment.ts | 56 ++-- frontend/src/index.html | 6 +- 62 files changed, 1059 insertions(+), 638 deletions(-) create mode 100644 frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.html create mode 100644 frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.scss create mode 100644 frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.ts delete mode 100644 frontend/src/app/features/dashboard/mina-card/mina-card.component.html delete mode 100644 frontend/src/app/features/dashboard/mina-card/mina-card.component.scss delete mode 100644 frontend/src/app/features/dashboard/mina-card/mina-card.component.ts create mode 100644 frontend/src/app/shared/base-classes/mina-table-wrapper.class.ts create mode 100644 frontend/src/app/shared/components/mina-table/mina-table.component.html create mode 100644 frontend/src/app/shared/components/mina-table/mina-table.component.scss create mode 100644 frontend/src/app/shared/components/mina-table/mina-table.component.ts diff --git a/frontend/README.md b/frontend/README.md index ded497ce4c..7bff8ff863 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -4,13 +4,13 @@ This is a simple Angular application that will help you to see the behaviour of ## Prerequisites -### 1. Node.js v20.11.1 +### 1. Node.js v23.1.0 #### MacOS ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" -brew install node@20.11.1 +brew install node@23.1.0 ``` #### Linux @@ -18,17 +18,17 @@ brew install node@20.11.1 ```bash curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash source ~/.bashrc -nvm install 20.11.1 +nvm install 23.1.0 ``` #### Windows -Download [Node.js v20.11.1](https://nodejs.org/) from the official website, open the installer and follow the prompts to complete the installation. +Download [Node.js v23.1.0](https://nodejs.org/) from the official website, open the installer and follow the prompts to complete the installation. -### 2. Angular CLI v16.2.0 +### 2. Angular CLI v17.3.0 ```bash -npm install -g @angular/cli@16.2.0 +npm install -g @angular/cli@17.3.0 ``` ### 3. Installation diff --git a/frontend/httpd.conf b/frontend/httpd.conf index 4112d112d5..168d4180cf 100644 --- a/frontend/httpd.conf +++ b/frontend/httpd.conf @@ -211,9 +211,8 @@ LoadModule rewrite_module modules/mod_rewrite.so RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d RewriteRule ^ - [L] - # If the requested resource doesn't exist, use index.html - RewriteCond %{REQUEST_URI} ^/index\.html$ - RewriteRule ^(.*)$ $1?bust=$RANDOM [L,QSA] + # Rewrite all other requests to index.html + RewriteRule ^(.*)$ /index.html [L] SSLProxyEngine On diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fe90e90537..f39bda174d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "frontend", - "version": "1.0.16", + "version": "1.0.20", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "frontend", - "version": "1.0.16", + "version": "1.0.20", "dependencies": { "@angular/animations": "^17.3.12", "@angular/cdk": "^17.3.10", @@ -27,7 +27,7 @@ "@ngrx/router-store": "^17.2.0", "@ngrx/store": "^17.2.0", "@nguniversal/express-engine": "^7.0.2", - "@openmina/shared": "^0.108.0", + "@openmina/shared": "^0.116.0", "@sentry/angular": "^8.35.0", "@sentry/cli": "^2.38.2", "@sentry/tracing": "^7.114.0", @@ -6204,9 +6204,9 @@ } }, "node_modules/@openmina/shared": { - "version": "0.108.0", - "resolved": "https://registry.npmjs.org/@openmina/shared/-/shared-0.108.0.tgz", - "integrity": "sha512-vHeP4wO2lfIATOBbO34h9+sigIeWqvioyh/XnBJSDauKEEanjAVof1zbppY/8EhVtEBHLVL2507PV7q8Tnwd2A==", + "version": "0.116.0", + "resolved": "https://registry.npmjs.org/@openmina/shared/-/shared-0.116.0.tgz", + "integrity": "sha512-58wxJEI7T+0vTeBp+X1hn2xq9Y95dm+FiD+z40OuAeIDMpILjnWuhaiMXXdjv/CMfZRC2D42I7OJIwDChmVtvA==", "license": "Apache License, Version 2.0", "dependencies": { "tslib": ">=2.3.0" diff --git a/frontend/package.json b/frontend/package.json index 0aa1340037..269d361c14 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "1.0.20", + "version": "1.0.21", "scripts": { "install:deps": "npm install", "start": "npm install && ng serve --configuration local --open", @@ -42,7 +42,7 @@ "@ngrx/router-store": "^17.2.0", "@ngrx/store": "^17.2.0", "@nguniversal/express-engine": "^7.0.2", - "@openmina/shared": "^0.108.0", + "@openmina/shared": "^0.116.0", "@sentry/angular": "^8.35.0", "@sentry/cli": "^2.38.2", "@sentry/tracing": "^7.114.0", diff --git a/frontend/src/app/app.component.scss b/frontend/src/app/app.component.scss index 994482b668..58810077a6 100644 --- a/frontend/src/app/app.component.scss +++ b/frontend/src/app/app.component.scss @@ -2,7 +2,7 @@ mat-sidenav { width: 160px; - border-right: 1px solid $base-divider; + border-right: none; background-color: $base-background; transition: 200ms ease-out !important; visibility: visible !important; @@ -55,7 +55,7 @@ mat-sidenav-content { color: inherit; background-color: $base-surface; - @media (max-width: 699px) { + @media (max-width: 767px) { background-color: $base-background; } } @@ -63,16 +63,23 @@ mat-sidenav-content { .mina-content { $toolbar: 40px; height: calc(100% - #{$toolbar}); + border-top-left-radius: 6px; + overflow: hidden; + background-color: $base-surface; &.no-toolbar { height: 100%; } &.mobile { - $toolbar: 56px; + $toolbar: 96px; $subMenus: 56px; $tabs: 56px; height: calc(100% - #{$toolbar} - #{$subMenus} - #{$tabs}); + margin-left: 4px; + margin-right: 4px; + margin-bottom: 4px; + border-top-right-radius: 6px; &.no-toolbar { height: calc(100% - #{$subMenus} - #{$tabs}); diff --git a/frontend/src/app/app.effects.ts b/frontend/src/app/app.effects.ts index e5b86fc7e3..fac206adf8 100644 --- a/frontend/src/app/app.effects.ts +++ b/frontend/src/app/app.effects.ts @@ -89,7 +89,6 @@ export class AppEffects extends BaseEffect { filter(() => !this.requestInProgress), tap(() => this.requestInProgress = true), switchMap(() => this.appService.getActiveNodeDetails()), - tap(() => this.requestInProgress = false), map(details => AppActions.getNodeDetailsSuccess({ details })), catchErrorAndRepeat2(MinaErrorType.GENERIC, AppActions.getNodeDetailsSuccess({ details: { @@ -106,6 +105,7 @@ export class AppEffects extends BaseEffect { producingBlockStatus: null, }, })), + tap(() => this.requestInProgress = false), )); } } diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.html b/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.html new file mode 100644 index 0000000000..9b82e4dbad --- /dev/null +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.html @@ -0,0 +1 @@ +{{ epoch }} diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.scss b/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.ts new file mode 100644 index 0000000000..d51a76a631 --- /dev/null +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component.ts @@ -0,0 +1,46 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; +import { BlockProductionWonSlotsSelectors } from '@block-production/won-slots/block-production-won-slots.state'; +import { filter } from 'rxjs'; +import { BlockProductionWonSlotsSlot } from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type'; +import { BlockProductionWonSlotsEpoch } from '@shared/types/block-production/won-slots/block-production-won-slots-epoch.type'; +import { ONE_BILLION, ONE_THOUSAND } from '@openmina/shared'; +import { getTimeDiff } from '@shared/helpers/date.helper'; + +@Component({ + selector: 'mina-block-production-won-slots-epoch', + templateUrl: './block-production-won-slots-epoch.component.html', + styleUrl: './block-production-won-slots-epoch.component.scss', + changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'h-lg pl-12 f-600 fx-row-vert-cent border-bottom' }, +}) +export class BlockProductionWonSlotsEpochComponent extends StoreDispatcher implements OnInit { + epoch: string; + startedAgo: string; + + ngOnInit(): void { + this.listenToEpoch(); + this.listenToEpoch2(); + } + + private listenToEpoch(): void { + this.select(BlockProductionWonSlotsSelectors.slots, (slots: BlockProductionWonSlotsSlot[]) => { + this.epoch = 'Epoch ' + slots[0].epoch; + this.detect(); + }, filter(slots => slots.length > 0)); + } + + private listenToEpoch2(): void { + this.select(BlockProductionWonSlotsSelectors.epoch, (epoch: BlockProductionWonSlotsEpoch) => { + const epochStartTime = this.addMinutesToTimestamp(Math.floor(epoch.currentTime / ONE_BILLION), -(epoch.currentGlobalSlot - epoch.start) * 3); + this.startedAgo = getTimeDiff(Math.floor(epochStartTime * ONE_THOUSAND)).diff; + + this.detect(); + }, filter(Boolean)); + } + + private addMinutesToTimestamp(timestampInSeconds: number, minutesToAdd: number): number { + const secondsToAdd = minutesToAdd * 60; + return timestampInSeconds + secondsToAdd; + } +} diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html index 0f2bc3efd6..182a58a2e0 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.html @@ -8,7 +8,10 @@
- + + @if (isDesktop) { + + }
diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss index 76009ed69f..c1d2d3ea65 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.scss @@ -1,3 +1,14 @@ -mina-block-production-won-slots-cards + div { +@media (max-width: 767px) { + :host ::ng-deep mina-horizontal-resizable-container aside { + right: unset !important; + transition: left .4s cubic-bezier(.22, 1, .36, 1) !important; + &.in-view { + left: 0; + } + + &:not(.in-view) { + left: -100%; + } + } } diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts index 666a02d44d..81d78072d0 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.component.ts @@ -16,6 +16,7 @@ import { BlockProductionWonSlotsSelectors } from '@block-production/won-slots/bl export class BlockProductionWonSlotsComponent extends StoreDispatcher implements OnInit, OnDestroy { showSidePanel: boolean = isDesktop(); + isDesktop: boolean = isDesktop(); constructor(protected el: ElementRef) { super(); } diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts index 0cd95a3eab..bbccec2c35 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.effects.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { MinaState, selectMinaState } from '@app/app.setup'; import { Actions, createEffect, ofType } from '@ngrx/effects'; -import { Effect, isDesktop } from '@openmina/shared'; +import { Effect, isDesktop, isMobile } from '@openmina/shared'; import { EMPTY, map, switchMap } from 'rxjs'; import { catchErrorAndRepeat2 } from '@shared/constants/store-functions'; import { MinaErrorType } from '@shared/types/error-preview/mina-error-type.enum'; @@ -9,13 +9,8 @@ import { Store } from '@ngrx/store'; import { BaseEffect } from '@shared/base-classes/mina-rust-base.effect'; import { BlockProductionModule } from '@block-production/block-production.module'; import { BlockProductionWonSlotsService } from '@block-production/won-slots/block-production-won-slots.service'; -import { - BLOCK_PRODUCTION_WON_SLOTS_KEY, - BlockProductionWonSlotsActions, -} from '@block-production/won-slots/block-production-won-slots.actions'; -import { - BlockProductionWonSlotsStatus, -} from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type'; +import { BLOCK_PRODUCTION_WON_SLOTS_KEY, BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions'; +import { BlockProductionWonSlotsStatus } from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type'; import { Router } from '@angular/router'; import { Routes } from '@shared/enums/routes.enum'; import { fromPromise } from 'rxjs/internal/observable/innerFrom'; @@ -51,11 +46,15 @@ export class BlockProductionWonSlotsEffects extends BaseEffect { const bpState = state.blockProduction[BLOCK_PRODUCTION_WON_SLOTS_KEY]; const activeSlotRoute = bpState.activeSlotRoute; let newActiveSlot = slots.find(s => s.globalSlot.toString() === activeSlotRoute); - if (!activeSlotRoute || (activeSlotRoute && !newActiveSlot)) { + if ( + (isDesktop() && !activeSlotRoute) + || (activeSlotRoute && !newActiveSlot) + || (isMobile() && !activeSlotRoute && bpState.slots.length === 0) + ) { newActiveSlot = slots.find(s => s.active) ?? slots.find(s => s.status === BlockProductionWonSlotsStatus.Committed) ?? slots.find(s => s.status === BlockProductionWonSlotsStatus.Scheduled) - ?? null; + ?? slots.find(s => !s.status); } const routes: string[] = [Routes.BLOCK_PRODUCTION, Routes.WON_SLOTS]; if ( diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts index df36ef612c..ce1b55df9c 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.module.ts @@ -20,6 +20,7 @@ import { BlockProductionWonSlotsCardsComponent, } from '@block-production/won-slots/cards/block-production-won-slots-cards.component'; import { SharedModule } from '@shared/shared.module'; +import { BlockProductionWonSlotsEpochComponent } from '@block-production/won-slots/block-production-won-slots-epoch/block-production-won-slots-epoch.component'; @NgModule({ @@ -29,6 +30,7 @@ import { SharedModule } from '@shared/shared.module'; BlockProductionWonSlotsSidePanelComponent, BlockProductionWonSlotsFiltersComponent, BlockProductionWonSlotsCardsComponent, + BlockProductionWonSlotsEpochComponent, ], imports: [ CommonModule, diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts index 63fd852440..d18bc61073 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.reducer.ts @@ -59,8 +59,8 @@ export const blockProductionWonSlotsReducer = createReducer( on(BlockProductionWonSlotsActions.toggleSidePanel, state => ({ ...state, openSidePanel: !state.openSidePanel, - activeSlot: !state.openSidePanel ? state.activeSlot : undefined, - activeSlotRoute: !state.openSidePanel ? state.activeSlotRoute : undefined, + activeSlot: state.openSidePanel ? undefined : state.activeSlot, + activeSlotRoute: state.openSidePanel ? undefined : state.activeSlotRoute, })), on(BlockProductionWonSlotsActions.close, () => initialState), ); diff --git a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts index 3fd23cc33b..f99d53354d 100644 --- a/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts +++ b/frontend/src/app/features/block-production/won-slots/block-production-won-slots.service.ts @@ -5,13 +5,10 @@ import { BlockProductionWonSlotsSlot, BlockProductionWonSlotsStatus, } from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type'; -import { BlockProductionModule } from '@block-production/block-production.module'; -import { hasValue, nanOrElse, ONE_BILLION, ONE_MILLION } from '@openmina/shared'; +import { hasValue, isDesktop, nanOrElse, ONE_BILLION, ONE_MILLION } from '@openmina/shared'; import { getTimeDiff } from '@shared/helpers/date.helper'; import { RustService } from '@core/services/rust.service'; -import { - BlockProductionWonSlotsEpoch, -} from '@shared/types/block-production/won-slots/block-production-won-slots-epoch.type'; +import { BlockProductionWonSlotsEpoch } from '@shared/types/block-production/won-slots/block-production-won-slots-epoch.type'; @Injectable({ providedIn: 'root', @@ -90,7 +87,7 @@ export class BlockProductionWonSlotsService { const futureWonSlots = response.future_won_slots.map((slot: WonSlot) => { slot.slot_time = Math.floor(slot.slot_time / ONE_MILLION); return { - message: 'Upcoming Won Slot', + message: this.getMessage({ won_slot: slot } as Attempt), age: this.calculateTimeAgo({ won_slot: slot }), slotTime: slot.slot_time, globalSlot: slot.global_slot, @@ -120,10 +117,10 @@ export class BlockProductionWonSlotsService { private getMessage(attempt: Attempt): string { if (attempt.active) { - return 'Produced'; + return 'Producing'; } if (attempt.status === BlockProductionWonSlotsStatus.Scheduled) { - return 'Production Scheduled'; + return (isDesktop() ? 'Production ' : '') + 'Scheduled'; } else if (attempt.status === BlockProductionWonSlotsStatus.Canonical) { return 'Produced Block'; } else if (attempt.status === BlockProductionWonSlotsStatus.Orphaned) { @@ -131,9 +128,9 @@ export class BlockProductionWonSlotsService { } else if (attempt.status === BlockProductionWonSlotsStatus.Discarded) { return BlockProductionWonSlotsStatus.Discarded + ' Block'; } else if (attempt.status === BlockProductionWonSlotsStatus.Committed) { - return 'Waiting for Confirmation'; + return isDesktop() ? 'Waiting for Confirmation' : 'Confirming'; } - return 'Upcoming Won Slot'; + return (isDesktop() ? 'Upcoming ' : '') + 'Won Slot'; } private calculateTimeAgo({ active, won_slot }: { active?: boolean; won_slot: WonSlot }): string { diff --git a/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html b/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html index 37d961d2b3..0e8049dc34 100644 --- a/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html +++ b/frontend/src/app/features/block-production/won-slots/cards/block-production-won-slots-cards.component.html @@ -2,37 +2,30 @@
- { - const epochStartTime = this.addMinutesToTimestamp(Math.floor(epoch.currentTime / ONE_BILLION), -(epoch.currentGlobalSlot - epoch.start) * 3); - this.card1.startedAgo = getTimeDiff(Math.floor(epochStartTime * ONE_THOUSAND)).diff; - const epochEndTime = this.addMinutesToTimestamp(epoch.currentTime / ONE_BILLION, (epoch.end - epoch.currentGlobalSlot) * 3); - this.card5.endIn = getTimeDiff(epochEndTime * ONE_THOUSAND).diff; - this.card5.epochProgress = Math.floor((epoch.currentGlobalSlot - epoch.start) / (epoch.end - epoch.start) * 100) + '%'; + this.card4.endIn = getTimeDiff(epochEndTime * ONE_THOUSAND).diff; + this.card4.epochProgress = Math.floor((epoch.currentGlobalSlot - epoch.start) / (epoch.end - epoch.start) * 100) + '%'; this.detect(); }, filter(Boolean)); @@ -49,29 +45,28 @@ export class BlockProductionWonSlotsCardsComponent extends StoreDispatcher imple private listenToSlots(): void { this.select(BlockProductionWonSlotsSelectors.slots, (slots: BlockProductionWonSlotsSlot[]) => { - this.card1.epoch = slots[0].epoch; const nextSlot = slots.find(s => s.status === BlockProductionWonSlotsStatus.Scheduled || !s.status); if (nextSlot) { - this.card2.nextWonSlot = getTimeDiff(nextSlot.slotTime).diff; - this.card2.slot = nextSlot.globalSlot; + this.card1.nextWonSlot = getTimeDiff(nextSlot.slotTime).diff; + this.card1.slot = nextSlot.globalSlot; } else { - this.card2.nextWonSlot = 'Now'; - this.card2.slot = slots.find(s => s.active)?.globalSlot; + this.card1.nextWonSlot = 'Now'; + this.card1.slot = slots.find(s => s.active)?.globalSlot; } - this.card3.wonSlots = slots.length; - this.card3.slotsUsed = slots.filter( + this.card2.wonSlots = slots.length; + this.card2.slotsUsed = slots.filter( s => [BlockProductionWonSlotsStatus.Canonical, BlockProductionWonSlotsStatus.Orphaned, BlockProductionWonSlotsStatus.Discarded].includes(s.status), ).length; - this.card4.acceptedBlocks = slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical).length; - this.card4.lastBlockTime = getTimeDiff(lastItem(slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical))?.slotTime).diff; + this.card3.acceptedBlocks = slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical).length; + this.card3.lastBlockTime = getTimeDiff(lastItem(slots.filter(s => s.status === BlockProductionWonSlotsStatus.Canonical))?.slotTime).diff; - this.card6.totalRewards = slots + this.card5.totalRewards = slots .filter(s => [BlockProductionWonSlotsStatus.Canonical].includes(s.status)) .map(s => s.coinbaseRewards + s.txFeesRewards).reduce((a, b) => a + b, 0).toFixed(0); - this.card6.totalRewards = isNaN(+this.card6.totalRewards) ? '0' : this.card6.totalRewards; + this.card5.totalRewards = isNaN(+this.card5.totalRewards) ? '0' : this.card5.totalRewards; this.detect(); }, filter(slots => slots.length > 0)); } diff --git a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html index 4c48efdc23..dc6ca90815 100644 --- a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html +++ b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.html @@ -1,13 +1,19 @@ -
- {{ title }} +
+
+ @if (isMobile) { +
+
+ arrow_back +
+
+ } + {{ title }} +
@if (percentage !== null && percentage !== undefined) {
{{ percentage }}%
} - @if (isMobile) { - close - }
@@ -15,7 +21,7 @@
-
+
Global slot diff --git a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss index d2b6dceb15..bb189a824f 100644 --- a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss +++ b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.scss @@ -1,10 +1,23 @@ @import 'openmina'; +$blue: #57d7ff; +$pink: #fda2ff; +$orange: #ff833d; + +:host { + //linear-gradient(64deg, rgba(87, 215, 255, 0.15) 10%, rgba(253, 162, 255, 0.15) 60%, rgba(255, 131, 61, 0.15) 82%) + background: linear-gradient(70deg, rgba($blue, .15) 5%, rgba($pink, .15) 45%, rgba($orange, .15) 90%); +} + +.top { + border-bottom: 2px solid transparent; +} + .progress-bar { overflow: hidden; height: 6px; left: 0; - bottom: 0; + bottom: -3px; transition: width 0.4s ease-in-out; min-width: 20px; diff --git a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts index de74fe2848..486440ef00 100644 --- a/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts +++ b/frontend/src/app/features/block-production/won-slots/side-panel/block-production-won-slots-side-panel.component.ts @@ -7,7 +7,7 @@ import { BlockProductionWonSlotTimes, } from '@shared/types/block-production/won-slots/block-production-won-slots-slot.type'; import { getTimeDiff } from '@shared/helpers/date.helper'; -import { any, hasValue, isMobile, noMillisFormat, ONE_THOUSAND, safelyExecuteInBrowser, SecDurationConfig, toReadableDate } from '@openmina/shared'; +import { any, hasValue, isDesktop, isMobile, noMillisFormat, ONE_THOUSAND, safelyExecuteInBrowser, SecDurationConfig, toReadableDate } from '@openmina/shared'; import { filter } from 'rxjs'; import { BlockProductionWonSlotsActions } from '@block-production/won-slots/block-production-won-slots.actions'; import { AppSelectors } from '@app/app.state'; @@ -20,6 +20,7 @@ import { Routes } from '@shared/enums/routes.enum'; templateUrl: './block-production-won-slots-side-panel.component.html', styleUrls: ['./block-production-won-slots-side-panel.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + host: { class: 'flex-column h-100' }, }) export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher implements OnInit, OnDestroy { @@ -194,4 +195,5 @@ export class BlockProductionWonSlotsSidePanelComponent extends StoreDispatcher i super.ngOnDestroy(); clearInterval(this.timer); } + } diff --git a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html index 3e42122963..1c4d6d44ca 100644 --- a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html +++ b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.html @@ -6,7 +6,7 @@ *ngIf="row.active || row.status === BlockProductionWonSlotsStatus.Committed; else icon"> schedule + class="schedule mina-icon icon-200 f-20">schedule circle diff --git a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss index 25d6f79317..f8fc4575fc 100644 --- a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss +++ b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.scss @@ -1,5 +1,9 @@ @import 'openmina'; +$blue: #57d7ff; +$pink: #fda2ff; +$orange: #ff833d; + .mina-icon.f-big { font-variation-settings: 'FILL' 1, 'wght' 400 !important; } @@ -36,6 +40,21 @@ border-color: $selected-primary !important; border-top-color: transparent !important; } + + .schedule, + .Scheduled, + .Producing { + background: linear-gradient(5deg, $blue 30%, $pink 55%, $orange 100%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; + } + + .schedule { + @media (max-width: 767px) { + font-size: 16px !important; + } + } } .fx-row-vert-cent { diff --git a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts index 0334986ccb..4f23b56c9c 100644 --- a/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts +++ b/frontend/src/app/features/block-production/won-slots/table/block-production-won-slots-table.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectionStrategy, Component, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { MinaTableRustWrapper } from '@shared/base-classes/mina-table-rust-wrapper.class'; -import { getMergedRoute, MergedRoute, SecDurationConfig, TableColumnList } from '@openmina/shared'; +import { getMergedRoute, isDesktop, MergedRoute, SecDurationConfig, TableColumnList } from '@openmina/shared'; import { Router } from '@angular/router'; import { SnarksWorkPoolToggleSidePanel } from '@snarks/work-pool/snarks-work-pool.actions'; import { filter, take } from 'rxjs'; @@ -57,7 +57,7 @@ export class BlockProductionWonSlotsTableComponent extends MinaTableRustWrapper< } protected override setupTable(): void { - this.table.gridTemplateColumns = [210, 140, 90, 140, 120, 120, 120, 120, 150, 150]; + this.table.gridTemplateColumns = isDesktop() ? [210, 140, 90, 142, 120, 120, 120, 124, 159, 150] : [150, 100, 90, 130, 100, 105, 100, 114, 139, 130]; this.table.propertyForActiveCheck = 'globalSlot'; this.table.thGroupsTemplate = this.thGroupsTemplate; this.table.sortAction = BlockProductionWonSlotsActions.sort; @@ -96,7 +96,7 @@ export class BlockProductionWonSlotsTableComponent extends MinaTableRustWrapper< this.table.activeRow = slot; this.table.detect(); this.detect(); - }, filter(Boolean)); + }); } private scrollToElement(): void { @@ -107,11 +107,11 @@ export class BlockProductionWonSlotsTableComponent extends MinaTableRustWrapper< this.onRowClick(this.table.rows[i]); } - protected override onRowClick(slot: BlockProductionWonSlotsSlot): void { + protected override onRowClick(slot: BlockProductionWonSlotsSlot, isRealClick?: boolean): void { if (!slot) { return; } - if (this.table.activeRow?.globalSlot !== slot?.globalSlot) { + if (this.table.activeRow?.globalSlot !== slot?.globalSlot || isRealClick) { this.dispatch2(BlockProductionWonSlotsActions.setActiveSlot({ slot })); this.router.navigate([Routes.BLOCK_PRODUCTION, Routes.WON_SLOTS, slot.globalSlot], { queryParamsHandling: 'merge' }); } diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html index 46037f53df..b8ec53bcfe 100644 --- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html +++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.html @@ -1,38 +1,46 @@ -
-
-
Last 290 blocks
-
{{ syncProgress }}
-
-
-
+
+
+
+ @if (appliedPercentage === 100) { + check_circle + } @else { + + } +
Last 290 blocks
+
{{ syncProgress }}
-
+
- + @if (isDesktop) { + + } - + @if (isDesktop) { + + }
diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss index 51d9324e02..c5bb8daa6e 100644 --- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss +++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.scss @@ -1,42 +1,9 @@ -@import 'openmina'; - -.progress-bar { - overflow: hidden; - height: 6px; - left: 0; - bottom: 0; - transition: width 0.6s ease-in-out; - min-width: 20px; - - .highlight { - height: 4px; - top: 1px; - left: -50px; - width: 50px; - background: radial-gradient($success-primary, $success-primary, $success-primary, $success-primary, transparent, transparent); - border-radius: 20px; - animation: move 9s linear infinite; - } - - .progress { - height: 2px; - background-color: $success-primary; - top: 2px; - left: 0; - } +.mina-icon { + font-variation-settings: 'FILL' 1, 'wght' 300 !important; } -@keyframes move { - 0% { - left: -50px; - } - 10% { - left: -50px; - } - 80% { - left: 100%; - } - 100% { - left: 100%; +@media (max-width: 767px) { + .mobile mina-card { + width: 33%; } } diff --git a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts index bd29e3cb04..4caaf5090d 100644 --- a/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-blocks-sync/dashboard-blocks-sync.component.ts @@ -2,9 +2,8 @@ import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; import { selectDashboardNodesAndPeers } from '@dashboard/dashboard.state'; import { NodesOverviewNode } from '@shared/types/nodes/dashboard/nodes-overview-node.type'; -import { filter } from 'rxjs'; import { NodesOverviewNodeBlockStatus } from '@shared/types/nodes/dashboard/nodes-overview-block.type'; -import { lastItem, ONE_MILLION } from '@openmina/shared'; +import { isDesktop, lastItem, ONE_MILLION } from '@openmina/shared'; import { DashboardPeer } from '@shared/types/dashboard/dashboard.peer'; const PENDING = 'Pending'; @@ -28,6 +27,7 @@ export class DashboardBlocksSyncComponent extends StoreDispatcher implements OnI bestTipBlockSyncedText: string = PENDING; targetBlock: number; syncProgress: string; + isDesktop: boolean = isDesktop(); ngOnInit(): void { this.listenToNodesChanges(); diff --git a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html index def4089c6b..193765b65f 100644 --- a/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html +++ b/frontend/src/app/features/dashboard/dashboard-ledger/dashboard-ledger.component.html @@ -1,12 +1,14 @@ -
-
-
Ledgers
-
{{ progress }}
-
-
-
+
+
+
+ @if (totalProgress === 100) { + check_circle + } @else { + + } +
Ledgers
+
{{ progress }}
- - Network -
-
-
-
Peers
-
-
-
-
+
+
+ @if (stats.connected) { + check_circle + } @else { + + } +
Peers
-
+
-
+
- - - account_tree - - - - diff --git a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss index 818da2c61d..6e1afba0ea 100644 --- a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss +++ b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.scss @@ -1,60 +1,3 @@ -@import 'openmina'; - -:host { - max-width: 384px; -} - -.loading-icon { - animation: spin 2s linear infinite; -} - -@keyframes spin { - 0% { - transform: rotate(0); - } - 100% { - transform: rotate(360deg); - } -} - - -.progress-bar { - overflow: hidden; - height: 6px; - left: 0; - bottom: 0; - transition: width 0.6s ease-in-out; - min-width: 20px; - - .highlight { - height: 4px; - top: 1px; - left: -50px; - width: 50px; - background: radial-gradient($success-primary, $success-primary, $success-primary, $success-primary, transparent, transparent); - border-radius: 20px; - animation: move 9s linear infinite; - } - - .progress { - height: 2px; - background-color: $success-primary; - top: 2px; - left: 0; - } -} - -@keyframes move { - 0% { - left: -50px; - } - 10% { - left: -50px; - } - 80% { - left: 100%; - } - 100% { - left: 100%; - } +.mina-icon { + font-variation-settings: 'FILL' 1, 'wght' 300 !important; } diff --git a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts index 3c8b63e204..ff47a7e17f 100644 --- a/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts +++ b/frontend/src/app/features/dashboard/dashboard-network/dashboard-network.component.ts @@ -3,7 +3,6 @@ import { DashboardPeersStats } from '@shared/types/dashboard/dashboard-peers-sta import { selectDashboardPeersStats } from '@dashboard/dashboard.state'; import { skip } from 'rxjs'; import { StoreDispatcher } from '@shared/base-classes/store-dispatcher.class'; -import { NodesOverviewLedgerStepState } from '@shared/types/nodes/dashboard/nodes-overview-ledger.type'; @Component({ selector: 'mina-dashboard-network', diff --git a/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss b/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss index bb3787a918..53e4ca2058 100644 --- a/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss +++ b/frontend/src/app/features/dashboard/dashboard-peers-minimal-table/dashboard-peers-minimal-table.component.scss @@ -14,6 +14,7 @@ :host ::ng-deep cdk-virtual-scroll-viewport { overflow-y: auto !important; + border-bottom: none !important; } .fx-row-vert-cent { diff --git a/frontend/src/app/features/dashboard/dashboard.component.html b/frontend/src/app/features/dashboard/dashboard.component.html index 97e54aa457..7e2a718d84 100644 --- a/frontend/src/app/features/dashboard/dashboard.component.html +++ b/frontend/src/app/features/dashboard/dashboard.component.html @@ -1,4 +1,18 @@ -
+
+
+ @if (connectedProgress + ledgerProgress + blockSyncProgress < 100) { +
+ } +
+
+
+ {{ updateAction }}{{ updateDetail }} +
+
+
- + +
diff --git a/frontend/src/app/features/dashboard/dashboard.component.scss b/frontend/src/app/features/dashboard/dashboard.component.scss index cd151fd9f7..ceee98e524 100644 --- a/frontend/src/app/features/dashboard/dashboard.component.scss +++ b/frontend/src/app/features/dashboard/dashboard.component.scss @@ -4,6 +4,93 @@ background-color: $base-background; } -div { +.comps { gap: 8px; + padding-right: 8px; + + @media (max-width: 767px) { + padding: 0 8px; + } +} + +mina-dashboard-network { + max-width: 384px; +} + +mina-dashboard-ledger { + width: 100%; + max-width: 384px; + min-width: 250px; +} + +mina-dashboard-blocks-sync { + width: 100%; + max-width: 640px; +} + +@media (max-width: 767px) { + mina-dashboard-network, + mina-dashboard-ledger, + mina-dashboard-blocks-sync { + max-width: 100%; + } +} + +.header { + transition: height .6s ease-in, opacity .4s .4s ease-in; + + &.hide { + height: 0 !important; + opacity: 0; + } + + @media (max-width: 767px) { + &.hide { + height: 8px !important; + } + .primary { + padding-left: 8px; + } + } +} + +.progress-bar { + overflow: hidden; + height: 4px; + left: 0; + top: -2px; + transition: width 0.6s ease-in-out; + min-width: 20px; + + .highlight { + height: 4px; + top: 1px; + left: -50px; + width: 50px; + background: radial-gradient($success-primary, $success-primary, $success-primary, $success-primary, transparent, transparent); + border-radius: 20px; + animation: move 9s linear infinite; + } + + .progress { + height: 2px; + background-color: $success-primary; + top: 2px; + left: 0; + } +} + +@keyframes move { + 0% { + left: -50px; + } + 10% { + left: -50px; + } + 80% { + left: 100%; + } + 100% { + left: 100%; + } } diff --git a/frontend/src/app/features/dashboard/dashboard.component.ts b/frontend/src/app/features/dashboard/dashboard.component.ts index cc2dbaaf7e..bcb8fc4605 100644 --- a/frontend/src/app/features/dashboard/dashboard.component.ts +++ b/frontend/src/app/features/dashboard/dashboard.component.ts @@ -4,18 +4,43 @@ import { DashboardGetData, DashboardInit } from '@dashboard/dashboard.actions'; import { filter, skip, tap, timer } from 'rxjs'; import { untilDestroyed } from '@ngneat/until-destroy'; import { AppSelectors } from '@app/app.state'; +import { selectDashboardNodesAndPeers, selectDashboardNodesAndRpcStats, selectDashboardPeersStats } from '@dashboard/dashboard.state'; +import { DashboardPeersStats } from '@shared/types/dashboard/dashboard-peers-stats.type'; +import { NodesOverviewNode } from '@shared/types/nodes/dashboard/nodes-overview-node.type'; +import { DashboardRpcStats } from '@shared/types/dashboard/dashboard-rpc-stats.type'; +import { NodesOverviewLedgerStepState } from '@shared/types/nodes/dashboard/nodes-overview-ledger.type'; +import { lastItem, ONE_MILLION, SecDurationConfig } from '@openmina/shared'; +import { DashboardPeer } from '@shared/types/dashboard/dashboard.peer'; +import { NodesOverviewNodeBlockStatus } from '@shared/types/nodes/dashboard/nodes-overview-block.type'; @Component({ selector: 'mina-dashboard', templateUrl: './dashboard.component.html', styleUrls: ['./dashboard.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'h-100 w-100 flex-row' }, + host: { class: 'h-100 w-100 flex-column' }, }) export class DashboardComponent extends StoreDispatcher implements OnInit { + updateAction: string; + updateDetail: string; + connectedProgress: number = 0; + ledgerProgress: number = 0; + blockSyncProgress: number = 0; + + private connected: boolean = false; + ngOnInit(): void { + this.updateAction = 'Connecting to peers'; + this.listenToNodeChanging(); + this.listenToPeersChanges(); + this.getDashboardData(); + this.listenToNodesChanges(); + this.listenToNodesChanges(); + } + + private getDashboardData(): void { this.dispatch(DashboardInit); timer(4000, 4000) .pipe( @@ -28,6 +53,85 @@ export class DashboardComponent extends StoreDispatcher implements OnInit { private listenToNodeChanging(): void { this.select(AppSelectors.activeNode, () => { this.dispatch(DashboardGetData, { force: true }); - }, filter(Boolean), skip(1), untilDestroyed(this)); + }, filter(Boolean), skip(1)); + } + + private listenToPeersChanges(): void { + this.select(selectDashboardPeersStats, (stats: DashboardPeersStats) => { + if (this.connected && stats.connected === 0 || !this.connected) { + if (stats.connected > 0) { + this.updateAction = `Connected to ${stats.connected} peer${stats.connected !== 1 ? 's' : ''}`; + } else if (stats.connecting > 0) { + this.updateAction = `Connecting to ${stats.connecting} peer${stats.connecting !== 1 ? 's' : ''}`; + } else { + this.updateAction = 'Looking for peers'; + } + if (stats.connected) { + this.connectedProgress = 33; + } else { + this.connectedProgress = 0; + this.ledgerProgress = 0; + this.blockSyncProgress = 0; + } + this.detect(); + } + this.connected = stats.connected > 0; + }, skip(1)); + } + + private listenToNodesChanges(): void { + this.select(selectDashboardNodesAndRpcStats, ([nodes, rpcStats]: [NodesOverviewNode[], DashboardRpcStats]) => { + if (nodes.length !== 0) { + const ledgers = nodes[0].ledgers; + + let stakingProgress = rpcStats.stakingLedger?.fetched / rpcStats.stakingLedger?.estimation * 100 || 0; + let nextProgress = rpcStats.nextLedger?.fetched / rpcStats.nextLedger?.estimation * 100 || 0; + let rootSnarkedProgress = rpcStats.rootLedger?.fetched / rpcStats.rootLedger?.estimation * 100 || 0; + let rootStagedProgress = ledgers.rootStaged.staged.fetchPartsEnd ? 50 : 0; + + if (ledgers.stakingEpoch.state === NodesOverviewLedgerStepState.SUCCESS) { + stakingProgress = 100; + this.updateAction = 'Downloading Next Epoch Ledger'; + } + if (ledgers.nextEpoch.state === NodesOverviewLedgerStepState.SUCCESS) { + nextProgress = 100; + this.updateAction = 'Downloading Root Snarked Ledger'; + } + if (ledgers.rootSnarked.state === NodesOverviewLedgerStepState.SUCCESS) { + rootSnarkedProgress = 100; + this.updateAction = 'Downloading Root Staged Ledger'; + } + if (ledgers.rootStaged.state === NodesOverviewLedgerStepState.SUCCESS) { + rootStagedProgress = 100; + this.updateAction = 'Fetching Blocks'; + } + const ledgerProgressTotal = (stakingProgress + nextProgress + rootSnarkedProgress + rootStagedProgress) / 4; + this.ledgerProgress = ledgerProgressTotal * 0.33; + + let blocks = nodes[0].blocks; + + blocks = blocks.slice(1); + + const fetched = blocks.filter(b => ![NodesOverviewNodeBlockStatus.MISSING, NodesOverviewNodeBlockStatus.FETCHING].includes(b.status)).length; + const fetchedPercentage = Math.round(fetched * 100 / 291); + + const applied = blocks.filter(b => b.status === NodesOverviewNodeBlockStatus.APPLIED).length; + const appliedPercentage = Math.round(applied * 100 / 291); + this.blockSyncProgress = appliedPercentage * 0.34; + + if (fetchedPercentage < 100) { + this.updateAction = `Fetching Blocks (${fetchedPercentage}%)`; + } else if (appliedPercentage < 100) { + this.updateAction = `Applying Blocks (${appliedPercentage}%)`; + } else { + this.updateAction = ''; + } + + } else { + this.ledgerProgress = 0; + this.blockSyncProgress = 0; + } + this.detect(); + }); } } diff --git a/frontend/src/app/features/dashboard/mina-card/mina-card.component.html b/frontend/src/app/features/dashboard/mina-card/mina-card.component.html deleted file mode 100644 index a83c6194b1..0000000000 --- a/frontend/src/app/features/dashboard/mina-card/mina-card.component.html +++ /dev/null @@ -1,10 +0,0 @@ -
- {{ label }} - {{ icon }} -
-
{{ value ?? '-' }}
-
{{ hint }}
diff --git a/frontend/src/app/features/dashboard/mina-card/mina-card.component.scss b/frontend/src/app/features/dashboard/mina-card/mina-card.component.scss deleted file mode 100644 index a5a87f8b45..0000000000 --- a/frontend/src/app/features/dashboard/mina-card/mina-card.component.scss +++ /dev/null @@ -1,18 +0,0 @@ -:host { - height: 128px; - width: 128px; - - @media (max-width: 700px) { - height: 140px; - width: 140px; - } -} - -.mina-icon { - margin-left: 3px; -} - -.value { - font-size: 24px; - letter-spacing: 0.15px; -} diff --git a/frontend/src/app/features/dashboard/mina-card/mina-card.component.ts b/frontend/src/app/features/dashboard/mina-card/mina-card.component.ts deleted file mode 100644 index 9681cbd29a..0000000000 --- a/frontend/src/app/features/dashboard/mina-card/mina-card.component.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; - -@Component({ - selector: 'mina-card', - templateUrl: './mina-card.component.html', - styleUrls: ['./mina-card.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - host: { class: 'flex-column flex-between pt-8 pb-8 pl-12 border-rad-8 bg-surface' }, -}) -export class MinaCardComponent { - - @Input() color: string = 'var(--base-primary)'; - @Input() labelColor: string = 'var(--base-primary)'; - @Input() hintColor: string = 'var(--base-tertiary)'; - @Input() icon: string; - @Input() label: string | number; - @Input() value: string | number; - @Input() hint: string | number; - @Input() tooltipText: string; - -} diff --git a/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html b/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html index 2bfabe225f..5fac9c6f20 100644 --- a/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html +++ b/frontend/src/app/features/webnode/web-node-initialization/web-node-initialization.component.html @@ -12,7 +12,8 @@
{{ loadingMessage || downloadingMessage }}
+ [@messageChange]="loadingMessage">{{ loadingMessage || downloadingMessage }} +
@@ -35,21 +36,6 @@
- - - - - - - - - - - - - - -