2024-01-09 22:05:45 +01:00
|
|
|
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter, TemplateRef } from '@angular/core';
|
2022-01-11 00:32:21 +01:00
|
|
|
import { Router } from '@angular/router';
|
2021-12-24 15:21:22 +01:00
|
|
|
import { ApiClientService } from 'src/app/_services/api-client.service';
|
2021-12-27 19:58:38 +01:00
|
|
|
import { AuthService } from '../../_services/auth.service';
|
2022-01-11 13:33:39 +01:00
|
|
|
import { ToastrService } from 'ngx-toastr';
|
2024-01-09 22:05:45 +01:00
|
|
|
import { PageChangedEvent } from 'ngx-bootstrap/pagination';
|
2022-03-01 00:07:51 +01:00
|
|
|
import { TranslateService } from '@ngx-translate/core';
|
2022-01-11 13:33:39 +01:00
|
|
|
import Swal from 'sweetalert2';
|
2024-01-09 22:05:45 +01:00
|
|
|
import * as UAParser from 'ua-parser-js';
|
|
|
|
import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
|
2021-12-22 23:06:58 +01:00
|
|
|
|
|
|
|
@Component({
|
|
|
|
selector: 'app-table',
|
|
|
|
templateUrl: './table.component.html',
|
|
|
|
styleUrls: ['./table.component.scss']
|
|
|
|
})
|
2021-12-30 11:58:25 +01:00
|
|
|
export class TableComponent implements OnInit, OnDestroy {
|
2021-12-22 23:06:58 +01:00
|
|
|
|
|
|
|
@Input() sourceType?: string;
|
2021-12-29 13:12:16 +01:00
|
|
|
@Input() refreshInterval?: number;
|
2021-12-22 23:06:58 +01:00
|
|
|
|
2023-09-02 00:43:17 +02:00
|
|
|
enablePaginationTypes: string[] = ['logs', 'services', 'trainings'];
|
2023-09-02 16:53:42 +02:00
|
|
|
searchPropertiesBlacklist: string[] = [
|
|
|
|
"chief_id",
|
|
|
|
"type_id",
|
|
|
|
"pivot",
|
|
|
|
"place_id",
|
|
|
|
"display_name",
|
|
|
|
"licence",
|
|
|
|
"lat",
|
|
|
|
"lon",
|
|
|
|
"id",
|
|
|
|
"updated_at",
|
|
|
|
"added_by_id",
|
|
|
|
"updated_by_id",
|
|
|
|
"changed_id",
|
|
|
|
"editor_id"
|
2024-01-02 01:21:13 +01:00
|
|
|
];
|
|
|
|
|
2024-01-10 16:07:19 +01:00
|
|
|
enableDateRangePickerTypes: string[] = ['services', 'trainings', 'logs'];
|
2024-01-02 01:21:13 +01:00
|
|
|
range: (Date | undefined)[] | undefined = undefined;
|
|
|
|
lastRange: (Date | undefined)[] | undefined = undefined;
|
|
|
|
rangePicked = false;
|
|
|
|
filterStart: Date | undefined;
|
|
|
|
filterEnd: Date | undefined;
|
2023-09-02 00:43:17 +02:00
|
|
|
|
2024-01-10 16:07:19 +01:00
|
|
|
@Input() initialStartFilter: Date | undefined;
|
|
|
|
|
2023-09-02 00:43:17 +02:00
|
|
|
_maxPaginationSize: number = 10;
|
|
|
|
_rowsPerPage: number = 20;
|
|
|
|
|
|
|
|
@Input('maxPaginationSize')
|
|
|
|
get maxPaginationSize(): any {
|
|
|
|
return this._maxPaginationSize;
|
|
|
|
}
|
|
|
|
set maxPaginationSize(value: any) {
|
|
|
|
if(!isNaN(value)) this._maxPaginationSize = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Input('rowsPerPage')
|
|
|
|
get rowsPerPage(): any {
|
|
|
|
return this._rowsPerPage;
|
|
|
|
}
|
|
|
|
set rowsPerPage(value: any) {
|
|
|
|
if(!isNaN(value)) this._rowsPerPage = value;
|
|
|
|
}
|
|
|
|
|
2021-12-24 15:21:22 +01:00
|
|
|
@Output() changeAvailability: EventEmitter<{user: number, newState: 0|1}> = new EventEmitter<{user: number, newState: 0|1}>();
|
2022-02-14 11:40:47 +01:00
|
|
|
@Output() userImpersonate: EventEmitter<number> = new EventEmitter<number>();
|
2024-01-04 00:41:41 +01:00
|
|
|
@Output() moreDetails: EventEmitter<{rowId: number}> = new EventEmitter<{rowId: number}>();
|
2021-12-24 15:21:22 +01:00
|
|
|
|
2021-12-22 23:06:58 +01:00
|
|
|
public data: any = [];
|
2023-09-02 00:43:17 +02:00
|
|
|
public displayedData: any = [];
|
2023-09-02 16:53:42 +02:00
|
|
|
public originalData: any = [];
|
2023-10-23 08:08:10 +02:00
|
|
|
private etag: string = "";
|
2021-12-22 23:06:58 +01:00
|
|
|
|
2023-12-24 19:06:17 +01:00
|
|
|
public loadDataInterval: any = undefined;
|
2021-12-29 13:12:16 +01:00
|
|
|
|
2023-09-02 00:43:17 +02:00
|
|
|
public currentPage: number = 1;
|
|
|
|
public totalElements: number = 1;
|
|
|
|
|
2023-09-02 16:53:42 +02:00
|
|
|
public searchText: string = "";
|
|
|
|
public searchData: any = [];
|
|
|
|
|
2024-01-09 22:05:45 +01:00
|
|
|
public useragentInfoModalRef: BsModalRef | undefined;
|
|
|
|
public processedUA: any = {};
|
|
|
|
|
2022-01-11 00:32:21 +01:00
|
|
|
constructor(
|
2022-01-11 13:33:39 +01:00
|
|
|
private api: ApiClientService,
|
2022-01-11 00:32:21 +01:00
|
|
|
public auth: AuthService,
|
2022-01-11 13:33:39 +01:00
|
|
|
private router: Router,
|
2022-03-01 00:07:51 +01:00
|
|
|
private toastr: ToastrService,
|
2024-01-09 22:05:45 +01:00
|
|
|
private translate: TranslateService,
|
|
|
|
private modalService: BsModalService
|
2022-01-11 00:32:21 +01:00
|
|
|
) { }
|
2021-12-22 23:06:58 +01:00
|
|
|
|
2021-12-24 15:21:22 +01:00
|
|
|
loadTableData() {
|
2022-03-12 20:58:49 +01:00
|
|
|
if(!this.sourceType) this.sourceType = "list";
|
2024-01-02 01:21:13 +01:00
|
|
|
this.api.get(this.sourceType, this.rangePicked ? {
|
|
|
|
from: this.filterStart ? this.filterStart.toISOString() : undefined,
|
|
|
|
to: this.filterEnd ? this.filterEnd.toISOString() : undefined
|
|
|
|
} : {}, this.etag).then((data: any) => {
|
2023-10-23 08:08:10 +02:00
|
|
|
if(this.api.isLastSame) return;
|
|
|
|
this.etag = this.api.lastEtag;
|
2024-01-12 19:20:24 +01:00
|
|
|
if(typeof data === 'undefined' || data === null) return;
|
2022-03-12 20:58:49 +01:00
|
|
|
this.data = data.filter((row: any) => typeof row.hidden !== 'undefined' ? !row.hidden : true);
|
2023-09-02 16:53:42 +02:00
|
|
|
this.originalData = this.data;
|
2023-09-02 00:43:17 +02:00
|
|
|
this.totalElements = this.data.length;
|
2024-01-10 16:07:19 +01:00
|
|
|
this.displayedData = this.data.slice((this.currentPage - 1) * this.rowsPerPage, this.currentPage * this.rowsPerPage);
|
2022-03-12 20:58:49 +01:00
|
|
|
if(this.sourceType === 'list') {
|
|
|
|
this.api.availableUsers = this.data.filter((row: any) => row.available).length;
|
|
|
|
}
|
2023-09-02 16:53:42 +02:00
|
|
|
this.initializeSearchData();
|
2023-09-03 01:04:19 +02:00
|
|
|
}).catch((e) => {
|
|
|
|
console.error(e);
|
2021-12-22 23:06:58 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-01-02 01:21:13 +01:00
|
|
|
filterDateRangeChanged($event: Date[]) {
|
|
|
|
console.log($event);
|
|
|
|
if (typeof($event) !== "object" || ($event !== null && $event.length === 0)) {
|
|
|
|
this.filterStart = undefined;
|
|
|
|
this.filterEnd = undefined;
|
|
|
|
this.rangePicked = false;
|
|
|
|
} else {
|
|
|
|
this.filterStart = $event[0];
|
|
|
|
this.filterEnd = $event[1];
|
|
|
|
this.rangePicked = true;
|
|
|
|
}
|
|
|
|
if(this.lastRange !== this.range) this.loadTableData();
|
|
|
|
this.lastRange = this.range;
|
|
|
|
}
|
|
|
|
|
2023-09-02 00:43:17 +02:00
|
|
|
pageChanged(event: PageChangedEvent): void {
|
|
|
|
const startItem = (event.page - 1) * event.itemsPerPage;
|
|
|
|
const endItem = event.page * event.itemsPerPage;
|
|
|
|
this.displayedData = this.data.slice(startItem, endItem);
|
|
|
|
}
|
|
|
|
|
2023-09-02 16:53:42 +02:00
|
|
|
initializeSearchData() {
|
|
|
|
const searchPropertiesBlacklist = this.searchPropertiesBlacklist;
|
|
|
|
function flattenObj(obj: any, parent: any, res: any = {}){
|
|
|
|
//Based on https://stackoverflow.com/a/56253298
|
|
|
|
for(let key in obj){
|
|
|
|
if(typeof obj[key] == 'undefined' || obj[key] == null) continue;
|
|
|
|
if(searchPropertiesBlacklist.includes(key)) continue;
|
|
|
|
let propName = parent ? parent + '_' + key : key;
|
|
|
|
if(typeof obj[key] == 'object'){
|
|
|
|
flattenObj(obj[key], propName, res);
|
|
|
|
} else {
|
|
|
|
res[propName] = obj[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.searchData = this.data.map((row: any) => flattenObj(row, null));
|
|
|
|
}
|
|
|
|
|
|
|
|
onSearchTextChange(search: string) {
|
|
|
|
if(search.length == 0) {
|
|
|
|
this.data = this.originalData;
|
|
|
|
this.displayedData = this.data.slice(0, this.rowsPerPage);
|
|
|
|
this.totalElements = this.data.length;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.data = this.originalData.filter((row: any, index: number) => {
|
|
|
|
return Object.values(this.searchData[index]).some((value: any) => {
|
|
|
|
return value.toString().toLowerCase().includes(search.toLowerCase());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
this.displayedData = this.data.slice(0, this.rowsPerPage);
|
|
|
|
this.totalElements = this.data.length;
|
|
|
|
}
|
|
|
|
|
2021-12-24 15:21:22 +01:00
|
|
|
ngOnInit(): void {
|
|
|
|
console.log(this.sourceType);
|
2024-01-10 16:07:19 +01:00
|
|
|
if(this.initialStartFilter !== undefined) {
|
|
|
|
this.filterStart = this.initialStartFilter;
|
|
|
|
this.filterEnd = new Date();
|
|
|
|
this.rangePicked = true;
|
|
|
|
this.range = [this.filterStart, this.filterEnd];
|
|
|
|
}
|
2021-12-24 15:21:22 +01:00
|
|
|
this.loadTableData();
|
2021-12-29 13:12:16 +01:00
|
|
|
this.loadDataInterval = setInterval(() => {
|
2022-01-10 12:18:55 +01:00
|
|
|
if(typeof (window as any).skipTableReload !== 'undefined' && (window as any).skipTableReload) {
|
|
|
|
return;
|
|
|
|
}
|
2021-12-29 13:12:16 +01:00
|
|
|
console.log("Refreshing data...");
|
|
|
|
this.loadTableData();
|
|
|
|
}, this.refreshInterval || 10000);
|
2022-02-14 16:49:55 +01:00
|
|
|
this.auth.authChanged.subscribe({
|
|
|
|
next: () => this.loadTableData()
|
|
|
|
});
|
2021-12-24 15:21:22 +01:00
|
|
|
}
|
|
|
|
|
2021-12-30 11:58:25 +01:00
|
|
|
ngOnDestroy(): void {
|
|
|
|
if(typeof this.loadDataInterval !== 'undefined') {
|
2023-12-24 19:06:17 +01:00
|
|
|
clearInterval(this.loadDataInterval as number);
|
2021-12-30 11:58:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-27 19:58:38 +01:00
|
|
|
onChangeAvailability(user: number, newState: 0|1) {
|
2023-06-06 17:18:30 +02:00
|
|
|
if(this.auth.profile.can('users-read')) {
|
2021-12-27 19:58:38 +01:00
|
|
|
this.changeAvailability.emit({user, newState});
|
|
|
|
}
|
|
|
|
}
|
2022-01-11 00:32:21 +01:00
|
|
|
|
2022-02-14 11:40:47 +01:00
|
|
|
onUserImpersonate(user: number) {
|
2023-06-06 18:53:49 +02:00
|
|
|
if(this.auth.profile.can('users-impersonate')) {
|
|
|
|
this.auth.impersonate(user).then(() => {
|
2022-02-14 11:40:47 +01:00
|
|
|
this.loadTableData();
|
2023-06-06 18:53:49 +02:00
|
|
|
this.userImpersonate.emit(1);
|
2024-01-11 22:52:30 +01:00
|
|
|
}).catch((errMessage: any) => {
|
|
|
|
console.error(errMessage);
|
|
|
|
Swal.fire({
|
|
|
|
title: this.translate.instant("error_title"),
|
|
|
|
text: errMessage,
|
|
|
|
icon: 'error',
|
|
|
|
confirmButtonText: 'Ok'
|
|
|
|
});
|
2022-02-14 11:40:47 +01:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-04 00:41:41 +01:00
|
|
|
onMoreDetails(rowId: number) {
|
2024-01-07 18:43:52 +01:00
|
|
|
if(this.auth.profile.can('users-update')) {
|
|
|
|
this.moreDetails.emit({rowId});
|
|
|
|
}
|
2024-01-04 00:41:41 +01:00
|
|
|
}
|
|
|
|
|
2024-01-07 02:02:50 +01:00
|
|
|
editUser(id: number) {
|
|
|
|
this.router.navigate(['/users', id]);
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:21:35 +02:00
|
|
|
openPlaceDetails(id: number) {
|
|
|
|
this.router.navigate(['/place-details', id]);
|
2022-01-11 00:32:21 +01:00
|
|
|
}
|
2022-01-11 13:33:39 +01:00
|
|
|
|
2022-02-24 23:41:46 +01:00
|
|
|
editService(id: number) {
|
|
|
|
this.router.navigate(['/services', id]);
|
|
|
|
}
|
|
|
|
|
2022-01-11 13:33:39 +01:00
|
|
|
deleteService(id: number) {
|
2022-03-01 00:07:51 +01:00
|
|
|
this.translate.get(['table.yes_remove', 'table.cancel', 'table.remove_service_confirm', 'table.remove_service_text']).subscribe((res: { [key: string]: string; }) => {
|
|
|
|
Swal.fire({
|
|
|
|
title: res['table.remove_service_confirm'],
|
|
|
|
text: res['table.remove_service_confirm_text'],
|
|
|
|
icon: 'warning',
|
|
|
|
showCancelButton: true,
|
|
|
|
confirmButtonColor: '#3085d6',
|
|
|
|
cancelButtonColor: '#d33',
|
|
|
|
confirmButtonText: res['table.yes_remove'],
|
|
|
|
cancelButtonText: res['table.cancel']
|
|
|
|
}).then((result) => {
|
|
|
|
if (result.isConfirmed) {
|
|
|
|
this.api.delete(`services/${id}`).then((response) => {
|
2022-04-02 22:28:00 +02:00
|
|
|
this.translate.get('table.service_deleted_successfully').subscribe((res: string) => {
|
2022-03-01 00:07:51 +01:00
|
|
|
this.toastr.success(res);
|
|
|
|
});
|
|
|
|
this.loadTableData();
|
|
|
|
}).catch((e) => {
|
2022-04-02 22:28:00 +02:00
|
|
|
this.translate.get('table.service_deleted_error').subscribe((res: string) => {
|
2022-03-01 00:07:51 +01:00
|
|
|
this.toastr.error(res);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2022-01-11 13:33:39 +01:00
|
|
|
}
|
2023-09-01 00:21:35 +02:00
|
|
|
|
2023-09-07 14:01:54 +02:00
|
|
|
editTraining(id: number) {
|
|
|
|
this.router.navigate(['/trainings', id]);
|
|
|
|
}
|
|
|
|
|
|
|
|
deleteTraining(id: number) {
|
|
|
|
this.translate.get(['table.yes_remove', 'table.cancel', 'table.remove_training_confirm', 'table.remove_training_text']).subscribe((res: { [key: string]: string; }) => {
|
|
|
|
Swal.fire({
|
|
|
|
title: res['table.remove_training_confirm'],
|
|
|
|
text: res['table.remove_training_confirm_text'],
|
|
|
|
icon: 'warning',
|
|
|
|
showCancelButton: true,
|
|
|
|
confirmButtonColor: '#3085d6',
|
|
|
|
cancelButtonColor: '#d33',
|
|
|
|
confirmButtonText: res['table.yes_remove'],
|
|
|
|
cancelButtonText: res['table.cancel']
|
|
|
|
}).then((result) => {
|
|
|
|
if (result.isConfirmed) {
|
|
|
|
this.api.delete(`trainings/${id}`).then((response) => {
|
|
|
|
this.translate.get('table.training_deleted_successfully').subscribe((res: string) => {
|
|
|
|
this.toastr.success(res);
|
|
|
|
});
|
|
|
|
this.loadTableData();
|
|
|
|
}).catch((e) => {
|
|
|
|
this.translate.get('table.training_deleted_error').subscribe((res: string) => {
|
|
|
|
this.toastr.error(res);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-01 00:21:35 +02:00
|
|
|
extractNamesFromObject(obj: any) {
|
2024-01-12 19:11:48 +01:00
|
|
|
if(typeof obj === 'undefined') return "";
|
2024-01-11 21:09:53 +01:00
|
|
|
return obj.flatMap((e: any) => {
|
|
|
|
if(e.surname === null) return e.name;
|
|
|
|
return e.surname+" "+e.name;
|
|
|
|
});
|
2023-09-01 00:21:35 +02:00
|
|
|
}
|
2024-01-09 22:05:45 +01:00
|
|
|
|
|
|
|
userAgentToIcons(userAgentString: string) {
|
|
|
|
const parser = new UAParser(userAgentString);
|
|
|
|
let icons = [];
|
|
|
|
|
|
|
|
switch (parser.getBrowser().name) {
|
|
|
|
case 'Chrome':
|
|
|
|
case 'Chromium':
|
|
|
|
case 'Chrome WebView':
|
|
|
|
case 'Chrome Headless':
|
|
|
|
icons.push('fab fa-chrome');
|
|
|
|
break;
|
|
|
|
case 'Mozilla':
|
|
|
|
case 'Firefox [Focus/Reality]':
|
|
|
|
icons.push('fab fa-firefox-browser');
|
|
|
|
break;
|
|
|
|
case 'Safari':
|
|
|
|
icons.push('fab fa-safari');
|
|
|
|
break;
|
|
|
|
case 'IE':
|
|
|
|
case 'IEMobile':
|
|
|
|
icons.push('fa fa-skull-crossbones');
|
|
|
|
break;
|
|
|
|
case 'Edge':
|
|
|
|
icons.push('fab fa-edge');
|
|
|
|
break;
|
|
|
|
case 'Android Browser':
|
|
|
|
case 'Huawei Browser':
|
|
|
|
case 'Samsung Browser':
|
|
|
|
icons.push('fab fa-android');
|
|
|
|
break;
|
|
|
|
case 'Silk':
|
|
|
|
icons.push('fab fa-amazon');
|
|
|
|
break;
|
|
|
|
case 'Instagram':
|
|
|
|
case 'TikTok':
|
|
|
|
case 'Snapchat':
|
|
|
|
case 'Facebook':
|
|
|
|
case 'WeChat':
|
|
|
|
icons.push('fa fa-square-share-nodes');
|
|
|
|
break;
|
|
|
|
case 'Electron':
|
|
|
|
case 'PhantomJS':
|
|
|
|
icons.push('fa fa-code');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
icons.push('fa fa-question-circle');
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (parser.getDevice().type) {
|
|
|
|
case 'mobile':
|
|
|
|
icons.push('fa fa-mobile');
|
|
|
|
break;
|
|
|
|
case 'tablet':
|
|
|
|
icons.push('fa fa-tablet');
|
|
|
|
break;
|
|
|
|
case 'smarttv':
|
|
|
|
icons.push('fa fa-tv');
|
|
|
|
break;
|
|
|
|
case 'console':
|
|
|
|
icons.push('fa fa-gamepad');
|
|
|
|
break;
|
|
|
|
case 'wearable':
|
|
|
|
icons.push('fa fa-watch');
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
icons.push('fa fa-desktop');
|
|
|
|
}
|
|
|
|
|
|
|
|
return icons;
|
|
|
|
}
|
|
|
|
|
|
|
|
isPublicIp(ipAddress: string) {
|
|
|
|
const parts = ipAddress.split('.');
|
|
|
|
if (parts.length === 4) {
|
|
|
|
return !(
|
|
|
|
parts[0] === '10' ||
|
|
|
|
(parts[0] === '172' && parseInt(parts[1], 10) >= 16 && parseInt(parts[1], 10) <= 31) ||
|
|
|
|
(parts[0] === '192' && parts[1] === '168') ||
|
|
|
|
ipAddress === '127.0.0.1'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
openUserAgentInfoModal(userAgentString: string, template: TemplateRef<void>) {
|
|
|
|
this.processedUA = new UAParser(userAgentString).getResult();
|
|
|
|
this.useragentInfoModalRef = this.modalService.show(template);
|
|
|
|
}
|
2021-12-22 23:06:58 +01:00
|
|
|
}
|