Use Partman intervals, chart improvements #121
5 changed files with 118 additions and 91 deletions
|
@ -1 +1,2 @@
|
||||||
<div class="chart"></div>
|
<div class="chart"></div>
|
||||||
|
<div class="spinner" *ngIf="loading"><mat-spinner></mat-spinner></div>
|
||||||
|
|
|
@ -7,10 +7,7 @@ import { NgIf } from '@angular/common';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'chart',
|
selector: 'chart',
|
||||||
imports: [
|
imports: [NgIf, MatProgressSpinnerModule],
|
||||||
NgIf,
|
|
||||||
MatProgressSpinnerModule,
|
|
||||||
],
|
|
||||||
templateUrl: './charts.component.html',
|
templateUrl: './charts.component.html',
|
||||||
styleUrl: './charts.component.scss',
|
styleUrl: './charts.component.scss',
|
||||||
})
|
})
|
||||||
|
@ -50,77 +47,87 @@ export class ChartsComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.interval.pipe(switchMap((intv) => {
|
this.interval
|
||||||
this.loading = true;
|
.pipe(
|
||||||
return this.callsSvc.getCallStats(intv);
|
switchMap((intv) => {
|
||||||
})).
|
d3.select(this.elementRef.nativeElement).select('.chart').html('');
|
||||||
subscribe((stats) => {
|
this.loading = true;
|
||||||
let cMax = 0;
|
return this.callsSvc.getCallStats(intv);
|
||||||
var cMin = 0;
|
}),
|
||||||
let data = stats.stats.map((rec) => {
|
)
|
||||||
if (cMin == 0 && rec.count > cMin) {
|
.subscribe((stats) => {
|
||||||
cMin = rec.count;
|
let cMax = 0;
|
||||||
}
|
var cMin = 0;
|
||||||
if (rec.count < cMin) {
|
let data = stats.stats.map((rec) => {
|
||||||
cMin = rec.count;
|
if (cMin == 0 && rec.count > cMin) {
|
||||||
}
|
cMin = rec.count;
|
||||||
if (rec.count > cMax) {
|
}
|
||||||
cMax = rec.count;
|
if (rec.count < cMin) {
|
||||||
}
|
cMin = rec.count;
|
||||||
return { count: rec.count, time: this.dateFormat(new Date(rec.time), stats.interval) };
|
}
|
||||||
});
|
if (rec.count > cMax) {
|
||||||
// set the dimensions and margins of the graph
|
cMax = rec.count;
|
||||||
var margin = { top: 30, right: 30, bottom: 70, left: 60 },
|
}
|
||||||
width = 460 - margin.left - margin.right,
|
return {
|
||||||
height = 400 - margin.top - margin.bottom;
|
count: rec.count,
|
||||||
// clear the old one
|
time: this.dateFormat(new Date(rec.time), stats.interval),
|
||||||
d3.select(this.elementRef.nativeElement).select('.chart svg').remove();
|
};
|
||||||
const svg = d3
|
|
||||||
.select(this.elementRef.nativeElement)
|
|
||||||
.select('.chart')
|
|
||||||
.append('svg')
|
|
||||||
.attr('width', width + margin.left + margin.right)
|
|
||||||
.attr('height', height + margin.top + margin.bottom)
|
|
||||||
.append('g')
|
|
||||||
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
|
||||||
// X axis
|
|
||||||
var x = d3
|
|
||||||
.scaleBand()
|
|
||||||
.range([0, width])
|
|
||||||
.domain(
|
|
||||||
data.map(function (d) {
|
|
||||||
return d.time;
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.padding(0.2);
|
|
||||||
svg
|
|
||||||
.append('g')
|
|
||||||
.attr('transform', 'translate(0,' + height + ')')
|
|
||||||
.call(d3.axisBottom(x))
|
|
||||||
.selectAll('text')
|
|
||||||
.attr('transform', 'translate(-10,0)rotate(-45)')
|
|
||||||
.style('text-anchor', 'end');
|
|
||||||
|
|
||||||
// Add Y axis
|
|
||||||
var y = d3.scaleLinear().domain([0, cMax]).range([height, 0]);
|
|
||||||
svg.append('g').call(d3.axisLeft(y));
|
|
||||||
svg
|
|
||||||
.selectAll('mybar')
|
|
||||||
.data(data)
|
|
||||||
.enter()
|
|
||||||
.append('rect')
|
|
||||||
.attr('x', (d) => x(d.time)!)
|
|
||||||
.attr('y', function (d) {
|
|
||||||
return y(d.count);
|
|
||||||
})
|
|
||||||
.attr('width', x.bandwidth())
|
|
||||||
.attr('height', function (d) {
|
|
||||||
return height - y(d.count);
|
|
||||||
})
|
|
||||||
.attr('fill', function (d) {
|
|
||||||
return d3.interpolateTurbo((d.count - cMin) / (cMax - cMin));
|
|
||||||
});
|
});
|
||||||
this.loading = false;
|
// set the dimensions and margins of the graph
|
||||||
});
|
var margin = { top: 30, right: 30, bottom: 70, left: 60 },
|
||||||
|
width = 460 - margin.left - margin.right,
|
||||||
|
height = 400 - margin.top - margin.bottom;
|
||||||
|
// clear the old one
|
||||||
|
d3.select(this.elementRef.nativeElement).select('.chart').html('');
|
||||||
|
const svg = d3
|
||||||
|
.select(this.elementRef.nativeElement)
|
||||||
|
.select('.chart')
|
||||||
|
.append('svg')
|
||||||
|
.attr('width', width + margin.left + margin.right)
|
||||||
|
.attr('height', height + margin.top + margin.bottom)
|
||||||
|
.append('g')
|
||||||
|
.attr(
|
||||||
|
'transform',
|
||||||
|
'translate(' + margin.left + ',' + margin.top + ')',
|
||||||
|
);
|
||||||
|
// X axis
|
||||||
|
var x = d3
|
||||||
|
.scaleBand()
|
||||||
|
.range([0, width])
|
||||||
|
.domain(
|
||||||
|
data.map(function (d) {
|
||||||
|
return d.time;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.padding(0.2);
|
||||||
|
svg
|
||||||
|
.append('g')
|
||||||
|
.attr('transform', 'translate(0,' + height + ')')
|
||||||
|
.call(d3.axisBottom(x))
|
||||||
|
.selectAll('text')
|
||||||
|
.attr('transform', 'translate(-10,0)rotate(-45)')
|
||||||
|
.style('text-anchor', 'end');
|
||||||
|
|
||||||
|
// Add Y axis
|
||||||
|
var y = d3.scaleLinear().domain([0, cMax]).range([height, 0]);
|
||||||
|
svg.append('g').call(d3.axisLeft(y));
|
||||||
|
svg
|
||||||
|
.selectAll('mybar')
|
||||||
|
.data(data)
|
||||||
|
.enter()
|
||||||
|
.append('rect')
|
||||||
|
.attr('x', (d) => x(d.time)!)
|
||||||
|
.attr('y', function (d) {
|
||||||
|
return y(d.count);
|
||||||
|
})
|
||||||
|
.attr('width', x.bandwidth())
|
||||||
|
.attr('height', function (d) {
|
||||||
|
return height - y(d.count);
|
||||||
|
})
|
||||||
|
.attr('fill', function (d) {
|
||||||
|
return d3.interpolateTurbo((d.count - cMin) / (cMax - cMin));
|
||||||
|
});
|
||||||
|
this.loading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<mat-card class="chart" appearance="outlined">
|
<mat-card class="chart" appearance="outlined">
|
||||||
<div class="chartTitle">Calls By</div>
|
|
||||||
<form [formGroup]="form">
|
<form [formGroup]="form">
|
||||||
<mat-form-field>
|
<mat-form-field>
|
||||||
<mat-select formControlName="callsPer">
|
<label>Calls By</label>
|
||||||
|
<mat-select formControlName="callsPer">
|
||||||
<mat-option value="hour">Hour</mat-option>
|
<mat-option value="hour">Hour</mat-option>
|
||||||
<mat-option value="day">Day</mat-option>
|
<mat-option value="day">Day</mat-option>
|
||||||
<mat-option value="week">Week</mat-option>
|
<mat-option value="week">Week</mat-option>
|
||||||
<mat-option value="month">Month</mat-option>
|
<mat-option value="month">Month</mat-option>
|
||||||
</mat-select>
|
</mat-select>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</form>
|
</form>
|
||||||
<chart [interval]="callsOb"></chart>
|
<chart #chart [interval]="callsOb"></chart>
|
||||||
</mat-card>
|
</mat-card>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mat-card.chart {
|
mat-card.chart {
|
||||||
width: 500px;
|
width: 500px;
|
||||||
height: 400px;
|
height: 500px;
|
||||||
margin: 30px 30px 40px 40px;
|
margin: 30px 30px 40px 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,35 @@
|
||||||
import { Component, computed, signal, ViewChild } from '@angular/core';
|
import { Component, computed, signal, ViewChild } from '@angular/core';
|
||||||
import { ChartsComponent } from '../charts/charts.component';
|
import { ChartsComponent } from '../charts/charts.component';
|
||||||
import { MatCardModule } from '@angular/material/card';
|
import { MatCardModule } from '@angular/material/card';
|
||||||
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
import {
|
||||||
import { debounceTime, filter, Observable, startWith, switchAll, switchMap } from 'rxjs';
|
FormControl,
|
||||||
|
FormGroup,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
} from '@angular/forms';
|
||||||
|
import {
|
||||||
|
debounceTime,
|
||||||
|
filter,
|
||||||
|
Observable,
|
||||||
|
startWith,
|
||||||
|
switchAll,
|
||||||
|
switchMap,
|
||||||
|
} from 'rxjs';
|
||||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||||
import { MatSelectModule } from '@angular/material/select';
|
import { MatSelectModule } from '@angular/material/select';
|
||||||
import { MatInputModule } from '@angular/material/input';
|
import { MatInputModule } from '@angular/material/input';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-home',
|
selector: 'app-home',
|
||||||
imports: [ChartsComponent, MatCardModule, MatFormFieldModule, MatSelectModule, MatInputModule, FormsModule, ReactiveFormsModule ],
|
imports: [
|
||||||
|
ChartsComponent,
|
||||||
|
MatCardModule,
|
||||||
|
MatFormFieldModule,
|
||||||
|
MatSelectModule,
|
||||||
|
MatInputModule,
|
||||||
|
FormsModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
],
|
||||||
templateUrl: './home.component.html',
|
templateUrl: './home.component.html',
|
||||||
styleUrl: './home.component.scss',
|
styleUrl: './home.component.scss',
|
||||||
})
|
})
|
||||||
|
@ -20,12 +40,11 @@ export class HomeComponent {
|
||||||
callsOb!: Observable<string>;
|
callsOb!: Observable<string>;
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.form.controls["callsPer"].setValue("week");
|
this.form.controls['callsPer'].setValue('week');
|
||||||
this.callsOb = this.form.controls['callsPer'].valueChanges
|
this.callsOb = this.form.controls['callsPer'].valueChanges.pipe(
|
||||||
.pipe(
|
startWith('week'),
|
||||||
startWith("week"),
|
|
||||||
debounceTime(300),
|
debounceTime(300),
|
||||||
filter(val => val !== null)
|
filter((val) => val !== null),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue