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 { AuthGuard } from './cores/guards/auth.guard';
|
||||
import { RegisterComponent } from './pages/register/register.component';
|
||||
import { HistorygraphComponent } from './pages/dashboard/page/historygraph/historygraph.component';
|
||||
|
||||
export const routes: Routes = [
|
||||
{
|
||||
|
|
@ -28,6 +29,11 @@ export const routes: Routes = [
|
|||
component: DashboardComponent,
|
||||
canActivate: [AuthGuard]
|
||||
},
|
||||
{
|
||||
path: 'historygraph',
|
||||
component: HistorygraphComponent,
|
||||
canActivate: [AuthGuard],
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export interface ParameterSensor {
|
||||
hour: number;
|
||||
day: number;
|
||||
|
||||
//for DHT sensor
|
||||
vicitemperature?: number;
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@
|
|||
<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>
|
||||
</li>
|
||||
<!-- <li>
|
||||
<a routerLink='/graph' 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>
|
||||
</li> -->
|
||||
<li>
|
||||
<a routerLink='/historygraph' data-bs-toggle="collapse" class="nav-link px-0 align-middle">
|
||||
<i class="bi bi-file-earmark-text title"></i> <span class="ms-1 d-none d-sm-inline menu">History Graph</span> </a>
|
||||
</li>
|
||||
</ul>
|
||||
<hr>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import { Component } from '@angular/core';
|
||||
import { AuthService } from '../../../../cores/services/auth.service';
|
||||
import { Router } from '@angular/router';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-sidebar',
|
||||
standalone: true,
|
||||
imports: [RouterModule],
|
||||
templateUrl: './sidebar.component.html',
|
||||
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 { SensorService } from '../../../../cores/services/sensor.service';
|
||||
import { ApiResponse, ParameterSensor } from '../../../../cores/interface/sensor-data';
|
||||
|
|
@ -27,10 +27,12 @@ const parameterColors: { [key: string]: string } = {
|
|||
templateUrl: './graph.component.html',
|
||||
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('myChartNPK1', { static: false }) npk1ChartElement!: ElementRef<HTMLCanvasElement>;
|
||||
@ViewChild('myChartNPK2', { static: false }) npk2ChartElement!: ElementRef<HTMLCanvasElement>;
|
||||
@Input() interval: string = 'hourly';
|
||||
selectedInterval: string = 'HOURLY';
|
||||
|
||||
isLoadingDHT: boolean = true;
|
||||
isLoadingNPK1: boolean = true;
|
||||
|
|
@ -72,14 +74,23 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
private resizeListener!: () => void;
|
||||
|
||||
constructor(private sensorService: SensorService) {}
|
||||
constructor(private sensorService: SensorService, private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.resizeListener = this.onResize.bind(this);
|
||||
window.addEventListener('resize', this.resizeListener);
|
||||
this.updateCharts();
|
||||
this.updateCharts();
|
||||
}
|
||||
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['interval'] && !changes['interval'].firstChange) {
|
||||
this.selectedInterval = changes['interval'].currentValue;
|
||||
this.updateCharts();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.onResize();
|
||||
}
|
||||
|
|
@ -97,45 +108,37 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.updateCharts();
|
||||
}
|
||||
|
||||
updateCharts(): void {
|
||||
this.isLoadingDHT = this.isLoadingNPK1 = this.isLoadingNPK2 = true;
|
||||
|
||||
getDate(): string {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(today.getDate()).padStart(2, '0');
|
||||
|
||||
|
||||
const startEnd = `${year}-${month}-${day}`;
|
||||
const timeRange = 'HOURLY';
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
Object.keys(this.charts).forEach(key => {
|
||||
if (this.charts[key]) {
|
||||
this.charts[key]?.destroy();
|
||||
this.charts[key] = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
// Fetch data for DHT
|
||||
this.sensorService.getSensorData('dht', '', startEnd, timeRange).subscribe({
|
||||
fetchDHTData(timeRange: string): void {
|
||||
const startEnd = this.getDate();
|
||||
this.sensorService.getSensorData('dht', 'npk', startEnd, timeRange).subscribe({
|
||||
next: (response) => {
|
||||
this.isLoadingDHT = false;
|
||||
if (response.statusCode === 200 && response.data.dht?.length > 0) {
|
||||
this.createChart(this.dhtChartElement.nativeElement, response, 'dht', 'npk');
|
||||
this.isNoDataDHT = false;
|
||||
} else {
|
||||
this.isNoDataDHT = true;
|
||||
}
|
||||
this.isLoadingDHT = false;
|
||||
if (response.statusCode === 200 && response.data.dht?.length > 0) {
|
||||
this.createChart(this.dhtChartElement.nativeElement, response, 'dht', 'npk');
|
||||
this.isNoDataDHT = false;
|
||||
} else {
|
||||
this.isNoDataDHT = true;
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
this.isLoadingDHT = false;
|
||||
this.isNoDataDHT = true;
|
||||
this.isLoadingDHT = false;
|
||||
this.isNoDataDHT = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Fetch data for NPK1
|
||||
this.sensorService.getSensorData('npk1', '', startEnd, timeRange).subscribe({
|
||||
fetchNPK1Data(timeRange: string): void {
|
||||
const startEnd = this.getDate();
|
||||
this.sensorService.getSensorData('npk1', this.selectedNPK1, startEnd, timeRange).subscribe({
|
||||
next: (response) => {
|
||||
this.isLoadingNPK1 = false;
|
||||
if (response.statusCode === 200 && response.data.npk1?.length > 0) {
|
||||
|
|
@ -150,10 +153,14 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.isNoDataNPK1 = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Fetch data for NPK2
|
||||
this.sensorService.getSensorData('npk2', '', startEnd, timeRange).subscribe({
|
||||
fetchNPK2Data(savedTimeRange: string): void {
|
||||
const startEnd = this.getDate();
|
||||
const timeRange = this.interval;
|
||||
this.sensorService.getSensorData('npk2', this.selectedNPK2, startEnd, savedTimeRange).subscribe({
|
||||
next: (response) => {
|
||||
console.log(savedTimeRange);
|
||||
this.isLoadingNPK2 = false;
|
||||
if (response.statusCode === 200 && response.data.npk2?.length > 0) {
|
||||
this.createChart(this.npk2ChartElement.nativeElement, response, 'npk2', this.selectedNPK2);
|
||||
|
|
@ -169,123 +176,155 @@ 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 {
|
||||
const ctx = canvas.getContext('2d');
|
||||
const parameters = this.sensorParameters[sensor];
|
||||
|
||||
|
||||
if (!ctx) {
|
||||
console.error('Failed to get canvas context for sensor:', sensor);
|
||||
return;
|
||||
console.error('Failed to get canvas context for sensor:', sensor);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let datasets: any[] = [];
|
||||
|
||||
|
||||
if (sensor === 'dht') {
|
||||
datasets = ['vicitemperature', 'viciluminosity', 'vicihumidity'].map(parameter => {
|
||||
const { data, labels } = this.getDataFromResponse(response, sensor, parameter);
|
||||
|
||||
if (data.length === 0) {
|
||||
console.warn(`No data found for parameter: ${parameter}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayName = this.parameterDisplayNames[parameter] || parameter;
|
||||
const borderColor = parameterColors[parameter] || '#000000';
|
||||
const backgroundColor = `${borderColor}4D`;
|
||||
|
||||
return {
|
||||
label: displayName,
|
||||
data,
|
||||
borderColor,
|
||||
borderWidth: 1.5,
|
||||
fill: true,
|
||||
backgroundColor,
|
||||
tension: 0.5,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 0,
|
||||
};
|
||||
}).filter(dataset => dataset !== null);
|
||||
datasets = ['vicitemperature', 'viciluminosity', 'vicihumidity'].map(parameter => {
|
||||
const { data, labels } = this.getDataFromResponse(response, sensor, parameter);
|
||||
|
||||
if (data.length === 0) {
|
||||
console.warn(`No data found for parameter: ${parameter}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayName = this.parameterDisplayNames[parameter] || parameter;
|
||||
const borderColor = parameterColors[parameter] || '#000000';
|
||||
const backgroundColor = `${borderColor}4D`;
|
||||
|
||||
const pointRadius = data.length === 1 ? 5 : 0;
|
||||
const pointHoverRadius = data.length === 1 ? 7 : 0;
|
||||
|
||||
return {
|
||||
label: displayName,
|
||||
data,
|
||||
borderColor,
|
||||
borderWidth: 1.5,
|
||||
fill: true,
|
||||
backgroundColor,
|
||||
tension: 0.5,
|
||||
pointRadius,
|
||||
pointHoverRadius,
|
||||
};
|
||||
}).filter(dataset => dataset !== null);
|
||||
} else {
|
||||
datasets = parameters
|
||||
.filter(parameter => {
|
||||
if (selectedOption === 'npk') {
|
||||
return ['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter);
|
||||
} else if (selectedOption === 'others') {
|
||||
return !['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(parameter => {
|
||||
const { data, labels } = this.getDataFromResponse(response, sensor, parameter);
|
||||
|
||||
if (data.length === 0) {
|
||||
console.warn(`No data found for parameter: ${parameter}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayName = this.parameterDisplayNames[parameter] || parameter;
|
||||
const borderColor = parameterColors[parameter] || '#000000';
|
||||
const backgroundColor = `${borderColor}4D`;
|
||||
|
||||
return {
|
||||
label: displayName,
|
||||
data,
|
||||
borderColor,
|
||||
borderWidth: 1.5,
|
||||
fill: true,
|
||||
backgroundColor,
|
||||
tension: 0.5,
|
||||
pointRadius: 0,
|
||||
pointHoverRadius: 0,
|
||||
};
|
||||
})
|
||||
.filter(dataset => dataset !== null);
|
||||
datasets = parameters
|
||||
.filter(parameter => {
|
||||
if (selectedOption === 'npk') {
|
||||
return ['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter);
|
||||
} else if (selectedOption === 'others') {
|
||||
return !['soilphosphorus', 'soilnitrogen', 'soilpotassium'].includes(parameter);
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.map(parameter => {
|
||||
const { data, labels } = this.getDataFromResponse(response, sensor, parameter);
|
||||
|
||||
if (data.length === 0) {
|
||||
console.warn(`No data found for parameter: ${parameter}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayName = this.parameterDisplayNames[parameter] || parameter;
|
||||
const borderColor = parameterColors[parameter] || '#000000';
|
||||
const backgroundColor = `${borderColor}4D`;
|
||||
|
||||
const pointRadius = data.length === 1 ? 5 : 0;
|
||||
const pointHoverRadius = data.length === 1 ? 7 : 0;
|
||||
|
||||
return {
|
||||
label: displayName,
|
||||
data,
|
||||
borderColor,
|
||||
borderWidth: 1.5,
|
||||
fill: true,
|
||||
backgroundColor,
|
||||
tension: 0.5,
|
||||
pointRadius,
|
||||
pointHoverRadius,
|
||||
};
|
||||
})
|
||||
.filter(dataset => dataset !== null);
|
||||
}
|
||||
|
||||
|
||||
if (datasets.length === 0) {
|
||||
console.warn('No valid datasets to render for sensor:', sensor);
|
||||
return;
|
||||
console.warn('No valid datasets to render for sensor:', sensor);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (this.charts[sensor]) {
|
||||
this.charts[sensor]?.destroy();
|
||||
this.charts[sensor]?.destroy();
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.charts[sensor] = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: this.getLabels(response, sensor),
|
||||
datasets,
|
||||
labels: this.getLabels(response, sensor),
|
||||
datasets,
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
aspectRatio: 2,
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: (tooltipItem) => {
|
||||
const paramLabel = tooltipItem.dataset.label;
|
||||
const value = tooltipItem.formattedValue;
|
||||
return `${paramLabel}: ${value}`;
|
||||
}
|
||||
}
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
aspectRatio: 2,
|
||||
plugins: {
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
mode: 'nearest',
|
||||
intersect: false,
|
||||
callbacks: {
|
||||
label: (tooltipItem) => {
|
||||
const paramLabel = tooltipItem.dataset.label;
|
||||
const value = tooltipItem.formattedValue;
|
||||
return `${paramLabel}: ${value}`;
|
||||
}
|
||||
}
|
||||
},
|
||||
legend: { display: true }
|
||||
},
|
||||
legend: { display: true }
|
||||
},
|
||||
scales: {
|
||||
x: { grid: { display: false }, beginAtZero: true },
|
||||
y: { grid: { display: false }, beginAtZero: true }
|
||||
}
|
||||
scales: {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
getDataFromResponse(response: ApiResponse, sensor: string, parameter: string): { data: number[], labels: string[] } {
|
||||
const sensorData = response.data[sensor as keyof typeof response.data];
|
||||
|
||||
|
|
@ -295,7 +334,12 @@ export class GraphComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
if (sensorData) {
|
||||
sensorData.forEach(item => {
|
||||
data.push(item[parameter as keyof ParameterSensor] ?? 0);
|
||||
labels.push(`${item.hour}.00`);
|
||||
if(this.interval === 'HOURLY') {
|
||||
labels.push(`${item.hour}.00`);
|
||||
}
|
||||
else if (this.interval === 'DAILY') {
|
||||
labels.push(item.day);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
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[] {
|
||||
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