diff --git a/package-lock.json b/package-lock.json
index e00ab1ff2f806704e71a481d74e1ae4d11852c03..f9c86e9612a2cf9f6187de22f812bd8a9559b811 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -19,6 +19,7 @@
         "@angular/platform-browser-dynamic": "^19.0.0",
         "@angular/router": "^19.0.0",
         "@primeng/themes": "^19.0.6",
+        "chart.js": "^4.4.8",
         "ngx-skeleton-loader": "^10.0.0",
         "primeicons": "^7.0.0",
         "primeng": "^19.0.6",
@@ -3070,6 +3071,11 @@
         "tslib": "2"
       }
     },
+    "node_modules/@kurkle/color": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
+      "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w=="
+    },
     "node_modules/@leichtgewicht/ip-codec": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
@@ -5804,6 +5810,17 @@
       "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
       "dev": true
     },
+    "node_modules/chart.js": {
+      "version": "4.4.8",
+      "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz",
+      "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==",
+      "dependencies": {
+        "@kurkle/color": "^0.3.0"
+      },
+      "engines": {
+        "pnpm": ">=8"
+      }
+    },
     "node_modules/chokidar": {
       "version": "4.0.3",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
diff --git a/package.json b/package.json
index aa94cd0823438fb7def27972152e47ab9500b9ac..ea14e070392bddcc5d3e6f7470e0bd394e8c8e37 100644
--- a/package.json
+++ b/package.json
@@ -21,6 +21,7 @@
     "@angular/platform-browser-dynamic": "^19.0.0",
     "@angular/router": "^19.0.0",
     "@primeng/themes": "^19.0.6",
+    "chart.js": "^4.4.8",
     "ngx-skeleton-loader": "^10.0.0",
     "primeicons": "^7.0.0",
     "primeng": "^19.0.6",
diff --git a/src/app/api/configuration.api.ts b/src/app/api/configuration.api.ts
index 064fd8503d330dfd8f8e5a438860fbe2ba3e064c..fd761e3194002159711e4140edf82603bbbc1736 100644
--- a/src/app/api/configuration.api.ts
+++ b/src/app/api/configuration.api.ts
@@ -9,7 +9,7 @@ import {
 } from '../models/configuration';
 import { ApiResponse } from '../models/api-response';
 
-export const CONFIGURATION_API_URL = '/api/v1/configuration';
+export const CONFIGURATION_API_URL = '/api/configurations';
 
 @Injectable({
   providedIn: 'root',
diff --git a/src/app/api/dashboard.api.ts b/src/app/api/dashboard.api.ts
index e81df041320410aa5f893f6c1f7044fbc3286c44..3168c8b979db259d18df8d314db390a7804a6023 100644
--- a/src/app/api/dashboard.api.ts
+++ b/src/app/api/dashboard.api.ts
@@ -1,12 +1,15 @@
+import { ApiResponse } from '../models/api-response';
+import { ChartFrames } from '../models/chart-frames';
+import { map, Observable, tap } from 'rxjs';
+import { ChartInformationRate } from '../models/chart-information-rate';
+import { ChartProtocol } from '../models/chart-protocol';
 import { inject, Injectable } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
-import { map, Observable, tap } from 'rxjs';
 import {
   DashboardStatistics,
   DashboardStatisticsDto,
   dtoToDashboardStatistics,
 } from '../models/dashboard-statistics';
-import { ApiResponse } from '../models/api-response';
 
 export const DASHBOARD_API_URL = '/api/statistics';
 
@@ -21,4 +24,70 @@ export class DashboardApi {
       .get<ApiResponse<DashboardStatisticsDto>>(DASHBOARD_API_URL)
       .pipe(map(dto => dtoToDashboardStatistics(dto.data)));
   }
+
+  fetchFramesHistory(interval: number): Observable<ChartFrames[]> {
+    return this.httpClient
+      .get<ApiResponse<ChartFrames[]>>(
+        `${DASHBOARD_API_URL}/frames/historical`,
+        {
+          params: {
+            interval: interval.toString(),
+          },
+        }
+      )
+      .pipe(map(response => response.data));
+  }
+
+  fetchFrames(): Observable<ChartFrames> {
+    return this.httpClient
+      .get<ApiResponse<ChartFrames>>(`${DASHBOARD_API_URL}/frames/current`)
+      .pipe(map(response => response.data));
+  }
+
+  fetchInformationRateHistory(
+    interval: number
+  ): Observable<ChartInformationRate[]> {
+    return this.httpClient
+      .get<ApiResponse<ChartInformationRate[]>>(
+        `${DASHBOARD_API_URL}/information-rate/historical`,
+        {
+          params: {
+            interval: interval.toString(),
+          },
+        }
+      )
+      .pipe(map(response => response.data));
+  }
+
+  fetchInformationRate(): Observable<ChartInformationRate> {
+    return this.httpClient
+      .get<
+        ApiResponse<ChartInformationRate>
+      >(`${DASHBOARD_API_URL}/information-rate/current`)
+      .pipe(map(response => response.data));
+  }
+
+  fetchProtocolHistory(
+    protocolName: string,
+    interval: number
+  ): Observable<ChartProtocol[]> {
+    return this.httpClient
+      .get<ApiResponse<ChartProtocol[]>>(
+        `${DASHBOARD_API_URL}/${protocolName}/historical`,
+        {
+          params: {
+            interval: interval.toString(),
+          },
+        }
+      )
+      .pipe(map(response => response.data));
+  }
+
+  fetchProtocol(protocolName: string): Observable<ChartProtocol> {
+    return this.httpClient
+      .get<
+        ApiResponse<ChartProtocol>
+      >(`${DASHBOARD_API_URL}/${protocolName}/current`)
+      .pipe(map(response => response.data));
+  }
 }
diff --git a/src/app/components/button/button.component.ts b/src/app/components/button/button.component.ts
index cc2d400904cb38c0b53075e7e8d254c8c2b20f6b..3682c840dade7cc8ee306becf87517588f2854b3 100644
--- a/src/app/components/button/button.component.ts
+++ b/src/app/components/button/button.component.ts
@@ -1,4 +1,4 @@
-import { Component, input } from '@angular/core';
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
 import { MatButton } from '@angular/material/button';
 import { NgClass } from '@angular/common';
 
@@ -7,6 +7,7 @@ import { NgClass } from '@angular/common';
   imports: [MatButton, NgClass],
   templateUrl: './button.component.html',
   styleUrl: './button.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class ButtonComponent {
   secondary = input<string | null>(null);
diff --git a/src/app/components/configuration-form/configuration-form.component.ts b/src/app/components/configuration-form/configuration-form.component.ts
index 0996c777d0ac25d824cd26344fc76e5d9722e5e8..456bd9d173b0ed7bbf1ec0ab51e608304e73f516 100644
--- a/src/app/components/configuration-form/configuration-form.component.ts
+++ b/src/app/components/configuration-form/configuration-form.component.ts
@@ -1,4 +1,5 @@
 import {
+  ChangeDetectionStrategy,
   Component,
   inject,
   input,
@@ -43,6 +44,7 @@ import { ButtonComponent } from '../button/button.component';
   ],
   templateUrl: './configuration-form.component.html',
   styleUrl: './configuration-form.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class ConfigurationFormComponent implements OnInit {
   configurationFormBuilder = inject(ConfigurationFormBuilder);
diff --git a/src/app/components/configuration-template/configuration-template.component.ts b/src/app/components/configuration-template/configuration-template.component.ts
index d6f45a2ae79cb75f4cc59ddaea46e445c3975ff5..413982568dd01316e80b76cfaf6bd0ba3d1d7782 100644
--- a/src/app/components/configuration-template/configuration-template.component.ts
+++ b/src/app/components/configuration-template/configuration-template.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 import { MatDivider } from '@angular/material/divider';
 
 @Component({
@@ -6,5 +6,6 @@ import { MatDivider } from '@angular/material/divider';
   imports: [MatDivider],
   templateUrl: './configuration-template.component.html',
   styleUrl: './configuration-template.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class ConfigurationTemplateComponent {}
diff --git a/src/app/components/configuration/configuration.component.ts b/src/app/components/configuration/configuration.component.ts
index 5645562d5997cbc2a9bb49dd687dcdac4efc6ed2..910833bff1914b10cf030a4b681c3ae0eed406db 100644
--- a/src/app/components/configuration/configuration.component.ts
+++ b/src/app/components/configuration/configuration.component.ts
@@ -1,8 +1,9 @@
 import {
-  afterNextRender,
+  ChangeDetectionStrategy,
   Component,
   effect,
   inject,
+  OnInit,
   signal,
 } from '@angular/core';
 import { PageWrapperComponent } from '../page-wrapper/page-wrapper.component';
@@ -47,8 +48,9 @@ enum ConfigurationPageMode {
   ],
   templateUrl: './configuration.component.html',
   styleUrl: './configuration.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class ConfigurationComponent {
+export class ConfigurationComponent implements OnInit {
   configurationApi = inject(ConfigurationApi);
 
   pageMode = signal<ConfigurationPageMode>(ConfigurationPageMode.READ);
@@ -68,27 +70,15 @@ export class ConfigurationComponent {
         });
       }
     });
+  }
 
-    afterNextRender(() => {
-      this.fetchConfigurationOptions().subscribe(configurations => {
-        const appliedConfiguration = configurations.find(c => c.is_applied);
-        if (appliedConfiguration) {
-          this.chosenConfiguration.set(appliedConfiguration.id);
-        }
-        this.configurationOptions.set(configurations);
-      });
-    });
+  ngOnInit() {
+    this.updateConfigurationOptions();
   }
 
   onDeleteConfiguration() {
     this.configurationApi.delete(this.chosenConfiguration()!).subscribe(() => {
-      this.fetchConfigurationOptions().subscribe(configurations => {
-        const appliedConfiguration = configurations.find(c => c.is_applied);
-        if (appliedConfiguration) {
-          this.chosenConfiguration.set(appliedConfiguration.id);
-        }
-        this.configurationOptions.set(configurations);
-      });
+      this.updateConfigurationOptions();
     });
   }
 
@@ -126,5 +116,15 @@ export class ConfigurationComponent {
     );
   }
 
+  private updateConfigurationOptions() {
+    this.fetchConfigurationOptions().subscribe(configurations => {
+      const appliedConfiguration = configurations.find(c => c.is_applied);
+      if (appliedConfiguration) {
+        this.chosenConfiguration.set(appliedConfiguration.id);
+      }
+      this.configurationOptions.set(configurations);
+    });
+  }
+
   protected readonly ConfigurationPageMode = ConfigurationPageMode;
 }
diff --git a/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.html b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ce1f83fe6455c4803838efe6afa49606b4d2fa1a
--- /dev/null
+++ b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.html
@@ -0,0 +1,20 @@
+<div class="dashboard-charts__interval">
+  <mat-form-field>
+    <mat-label>Interval</mat-label>
+    <input
+      matInput
+      type="number"
+      [ngModel]="recordsInterval()"
+      (ngModelChange)="recordsInterval.set($event)" />
+  </mat-form-field>
+  <div class="dashboard-charts__interval-slider">
+    <mat-slider [max]="100" [min]="1" [discrete]="true">
+      <input
+        matSliderThumb
+        [ngModel]="recordsInterval()"
+        (ngModelChange)="recordsInterval.set($event)" />
+    </mat-slider>
+  </div>
+</div>
+
+<p-chart type="line" [data]="data()" [options]="options" />
diff --git a/src/app/service/.gitkeep b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.scss
similarity index 100%
rename from src/app/service/.gitkeep
rename to src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.scss
diff --git a/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7c79268ea5ccbebd1fdb12bdd20bc8fa44adafab
--- /dev/null
+++ b/src/app/components/dashboard-chart-frames/dashboard-chart-frames.component.ts
@@ -0,0 +1,77 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  computed,
+  effect,
+  inject,
+  input,
+  signal,
+} from '@angular/core';
+import { DashboardApi } from '../../api/dashboard.api';
+import { ChartFrames } from '../../models/chart-frames';
+import { UIChart } from 'primeng/chart';
+import { interval, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
+import { MatFormField, MatLabel } from '@angular/material/form-field';
+import { MatInput } from '@angular/material/input';
+import { MatSlider, MatSliderThumb } from '@angular/material/slider';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ChartService } from '../../service/chart.service';
+
+@Component({
+  selector: 'app-dashboard-chart-frames',
+  imports: [
+    UIChart,
+    MatFormField,
+    MatInput,
+    MatLabel,
+    MatSlider,
+    MatSliderThumb,
+    ReactiveFormsModule,
+    FormsModule,
+  ],
+  templateUrl: './dashboard-chart-frames.component.html',
+  styleUrl: './dashboard-chart-frames.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DashboardChartFramesComponent {
+  readonly #dashboardApi = inject(DashboardApi);
+  readonly #chartService = inject(ChartService);
+  recordsLimit = input<number>(60);
+  recordsInterval = signal<number>(1);
+  framesHistory = signal<ChartFrames[]>([]);
+  data = computed(() => {
+    const frames = this.framesHistory();
+    if (!frames) return;
+    return this.#chartService.getFramesChartDataConfig(
+      frames,
+      this.recordsInterval()
+    );
+  });
+
+  readonly options = this.#chartService.getDefaultChartOptions();
+
+  private stopFetching = new Subject();
+
+  constructor() {
+    effect(() => {
+      this.stopFetching.next(undefined);
+
+      this.#dashboardApi
+        .fetchFramesHistory(this.recordsInterval())
+        .subscribe(response => this.framesHistory.set(response));
+
+      interval(this.recordsInterval() * 1000)
+        .pipe(
+          takeUntil(this.stopFetching),
+          switchMap(() => this.#dashboardApi.fetchFrames()),
+          shareReplay(1)
+        )
+        .subscribe(frame => {
+          this.framesHistory.update(frames => {
+            if (frames.length < this.recordsLimit()) return [...frames, frame];
+            return [...frames.splice(1), frame];
+          });
+        });
+    });
+  }
+}
diff --git a/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.html b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ce1f83fe6455c4803838efe6afa49606b4d2fa1a
--- /dev/null
+++ b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.html
@@ -0,0 +1,20 @@
+<div class="dashboard-charts__interval">
+  <mat-form-field>
+    <mat-label>Interval</mat-label>
+    <input
+      matInput
+      type="number"
+      [ngModel]="recordsInterval()"
+      (ngModelChange)="recordsInterval.set($event)" />
+  </mat-form-field>
+  <div class="dashboard-charts__interval-slider">
+    <mat-slider [max]="100" [min]="1" [discrete]="true">
+      <input
+        matSliderThumb
+        [ngModel]="recordsInterval()"
+        (ngModelChange)="recordsInterval.set($event)" />
+    </mat-slider>
+  </div>
+</div>
+
+<p-chart type="line" [data]="data()" [options]="options" />
diff --git a/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.scss b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..99d01f1bb58e9bed98d57a68043aff406b708bd4
--- /dev/null
+++ b/src/app/components/dashboard-chart-information-rate/dashboard-chart-information-rate.component.ts
@@ -0,0 +1,78 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  computed,
+  effect,
+  inject,
+  input,
+  signal,
+} from '@angular/core';
+import { UIChart } from 'primeng/chart';
+import { DashboardApi } from '../../api/dashboard.api';
+import { interval, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
+import { ChartInformationRate } from '../../models/chart-information-rate';
+import { MatFormField, MatLabel } from '@angular/material/form-field';
+import { MatInput } from '@angular/material/input';
+import { MatSlider, MatSliderThumb } from '@angular/material/slider';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { ChartService } from '../../service/chart.service';
+
+@Component({
+  selector: 'app-dashboard-chart-information-rate',
+  imports: [
+    UIChart,
+    MatFormField,
+    MatInput,
+    MatLabel,
+    MatSlider,
+    MatSliderThumb,
+    ReactiveFormsModule,
+    FormsModule,
+  ],
+  templateUrl: './dashboard-chart-information-rate.component.html',
+  styleUrl: './dashboard-chart-information-rate.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DashboardChartInformationRateComponent {
+  readonly #dashboardApi = inject(DashboardApi);
+  readonly #chartService = inject(ChartService);
+  recordsLimit = input<number>(60);
+  recordsInterval = signal<number>(1);
+  irHistory = signal<ChartInformationRate[]>([]);
+  data = computed(() => {
+    const informationRates = this.irHistory();
+    if (!informationRates) return;
+    return this.#chartService.getIRChartDataConfig(
+      informationRates,
+      this.recordsInterval()
+    );
+  });
+
+  readonly options = this.#chartService.getDefaultChartOptions();
+
+  private stopFetching = new Subject();
+
+  constructor() {
+    effect(() => {
+      this.stopFetching.next(undefined);
+
+      this.#dashboardApi
+        .fetchInformationRateHistory(this.recordsInterval())
+        .subscribe(response => this.irHistory.set(response));
+
+      interval(this.recordsInterval() * 1000)
+        .pipe(
+          takeUntil(this.stopFetching),
+          switchMap(() => this.#dashboardApi.fetchInformationRate()),
+          shareReplay(1)
+        )
+        .subscribe(ir => {
+          this.irHistory.update(informationRates => {
+            if (informationRates.length < this.recordsLimit())
+              return [...informationRates, ir];
+            return [...informationRates.splice(1), ir];
+          });
+        });
+    });
+  }
+}
diff --git a/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.html b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.html
new file mode 100644
index 0000000000000000000000000000000000000000..ce1f83fe6455c4803838efe6afa49606b4d2fa1a
--- /dev/null
+++ b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.html
@@ -0,0 +1,20 @@
+<div class="dashboard-charts__interval">
+  <mat-form-field>
+    <mat-label>Interval</mat-label>
+    <input
+      matInput
+      type="number"
+      [ngModel]="recordsInterval()"
+      (ngModelChange)="recordsInterval.set($event)" />
+  </mat-form-field>
+  <div class="dashboard-charts__interval-slider">
+    <mat-slider [max]="100" [min]="1" [discrete]="true">
+      <input
+        matSliderThumb
+        [ngModel]="recordsInterval()"
+        (ngModelChange)="recordsInterval.set($event)" />
+    </mat-slider>
+  </div>
+</div>
+
+<p-chart type="line" [data]="data()" [options]="options" />
diff --git a/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.scss b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.scss
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aae9f5d7d6bd052f1d796e2b7cc2a91bef24452a
--- /dev/null
+++ b/src/app/components/dashboard-chart-protocol/dashboard-chart-protocol.component.ts
@@ -0,0 +1,81 @@
+import {
+  ChangeDetectionStrategy,
+  Component,
+  computed,
+  effect,
+  inject,
+  input,
+  signal,
+} from '@angular/core';
+import { UIChart } from 'primeng/chart';
+import { MatFormField, MatLabel } from '@angular/material/form-field';
+import { MatInput } from '@angular/material/input';
+import { MatSlider, MatSliderThumb } from '@angular/material/slider';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { DashboardApi } from '../../api/dashboard.api';
+import { ChartProtocol } from '../../models/chart-protocol';
+import { interval, shareReplay, Subject, switchMap, takeUntil } from 'rxjs';
+import { ChartService } from '../../service/chart.service';
+
+@Component({
+  selector: 'app-dashboard-chart-protocol',
+  imports: [
+    UIChart,
+    MatFormField,
+    MatInput,
+    MatLabel,
+    MatSlider,
+    MatSliderThumb,
+    ReactiveFormsModule,
+    FormsModule,
+  ],
+  templateUrl: './dashboard-chart-protocol.component.html',
+  styleUrl: './dashboard-chart-protocol.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class DashboardChartProtocolComponent {
+  readonly #dashboardApi = inject(DashboardApi);
+  readonly #chartService = inject(ChartService);
+  protocolName = input.required<string>();
+  recordsLimit = input<number>(60);
+  recordsInterval = signal<number>(1);
+  protocolHistory = signal<ChartProtocol[]>([]);
+  data = computed(() => {
+    const protocols = this.protocolHistory();
+    if (!protocols) return;
+    return this.#chartService.getProtocolChartDataConfig(
+      protocols,
+      this.recordsInterval()
+    );
+  });
+
+  readonly options = this.#chartService.getMultipleYAxesChartOptions();
+
+  private stopFetching = new Subject();
+
+  constructor() {
+    effect(() => {
+      this.stopFetching.next(undefined);
+
+      this.#dashboardApi
+        .fetchProtocolHistory(this.protocolName(), this.recordsInterval())
+        .subscribe(response => this.protocolHistory.set(response));
+
+      interval(this.recordsInterval() * 1000)
+        .pipe(
+          takeUntil(this.stopFetching),
+          switchMap(() =>
+            this.#dashboardApi.fetchProtocol(this.protocolName())
+          ),
+          shareReplay(1)
+        )
+        .subscribe(ir => {
+          this.protocolHistory.update(protocols => {
+            if (protocols.length < this.recordsLimit())
+              return [...protocols, ir];
+            return [...protocols.splice(1), ir];
+          });
+        });
+    });
+  }
+}
diff --git a/src/app/components/dashboard-charts/dashboard-charts.component.html b/src/app/components/dashboard-charts/dashboard-charts.component.html
index 12e92e1088a4c2ca23c5013fbb799f1c49dbcb2e..9434f6470bf1e5e63db55a41bb2d363dbf717d82 100644
--- a/src/app/components/dashboard-charts/dashboard-charts.component.html
+++ b/src/app/components/dashboard-charts/dashboard-charts.component.html
@@ -1 +1,16 @@
-<ngx-skeleton-loader count="10" />
+<mat-tab-group>
+  <mat-tab label="Frames">
+    <app-dashboard-chart-frames />
+  </mat-tab>
+  <mat-tab label="Information Rate">
+    <app-dashboard-chart-information-rate />
+  </mat-tab>
+  @for (protocol of protocols(); track $index) {
+    <mat-tab [label]="protocol">
+      <app-dashboard-chart-protocol [protocolName]="protocol" />
+    </mat-tab>
+  }
+</mat-tab-group>
+
+<!--@todo: find other way to fix scrolling to the top of the page-->
+<p-skeleton height="300px" />
diff --git a/src/app/components/dashboard-charts/dashboard-charts.component.ts b/src/app/components/dashboard-charts/dashboard-charts.component.ts
index ddf7cf2b24cee9bfe912857526c60b4b186232cd..f2da7bd11e790b79d3cef2c743378dba0b26bfd6 100644
--- a/src/app/components/dashboard-charts/dashboard-charts.component.ts
+++ b/src/app/components/dashboard-charts/dashboard-charts.component.ts
@@ -1,10 +1,28 @@
-import { Component } from '@angular/core';
-import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
+import { ChangeDetectionStrategy, Component, input } from '@angular/core';
+import { MatTab, MatTabGroup } from '@angular/material/tabs';
+import { DashboardChartFramesComponent } from '../dashboard-chart-frames/dashboard-chart-frames.component';
+import { DashboardChartInformationRateComponent } from '../dashboard-chart-information-rate/dashboard-chart-information-rate.component';
+import { DashboardChartProtocolComponent } from '../dashboard-chart-protocol/dashboard-chart-protocol.component';
+import { SliderModule } from 'primeng/slider';
+import { FormsModule } from '@angular/forms';
+import { Skeleton } from 'primeng/skeleton';
 
 @Component({
   selector: 'app-dashboard-charts',
-  imports: [NgxSkeletonLoaderComponent],
+  imports: [
+    MatTabGroup,
+    MatTab,
+    DashboardChartFramesComponent,
+    DashboardChartInformationRateComponent,
+    DashboardChartProtocolComponent,
+    SliderModule,
+    FormsModule,
+    Skeleton,
+  ],
   templateUrl: './dashboard-charts.component.html',
   styleUrl: './dashboard-charts.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class DashboardChartsComponent {}
+export class DashboardChartsComponent {
+  protocols = input.required<string[]>();
+}
diff --git a/src/app/components/dashboard-statistics/dashboard-statistics.component.ts b/src/app/components/dashboard-statistics/dashboard-statistics.component.ts
index d9040eefc2f0c494965136360d354b45bc934222..2b6f6289f1cb04b49be36b8caebc8bea25d85bfa 100644
--- a/src/app/components/dashboard-statistics/dashboard-statistics.component.ts
+++ b/src/app/components/dashboard-statistics/dashboard-statistics.component.ts
@@ -1,19 +1,9 @@
-import {
-  afterNextRender,
-  Component,
-  effect,
-  inject,
-  signal,
-} from '@angular/core';
+import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
 import { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
-import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
+import { AsyncPipe } from '@angular/common';
 import { DashboardApi } from '../../api/dashboard.api';
-import { interval, map, Observable, shareReplay, switchMap, tap } from 'rxjs';
-import {
-  DashboardStatistics,
-  ProtocolStatistics,
-} from '../../models/dashboard-statistics';
-import { PillComponent } from '../pill/pill.component';
+import { interval, shareReplay, switchMap } from 'rxjs';
+import { ProtocolStatistics } from '../../models/dashboard-statistics';
 import { TimePipe } from '../../pipes/time.pipe';
 import { DecimalPipe } from '../../pipes/decimal.pipe';
 
@@ -22,6 +12,7 @@ import { DecimalPipe } from '../../pipes/decimal.pipe';
   imports: [NgxSkeletonLoaderComponent, AsyncPipe, TimePipe, DecimalPipe],
   templateUrl: './dashboard-statistics.component.html',
   styleUrl: './dashboard-statistics.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class DashboardStatisticsComponent {
   dashboardApi = inject(DashboardApi);
diff --git a/src/app/components/dashboard/dashboard.component.html b/src/app/components/dashboard/dashboard.component.html
index c8259a3f08b0d4aa1a6e0eba7a4994163a389196..c86a0de5c150b18f00efc5b00547b72570324abb 100644
--- a/src/app/components/dashboard/dashboard.component.html
+++ b/src/app/components/dashboard/dashboard.component.html
@@ -22,7 +22,7 @@
           Charts
         </mat-panel-title>
       </mat-expansion-panel-header>
-      <app-dashboard-charts />
+      <app-dashboard-charts [protocols]="['ipv4']" />
     </mat-expansion-panel>
   </mat-accordion>
 </app-page-wrapper>
diff --git a/src/app/components/dashboard/dashboard.component.ts b/src/app/components/dashboard/dashboard.component.ts
index ab1f72fa34deef45018254ed349cc92a37fd6520..c9ea330f3a33f59d37829b1e3c2c53d2924a7491 100644
--- a/src/app/components/dashboard/dashboard.component.ts
+++ b/src/app/components/dashboard/dashboard.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 import { PageWrapperComponent } from '../page-wrapper/page-wrapper.component';
 import { MatExpansionModule } from '@angular/material/expansion';
 import { MatIcon } from '@angular/material/icon';
@@ -18,6 +18,7 @@ import { DashboardChartsComponent } from '../dashboard-charts/dashboard-charts.c
   ],
   templateUrl: './dashboard.component.html',
   styleUrl: './dashboard.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class DashboardComponent {
   openSettings() {}
diff --git a/src/app/components/header/header.component.ts b/src/app/components/header/header.component.ts
index 267d2a72b9dfd64e6f8a61655c60b26dba43e9cc..f13bffabcaa092ec892dd65647d7c5edd05c685c 100644
--- a/src/app/components/header/header.component.ts
+++ b/src/app/components/header/header.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 import { DividerModule } from 'primeng/divider';
 import { MatDivider } from '@angular/material/divider';
 import { MatIcon } from '@angular/material/icon';
@@ -9,5 +9,6 @@ import { MatButton } from '@angular/material/button';
   imports: [DividerModule, MatDivider, MatIcon, MatButton],
   templateUrl: './header.component.html',
   styleUrl: './header.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class HeaderComponent {}
diff --git a/src/app/components/not-found/not-found.component.ts b/src/app/components/not-found/not-found.component.ts
index 03ff4dc556e1345c1ac569ff4882f5d64878dd8a..a6ac140ff58296a6711904b12b3d311edffa94ac 100644
--- a/src/app/components/not-found/not-found.component.ts
+++ b/src/app/components/not-found/not-found.component.ts
@@ -1,9 +1,10 @@
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 
 @Component({
   selector: 'app-not-found',
   imports: [],
   templateUrl: './not-found.component.html',
   styleUrl: './not-found.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class NotFoundComponent {}
diff --git a/src/app/components/page-wrapper/page-wrapper.component.ts b/src/app/components/page-wrapper/page-wrapper.component.ts
index 3046fbcd7f1447eb25d537a917fe158b4bcb8223..2531cf897809b6811bd5b7979443995ce8f27867 100644
--- a/src/app/components/page-wrapper/page-wrapper.component.ts
+++ b/src/app/components/page-wrapper/page-wrapper.component.ts
@@ -1,4 +1,4 @@
-import { Component } from '@angular/core';
+import { ChangeDetectionStrategy, Component } from '@angular/core';
 import { MatDivider } from '@angular/material/divider';
 
 @Component({
@@ -6,5 +6,6 @@ import { MatDivider } from '@angular/material/divider';
   imports: [MatDivider],
   templateUrl: './page-wrapper.component.html',
   styleUrl: './page-wrapper.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class PageWrapperComponent {}
diff --git a/src/app/components/pill/pill.component.ts b/src/app/components/pill/pill.component.ts
index f43c8ace3b9566369df724bce387c1871662b6be..be975e081913cbd517819336ec51dee28a4fb867 100644
--- a/src/app/components/pill/pill.component.ts
+++ b/src/app/components/pill/pill.component.ts
@@ -1,4 +1,9 @@
-import { Component, input, output } from '@angular/core';
+import {
+  ChangeDetectionStrategy,
+  Component,
+  input,
+  output,
+} from '@angular/core';
 import { MatIcon } from '@angular/material/icon';
 
 @Component({
@@ -6,6 +11,7 @@ import { MatIcon } from '@angular/material/icon';
   imports: [MatIcon],
   templateUrl: './pill.component.html',
   styleUrl: './pill.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class PillComponent {
   removable = input<boolean | null>(null);
diff --git a/src/app/components/sidenav/sidenav.component.ts b/src/app/components/sidenav/sidenav.component.ts
index a630fe2959e7f2106957b7664ff0dc3063d3151d..dc9691e497cfa41019a7a9e8e73d4208598c0ed8 100644
--- a/src/app/components/sidenav/sidenav.component.ts
+++ b/src/app/components/sidenav/sidenav.component.ts
@@ -1,5 +1,6 @@
 import {
   afterNextRender,
+  ChangeDetectionStrategy,
   Component,
   inject,
   signal,
@@ -24,6 +25,7 @@ import { MatIcon } from '@angular/material/icon';
   ],
   templateUrl: './sidenav.component.html',
   styleUrl: './sidenav.component.scss',
+  changeDetection: ChangeDetectionStrategy.OnPush,
 })
 export class SidenavComponent {
   router = inject(Router);
diff --git a/src/app/interceptor/interceptor.ts b/src/app/interceptor/interceptor.ts
index 8e088acb883c638b72a88c594d0f501efd77132d..93d051b41e68a73b5f4e5288ea43755e2b795400 100644
--- a/src/app/interceptor/interceptor.ts
+++ b/src/app/interceptor/interceptor.ts
@@ -1,23 +1,54 @@
 import * as configurationList from './mocks/configurations.json';
 import * as configuration1 from './mocks/configuration-1.json';
 import * as configuration2 from './mocks/configuration-2.json';
+import * as framesCurrent from './mocks/frames-current.json';
+import * as framesHistorical from './mocks/frames-historical.json';
+import * as irCurrent from './mocks/ir-current.json';
+import * as irHistorical from './mocks/ir-historical.json';
+import * as ipv4Current from './mocks/ipv4-current.json';
+import * as ipv4Historical from './mocks/ipv4-historical.json';
 import * as dashboardStats from './mocks/dashboard-statistics.json';
+import * as dashboardStats2 from './mocks/dashboard-statistics-2.json';
 
 export const urls = [
   {
-    url: '/api/v1/configuration/1abc',
-    json: configuration1,
+    url: '/api/configuration/1abc',
+    json: () => configuration1,
   },
   {
-    url: '/api/v1/configuration/2def',
-    json: configuration2,
+    url: '/api/configuration/2def',
+    json: () => configuration2,
   },
   {
-    url: '/api/v1/configuration',
-    json: configurationList,
+    url: '/api/configuration',
+    json: () => configurationList,
+  },
+  {
+    url: '/api/statistics/frames/current',
+    json: () => framesCurrent,
+  },
+  {
+    url: '/api/statistics/frames/historical',
+    json: () => framesHistorical,
+  },
+  {
+    url: '/api/statistics/information-rate/current',
+    json: () => irCurrent,
+  },
+  {
+    url: '/api/statistics/information-rate/historical',
+    json: () => irHistorical,
+  },
+  {
+    url: '/api/statistics/ipv4/current',
+    json: () => ipv4Current,
+  },
+  {
+    url: '/api/statistics/ipv4/historical',
+    json: () => ipv4Historical,
   },
   {
     url: '/api/statistics',
-    json: dashboardStats,
+    json: () => (Math.random() < 0.5 ? dashboardStats2 : dashboardStats),
   },
 ];
diff --git a/src/app/interceptor/mock-interceptor.ts b/src/app/interceptor/mock-interceptor.ts
index d1ae561c1d7b35f3c2ae8fb39c14f7865087fc90..0ce7c4cf8958d41aa28f5c0a537661328b8adb04 100644
--- a/src/app/interceptor/mock-interceptor.ts
+++ b/src/app/interceptor/mock-interceptor.ts
@@ -1,17 +1,58 @@
 import { HttpInterceptorFn, HttpResponse } from '@angular/common/http';
 import { delay, of } from 'rxjs';
 import { urls } from './interceptor';
+import { ChartFrames } from '../models/chart-frames';
+import { DASHBOARD_API_URL } from '../api/dashboard.api';
+import { ChartInformationRate } from '../models/chart-information-rate';
+import { ChartProtocol } from '../models/chart-protocol';
 
 export const MockInterceptor: HttpInterceptorFn = (req, next) => {
-  const { url, method } = req;
+  const { url } = req;
 
   for (const element of urls) {
+    let body = (element.json() as any).default;
+
+    if (url === DASHBOARD_API_URL + '/frames/current') {
+      body = getRandomFrameRecord();
+    } else if (url === DASHBOARD_API_URL + '/information-rate/current') {
+      body = getRandomInformationRateRecord();
+    } else if (url === DASHBOARD_API_URL + '/ipv4/current') {
+      body = getRandomProtocolRecord();
+    }
+
     if (url.includes(element.url)) {
-      console.log('Loaded from json for url: ' + url, element.json);
-      return of(
-        new HttpResponse({ status: 200, body: (element.json as any).default })
-      ).pipe(delay(300));
+      return of(new HttpResponse({ status: 200, body: body })).pipe(delay(300));
     }
   }
   return next(req);
 };
+
+function getRandomFrameRecord(): { data: ChartFrames } {
+  const validNumber = Math.floor(Math.random() * 5000) + 200;
+  return {
+    data: {
+      valid: validNumber,
+      invalid: Math.floor(Math.random() * validNumber * 0.3),
+    },
+  };
+}
+
+function getRandomInformationRateRecord(): { data: ChartInformationRate } {
+  const current = Math.floor(Math.random() * 1000) + 30;
+  return {
+    data: {
+      current: current,
+      average: Math.floor(Math.random() * current * 0.3),
+    },
+  };
+}
+
+function getRandomProtocolRecord(): { data: ChartProtocol } {
+  const packets = Math.floor(Math.random() * 1000) + 30;
+  return {
+    data: {
+      packets: packets,
+      bytes: packets * Math.floor(Math.random() * Math.sqrt(packets) * 16),
+    },
+  };
+}
diff --git a/src/app/interceptor/mocks/dashboard-statistics-2.json b/src/app/interceptor/mocks/dashboard-statistics-2.json
new file mode 100644
index 0000000000000000000000000000000000000000..9d58d0f611f90c47edf6bbd676fde91d8058d2ea
--- /dev/null
+++ b/src/app/interceptor/mocks/dashboard-statistics-2.json
@@ -0,0 +1,33 @@
+{
+  "data": {
+    "id": "statistics",
+    "total-time": 160402,
+    "protocols": [
+      {
+        "name": "ETH",
+        "total-packets": 1237890,
+        "total-bytes": 123678901
+      },
+      {
+        "name": "TCP",
+        "total-packets": 323344,
+        "total-bytes": 32112345
+      },
+      {
+        "name": "IPv4",
+        "total-packets": 3399,
+        "total-bytes": 1000
+      },
+      {
+        "name": "IPv6",
+        "total-packets": 13,
+        "total-bytes": 778
+      }
+    ],
+    "information-rate": {
+      "min": 0,
+      "max": 10.53,
+      "current": 10.53
+    }
+  }
+}
diff --git a/src/app/interceptor/mocks/frames-current.json b/src/app/interceptor/mocks/frames-current.json
new file mode 100644
index 0000000000000000000000000000000000000000..1df35e6c7b10e07e3568e4774defd33219831325
--- /dev/null
+++ b/src/app/interceptor/mocks/frames-current.json
@@ -0,0 +1,6 @@
+{
+  "data": {
+    "valid": 2345,
+    "invalid": 234
+  }
+}
diff --git a/src/app/interceptor/mocks/frames-historical.json b/src/app/interceptor/mocks/frames-historical.json
new file mode 100644
index 0000000000000000000000000000000000000000..1599d221acb9309c9a8b90181e80d1464cd74e5f
--- /dev/null
+++ b/src/app/interceptor/mocks/frames-historical.json
@@ -0,0 +1,64 @@
+{
+  "data": [
+    {
+      "valid": 1000,
+      "invalid": 15
+    },
+    {
+      "valid": 1500,
+      "invalid": 0
+    },
+    {
+      "valid": 100,
+      "invalid": 99
+    },
+    {
+      "valid": 1234,
+      "invalid": 123
+    },
+    {
+      "valid": 1555,
+      "invalid": 25
+    },
+    {
+      "valid": 1000,
+      "invalid": 15
+    },
+    {
+      "valid": 1500,
+      "invalid": 0
+    },
+    {
+      "valid": 100,
+      "invalid": 99
+    },
+    {
+      "valid": 1234,
+      "invalid": 123
+    },
+    {
+      "valid": 1555,
+      "invalid": 25
+    },
+    {
+      "valid": 1000,
+      "invalid": 15
+    },
+    {
+      "valid": 1500,
+      "invalid": 0
+    },
+    {
+      "valid": 100,
+      "invalid": 99
+    },
+    {
+      "valid": 1234,
+      "invalid": 123
+    },
+    {
+      "valid": 1555,
+      "invalid": 25
+    }
+  ]
+}
diff --git a/src/app/interceptor/mocks/ipv4-current.json b/src/app/interceptor/mocks/ipv4-current.json
new file mode 100644
index 0000000000000000000000000000000000000000..789f453a211be42d1dc937d02a3f7fd8c2c3139e
--- /dev/null
+++ b/src/app/interceptor/mocks/ipv4-current.json
@@ -0,0 +1,6 @@
+{
+  "data": {
+    "bytes": 3000,
+    "packets": 58
+  }
+}
diff --git a/src/app/interceptor/mocks/ipv4-historical.json b/src/app/interceptor/mocks/ipv4-historical.json
new file mode 100644
index 0000000000000000000000000000000000000000..1c3fe5775e9236f584493416b004898898f158e5
--- /dev/null
+++ b/src/app/interceptor/mocks/ipv4-historical.json
@@ -0,0 +1,68 @@
+{
+  "data": [
+    {
+      "bytes": 2048,
+      "packets": 24
+    },
+    {
+      "bytes": 1024,
+      "packets": 16
+    },
+    {
+      "bytes": 1526,
+      "packets": 32
+    },
+    {
+      "bytes": 1920,
+      "packets": 48
+    },
+    {
+      "bytes": 2048,
+      "packets": 24
+    },
+    {
+      "bytes": 1024,
+      "packets": 16
+    },
+    {
+      "bytes": 1526,
+      "packets": 32
+    },
+    {
+      "bytes": 1920,
+      "packets": 48
+    },
+    {
+      "bytes": 2048,
+      "packets": 24
+    },
+    {
+      "bytes": 1024,
+      "packets": 16
+    },
+    {
+      "bytes": 1526,
+      "packets": 32
+    },
+    {
+      "bytes": 1920,
+      "packets": 48
+    },
+    {
+      "bytes": 2048,
+      "packets": 24
+    },
+    {
+      "bytes": 1024,
+      "packets": 16
+    },
+    {
+      "bytes": 1526,
+      "packets": 32
+    },
+    {
+      "bytes": 1920,
+      "packets": 48
+    }
+  ]
+}
diff --git a/src/app/interceptor/mocks/ir-current.json b/src/app/interceptor/mocks/ir-current.json
new file mode 100644
index 0000000000000000000000000000000000000000..946c342cb8000270b79a0b423ba2f40fa579671c
--- /dev/null
+++ b/src/app/interceptor/mocks/ir-current.json
@@ -0,0 +1,6 @@
+{
+  "data": {
+    "current": 123.45,
+    "average": 123.4
+  }
+}
diff --git a/src/app/interceptor/mocks/ir-historical.json b/src/app/interceptor/mocks/ir-historical.json
new file mode 100644
index 0000000000000000000000000000000000000000..2663f3aec160b96c9be42f318666e844002d69fa
--- /dev/null
+++ b/src/app/interceptor/mocks/ir-historical.json
@@ -0,0 +1,52 @@
+{
+  "data": [
+    {
+      "current": 123.45,
+      "average": 134.6
+    },
+    {
+      "current": 103.45,
+      "average": 114.6
+    },
+    {
+      "current": 153.45,
+      "average": 124.6
+    },
+    {
+      "current": 193.45,
+      "average": 144.6
+    },
+    {
+      "current": 123.45,
+      "average": 134.6
+    },
+    {
+      "current": 103.45,
+      "average": 114.6
+    },
+    {
+      "current": 153.45,
+      "average": 124.6
+    },
+    {
+      "current": 193.45,
+      "average": 144.6
+    },
+    {
+      "current": 123.45,
+      "average": 134.6
+    },
+    {
+      "current": 103.45,
+      "average": 114.6
+    },
+    {
+      "current": 153.45,
+      "average": 124.6
+    },
+    {
+      "current": 193.45,
+      "average": 144.6
+    }
+  ]
+}
diff --git a/src/app/models/chart-frames.ts b/src/app/models/chart-frames.ts
new file mode 100644
index 0000000000000000000000000000000000000000..03956ce7a95779a5011bb0b094807d906e90cdb5
--- /dev/null
+++ b/src/app/models/chart-frames.ts
@@ -0,0 +1,4 @@
+export interface ChartFrames {
+  valid: number;
+  invalid: number;
+}
diff --git a/src/app/models/chart-information-rate.ts b/src/app/models/chart-information-rate.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ee2919dd1e4b8a88963943f70eb749fd3e0fd5ea
--- /dev/null
+++ b/src/app/models/chart-information-rate.ts
@@ -0,0 +1,4 @@
+export interface ChartInformationRate {
+  current: number;
+  average: number;
+}
diff --git a/src/app/models/chart-protocol.ts b/src/app/models/chart-protocol.ts
new file mode 100644
index 0000000000000000000000000000000000000000..22844a0597a9f35b6354e6d49fa4197c00449056
--- /dev/null
+++ b/src/app/models/chart-protocol.ts
@@ -0,0 +1,4 @@
+export interface ChartProtocol {
+  bytes: number;
+  packets: number;
+}
diff --git a/src/app/pipes/decimal.pipe.ts b/src/app/pipes/decimal.pipe.ts
index 1d9a6e1a701c08b48bb6e60c15e014a713d7c9cd..8311eabce33825a94a7fae095311bd91c3beb30f 100644
--- a/src/app/pipes/decimal.pipe.ts
+++ b/src/app/pipes/decimal.pipe.ts
@@ -9,8 +9,8 @@ export class DecimalPipe implements PipeTransform {
       return '';
     }
 
-    let num = Number(value).toFixed(decimalPlaces);
-
-    return num.replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
+    return Number(value).toLocaleString('pl-PL', {
+      maximumFractionDigits: decimalPlaces,
+    });
   }
 }
diff --git a/src/app/service/chart.service.ts b/src/app/service/chart.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..861c9e0de847fd4bc7bdb26014324afc0e51828f
--- /dev/null
+++ b/src/app/service/chart.service.ts
@@ -0,0 +1,131 @@
+import { Injectable } from '@angular/core';
+import { ChartInformationRate } from '../models/chart-information-rate';
+import { ChartFrames } from '../models/chart-frames';
+import { ChartProtocol } from '../models/chart-protocol';
+
+@Injectable({
+  providedIn: 'root',
+})
+export class ChartService {
+  getChartLabels(
+    labelsNumber: number,
+    labelTimeInterval: number,
+    labelsStep: number = 5
+  ): string[] {
+    const now = new Date().getTime();
+    return Array.from({ length: labelsNumber }, (_, index) => {
+      if (index % labelsStep && index != 0 && index != labelsNumber - 1) {
+        return '';
+      }
+      return new Date(
+        now - (labelsNumber - index) * 1000 * labelTimeInterval
+      ).toLocaleTimeString();
+    });
+  }
+
+  getIRChartDataConfig(
+    informationRates: ChartInformationRate[],
+    recordsInterval: number
+  ) {
+    return {
+      labels: this.getChartLabels(informationRates.length, recordsInterval),
+      datasets: [
+        {
+          label: 'Current IR',
+          data: informationRates.map(ir => ir.current),
+          fill: true,
+          backgroundColor: 'rgba(144,147,239,0.32)',
+          tension: 0.4,
+          borderColor: '#5b70f8',
+        },
+        {
+          label: 'Average IR',
+          data: informationRates.map(ir => ir.average),
+          tension: 0.4,
+          borderColor: '#535353',
+        },
+      ],
+    };
+  }
+
+  getFramesChartDataConfig(frames: ChartFrames[], recordsInterval: number) {
+    return {
+      labels: this.getChartLabels(frames.length, recordsInterval),
+      datasets: [
+        {
+          label: 'Valid Frames',
+          data: frames.map(frame => frame.valid),
+          fill: true,
+          backgroundColor: 'rgb(76, 175, 80, 0.2)',
+          tension: 0.4,
+          borderColor: '#4CAF50',
+        },
+        {
+          label: 'Invalid Frames',
+          data: frames.map(frame => frame.invalid),
+          fill: true,
+          tension: 0.4,
+          borderColor: '#f34d52',
+          backgroundColor: 'rgb(243, 77, 82, 0.2)',
+        },
+      ],
+    };
+  }
+
+  getProtocolChartDataConfig(
+    protocols: ChartProtocol[],
+    recordsInterval: number
+  ) {
+    return {
+      labels: this.getChartLabels(protocols.length, recordsInterval),
+      datasets: [
+        {
+          label: 'Bytes',
+          fill: false,
+          yAxisID: 'y',
+          tension: 0.4,
+          data: protocols.map(protocol => protocol.bytes),
+        },
+        {
+          label: 'Packets',
+          fill: false,
+          yAxisID: 'y1',
+          tension: 0.4,
+          data: protocols.map(protocol => protocol.packets),
+        },
+      ],
+    };
+  }
+
+  getDefaultChartOptions() {
+    return {
+      animation: {
+        duration: 0,
+      },
+    };
+  }
+
+  getMultipleYAxesChartOptions() {
+    return {
+      animation: {
+        duration: 0,
+      },
+      stacked: false,
+      scales: {
+        y: {
+          type: 'linear',
+          display: true,
+          position: 'left',
+        },
+        y1: {
+          type: 'linear',
+          display: true,
+          position: 'right',
+          grid: {
+            drawOnChartArea: false,
+          },
+        },
+      },
+    };
+  }
+}
diff --git a/src/styles.scss b/src/styles.scss
index e81a44670599e509ff814d887e2131c95f3351fe..ef564f1c87d46ecb4721cb344ec04a9530fe5658 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -12,3 +12,21 @@ body {
 mat-divider {
   border-color: map.get(vars.$grey, 30) !important;
 }
+
+.dashboard-charts {
+  &__interval {
+    display: grid;
+    grid-template-columns: 200px 1fr;
+    gap: map.get(vars.$spacing, 'xxl');
+    padding: map.get(vars.$spacing, 'xl');
+
+    &-slider {
+      display: grid;
+      align-items: center;
+      padding: map.get(vars.$spacing, 'none') map.get(vars.$spacing, 'xxl');
+      background-color: map.get(vars.$grey, 0);
+      border-radius: map.get(vars.$radius, 'md');
+      margin-bottom: map.get(vars.$spacing, 'lg');
+    }
+  }
+}