src/app/components/body/battery-test/show-test-result/show-test-result.component.ts
| selector | app-show-test-result |
| styleUrls | ./show-test-result.component.css |
| templateUrl | ./show-test-result.component.html |
Properties |
|
Methods |
constructor(route: ActivatedRoute, _testChamberService: TestChamberService, _componentStoreService: ComponentStoreService, modalService: NgbModal, location: Location, router: Router)
|
|||||||||||||||||||||
|
Parameters :
|
| appendNewData | |||||||||
appendNewData(prevMeasurements: MeasuredParameters, newMeasurements: MeasuredParameters)
|
|||||||||
|
Parameters :
Returns :
void
|
| changeStatus | ||||||
changeStatus(status: string | null)
|
||||||
|
Parameters :
Returns :
void
|
| createChart | ||||||
createChart(channelQuickResponse: QuickResponseMeasurement)
|
||||||
|
Parameters :
Returns :
void
|
| deleteTest |
deleteTest()
|
|
Returns :
void
|
| download | ||||||
download(channelNo: number)
|
||||||
|
Parameters :
Returns :
void
|
| edit |
edit()
|
|
Returns :
void
|
| keepUpdatingChart | ||||||
keepUpdatingChart(channelQuickResponse: QuickResponseMeasurement)
|
||||||
|
Parameters :
Returns :
void
|
| ngOnDestroy |
ngOnDestroy()
|
|
Returns :
void
|
| ngOnInit |
ngOnInit()
|
|
Returns :
void
|
| onClickChild | ||||||
onClickChild(event: MouseEvent)
|
||||||
|
Parameters :
Returns :
void
|
| onClickParent | ||||||
onClickParent(event: MouseEvent)
|
||||||
|
Parameters :
Returns :
void
|
| pause |
pause()
|
|
Returns :
void
|
| play |
play()
|
|
Returns :
void
|
| sampleData | |||||||||
sampleData(data: number[] | undefined, samplingRate: number)
|
|||||||||
|
Parameters :
Returns :
void
|
| showChartView | ||||||
showChartView(channelNo: number)
|
||||||
|
Parameters :
Returns :
void
|
| stop |
stop()
|
|
Returns :
void
|
| toggleShowChart |
toggleShowChart()
|
|
Returns :
void
|
| updateConnection |
updateConnection()
|
|
Returns :
void
|
| allCharts |
Type : Charts[]
|
Default value : []
|
| Optional chamberId |
Type : string | null
|
| Optional charts |
Type : QueryList<BaseChartDirective>
|
Decorators :
@ViewChildren(BaseChartDirective)
|
| Optional chartSub |
Type : Subscription
|
| Optional connSub |
Type : Subscription
|
| isConnected |
Type : boolean
|
Default value : false
|
| isConnectedIntervalId |
Type : any
|
| maxNoOfDataPoints |
Type : number
|
Default value : 1000
|
| measurementUpdateIntervalId |
Type : any
|
| modal |
Type : any
|
Decorators :
@ViewChild('myModal')
|
| Optional modalBody |
Type : string
|
| Optional modalTitle |
Type : string
|
| quickResponses |
Type : QuickResponseMeasurement[]
|
Default value : []
|
| showChart |
Type : boolean
|
Default value : false
|
| subs |
Type : Subscription[]
|
Default value : []
|
| targetSampleSize |
Type : number
|
Default value : 700
|
| Optional testId |
Type : string | null
|
| Optional testInfo |
Type : _TestResultDeep
|
| testInfoIntervalId |
Type : any
|
| Optional testInfoSub |
Type : Subscription
|
| updatedUptoIndex |
Type : number
|
Default value : 0
|
import { ComponentStoreService } from './../../../../services/component-store.service';
import {
_TestResultDeep,
QuickResponseMeasurement,
MeasuredParameters,
SensorObj,
} from './../../../../models/TestResult';
import { TestChamberService } from 'src/app/services/test-chamber.service';
import { of, Subscription, switchMap } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { saveAs } from 'file-saver';
import { ChartConfiguration, ChartType } from 'chart.js';
import {
Component,
OnInit,
OnDestroy,
QueryList,
ViewChildren,
ViewChild,
} from '@angular/core';
import { BaseChartDirective } from 'ng2-charts';
import { Location } from '@angular/common';
interface Charts {
name?: string;
chartData?: ChartConfiguration['data'] | any;
ChartOptions?: ChartConfiguration['options'] | any;
ChartType: ChartType;
}
@Component({
selector: 'app-show-test-result',
templateUrl: './show-test-result.component.html',
styleUrls: ['./show-test-result.component.css'],
})
export class ShowTestResultComponent implements OnInit, OnDestroy {
subs: Subscription[] = [];
testInfo?: _TestResultDeep;
showChart: boolean = false;
allCharts: Charts[] = [];
quickResponses: QuickResponseMeasurement[] = [];
chartSub?: Subscription;
testId?: string | null;
chamberId?: string | null;
measurementUpdateIntervalId: any;
testInfoIntervalId: any;
testInfoSub?: Subscription;
modalTitle?: string;
modalBody?: string;
isConnected: boolean = false;
isConnectedIntervalId: any;
connSub?: Subscription;
maxNoOfDataPoints: number = 1000;
targetSampleSize: number = 700;
updatedUptoIndex = 0;
@ViewChildren(BaseChartDirective) charts?: QueryList<BaseChartDirective>;
@ViewChild('myModal') modal: any;
constructor(
private route: ActivatedRoute,
private _testChamberService: TestChamberService,
private _componentStoreService: ComponentStoreService,
private modalService: NgbModal,
private location: Location,
private router: Router
) {}
toggleShowChart() {
this.showChart = !this.showChart;
clearInterval(this.measurementUpdateIntervalId);
this.chartSub?.unsubscribe();
}
onClickChild(event: MouseEvent) {
event.stopPropagation();
}
onClickParent(event: MouseEvent) {
this.toggleShowChart();
}
showChartView(channelNo: number) {
this.allCharts = [];
this.toggleShowChart();
const channelQuickResponse = this.quickResponses.find(
(res) => res.channelNo === channelNo
);
if (channelQuickResponse) {
this.createChart(channelQuickResponse);
this.keepUpdatingChart(channelQuickResponse);
} else {
const sub = this._testChamberService
.getQuickResponse(
this.chamberId as any,
this.testId as any,
channelNo as any
)
.subscribe((data: QuickResponseMeasurement) => {
this.createChart(data);
this.quickResponses.push(data);
if (data.statusCh === 'Running') {
//if channel is running then only keep updating the channel
this.keepUpdatingChart(data);
}
sub.unsubscribe();
});
}
}
createChart(channelQuickResponse: QuickResponseMeasurement) {
let blankData: MeasuredParameters = {};
blankData.time = [];
if (
channelQuickResponse.measuredParameters.current &&
channelQuickResponse.measuredParameters.current.length > 0
) {
blankData.current = [];
let chart: Charts = {
name: 'Current',
chartData: {
datasets: [
{
data: blankData.current,
label: 'Current',
},
],
labels: blankData.time,
},
ChartOptions: {
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time(S)',
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Cell Current (A)',
},
},
],
},
},
ChartType: 'line',
};
this.allCharts.push(chart);
}
if (
channelQuickResponse.measuredParameters.voltage &&
channelQuickResponse.measuredParameters.voltage.length > 0
) {
blankData.voltage = [];
let chart: Charts = {
name: 'Voltage',
chartData: {
datasets: [
{
data: blankData.voltage,
label: 'Voltage',
},
],
labels: blankData.time,
},
ChartOptions: {
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time(S)',
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Cell Voltage(V)',
},
},
],
},
},
ChartType: 'line',
};
this.allCharts.push(chart);
}
if (
channelQuickResponse.measuredParameters.chamberTemp &&
channelQuickResponse.measuredParameters.chamberTemp.length > 0
) {
blankData.chamberTemp = [];
let chart: Charts = {
name: 'Chamber Temperature',
chartData: {
datasets: [
{
data: blankData.chamberTemp,
label: 'Chamber Temperature',
},
],
labels: blankData.time,
},
ChartOptions: {
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time(S)',
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Chamber Temperature (°C)',
},
},
],
},
},
ChartType: 'line',
};
this.allCharts.push(chart);
}
if (
channelQuickResponse.measuredParameters.chamberHum &&
channelQuickResponse.measuredParameters.chamberHum.length > 0
) {
blankData.chamberHum = [];
let chart: Charts = {
name: 'Chamber Humidity',
chartData: {
datasets: [
{
data: blankData.chamberHum,
label: 'Chamber Humidity',
},
],
labels: blankData.time,
},
ChartOptions: {
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time(S)',
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Chamber Humidity(%)',
},
},
],
},
},
ChartType: 'line',
};
this.allCharts.push(chart);
}
if (
channelQuickResponse.measuredParameters.cellTemp &&
channelQuickResponse.measuredParameters.cellTemp.length > 0
) {
blankData.cellTemp = [];
let chart: Charts = {
name: 'Cell Temperature',
chartData: {
datasets: [],
labels: blankData.time,
},
ChartOptions: {
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Time(S)',
},
},
],
yAxes: [
{
scaleLabel: {
display: true,
labelString: 'Cell Temperatures(°C)',
},
},
],
},
},
ChartType: 'line',
};
channelQuickResponse.measuredParameters.cellTemp.forEach(
(tempObj: SensorObj) => {
let d: SensorObj = { sensorId: tempObj.sensorId, values: [] };
blankData.cellTemp?.push(d);
chart.chartData.datasets.push({
data: d.values,
label: 'Sensor' + tempObj.sensorId,
});
}
);
this.allCharts.push(chart);
}
this.appendNewData(blankData, channelQuickResponse.measuredParameters);
}
keepUpdatingChart(channelQuickResponse: QuickResponseMeasurement) {
clearInterval(this.measurementUpdateIntervalId);
this.measurementUpdateIntervalId = setInterval(() => {
this.chartSub = this._testChamberService
.getQuickResponse(
this.chamberId as any,
this.testId as any,
channelQuickResponse.channelNo as any,
this.updatedUptoIndex - 1
)
.subscribe((data: any) => {
this.appendNewData(
channelQuickResponse.measuredParameters,
data.measuredParameters as MeasuredParameters
);
this.charts?.forEach((chart) => chart.update());
this.chartSub?.unsubscribe();
if (data.status === 'Completed' || data.status === 'Stopped') {
clearInterval(this.measurementUpdateIntervalId);
}
});
}, 2000);
}
appendNewData(
prevMeasurements: MeasuredParameters,
newMeasurements: MeasuredParameters
) {
if (newMeasurements.current && newMeasurements.current?.length > 0) {
prevMeasurements.current?.push(...newMeasurements.current);
}
if (newMeasurements.voltage && newMeasurements.voltage?.length > 0) {
prevMeasurements.voltage?.push(...newMeasurements.voltage);
}
if (newMeasurements.chamberHum && newMeasurements.chamberHum?.length > 0) {
prevMeasurements.chamberHum?.push(...newMeasurements.chamberHum);
}
if (
newMeasurements.chamberTemp &&
newMeasurements.chamberTemp?.length > 0
) {
prevMeasurements.chamberTemp?.push(...newMeasurements.chamberTemp);
}
if (newMeasurements.time && newMeasurements.time?.length > 0) {
prevMeasurements.time?.push(...newMeasurements.time);
}
newMeasurements.cellTemp?.forEach((tempObjNew, i) => {
const tempObjOld = prevMeasurements.cellTemp?.find(
(tempObj) => tempObj.sensorId === tempObjNew.sensorId
);
if (tempObjOld) {
tempObjOld.values.push(...tempObjNew.values);
} else {
prevMeasurements.cellTemp?.push(tempObjNew);
}
});
this.updatedUptoIndex += newMeasurements.time
? newMeasurements.time.length
: 0;
if (
prevMeasurements.time &&
prevMeasurements.time.length > this.maxNoOfDataPoints
) {
// Use sampling technique
const samplingRate = prevMeasurements.time.length / this.targetSampleSize;
this.sampleData(prevMeasurements.time, samplingRate);
this.sampleData(prevMeasurements.current, samplingRate);
this.sampleData(prevMeasurements.voltage, samplingRate);
this.sampleData(prevMeasurements.chamberHum, samplingRate);
this.sampleData(prevMeasurements.chamberTemp, samplingRate);
prevMeasurements.cellTemp?.forEach((tempObj) => {
this.sampleData(tempObj.values, samplingRate);
});
}
}
sampleData(data: number[] | undefined, samplingRate: number): void {
if (!data || samplingRate <= 1) {
return;
}
let writeIndex = 0;
for (
let readIndex = 0;
readIndex < data.length;
readIndex += samplingRate
) {
data[writeIndex++] = data[Math.floor(readIndex)];
}
data.length = writeIndex;
}
ngOnInit(): void {
//console.log(this.router.url);
if (window.location.hostname != 'localhost') {
this.location.replaceState('./'); //on prod
}
const os$ = this.route.paramMap.pipe(
switchMap((params) => {
this.testId = params.get('testId');
this.chamberId = params.get('chamberId');
return this._testChamberService.getTestData(
this.chamberId as any,
this.testId as any
);
})
);
this.testInfoSub = os$.subscribe((testInfo: _TestResultDeep) => {
this.testInfo = testInfo;
this.testInfoSub?.unsubscribe();
});
this.testInfoIntervalId = setInterval(() => {
if (this.testId && this.chamberId) {
this.testInfoSub?.unsubscribe();
this.testInfoSub = this._testChamberService
.getTestData(this.chamberId as any, this.testId as any)
.subscribe((testInfo) => {
this.testInfo = testInfo;
this.testInfoSub?.unsubscribe();
if (
this.testInfo?.status === 'Completed' ||
this.testInfo?.status === 'Stopped'
) {
clearInterval(this.testInfoIntervalId);
}
});
}
}, 10000);
this.updateConnection();
this.isConnectedIntervalId = setInterval(
() => this.updateConnection(),
10000
);
}
deleteTest() {
this.modalTitle = 'Alert';
this.modalBody =
'Are you sure you want to delete all the information related to this experiment? Once you do it, you can never retrieve it.';
this.modalService.open(this.modal, { centered: true }).result.then(
(result) => {
//console.log('accepted');
this._testChamberService
.deleteTest(this.chamberId as any, this.testId as any)
.subscribe((res) => {
let url = '';
let path = this.router.url;
if (path.indexOf('view-all') > 0) {
url = '../../../../';
} else {
url = '../../../';
}
this.router.navigate([url], {
relativeTo: this.route,
skipLocationChange: true,
});
});
},
(reason) => {
//console.log('closed');
}
);
}
updateConnection() {
if (this.chamberId) {
this.connSub?.unsubscribe();
this.connSub = this._testChamberService
.getConnectionStatus(this.chamberId as any)
.subscribe({
next: (pay: any) => {
this.isConnected = pay.isConnected;
this.connSub?.unsubscribe();
},
error: (err) => {
this.isConnected = false;
},
});
}
}
edit() {}
play() {
this.changeStatus('Running');
}
pause() {
this.changeStatus('Paused');
}
stop() {
this.modalTitle = 'Alert';
this.modalBody =
'Are you sure you want to stop the experiment? Once you stop it, you can never start it again.';
this.modalService.open(this.modal, { centered: true }).result.then(
(result) => {
//console.log('accepted');
this.changeStatus('Stopped');
},
(reason) => {
//console.log('closed');
}
);
}
changeStatus(status: string | null) {
const sub = this._testChamberService
.forceStatus(this.chamberId as any, this.testId as any, status)
.subscribe((res: any) => {
if (res.acknowledged) {
this._componentStoreService.sendToastMsg({
msg: 'Status has been sent to the cloud. Wait for the Test Chamber to reflect the status',
timeOut: 10000,
color: 'black',
});
}
});
this.subs.push(sub);
}
download(channelNo: number) {
this._testChamberService
.downloadTestResult(this.chamberId as any, this.testId as any, channelNo)
.subscribe((response) => {
const blob = new Blob([response.body], {
type: 'text/csv;charset=utf-8;',
});
const url = URL.createObjectURL(blob);
const contentDispositionHeader = response.headers.get(
'content-disposition'
);
const filename = contentDispositionHeader
? contentDispositionHeader.split('filename=')[1].replace(/"/g, '')
: 'testResult.csv';
saveAs(url, filename);
});
}
ngOnDestroy(): void {
this.subs.forEach((sub) => sub.unsubscribe());
this.testInfoSub?.unsubscribe();
this.chartSub?.unsubscribe();
clearInterval(this.measurementUpdateIntervalId);
clearInterval(this.testInfoIntervalId);
this.connSub?.unsubscribe();
clearInterval(this.isConnectedIntervalId);
}
}
<div class="card test-info-container" *ngIf="testInfo">
<div class="card-header">
<div class="row">
<div class="col">Test Data</div>
<div class="col text-end">
Chamber -
<span *ngIf="isConnected; else not_connected"
>Online
<div
class="spinner-grow spinner-grow-sm text-light"
role="status"
*ngIf="true"
>
<span class="sr-only">Loading...</span>
</div>
</span>
<ng-template #not_connected>
<span>Offline</span>
</ng-template>
</div>
</div>
</div>
<div class="card-body">
<div class="overall">
<div class="row">
<div class="col-md-4"><strong>Test Id:</strong></div>
<div class="col-md-8">{{ testInfo._id }}</div>
</div>
<div class="row">
<div class="col-md-4"><strong>Test Name:</strong></div>
<div class="col-md-8">{{ testInfo.testName }}</div>
</div>
<div class="row">
<div class="col-md-4"><strong>Chamber Name:</strong></div>
<div class="col-md-8">{{ testInfo.chamberName }}</div>
</div>
<div class="row">
<div class="col-md-4"><strong>Status:</strong></div>
<div class="col-md-8">{{ testInfo.status }}</div>
</div>
<div class="row" *ngIf="testInfo.testScheduleDate">
<div class="col-4"><strong>Test Scheduled Date</strong></div>
<div class="col-8">
{{ testInfo.testScheduleDate | date : "MMM d, y, h:mm a" }}
</div>
</div>
<div class="row" *ngIf="testInfo.testStartDate">
<div class="col-4"><strong>Test Start Date</strong></div>
<div class="col-8">
{{ testInfo.testStartDate | date : "MMM d, y, h:mm a" }}
</div>
</div>
<div class="row" *ngIf="testInfo.testEndDate">
<div class="col-4"><strong>Test End Date</strong></div>
<div class="col-8">
{{ testInfo.testEndDate | date : "MMM d, y, h:mm a" }}
</div>
</div>
<div
class="row control justify-content-end text-end"
*ngIf="
testInfo.accessType === 'write' || testInfo.accessType === 'admin'
"
>
<div class="col">
<ng-container *ngIf="testInfo.status === 'Scheduled'">
<button class="btn btn-info" type="button" (click)="edit()">
<span class="material-symbols-outlined align-middle"> edit </span>
</button>
</ng-container>
<ng-container *ngIf="testInfo.status === 'Running'">
<button class="btn btn-warning" type="button" (click)="pause()">
<span class="material-symbols-outlined align-middle">
pause
</span>
</button>
</ng-container>
<ng-template #resume_btn>
<button type="button" class="btn btn-success" (click)="play()">
<span class="material-symbols-outlined align-middle"> play </span>
</button>
</ng-template>
<ng-container
*ngIf="
testInfo.status !== 'Scheduled' && testInfo.status !== 'Completed'
"
>
<button type="button" class="btn btn-danger">
<span
class="material-symbols-outlined align-middle"
type="button"
(click)="stop()"
>
stop
</span>
</button>
</ng-container>
</div>
</div>
</div>
<div class="channel-container">
<div class="row" *ngFor="let channel of testInfo.channels">
<div class="col">
<div class="card mb-3">
<div class="card-header">
<div class="row justify-content-between">
<div class="col">Channel {{ channel.channelNumber }}</div>
<div class="col text-end">
<ng-container
*ngIf="channel.status && channel.status !== 'Scheduled'"
>
<span class="text-nowrap">
<span
class="material-symbols-outlined show-chart align-middle text-center"
(click)="showChartView(channel.channelNumber)"
>
monitoring
</span>
<span
class="material-symbols-outlined download align-middle text-center"
(click)="download(channel.channelNumber)"
>
download
</span>
</span>
</ng-container>
{{ channel.status }}
<div
class="spinner-border spinner-border-sm text-primary align-middle"
role="status"
*ngIf="channel.status === 'Running'"
>
<span class="visually-hidden">Loading...</span>
</div>
</div>
</div>
</div>
<div class="card-body">
<div
class="row mb-3"
*ngFor="let row of channel.testFormats; let i = index"
>
<div class="col-md-1">
<strong class="text-center">{{ i + 1 }}</strong>
</div>
<div class="col md-11">
<div
class="row mb-2 justify-content-between align-items-center"
>
<div class="col">
<span class="badge bg-info">
<ng-container *ngFor="let field of row.fields">
<ng-container *ngIf="field.visibility">
<span
*ngIf="field.type !== 'file'; else drive_cycle"
>{{ field.value }}</span
>
<ng-template #drive_cycle>
<span>X</span>
</ng-template>
</ng-container>
</ng-container>
</span>
<span class="badge bg-info">
<span
class="material-symbols-outlined align-middle field"
>
cycle
</span>
<span>{{ row.multiplier }}</span>
</span>
<span class="badge bg-info">
<span
class="material-symbols-outlined align-middle field"
>
device_thermostat
</span>
<span> {{ row.ambTemp }}°C</span>
</span>
</div>
<div
class="col-md-2 text-end"
*ngIf="channel.rows && channel.rows[i]"
>
<small>{{ channel.rows[i].status }}</small>
</div>
</div>
<div
class="row mb-2 align-items-center"
*ngIf="
channel.rows && channel.rows[i];
else default_template
"
>
<div class="col-md-1 text-center">
@ {{ channel.rows[i].currentMultiplierIndex }}
</div>
<div class="col">
<div class="progress" style="padding: 0px">
<div
class="progress-bar progress-bar-striped"
role="progressbar"
[style.width.%]="
(channel.rows[i].currentMultiplierIndex /
channel.rows[i].multiplier) *
100
"
[attr.aria-valuenow]="
(channel.rows[i].currentMultiplierIndex /
channel.rows[i].multiplier) *
100
"
aria-valuemin="0"
aria-valuemax="100"
[class]="
channel.rows[i].status === 'Running'
? 'progress-bar-animated'
: null
"
></div>
</div>
</div>
</div>
<ng-template #default_template>
<div class="row mb-2">
<div class="col">
<small style="padding-left: 3px"
>Not yet started.</small
>
</div>
</div>
</ng-template>
</div>
</div>
<div class="row mb-3" style="border-top: 1px solid gray">
<div class="col text-start">Overall</div>
</div>
<ng-container
*ngIf="
channel.status && channel.status !== 'Scheduled';
else default_overall
"
>
<div class="row mb-2">
<div class="col-md-2">Current Cycle</div>
<div class="col-md-10">
<div class="row align-items-center">
<div class="col-md-1">
{{ channel.currentMultiplierIndex }}
</div>
<div class="col-md-11">
<div class="progress" style="padding: 0px">
<div
class="progress-bar bg-danger progress-bar-striped"
role="progressbar"
[style.width.%]="
channel.currentMultiplierIndex &&
channel.multiplier
? (channel.currentMultiplierIndex /
channel.multiplier) *
100
: 0
"
[attr.aria-valuenow]="
channel.currentMultiplierIndex &&
channel.multiplier
? (channel.currentMultiplierIndex /
channel.multiplier) *
100
: 0
"
aria-valuemin="0"
aria-valuemax="100"
[class]="
channel.status === 'Running'
? 'progress-bar-animated'
: null
"
></div>
</div>
</div>
</div>
</div>
</div>
<div class="row" *ngIf="channel.chStartDate">
<div class="col-md-2">Channel Start Date</div>
<div class="col-md-10">
{{ channel.chStartDate | date : "MMM d, y, h:mm a" }}
</div>
</div>
<div class="row" *ngIf="channel.chEndDate">
<div class="col-md-2">Channel Start Date</div>
<div class="col-md-10">
{{ channel.chEndDate | date : "MMM d, y, h:mm a" }}
</div>
</div>
</ng-container>
<ng-template #default_overall>
<div class="row mb-w">
<div class="col">Channel result not available</div>
</div>
</ng-template>
</div>
</div>
</div>
</div>
<div class="row mb-3" style="border-top: 1px solid gray"></div>
<div class="row">
<div class="col">
<small>@ Progress shows in terms of multiplier count.</small>
</div>
<div
class="col text-end align-middle"
*ngIf="
(testInfo.status == 'Scheduled' ||
testInfo.status == 'Completed' ||
testInfo.status == 'Stopped') &&
testInfo.accessType == 'admin'
"
>
<span class="material-symbols-outlined delete" (click)="deleteTest()">
delete_forever
</span>
</div>
</div>
</div>
</div>
</div>
<ng-container *ngIf="showChart">
<div class="chart-background" (click)="onClickParent($event)">
<div class="charts" (click)="onClickChild($event)">
<div class="card">
<div class="card-header">
<div class="row justify-content-between">
<div class="col">
<span
class="material-symbols-outlined align-middle cross"
(click)="toggleShowChart()"
>
close
</span>
<span class="align-middle">Charts</span>
</div>
</div>
</div>
<div class="card-body">
<ng-container *ngIf="allCharts.length > 0; else no_chart">
<div class="row justify-content-center">
<div
class="col-md-6"
*ngFor="let chart of allCharts; let i = index"
>
<canvas
baseChart
height="150"
[data]="chart.chartData"
[options]="chart.ChartOptions"
[type]="chart.ChartType"
>
</canvas>
</div>
</div>
</ng-container>
<ng-template #no_chart>
<span> Hold on, loading your charts.</span>
</ng-template>
</div>
</div>
</div>
</div>
</ng-container>
<ng-template #myModal let-modal>
<div class="modal-header">
<h4 class="modal-title" id="modal-basic-title">{{ modalTitle }}</h4>
<button
type="button"
class="close btn btn-danger"
aria-label="Close"
(click)="modal.dismiss('Cross click')"
>
<span aria-hidden="true">X</span>
</button>
</div>
<div class="modal-body">{{ modalBody }}</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-outline-dark"
(click)="modal.close('Save click')"
>
Ok
</button>
</div>
</ng-template>
./show-test-result.component.css
.test-info-container {
margin-top: 10px;
}
.test-info-container > .card-header {
background-color: #010305cf;
color: white;
}
.channel-container {
border-top: 5px solid #91699b57;
margin-top: 10px;
padding-top: 10px;
}
.badge .material-symbols-outlined {
font-size: inherit;
}
.badge > span {
height: 100%;
padding: 0px 2px;
vertical-align: middle;
font-size: large;
}
.badge {
margin: 2px 2px;
}
.overall .row {
margin-bottom: 3px;
font-family: Verdana, sans-serif;
}
.show-chart,
.download,
.cross {
margin: 0px 10px;
}
.show-chart:hover,
.download:hover,
.cross:hover {
color: red;
cursor: pointer;
}
.chart-background {
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
z-index: 7;
background: rgba(255, 255, 255, 0.192);
backdrop-filter: blur(10px);
overflow: hidden;
}
.cross {
float: right;
}
.charts {
position: relative;
top: 10%;
z-index: 8;
margin: auto;
width: 80%;
max-height: 80%;
overflow-y: auto;
}
.chart-background .card {
margin: 0px 10px;
}
.control button {
margin: 0px 2px;
}
.delete {
margin: 0px 2px;
}
.delete:hover {
color: red;
cursor: pointer;
}