aboutsummaryrefslogtreecommitdiff
path: root/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'frontend')
-rw-r--r--frontend/src/app/_elements/_charts/box-plot/box-plot.component.html2
-rw-r--r--frontend/src/app/_elements/_charts/line-chart/line-chart.component.html4
-rw-r--r--frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html2
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.css4
-rw-r--r--frontend/src/app/_elements/column-table/column-table.component.ts78
-rw-r--r--frontend/src/app/_elements/folder/folder.component.ts11
-rw-r--r--frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html4
-rw-r--r--frontend/src/app/_pages/profile/profile.component.css5
-rw-r--r--frontend/src/app/_pages/profile/profile.component.html8
-rw-r--r--frontend/src/app/_pages/profile/profile.component.ts44
-rw-r--r--frontend/src/app/app.component.ts114
-rw-r--r--frontend/src/styles/helper.css8
12 files changed, 222 insertions, 62 deletions
diff --git a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
index 688eafae..3b2bf976 100644
--- a/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
+++ b/frontend/src/app/_elements/_charts/box-plot/box-plot.component.html
@@ -1,3 +1,3 @@
-<div class="chart-wrapper">
+<div class="chart-wrapper position-relative">
<canvas #boxplot [width]="width" [height]="height"></canvas>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
index d48212cd..1c711562 100644
--- a/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
+++ b/frontend/src/app/_elements/_charts/line-chart/line-chart.component.html
@@ -1,4 +1,4 @@
-<div #wrapper style="width:100%;height:95%;">
- <canvas id="myChart" #canvas>
+<div #wrapper class="position-relative" style="width:100%;height:95%;">
+ <canvas id="myChart" #canvas>
</canvas>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
index 7faf3af0..fe3998ff 100644
--- a/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
+++ b/frontend/src/app/_elements/_charts/pie-chart/pie-chart.component.html
@@ -1,3 +1,3 @@
-<div class="chart-wrapper">
+<div class="chart-wrapper position-relative">
<canvas #piechart [width]="width" [height]="height"></canvas>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_elements/column-table/column-table.component.css b/frontend/src/app/_elements/column-table/column-table.component.css
index 311773f7..40ae3c05 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.css
+++ b/frontend/src/app/_elements/column-table/column-table.component.css
@@ -13,7 +13,7 @@ table.fixed {
}
table.fixed td {
- overflow: hidden;
+ /* overflow: hidden; */
max-width: 200px;
min-width: 200px;
vertical-align: middle;
@@ -21,7 +21,7 @@ table.fixed td {
}
table.fixed th {
- overflow: hidden;
+ /* overflow: hidden; */
max-width: 250px;
min-width: 120px;
vertical-align: middle;
diff --git a/frontend/src/app/_elements/column-table/column-table.component.ts b/frontend/src/app/_elements/column-table/column-table.component.ts
index 9a8cbeee..26228978 100644
--- a/frontend/src/app/_elements/column-table/column-table.component.ts
+++ b/frontend/src/app/_elements/column-table/column-table.component.ts
@@ -114,20 +114,11 @@ export class ColumnTableComponent implements AfterViewInit {
else
this.columnsChecked.push(false);
});
- this.nullValOption = [];
+ //this.nullValOption = [];
for (let i = 0; i < this.dataset!.columnInfo.length; i++) {
- let nullValRep = this.experiment.nullValuesReplacers.find(x => x.column == this.dataset!.columnInfo[i].columnName);
- if (nullValRep != undefined) {
- if (nullValRep.option == NullValueOptions.DeleteRows)
- this.nullValOption.push(`Obriši redove (${this.dataset.columnInfo[i].numNulls})`);
- else if (nullValRep.option == NullValueOptions.DeleteColumns)
- this.nullValOption.push(`Obriši kolonu`);
- else {
- this.nullValOption.push(`Popuni sa ${nullValRep.value}`);
- }
- }
- else
- this.nullValOption.push(`Obriši redove (${this.dataset.columnInfo[i].numNulls})`);
+ //let nullValRep = this.experiment.nullValuesReplacers.find(x => x.column == this.dataset!.columnInfo[i].columnName);
+ let nullValRep = this.experiment.nullValuesReplacers[i];
+ this.nullValOption[i] = (nullValRep.option == NullValueOptions.DeleteRows) ? `Obriši redove (${this.dataset.columnInfo[i].numNulls})` : ((nullValRep.option == NullValueOptions.DeleteColumns) ? `Isključi kolonu` : `Popuni sa ${nullValRep.value}`);
}
}
this.resetPagging();
@@ -171,10 +162,10 @@ export class ColumnTableComponent implements AfterViewInit {
}
setDeleteRowsForMissingValTreatment() {
- if (this.experiment != undefined) {
+ if (this.experiment != undefined && this.dataset != undefined) {
this.experiment.nullValues = NullValueOptions.DeleteRows;
this.experiment.nullValuesReplacers = [];
- for (let i = 0; i < this.experiment.inputColumns.length; i++) {
+ for (let i = 0; i < this.dataset?.columnInfo.length; i++) {
this.experiment.nullValuesReplacers.push({
column: this.experiment.inputColumns[i],
option: NullValueOptions.DeleteRows,
@@ -204,13 +195,17 @@ export class ColumnTableComponent implements AfterViewInit {
}
if (this.experiment.inputColumns.length == 1)
this.experiment.outputColumn = this.experiment.inputColumns[0];
+
+ let index = this.dataset?.columnInfo.findIndex(x => x.columnName == columnName)!;
+ this.nullValOption[index] = (this.experiment.nullValuesReplacers[index].option == NullValueOptions.DeleteRows) ? "Obriši redove (" + this.dataset?.columnInfo[index].numNulls + ")" : "Popuni sa " + this.experiment.nullValuesReplacers[index].value;
}
else {
this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != columnName);
//console.log("Input columns: ", this.experiment.inputColumns);
//TODO: da se zatamni kolona koja je unchecked
//this.experiment.encodings = this.experiment.encodings.filter(x => x.columnName != columnName); samo na kraju iz enkodinga skloni necekirane
- this.experiment.nullValuesReplacers = this.experiment.nullValuesReplacers.filter(x => x.column != columnName);
+ let index = this.dataset?.columnInfo.findIndex(x => x.columnName == columnName)!;
+ this.nullValOption[index] = "Isključi kolonu";
if (columnName == this.experiment.outputColumn) {
if (this.experiment.inputColumns.length > 0)
this.experiment.outputColumn = this.experiment.inputColumns[0];
@@ -224,11 +219,12 @@ export class ColumnTableComponent implements AfterViewInit {
outputColumnChanged() {
let outputColReplacer = this.experiment.nullValuesReplacers.find(x => x.column == this.experiment.outputColumn);
- let index = this.experiment.nullValuesReplacers.findIndex(x => x.column == this.experiment.outputColumn);
+ //let index = this.experiment.nullValuesReplacers.findIndex(x => x.column == this.experiment.outputColumn);
if (outputColReplacer != undefined) {
outputColReplacer.option = NullValueOptions.DeleteRows;
let numOfRowsToDelete = (this.dataset!.columnInfo.filter(x => x.columnName == this.experiment.outputColumn)[0]).numNulls;
+ let index = this.dataset!.columnInfo.findIndex(x => x.columnName == this.experiment.outputColumn);
this.nullValOption[index] = "Obriši redove (" + numOfRowsToDelete + ")";
}
@@ -285,7 +281,16 @@ export class ColumnTableComponent implements AfterViewInit {
if (this.experiment != undefined && this.dataset != undefined) {
if (selectedMissingValuesOption == NullValueOptions.DeleteColumns) {
- this.experiment.nullValues = NullValueOptions.DeleteColumns;
+ for (let i = 0; i < this.dataset.columnInfo.length; i++) {
+ if (this.dataset.columnInfo[i].numNulls > 0 && this.dataset.columnInfo[i].columnName != this.experiment.outputColumn) {
+ this.experiment.inputColumns = this.experiment.inputColumns.filter(x => x != this.dataset!.columnInfo[i].columnName);
+ this.columnsChecked[i] = false;
+ console.log(this.dataset!.columnInfo[i].columnName);
+
+ this.nullValOption[i] = "Isključi kolonu";
+ }
+ }
+ /*this.experiment.nullValues = NullValueOptions.DeleteColumns;
let outputColReplacer = this.experiment.nullValuesReplacers.find(x => x.column == this.experiment.outputColumn);
@@ -297,16 +302,18 @@ export class ColumnTableComponent implements AfterViewInit {
option: NullValueOptions.DeleteColumns,
value: ""
});
- this.nullValOption[i] = "Obriši kolonu";
+ let colIndex = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment.inputColumns[i]);
+ this.nullValOption[colIndex] = "Isključi kolonu";
}
else {
if (outputColReplacer != undefined) {
this.experiment.nullValuesReplacers.push(outputColReplacer);
let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[i])[0]).numNulls;
- this.nullValOption[i] = (outputColReplacer.option == NullValueOptions.DeleteRows) ? "Obriši redove (" + numOfRowsToDelete + ")" : "Popuni sa " + outputColReplacer.value + "";
+ let colIndex = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment.inputColumns[i]);
+ this.nullValOption[colIndex] = (outputColReplacer.option == NullValueOptions.DeleteRows) ? "Obriši redove (" + numOfRowsToDelete + ")" : "Popuni sa " + outputColReplacer.value + "";
}
}
- }
+ }*/
//obrisi kolone koje sadrze nedostajuce vrednosti iz input kolona
/*for (let i = 0; i < this.dataset.columnInfo.length; i++) {
if (this.dataset.columnInfo[i].numNulls > 0) {
@@ -326,7 +333,8 @@ export class ColumnTableComponent implements AfterViewInit {
value: ""
});
let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[i])[0]).numNulls;
- this.nullValOption[i] = "Obriši redove (" + numOfRowsToDelete + ")";
+ let colIndex = this.dataset.columnInfo.findIndex(x => x.columnName == this.experiment.inputColumns[i]);
+ this.nullValOption[colIndex] = "Obriši redove (" + numOfRowsToDelete + ")";
}
}
this.columnTableChangeDetected();
@@ -369,7 +377,16 @@ export class ColumnTableComponent implements AfterViewInit {
MissValsDeleteClicked(event: Event, replacementType: NullValueOptions, index: number) {
if (this.experiment != undefined && this.dataset != undefined) {
- let columnName = (<HTMLInputElement>event.currentTarget).value;
+
+ this.experiment.nullValuesReplacers[index].option = NullValueOptions.DeleteRows;
+ this.experiment.nullValuesReplacers[index].value = "";
+
+ this.nullValOption[index] = "Obriši redove (" + this.dataset.columnInfo[index].numNulls + ")";
+
+ this.columnTableChangeDetected();
+ }
+
+ /*let columnName = (<HTMLInputElement>event.currentTarget).value;
let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
if (arrayElement == undefined) {
@@ -385,14 +402,21 @@ export class ColumnTableComponent implements AfterViewInit {
}
let numOfRowsToDelete = (this.dataset.columnInfo.filter(x => x.columnName == this.experiment!.inputColumns[index])[0]).numNulls;
- this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Obriši kolonu" : "Obriši redove (" + numOfRowsToDelete + ")";
+ this.nullValOption[index] = (replacementType == NullValueOptions.DeleteColumns) ? "Isključi kolonu" : "Obriši redove (" + numOfRowsToDelete + ")";
this.columnTableChangeDetected();
- }
+ }*/
}
MissValsReplaceClicked(event: Event, columnName: string, index: number) {
if (this.experiment != undefined) {
- let fillValue = (<HTMLInputElement>event.currentTarget).value;
+ this.experiment.nullValuesReplacers[index].option = NullValueOptions.Replace;
+ let value = (<HTMLInputElement>event.currentTarget).value;
+ this.experiment.nullValuesReplacers[index].value = value;
+ this.nullValOption[index] = "Popuni sa " + value;
+
+ this.columnTableChangeDetected();
+
+ /*let fillValue = (<HTMLInputElement>event.currentTarget).value;
let arrayElement = this.experiment.nullValuesReplacers.filter(x => x.column == columnName)[0];
if (arrayElement == undefined) {
@@ -408,7 +432,7 @@ export class ColumnTableComponent implements AfterViewInit {
}
this.nullValOption[index] = "Popuni sa: " + fillValue;
- this.columnTableChangeDetected();
+ this.columnTableChangeDetected();*/
}
}
getValue(columnName: string): string {
diff --git a/frontend/src/app/_elements/folder/folder.component.ts b/frontend/src/app/_elements/folder/folder.component.ts
index 6e04f963..93e8ba98 100644
--- a/frontend/src/app/_elements/folder/folder.component.ts
+++ b/frontend/src/app/_elements/folder/folder.component.ts
@@ -182,6 +182,9 @@ export class FolderComponent implements AfterViewInit {
this.folders[TabType.MyModels] = models;
if (selectedModelId) {
this.selectFile(models.filter(x => x._id == selectedModelId)[0]);
+ setTimeout(() => {
+ this.okPressed.emit();
+ });
}
this.searchTermsChanged();
});
@@ -233,11 +236,14 @@ export class FolderComponent implements AfterViewInit {
}
saveNewFile() {
+ this.loadingAction = true;
switch (this.type) {
case FolderType.Dataset:
this.formDataset!.uploadDataset((dataset: Dataset) => {
this.newFile = undefined;
- Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se obradi.");
+ this.loadingAction = false;
+ this.okPressed.emit();
+ //Shared.openDialog("Obaveštenje", "Uspešno ste dodali novi izvor podataka u kolekciju. Molimo sačekajte par trenutaka da se obradi.");
this.refreshFiles();
},
() => {
@@ -248,7 +254,8 @@ export class FolderComponent implements AfterViewInit {
this.formModel.newModel.type = this.formModel.forProblemType;
this.modelsService.addModel(this.formModel.newModel).subscribe(model => {
this.newFile = undefined;
- Shared.openDialog("Obaveštenje", "Uspešno ste dodali novu konfiguraciju neuronske mreže u kolekciju.");
+ this.loadingAction = false;
+ //Shared.openDialog("Obaveštenje", "Uspešno ste dodali novu konfiguraciju neuronske mreže u kolekciju.");
this.refreshFiles(null, model._id); // todo select model
}, (err) => {
Shared.openDialog("Neuspeo pokušaj!", "Konfiguracija neuronske mreže sa unetim nazivom već postoji u Vašoj kolekciji. Izmenite naziv ili iskoristite postojeću konfiguraciju.");
diff --git a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html
index 259aec0d..0470d395 100644
--- a/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html
+++ b/frontend/src/app/_modals/yes-no-dialog/yes-no-dialog.component.html
@@ -5,6 +5,6 @@
</form>
</div>
<div mat-dialog-actions class="d-flex justify-content-center mt-4">
- <button id="btnYes" mat-stroked-button color="basic" (click)="onYesClick()" style="background-color: lightgray;">Da</button>
- <button id="btnNo" mat-stroked-button (click)="onNoClick()" style="background-color: lightgray;">Ne</button>
+ <button id="btnYes" mat-stroked-button color="basic" (click)="onYesClick()">Da</button>
+ <button id="btnNo" mat-stroked-button (click)="onNoClick()">Ne</button>
</div> \ No newline at end of file
diff --git a/frontend/src/app/_pages/profile/profile.component.css b/frontend/src/app/_pages/profile/profile.component.css
index bbd4c9ba..48b1304b 100644
--- a/frontend/src/app/_pages/profile/profile.component.css
+++ b/frontend/src/app/_pages/profile/profile.component.css
@@ -22,4 +22,9 @@ mat-form-field {
.selectedPicture {
background-color: var(--ns-accent);
+}
+
+.mat-raised-button {
+ width: 180px !important;
+ height: 50px !important;
} \ No newline at end of file
diff --git a/frontend/src/app/_pages/profile/profile.component.html b/frontend/src/app/_pages/profile/profile.component.html
index 8d655513..39f33255 100644
--- a/frontend/src/app/_pages/profile/profile.component.html
+++ b/frontend/src/app/_pages/profile/profile.component.html
@@ -27,11 +27,11 @@
<div class="row gx-3 mb-3">
<!-- Form Group (password)-->
<div class="col-md-6">
- <small *ngIf="wrongPassBool" class="form-text danger-Text">Neispravna lozinka.</small>
<mat-form-field appearance="fill">
<mat-label>Važeća lozinka</mat-label>
<input matInput id="inputPassword" name="inputPassword" type="password" placeholder="" [(ngModel)]="this.oldPass">
</mat-form-field>
+ <small *ngIf="wrongPassBool" class="form-text danger-Text">Neispravna lozinka.</small>
<small *ngIf="wrongOldPassBool" class="form-text danger-Text">Pogrešan format.</small>
</div>
<!-- Form Group (new password)-->
@@ -50,7 +50,7 @@
<div class="col-md-6">
<div class="col text-center">
<!-- Save changes button-->
- <button mat-raised-button color="primary" (click)="savePasswordChanges()">Promeni lozinku</button>
+ <button mat-raised-button color="basic" (click)="savePasswordChanges()">Promeni lozinku</button>
</div>
</div>
<!-- Form Group (new password again)-->
@@ -122,7 +122,7 @@
<div class="container">
<div class="card-group">
<!--bootstrap card with 3 horizontal images-->
- <div class="row overflow-auto" style="max-height: 200px;">
+ <div class="row overflow-auto" style="max-height: 255px;">
<div class="card col-md-3" *ngFor="let picture of this.pictures" (click)="this.photoId = picture.photoId.toString()" [ngClass]="{'selectedPicture': this.photoId == picture.photoId.toString()}">
<img src="{{picture.path}}">
</div>
@@ -134,7 +134,7 @@
<div class="row mt-5">
<div class="col text-center">
<!-- Save changes button-->
- <button mat-raised-button color="primary" (click)="saveInfoChanges()">Sačuvaj izmene</button>
+ <button mat-raised-button color="basic" (click)="saveInfoChanges()">Sačuvaj izmene</button>
</div>
</div>
</form>
diff --git a/frontend/src/app/_pages/profile/profile.component.ts b/frontend/src/app/_pages/profile/profile.component.ts
index 3c81883e..aed8e40c 100644
--- a/frontend/src/app/_pages/profile/profile.component.ts
+++ b/frontend/src/app/_pages/profile/profile.component.ts
@@ -44,7 +44,7 @@ export class ProfileComponent implements OnInit {
wrongNewPass2Bool: boolean = false;
pattName: RegExp = /^[a-zA-ZšŠđĐčČćĆžŽ]+([ \-][a-zA-ZšŠđĐčČćĆžŽ]+)*$/;
- pattUsername: RegExp = /^[a-zA-Z0-9]{6,18}$/;
+ pattUsername: RegExp = /^[a-zA-Z0-9]{4,18}$/;
pattTwoSpaces: RegExp = / /;
pattEmail: RegExp = /^[a-zA-Z0-9]+([\.\-\+][a-zA-Z0-9]+)*\@([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}$/;
pattPassword: RegExp = /.{6,30}$/;
@@ -96,24 +96,26 @@ export class ProfileComponent implements OnInit {
if (this.user.username != editedUser.username) { //promenio username, ide logout
this.user = editedUser;
this.resetInfo();
- shared.openDialog("Obaveštenje", "Nakon promene korisničkog imena, moraćete ponovo da se ulogujete.");
- this.authService.logOut();
- this.router.navigate(['']);
- return;
- }
- shared.openDialog("Obaveštenje", "Podaci su uspešno promenjeni.");
- this.user = editedUser;
- this.resetInfo();
- }, (error: any) =>{
- if (error.error == "Username already exists!") {
const dialogRef = this.dialog?.open(AlertDialogComponent, {
width: '350px',
- data: { title: "Obaveštenje", message: "Ukucano korisničko ime je već zauzeto! Izaberite neko drugo." }
+ data: { title: "Obaveštenje", message: "Nakon promene korisničkog imena, moraćete ponovo da se ulogujete." }
});
dialogRef?.afterClosed().subscribe(res => {
- this.resetInfo();
+ this.authService.logOut();
+ this.router.navigate(['']);
+ return;
});
}
+ else {
+ shared.openDialog("Obaveštenje", "Podaci su uspešno promenjeni.");
+ this.user = editedUser;
+ this.resetInfo();
+ }
+ }, (error: any) =>{
+ if (error.error == "Username already exists!") {
+ shared.openDialog("Obaveštenje", "Ukucano korisničko ime je već zauzeto! Izaberite neko drugo.");
+ this.resetInfo();
+ }
});
}
@@ -126,12 +128,10 @@ export class ProfileComponent implements OnInit {
if (this.newPass1 == '' && this.newPass2 == '') //ne zeli da promeni lozinku
return;
- //console.log("zeli da promeni lozinku");
if (this.newPass1 != this.newPass2) { //netacno ponovio novu lozinku
this.wrongNewPassBool = true;
this.resetNewPassInputs();
- //console.log("Netacno ponovljena lozinka");
return;
}
@@ -139,19 +139,23 @@ export class ProfileComponent implements OnInit {
this.userInfoService.changeUserPassword(passwordArray).subscribe((response: any) => {
//console.log("PROMENIO LOZINKU");
this.resetNewPassInputs();
- shared.openDialog("Obaveštenje", "Nakon promene lozinke, moraćete ponovo da se ulogujete.");
- this.authService.logOut();
- this.router.navigate(['']);
+ const dialogRef = this.dialog?.open(AlertDialogComponent, {
+ width: '350px',
+ data: { title: "Obaveštenje", message: "Nakon promene lozinke, moraćete ponovo da se ulogujete." }
+ });
+ dialogRef?.afterClosed().subscribe(res => {
+ this.authService.logOut();
+ this.router.navigate(['']);
+ return;
+ });
}, (error: any) => {
if (error.error == 'Wrong old password!') {
this.wrongPassBool = true;
- //(<HTMLSelectElement>document.getElementById("inputPassword")).focus();
return;
}
else if (error.error == 'Identical password!') {
shared.openDialog("Obaveštenje", "Stara i nova lozinka su identične.");
this.resetNewPassInputs();
- //(<HTMLSelectElement>document.getElementById("inputNewPassword")).focus();
return;
}
});
diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts
index e301b46f..5660f676 100644
--- a/frontend/src/app/app.component.ts
+++ b/frontend/src/app/app.component.ts
@@ -6,6 +6,7 @@ import { AuthService } from './_services/auth.service';
import { SignalRService } from './_services/signal-r.service';
import { HttpClient } from '@angular/common/http';
import Shared from './Shared';
+import { Chart } from 'chart.js';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
@@ -14,6 +15,117 @@ import Shared from './Shared';
export class AppComponent implements OnInit, AfterViewInit {
constructor(private router: Router, private titleService: Title, private authService: AuthService, private signalRService: SignalRService, private http: HttpClient) {
+ const getOrCreateTooltip = (chart: { canvas: { parentNode: { querySelector: (arg0: string) => any; appendChild: (arg0: any) => void; }; }; }) => {
+ let tooltipEl = chart.canvas.parentNode.querySelector('div');
+
+ if (!tooltipEl) {
+ tooltipEl = document.createElement('div');
+ tooltipEl.style.background = 'rgba(0, 0, 0, 0.7)';
+ tooltipEl.style.borderRadius = '3px';
+ tooltipEl.style.color = 'white';
+ tooltipEl.style.opacity = 1;
+ tooltipEl.style.pointerEvents = 'none';
+ tooltipEl.style.position = 'absolute';
+ tooltipEl.style.transform = 'translate(-50%, 0)';
+ tooltipEl.style.transition = 'all .1s ease';
+ tooltipEl.style.zIndex = 9000;
+
+ tooltipEl.classList.add("clearfix");
+
+ const table = document.createElement('table');
+ table.style.margin = '0px';
+
+ tooltipEl.appendChild(table);
+ chart.canvas.parentNode.appendChild(tooltipEl);
+ }
+
+ return tooltipEl;
+ };
+
+ const externalTooltipHandler = (context: { chart: any; tooltip: any; }) => {
+ // Tooltip Element
+ const { chart, tooltip } = context;
+ const tooltipEl = getOrCreateTooltip(chart);
+
+ // Hide if no tooltip
+ if (tooltip.opacity === 0) {
+ tooltipEl.style.opacity = 0;
+ return;
+ }
+
+ // Set Text
+ if (tooltip.body) {
+ const titleLines = tooltip.title || [];
+ const bodyLines = tooltip.body.map((b: { lines: any; }) => b.lines);
+
+ const tableHead = document.createElement('thead');
+
+ titleLines.forEach((title: string) => {
+ const tr = document.createElement('tr');
+ tr.style.borderWidth = '' + 0;
+
+ const th = document.createElement('th');
+ th.style.borderWidth = '' + 0;
+ const text = document.createTextNode(title);
+
+ th.appendChild(text);
+ tr.appendChild(th);
+ tableHead.appendChild(tr);
+ });
+
+ const tableBody = document.createElement('tbody');
+ bodyLines.forEach((body: string, i: string | number) => {
+ const colors = tooltip.labelColors[i];
+
+ const span = document.createElement('span');
+ span.style.background = colors.backgroundColor;
+ span.style.borderColor = colors.borderColor;
+ span.style.borderWidth = '2px';
+ span.style.marginRight = '10px';
+ span.style.height = '10px';
+ span.style.width = '10px';
+ span.style.display = 'inline-block';
+
+ const tr = document.createElement('tr');
+ tr.style.backgroundColor = 'inherit';
+ tr.style.borderWidth = '' + 0;
+
+ const td = document.createElement('td');
+ td.style.borderWidth = '' + 0;
+
+ const text = document.createTextNode(body);
+
+ td.appendChild(span);
+ td.appendChild(text);
+ tr.appendChild(td);
+ tableBody.appendChild(tr);
+ });
+
+ const tableRoot = tooltipEl.querySelector('table');
+
+ // Remove old children
+ while (tableRoot.firstChild) {
+ tableRoot.firstChild.remove();
+ }
+
+ // Add new children
+ tableRoot.appendChild(tableHead);
+ tableRoot.appendChild(tableBody);
+ }
+
+ const { offsetLeft: positionX, offsetTop: positionY } = chart.canvas;
+
+ // Display, position, and set styles for font
+ tooltipEl.style.opacity = 1;
+ tooltipEl.style.left = positionX + tooltip.caretX + 'px';
+ tooltipEl.style.top = positionY + tooltip.caretY + 'px';
+ tooltipEl.style.font = tooltip.options.bodyFont.string;
+ tooltipEl.style.padding = tooltip.options.padding + 'px ' + tooltip.options.padding + 'px';
+ };
+
+ Chart.defaults.plugins.tooltip.enabled = false;
+ Chart.defaults.plugins.tooltip.position = 'nearest';
+ Chart.defaults.plugins.tooltip.external = externalTooltipHandler;
}
ngAfterViewInit(): void {
}
@@ -40,7 +152,7 @@ export class AppComponent implements OnInit, AfterViewInit {
}
});
if (!this.authService.isAuthenticated()) {
- if(!this.authService.alreadyGuest())
+ if (!this.authService.alreadyGuest())
this.authService.addGuestToken();
}
this.signalRService.startConnection();
diff --git a/frontend/src/styles/helper.css b/frontend/src/styles/helper.css
index 971a2ed6..155fc3e1 100644
--- a/frontend/src/styles/helper.css
+++ b/frontend/src/styles/helper.css
@@ -142,4 +142,12 @@ select:-webkit-autofill {
a {
color: var(--ns-accent) !important;
+}
+
+.clearfix:after {
+ content: "\0020";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
} \ No newline at end of file