Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): make deployment target selectable in uptime chart #236

Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
<h1 class="text-center font-semibold text-sm text-gray-700 dark:text-gray-400">Deployments per Manager</h1>
@if (chartOptions) {
<apx-chart
class="block h-60 max-w-full max-h-full overflow-hidden"
class="block h-52 max-w-full max-h-full overflow-hidden"
[series]="chartOptions.series!"
[labels]="chartOptions.labels!"
[colors]="chartOptions.colors!"
[chart]="chartOptions.chart!"
[stroke]="chartOptions.stroke!"
[tooltip]="chartOptions.tooltip!"
[xaxis]="chartOptions.xaxis!"
[legend]="chartOptions.legend!"
[title]="chartOptions.title!">
[legend]="chartOptions.legend!">
</apx-chart>
} @else {
<p class="mt-2 text-sm text-gray-700 dark:text-gray-300">Nothing to display right now.</p>
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,59 @@ export class ChartTypeComponent implements OnDestroy {

private readonly destroyed$ = new Subject<void>();

loading: boolean = true;

constructor() {
this.deploymentTargets$.pipe(takeUntil(this.destroyed$)).subscribe((dts) => {
this.chartOptions = {
series: [],
labels: [],
colors: ['#0db7ed', '#326CE5', '#174c76'],
chart: {
height: 192,
type: 'donut',
sparkline: {
enabled: true,
this.loading = false;

if (dts.length > 0) {
this.chartOptions = {
series: [],
labels: [],
colors: ['#0db7ed', '#326CE5', '#174c76'],
chart: {
height: 192,
type: 'donut',
sparkline: {
enabled: true,
},
},
stroke: {
curve: 'smooth',
},
},
stroke: {
curve: 'smooth',
},
tooltip: {
enabled: false,
},
legend: {
show: true,
position: 'top',
fontFamily: 'Inter',
labels: {
colors: 'rgb(156, 163, 175)',
useSeriesColors: false,
tooltip: {
enabled: false,
},
},
title: {
text: 'Deployment Managers',
align: 'center',
offsetY: 10,
style: {
color: 'rgb(156, 163, 175)',
fontFamily: 'Poppins',
legend: {
show: true,
position: 'top',
fontFamily: 'Inter',
labels: {
colors: 'rgb(156, 163, 175)',
useSeriesColors: false,
},
},
},
plotOptions: {
pie: {
customScale: 0.8,
plotOptions: {
pie: {
customScale: 0.8,
},
},
},
};
const managers: {[key: string]: UserAccountWithRole} = {};
const counts: {[key: string]: number} = {};
for (const dt of dts) {
if (dt.createdBy?.id && !managers[dt.createdBy.id]) {
managers[dt.createdBy.id] = dt.createdBy;
counts[dt.createdBy.id] = 1;
} else if (dt.createdBy?.id && managers[dt.createdBy.id]) {
counts[dt.createdBy.id] = counts[dt.createdBy.id] + 1;
};
const managers: {[key: string]: UserAccountWithRole} = {};
const counts: {[key: string]: number} = {};
for (const dt of dts) {
if (dt.createdBy?.id && !managers[dt.createdBy.id]) {
managers[dt.createdBy.id] = dt.createdBy;
counts[dt.createdBy.id] = 1;
} else if (dt.createdBy?.id && managers[dt.createdBy.id]) {
counts[dt.createdBy.id] = counts[dt.createdBy.id] + 1;
}
}
}

this.chartOptions.labels = Object.values(managers).map((v) => v.name ?? v.email);
this.chartOptions.series = Object.values(counts);
this.chartOptions.labels = Object.values(managers).map((v) => v.name ?? v.email);
this.chartOptions.series = Object.values(counts);
}
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,55 @@
<div class="relative">
<h1 class="inline-flex self-center font-semibold text-sm text-gray-700 dark:text-gray-400">
Uptime{{ selectedDeploymentTarget ? ': ' + selectedDeploymentTarget.name : '' }}
</h1>

<button
cdkOverlayOrigin
#dropdownTrigger="cdkOverlayOrigin"
(click)="showDropdown = !showDropdown"
class="inline-block absolute top-0 right-0 p-1 text-sm font-medium text-center text-gray-900 bg-white rounded-lg hover:bg-gray-100 focus:ring-4 focus:outline-none dark:text-white focus:ring-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
type="button">
<fa-icon [icon]="faEllipsis"></fa-icon>
</button>
</div>

<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayHasBackdrop]="true"
(backdropClick)="showDropdown = false"
[cdkConnectedOverlayBackdropClass]="'transparent'"
[cdkConnectedOverlayOrigin]="dropdownTrigger"
[cdkConnectedOverlayOpen]="showDropdown">
<div
@dropdown
style="transform-origin: top center"
class="my-2 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600">
<ul class="py-1" role="none">
@for (dt of deploymentTargets$ | async; track dt.id) {
<li>
<button
type="button"
(click)="selectDeploymentTarget(dt)"
class="block w-full px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem">
{{ dt.name }}
</button>
</li>
}
</ul>
</div>
</ng-template>

@if (chartOptions) {
<apx-chart
class="block h-60 max-w-full max-h-full overflow-hidden"
class="block h-52 max-w-full max-h-full overflow-hidden"
[series]="chartOptions.series!"
[chart]="chartOptions.chart!"
[stroke]="chartOptions.stroke!"
[tooltip]="chartOptions.tooltip!"
[xaxis]="chartOptions.xaxis!"
[legend]="chartOptions.legend!"
[title]="chartOptions.title!">
[legend]="chartOptions.legend!">
</apx-chart>
} @else {
<p class="mt-2 text-sm text-gray-700 dark:text-gray-300">Nothing to display right now.</p>
}
Original file line number Diff line number Diff line change
@@ -1,84 +1,102 @@
import {Component, inject, OnInit} from '@angular/core';
import {ApexOptions, NgApexchartsModule} from 'ng-apexcharts';
import {firstValueFrom, lastValueFrom} from 'rxjs';
import {firstValueFrom} from 'rxjs';
import {DeploymentTargetsService} from '../../../services/deployment-targets.service';
import {MetricsService} from '../../../services/metrics.service';
import {FaIconComponent} from '@fortawesome/angular-fontawesome';
import {faEllipsis} from '@fortawesome/free-solid-svg-icons';
import {CdkConnectedOverlay, CdkOverlayOrigin} from '@angular/cdk/overlay';
import {dropdownAnimation} from '../../../animations/dropdown';
import {AsyncPipe} from '@angular/common';
import {DeploymentTarget} from '../../../types/deployment-target';

@Component({
selector: 'app-chart-uptime',
templateUrl: './chart-uptime.component.html',
imports: [NgApexchartsModule],
imports: [NgApexchartsModule, FaIconComponent, CdkOverlayOrigin, CdkConnectedOverlay, AsyncPipe],
animations: [dropdownAnimation],
})
export class ChartUptimeComponent implements OnInit {
private readonly LOCAL_STORAGE_KEY = 'dashboard_uptime_deployment_target_id';
public chartOptions?: ApexOptions;

private readonly deploymentTargets = inject(DeploymentTargetsService);
private readonly deploymentTargets$ = this.deploymentTargets.list();
readonly deploymentTargets$ = this.deploymentTargets.list();

private readonly metrics = inject(MetricsService);

showDropdown = false;
storedDeploymentTargetId?: string;
selectedDeploymentTarget?: DeploymentTarget;

protected readonly faEllipsis = faEllipsis;

async ngOnInit() {
this.storedDeploymentTargetId = window.localStorage[this.LOCAL_STORAGE_KEY];
christophenne marked this conversation as resolved.
Show resolved Hide resolved
const dts = await firstValueFrom(this.deploymentTargets$);
for (const dt of dts) {
if (dt.currentStatus) {
// temporarily: simply show uptime of the first deployment target with a status and a deployment
if (!dt.latestDeployment) {
continue;
}
this.metrics.getUptimeForDeployment(dt.latestDeployment.id!).subscribe((uptimes) => {
this.chartOptions = {
series: [
{
name: 'available',
data: uptimes.map((ut) => ut.total - ut.unknown),
color: '#00bfa5',
},
{
name: 'unknown',
data: uptimes.map((ut) => ut.unknown),
color: '#feb019',
},
],
chart: {
height: 220,
type: 'bar',
stacked: true,
sparkline: {
enabled: true,
},
},
stroke: {
curve: 'smooth',
},
tooltip: {
enabled: false,
},
xaxis: {
type: 'datetime',
categories: uptimes.map((ut) => ut.hour),
},
legend: {
show: true,
position: 'top',
fontFamily: 'Inter',
offsetY: -18,
labels: {
colors: 'rgb(156, 163, 175)',
useSeriesColors: false,
},
},
title: {
text: `${dt.name}`,
align: 'center',
style: {
color: 'rgb(156, 163, 175)',
fontFamily: 'Poppins',
},
},
};
});
return;
}
let initiallySelected = dts.find((dt) => dt.id === this.storedDeploymentTargetId);
if (!initiallySelected && dts.length > 0) {
initiallySelected = dts[0];
}
if (initiallySelected) {
await this.selectDeploymentTarget(initiallySelected);
} else {
delete window.localStorage[this.LOCAL_STORAGE_KEY];
}
}

async selectDeploymentTarget(dt: DeploymentTarget) {
this.showDropdown = false;
this.selectedDeploymentTarget = dt;
window.localStorage[this.LOCAL_STORAGE_KEY] = dt.id;
this.chartOptions = undefined;
if (dt.latestDeployment?.id) {
const uptimes = await firstValueFrom(this.metrics.getUptimeForDeployment(dt.latestDeployment.id));
this.chartOptions = {
series: [
{
name: 'available',
data: uptimes.map((ut) => ut.total - ut.unknown),
color: '#00bfa5',
},
{
name: 'unknown',
data: uptimes.map((ut) => ut.unknown),
color: '#feb019',
},
],
chart: {
offsetY: 10,
//width: '100%',
//height: '80%',
type: 'bar',
stacked: true,
sparkline: {
enabled: true,
},
},
stroke: {
curve: 'smooth',
},
tooltip: {
enabled: false,
},
xaxis: {
type: 'datetime',
categories: uptimes.map((ut) => ut.hour),
},
legend: {
show: true,
position: 'top',
fontFamily: 'Inter',
offsetY: -5,
floating: true,
labels: {
colors: 'rgb(156, 163, 175)',
useSeriesColors: false,
},
},
};
}
}
}
Loading