feat(historygraphs): adding history graph in hourly and daily
This commit is contained in:
parent
2864560c56
commit
4bb2c107d3
|
|
@ -4,6 +4,7 @@ import { LayoutsComponent } from './pages/dashboard/layouts/layouts.component';
|
||||||
import { AuthComponent } from './pages/auth/auth.component';
|
import { AuthComponent } from './pages/auth/auth.component';
|
||||||
import { AuthGuard } from './cores/guards/auth.guard';
|
import { AuthGuard } from './cores/guards/auth.guard';
|
||||||
import { RegisterComponent } from './pages/register/register.component';
|
import { RegisterComponent } from './pages/register/register.component';
|
||||||
|
import { HistorygraphComponent } from './pages/dashboard/page/historygraph/historygraph.component';
|
||||||
|
|
||||||
export const routes: Routes = [
|
export const routes: Routes = [
|
||||||
{
|
{
|
||||||
|
|
@ -28,6 +29,11 @@ export const routes: Routes = [
|
||||||
component: DashboardComponent,
|
component: DashboardComponent,
|
||||||
canActivate: [AuthGuard]
|
canActivate: [AuthGuard]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'historygraph',
|
||||||
|
component: HistorygraphComponent,
|
||||||
|
canActivate: [AuthGuard],
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export interface ParameterSensor {
|
export interface ParameterSensor {
|
||||||
hour: number;
|
hour: number;
|
||||||
|
day: number;
|
||||||
|
|
||||||
//for DHT sensor
|
//for DHT sensor
|
||||||
vicitemperature?: number;
|
vicitemperature?: number;
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@
|
||||||
<a routerLink='/dashboard' data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
<a routerLink='/dashboard' data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
||||||
<i class="bi bi-graph-up title"></i> <span class="ms-1 d-none d-sm-inline menu">Dashboard</span> </a>
|
<i class="bi bi-graph-up title"></i> <span class="ms-1 d-none d-sm-inline menu">Dashboard</span> </a>
|
||||||
</li>
|
</li>
|
||||||
<!-- <li>
|
<li>
|
||||||
<a routerLink='/graph' data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
<a routerLink='/historygraph' data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
||||||
<i class="bi bi-graph-up title"></i> <span class="ms-1 d-none d-sm-inline menu">Graph</span> </a>
|
<i class="bi bi-file-earmark-text title"></i> <span class="ms-1 d-none d-sm-inline menu">History Graph</span> </a>
|
||||||
</li> -->
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
import { AuthService } from '../../../../cores/services/auth.service';
|
import { AuthService } from '../../../../cores/services/auth.service';
|
||||||
import { Router } from '@angular/router';
|
import { Router, RouterModule } from '@angular/router';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-sidebar',
|
selector: 'app-sidebar',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
|
imports: [RouterModule],
|
||||||
templateUrl: './sidebar.component.html',
|
templateUrl: './sidebar.component.html',
|
||||||
styleUrls: ['./sidebar.component.scss']
|
styleUrls: ['./sidebar.component.scss']
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, OnDestroy } from '@angular/core';
|
import { Component, OnInit, ElementRef, ViewChild, AfterViewInit, OnDestroy, OnChanges, Input, SimpleChanges, ChangeDetectorRef } from '@angular/core';
|
||||||
import { Chart, registerables } from 'chart.js';
|
import { Chart, registerables } from 'chart.js';
|
||||||
import { SensorService } from '../../../../cores/services/sensor.service';
|
import { SensorService } from '../../../../cores/services/sensor.service';
|
||||||
import { ApiResponse, ParameterSensor } from '../../../../cores/interface/sensor-data';
|
import { ApiResponse, ParameterSensor } from '../../../../cores/interface/sensor-data';
|
||||||
|
|
@ -27,10 +27,12 @@ const parameterColors: { [key: string]: string } = {
|
||||||
templateUrl: './graph.component.html',
|
templateUrl: './graph.component.html',
|
||||||
styleUrls: ['./graph.component.scss']
|
styleUrls: ['./graph.component.scss']
|
||||||
})
|
})
|
||||||
export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
export class GraphComponent implements OnInit, AfterViewInit, OnDestroy, OnChanges{
|
||||||
@ViewChild('myChartDHT', { static: false }) dhtChartElement!: ElementRef<HTMLCanvasElement>;
|
@ViewChild('myChartDHT', { static: false }) dhtChartElement!: ElementRef<HTMLCanvasElement>;
|
||||||
@ViewChild('myChartNPK1', { static: false }) npk1ChartElement!: ElementRef<HTMLCanvasElement>;
|
@ViewChild('myChartNPK1', { static: false }) npk1ChartElement!: ElementRef<HTMLCanvasElement>;
|
||||||
@ViewChild('myChartNPK2', { static: false }) npk2ChartElement!: ElementRef<HTMLCanvasElement>;
|
@ViewChild('myChartNPK2', { static: false }) npk2ChartElement!: ElementRef<HTMLCanvasElement>;
|
||||||
|
@Input() interval: string = 'hourly';
|
||||||
|
selectedInterval: string = 'HOURLY';
|
||||||
|
|
||||||
isLoadingDHT: boolean = true;
|
isLoadingDHT: boolean = true;
|
||||||
isLoadingNPK1: boolean = true;
|
isLoadingNPK1: boolean = true;
|
||||||
|
|
@ -72,7 +74,7 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
private resizeListener!: () => void;
|
private resizeListener!: () => void;
|
||||||
|
|
||||||
constructor(private sensorService: SensorService) {}
|
constructor(private sensorService: SensorService, private cdr: ChangeDetectorRef) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.resizeListener = this.onResize.bind(this);
|
this.resizeListener = this.onResize.bind(this);
|
||||||
|
|
@ -80,6 +82,15 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.updateCharts();
|
this.updateCharts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (changes['interval'] && !changes['interval'].firstChange) {
|
||||||
|
this.selectedInterval = changes['interval'].currentValue;
|
||||||
|
this.updateCharts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
this.onResize();
|
this.onResize();
|
||||||
}
|
}
|
||||||
|
|
@ -97,27 +108,18 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.updateCharts();
|
this.updateCharts();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCharts(): void {
|
getDate(): string {
|
||||||
this.isLoadingDHT = this.isLoadingNPK1 = this.isLoadingNPK2 = true;
|
|
||||||
|
|
||||||
const today = new Date();
|
const today = new Date();
|
||||||
const year = today.getFullYear();
|
const year = today.getFullYear();
|
||||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||||
const day = String(today.getDate()).padStart(2, '0');
|
const day = String(today.getDate()).padStart(2, '0');
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
const startEnd = `${year}-${month}-${day}`;
|
|
||||||
const timeRange = 'HOURLY';
|
|
||||||
|
|
||||||
Object.keys(this.charts).forEach(key => {
|
|
||||||
if (this.charts[key]) {
|
|
||||||
this.charts[key]?.destroy();
|
|
||||||
this.charts[key] = undefined;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Fetch data for DHT
|
fetchDHTData(timeRange: string): void {
|
||||||
this.sensorService.getSensorData('dht', '', startEnd, timeRange).subscribe({
|
const startEnd = this.getDate();
|
||||||
|
this.sensorService.getSensorData('dht', 'npk', startEnd, timeRange).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.isLoadingDHT = false;
|
this.isLoadingDHT = false;
|
||||||
if (response.statusCode === 200 && response.data.dht?.length > 0) {
|
if (response.statusCode === 200 && response.data.dht?.length > 0) {
|
||||||
|
|
@ -132,10 +134,11 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.isNoDataDHT = true;
|
this.isNoDataDHT = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchNPK1Data(timeRange: string): void {
|
||||||
// Fetch data for NPK1
|
const startEnd = this.getDate();
|
||||||
this.sensorService.getSensorData('npk1', '', startEnd, timeRange).subscribe({
|
this.sensorService.getSensorData('npk1', this.selectedNPK1, startEnd, timeRange).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
this.isLoadingNPK1 = false;
|
this.isLoadingNPK1 = false;
|
||||||
if (response.statusCode === 200 && response.data.npk1?.length > 0) {
|
if (response.statusCode === 200 && response.data.npk1?.length > 0) {
|
||||||
|
|
@ -150,10 +153,14 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.isNoDataNPK1 = true;
|
this.isNoDataNPK1 = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch data for NPK2
|
fetchNPK2Data(savedTimeRange: string): void {
|
||||||
this.sensorService.getSensorData('npk2', '', startEnd, timeRange).subscribe({
|
const startEnd = this.getDate();
|
||||||
|
const timeRange = this.interval;
|
||||||
|
this.sensorService.getSensorData('npk2', this.selectedNPK2, startEnd, savedTimeRange).subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
|
console.log(savedTimeRange);
|
||||||
this.isLoadingNPK2 = false;
|
this.isLoadingNPK2 = false;
|
||||||
if (response.statusCode === 200 && response.data.npk2?.length > 0) {
|
if (response.statusCode === 200 && response.data.npk2?.length > 0) {
|
||||||
this.createChart(this.npk2ChartElement.nativeElement, response, 'npk2', this.selectedNPK2);
|
this.createChart(this.npk2ChartElement.nativeElement, response, 'npk2', this.selectedNPK2);
|
||||||
|
|
@ -169,6 +176,21 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateCharts(): void {
|
||||||
|
const interval = this.selectedInterval;
|
||||||
|
|
||||||
|
Object.keys(this.charts).forEach(key => {
|
||||||
|
if (this.charts[key]) {
|
||||||
|
this.charts[key]?.destroy();
|
||||||
|
this.charts[key] = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fetchDHTData(interval);
|
||||||
|
this.fetchNPK1Data(interval);
|
||||||
|
this.fetchNPK2Data(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
createChart(canvas: HTMLCanvasElement, response: ApiResponse, sensor: string, selectedOption: string): void {
|
createChart(canvas: HTMLCanvasElement, response: ApiResponse, sensor: string, selectedOption: string): void {
|
||||||
|
|
@ -195,6 +217,9 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
const borderColor = parameterColors[parameter] || '#000000';
|
const borderColor = parameterColors[parameter] || '#000000';
|
||||||
const backgroundColor = `${borderColor}4D`;
|
const backgroundColor = `${borderColor}4D`;
|
||||||
|
|
||||||
|
const pointRadius = data.length === 1 ? 5 : 0;
|
||||||
|
const pointHoverRadius = data.length === 1 ? 7 : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: displayName,
|
label: displayName,
|
||||||
data,
|
data,
|
||||||
|
|
@ -203,8 +228,8 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
fill: true,
|
fill: true,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
tension: 0.5,
|
tension: 0.5,
|
||||||
pointRadius: 0,
|
pointRadius,
|
||||||
pointHoverRadius: 0,
|
pointHoverRadius,
|
||||||
};
|
};
|
||||||
}).filter(dataset => dataset !== null);
|
}).filter(dataset => dataset !== null);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -229,6 +254,9 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
const borderColor = parameterColors[parameter] || '#000000';
|
const borderColor = parameterColors[parameter] || '#000000';
|
||||||
const backgroundColor = `${borderColor}4D`;
|
const backgroundColor = `${borderColor}4D`;
|
||||||
|
|
||||||
|
const pointRadius = data.length === 1 ? 5 : 0;
|
||||||
|
const pointHoverRadius = data.length === 1 ? 7 : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
label: displayName,
|
label: displayName,
|
||||||
data,
|
data,
|
||||||
|
|
@ -237,8 +265,8 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
fill: true,
|
fill: true,
|
||||||
backgroundColor,
|
backgroundColor,
|
||||||
tension: 0.5,
|
tension: 0.5,
|
||||||
pointRadius: 0,
|
pointRadius,
|
||||||
pointHoverRadius: 0,
|
pointHoverRadius,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter(dataset => dataset !== null);
|
.filter(dataset => dataset !== null);
|
||||||
|
|
@ -253,6 +281,7 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.charts[sensor]?.destroy();
|
this.charts[sensor]?.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.charts[sensor] = new Chart(ctx, {
|
this.charts[sensor] = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -279,11 +308,21 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
legend: { display: true }
|
legend: { display: true }
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: { grid: { display: false }, beginAtZero: true },
|
x: {
|
||||||
|
grid: { display: false },
|
||||||
|
beginAtZero: true,
|
||||||
|
ticks: {
|
||||||
|
callback: (value, index, values) => {
|
||||||
|
const labels = this.getLabels(response, sensor);
|
||||||
|
return labels[index] || value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
y: { grid: { display: false }, beginAtZero: true }
|
y: { grid: { display: false }, beginAtZero: true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getDataFromResponse(response: ApiResponse, sensor: string, parameter: string): { data: number[], labels: string[] } {
|
getDataFromResponse(response: ApiResponse, sensor: string, parameter: string): { data: number[], labels: string[] } {
|
||||||
|
|
@ -295,7 +334,12 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
if (sensorData) {
|
if (sensorData) {
|
||||||
sensorData.forEach(item => {
|
sensorData.forEach(item => {
|
||||||
data.push(item[parameter as keyof ParameterSensor] ?? 0);
|
data.push(item[parameter as keyof ParameterSensor] ?? 0);
|
||||||
|
if(this.interval === 'HOURLY') {
|
||||||
labels.push(`${item.hour}.00`);
|
labels.push(`${item.hour}.00`);
|
||||||
|
}
|
||||||
|
else if (this.interval === 'DAILY') {
|
||||||
|
labels.push(item.day);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.error('No data found for sensor:', sensor);
|
console.error('No data found for sensor:', sensor);
|
||||||
|
|
@ -306,6 +350,23 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
getLabels(response: ApiResponse, sensor: string): string[] {
|
getLabels(response: ApiResponse, sensor: string): string[] {
|
||||||
const sensorData = response.data[sensor as keyof typeof response.data];
|
const sensorData = response.data[sensor as keyof typeof response.data];
|
||||||
return sensorData ? sensorData.map(item => `${item.hour}.00`) : [];
|
|
||||||
|
return sensorData.map(item => {
|
||||||
|
if (this.interval === 'HOURLY' || this.selectedInterval === 'HOURLY') {
|
||||||
|
return `${item.hour}.00`;
|
||||||
|
} else if (this.interval === 'DAILY') {
|
||||||
|
const day = item.day;
|
||||||
|
return this.convertDateToDay(day);
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
convertDateToDay(dateString: string): string {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thrusday', 'Friday', 'Saturday'];
|
||||||
|
return days[date.getDay()];
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="container">
|
||||||
|
<div>
|
||||||
|
<h1 class="title">{{ greeting }}</h1>
|
||||||
|
<h3 class="description">View your historical data with customizable time ranges</h3>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 class="update">Latest Update: {{ latestUpdate }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button (click)="updateInterval('HOURLY')" [class.active-button]="selectedButton === 'hourly'">Hourly</button>
|
||||||
|
<button (click)="updateInterval('DAILY')" [class.active-button]="selectedButton === 'daily'">Daily</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="graph">
|
||||||
|
<app-graph [interval]="selectedInterval"></app-graph>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
.container {
|
||||||
|
font-family: "Onest", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: #49473C;
|
||||||
|
font-size: 30px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: #49473C;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update{
|
||||||
|
color: #49473C;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 30px;
|
||||||
|
margin-top: 20px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-parameter{
|
||||||
|
border: 1px solid #16423C;
|
||||||
|
color: #16423C;
|
||||||
|
padding: 20px 0px 20px 0px;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
flex: 1 1 30%;
|
||||||
|
max-width: 30%;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-parameter:hover{
|
||||||
|
background-color: #16423C;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content{
|
||||||
|
text-align: center;
|
||||||
|
margin:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-graph{
|
||||||
|
color: #49473C;
|
||||||
|
font-size: 23px;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.graph{
|
||||||
|
margin-top: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relay-container {
|
||||||
|
padding: 20px 0px 20px 0px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 30px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.relay-card {
|
||||||
|
border-radius: 8px;
|
||||||
|
flex: 1 1 30%;
|
||||||
|
max-width: 30%;
|
||||||
|
min-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.card-parameter{
|
||||||
|
flex: 1 1 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container{
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.card-parameter{
|
||||||
|
flex: 1 1 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container{
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active-button {
|
||||||
|
background-color: #cad849;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading{
|
||||||
|
font-size: 18px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-on {
|
||||||
|
color: #16423C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-off {
|
||||||
|
color: rgb(144, 6, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
color: #16423C
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { HistorygraphComponent } from './historygraph.component';
|
||||||
|
|
||||||
|
describe('HistorygraphComponent', () => {
|
||||||
|
let component: HistorygraphComponent;
|
||||||
|
let fixture: ComponentFixture<HistorygraphComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [HistorygraphComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(HistorygraphComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||||
|
import { GraphComponent } from '../graph/graph.component';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { ChangeDetectorRef } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-historygraph',
|
||||||
|
standalone: true,
|
||||||
|
imports: [GraphComponent, CommonModule],
|
||||||
|
templateUrl: './historygraph.component.html',
|
||||||
|
styleUrls: ['./historygraph.component.scss']
|
||||||
|
})
|
||||||
|
export class HistorygraphComponent implements OnInit, OnDestroy {
|
||||||
|
selectedButton: string = '';
|
||||||
|
selectedInterval: string = '';
|
||||||
|
latestUpdate: string = '';
|
||||||
|
intervalId: any;
|
||||||
|
greeting: string = '';
|
||||||
|
|
||||||
|
constructor(private cdr: ChangeDetectorRef) {}
|
||||||
|
|
||||||
|
@ViewChild(GraphComponent) graphComponent!: GraphComponent;
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.startClock();
|
||||||
|
this.updateGreeting();
|
||||||
|
this.selectedButton = 'hourly';
|
||||||
|
this.selectedInterval = 'HOURLY';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private debounceTimeout: any;
|
||||||
|
|
||||||
|
updateInterval(interval: string): void {
|
||||||
|
clearTimeout(this.debounceTimeout);
|
||||||
|
this.debounceTimeout = setTimeout(() => {
|
||||||
|
if (this.selectedInterval !== interval) {
|
||||||
|
this.selectedInterval = interval;
|
||||||
|
this.selectedButton = interval.toLowerCase();
|
||||||
|
this.cdr.detectChanges();
|
||||||
|
this.graphComponent.updateCharts();
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
if (this.intervalId) {
|
||||||
|
clearInterval(this.intervalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startClock(): void {
|
||||||
|
this.updateLatestTime();
|
||||||
|
this.intervalId = setInterval(() => this.updateLatestTime(), 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLatestTime(): void {
|
||||||
|
const now = new Date();
|
||||||
|
this.latestUpdate = now.toLocaleString('en-GB', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGreeting(): void {
|
||||||
|
const hour = new Date().getHours();
|
||||||
|
if (hour < 12) {
|
||||||
|
this.greeting = 'Good Morning';
|
||||||
|
} else if (hour < 18) {
|
||||||
|
this.greeting = 'Good Afternoon';
|
||||||
|
} else {
|
||||||
|
this.greeting = 'Good Evening';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user