import {AfterViewInit, Component, Input, OnDestroy, OnInit, Type, ViewChild} from '@angular/core'; import {MatSort, Sort} from "@angular/material/sort"; import {PagingComponent} from "@app/_components/paging/paging.component"; import {MatTableDataSource} from "@angular/material/table"; import {ListColDefinition} from "@app/_components/list/list-col-definition"; import {AppHelperService} from "@app/_helpers/app-helper.service"; import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type"; import {ListUpdateElementFunctionType} from "@app/_components/list/list-update-element-function-type"; import {FilterBarComponent} from "@app/_components/filter-bar/filter-bar.component"; import { Router } from '@angular/router'; import {interval, Subscription} from "rxjs"; import {AbstractCreateDataComponent} from "@app/_interfaces/AbstractCreateDataComponent"; @Component({ selector: 'app-list', templateUrl: './list.component.html', styleUrl: './list.component.scss' }) export class ListComponent implements OnInit, AfterViewInit, OnDestroy { @Input() public listId!: string; @Input() public getDataFunction!: ListGetDataFunctionType; @Input() public onSortFunction!: Function; @Input() public onNavigateToDetailsFunction!: Function; @Input() public onRemoveItemFunction!: Function; @Input() public onEditFunction!: Function; @Input() public onDownloadFunction!: Function; @Input() public onRowSelectedFunction!: Function; @Input() public onUpdateBooleanStateFunction!: ListUpdateElementFunctionType; @Input() public createDataComponent!: Type>; @Input() public searchable: boolean; @Input() public showDetailButton: boolean; @Input() public showPosition: boolean; @Input() public showFilterBar: boolean; @Input() public listColDefinitions!: ListColDefinition[]; @Input() public hidePageSize: boolean; @Input() public displayOptions!: { [key: string]: string }; @Input() public defaultDisplayOption!: string; @Input() public refreshIntervalSeconds?: number; @ViewChild(MatSort) sort; @ViewChild("pagingComponent", {static: false}) protected pagingComponent!: PagingComponent; @ViewChild("filterBarComponent", {static: false}) protected filterBarComponent!: FilterBarComponent; public static COLUMN_TYPE_ADDRESS: string = 'address'; public static COLUMN_TYPE_BOOLEAN: string = 'boolean'; public static COLUMN_TYPE_BTN_DOWNLOAD: string = 'btn_download'; public static COLUMN_TYPE_BTN_EDIT: string = 'btn_edit'; public static COLUMN_TYPE_BTN_REMOVE: string = 'btn_remove'; public static COLUMN_TYPE_CURRENCY: string = 'euro'; public static COLUMN_TYPE_DATE: string = 'date'; public static COLUMN_TYPE_DETAIL: string = 'detail'; public static COLUMN_TYPE_DETAIL_LINK: string = 'detail_link'; public static COLUMN_TYPE_EMAIL: string = 'email'; public static COLUMN_TYPE_IMAGE: string = 'image'; public static COLUMN_TYPE_COMBINED_IMAGES: string = 'combined_images'; public static COLUMN_TYPE_NUMBER: string = 'number'; public static COLUMN_TYPE_NUMBER_UNFORMATTED: string = 'number_unformatted'; public static COLUMN_TYPE_NUMBER_BOLD: string = 'number_bold'; public static COLUMN_TYPE_POSITION: string = 'position'; public static COLUMN_TYPE_TEXT: string = 'text'; public static COLUMN_TYPE_TEXT_BOLD: string = 'text_bold'; public static COLUMN_TYPE_TEXT_LINKED: string = 'text_linked'; public static COLUMN_TYPE_WEBSITE: string = 'website'; public activeFilterCount: number = 0; protected displayedColumns!: string[]; protected selectedRowIndex: number | null = null; protected dataSource; protected currentGroup!: string; protected filterExists!: boolean; protected sortObj!: any; protected filterObj!: any; protected listColDefinitionsByField!: any; protected filterConfig: string | null; private refreshSubscription?: Subscription; constructor( protected appHelperService: AppHelperService, private router: Router, ) { this.searchable = true; this.showDetailButton = true; this.showPosition = true; this.showFilterBar = true; this.filterExists = false; this.filterObj = {}; this.sort = new MatSort(); this.hidePageSize = false; this.dataSource = new MatTableDataSource(); this.filterConfig = null; } ngOnInit(): void { this.loadColumnConfig(); if (this.showPosition) { this.listColDefinitions.unshift(ListComponent.getDefaultColPosition()); } if (this.showDetailButton) { // this.listColDefinitions.unshift(ListComponent.getDefaultColDetailBtn()); this.listColDefinitions.unshift(ListComponent.getDefaultColDetailBtnLink(this.router.routerState.snapshot.url)); } if (this.displayOptions !== undefined) { this.currentGroup = this.defaultDisplayOption || Object.keys(this.displayOptions)[0] || ''; } this.listColDefinitionsByField = {}; this.listColDefinitions.forEach((value, index) => { if (value.visible === undefined) { value.visible = true; } this.listColDefinitionsByField[value['name']] = value; if (value.filterType !== undefined) { this.filterExists = true; } }) this.updateDisplayedColumns(); this.filterConfig = this.loadFilterConfig(); this.setupAutoRefresh(); } private setupAutoRefresh(): void { this.clearAutoRefresh(); if (this.refreshIntervalSeconds && this.refreshIntervalSeconds > 0) { this.refreshSubscription = interval(this.refreshIntervalSeconds * 1000).subscribe(() => { this.getData(); }); } } private clearAutoRefresh(): void { if (this.refreshSubscription) { this.refreshSubscription.unsubscribe(); } } saveFilterConfig(): void { localStorage.setItem(`filterConfig_${this.listId}`, this.getFilterJsonString()); } loadFilterConfig(): string | null { return localStorage.getItem(`filterConfig_${this.listId}`); } saveColumnConfig(): void { const config = this.listColDefinitions.map(col => ({ name: col.name, visible: col.visible })); localStorage.setItem(`listConfig_${this.listId}`, JSON.stringify(config)); } loadColumnConfig(): void { const savedConfig = localStorage.getItem(`listConfig_${this.listId}`); if (savedConfig) { const config = JSON.parse(savedConfig); this.listColDefinitions.forEach(col => { const savedCol = config.find((c: any) => c.name === col.name); if (savedCol) { col.visible = savedCol.visible; } }); this.updateDisplayedColumns(); } } updateDisplayedColumns(): void { this.displayedColumns = this.listColDefinitions .filter(col => col.visible !== false && (this.displayOptions === undefined || col.groups?.includes(this.currentGroup) || col.type === ListComponent.COLUMN_TYPE_DETAIL || col.type === ListComponent.COLUMN_TYPE_POSITION)) .map(col => col.name); } onDisplayOptionChange(option: string): void { this.currentGroup = option; this.updateDisplayedColumns(); } onToggleColumnVisibility(columnName: string): void { const column = this.listColDefinitions.find(col => col.name === columnName); if (column) { column.visible = !column.visible; this.updateDisplayedColumns(); } } showAllColumns() { this.listColDefinitions.forEach((value, index) => { value.visible = true; }); this.updateDisplayedColumns(); } getColumnVisibility(): { [key: string]: boolean } { const visibility: { [key: string]: boolean } = {}; this.listColDefinitions.forEach(col => { visibility[col.name] = col.visible !== false; }); return visibility; } ngAfterViewInit(): void { } getData = (): void => { this.getDataFunction( this.pagingComponent.getPageIndex(), this.pagingComponent.getPageSize(), this.pagingComponent.getSearchValue() ).subscribe( data => { this.dataSource = new MatTableDataSource(data['member']); this.pagingComponent.setDataLength(data["totalItems"]); } ) } onSortChange = (sortState: Sort) => { let listColDefinition: any = this.listColDefinitionsByField[sortState.active]; this.sortObj = sortState; this.sortObj['listColDefinition'] = listColDefinition; this.pagingComponent.resetPageIndex(); this.onSortFunction(sortState); this.getData(); } onRowSelected(row: any, index: number) { this.selectedRowIndex = index; if (this.onRowSelectedFunction !== undefined) { this.onRowSelectedFunction(row, index); } } getElementValue(element: any, column: ListColDefinition, multipleFieldIndex?: number): any | null { element = column.subResource !== undefined ? element[column.subResource] : element; if (element === undefined) { return null; } if (column.field !== undefined) { if ( column.displayedLength !== undefined && element[column.field] !== undefined && element[column.field].length > column.displayedLength ) { return element[column.field]?.slice(0, column.displayedLength) + '...'; } return element[column.field]; } if (column.multipleFields !== undefined) { if (multipleFieldIndex !== undefined) { return element[column.multipleFields[multipleFieldIndex]]; } let res: any[] = []; column.multipleFields.forEach((field, index) => { res.push(element[field]); }) return res; } if (column.address !== undefined) { const field = column.address; let addressString = ''; if (element[field.street] !== undefined && element[field.street] !== null) { addressString += `${element[field.street].trim()} `; } if (element[field.streetNo] !== undefined && element[field.streetNo] !== null) { addressString += `${element[field.streetNo].trim()} `; } addressString += '
'; if (element[field.zip] !== undefined && element[field.zip] !== null) { addressString += `${element[field.zip].trim()} `; } if (element[field.city] !== undefined && element[field.city] !== null) { addressString += `${element[field.city].trim()}`; } addressString += '
'; if (element[field.country] !== undefined && element[field.country] !== null) { addressString += `${element[field.country].trim()}`; } return addressString; } return element; } getElementImage(element: any, column: ListColDefinition): any { let elementValue = this.getElementValue(element, column); if (elementValue !== undefined && elementValue !== null) { return elementValue; } return "/assets/images/icons/dummy-product.png" } getColCssClass(column: ListColDefinition): string { switch (column.type) { case ListComponent.COLUMN_TYPE_DETAIL: return "spt-button-td"; case ListComponent.COLUMN_TYPE_BTN_REMOVE: return "spt-button-td text-end"; case ListComponent.COLUMN_TYPE_TEXT: return "spt-version-td"; default: return ""; } } public getPageIndex() { return this.pagingComponent.getPageIndex(); } public getPageSize() { return this.pagingComponent.getPageSize(); } public static getDefaultColDetailBtn(): ListColDefinition { return { name: 'detail', text: '', type: ListComponent.COLUMN_TYPE_DETAIL } as ListColDefinition; } public static getDefaultColDetailBtnLink(currentUrl: string): ListColDefinition { return { name: 'detaillink', text: '', url: currentUrl, type: ListComponent.COLUMN_TYPE_DETAIL_LINK } as ListColDefinition; } public static getDefaultColPosition(): ListColDefinition { return { name: 'pos', text: 'basic.number', type: ListComponent.COLUMN_TYPE_POSITION } as ListColDefinition; } public getSortingJsonString(): any { return JSON.stringify(this.sortObj); } public updateBooleanState = (element: any, index: number, column: ListColDefinition) => { if (this.onUpdateBooleanStateFunction === undefined) { throw new Error('no onUpdateBooleanStateFunction given'); } if (column.field !== undefined) { element[column.field] = !element[column.field]; } else { throw new Error('column.field is undefined'); } this.onUpdateBooleanStateFunction(element).subscribe( data => { this.updateRow(data, index); } ) } public updateRow(element: any, index: number) { const data = this.dataSource.data as any; data[index] = element; this.dataSource.data = data; } public onFilterInit(filterData: {filters: any, activeCount: number}) { this.filterObj = filterData.filters; this.activeFilterCount = filterData.activeCount; } public onFilterChanged(filterData: {filters: any, activeCount: number}) { console.log(filterData); const filterJson = JSON.stringify(filterData.filters); const currentFilterJson = JSON.stringify(this.filterObj); if (filterJson !== currentFilterJson) { this.filterObj = filterData.filters; this.activeFilterCount = filterData.activeCount; this.getData(); } } public onCreateData() { this.appHelperService.openModal( this.createDataComponent, null, this.getData ); } public getFilterJsonString(): any { return JSON.stringify(this.filterObj); } get COLUMN_TYPE_ADDRESS(): string { return ListComponent.COLUMN_TYPE_ADDRESS; } get COLUMN_TYPE_BOOLEAN(): string { return ListComponent.COLUMN_TYPE_BOOLEAN; } get COLUMN_TYPE_BTN_DOWNLOAD(): string { return ListComponent.COLUMN_TYPE_BTN_DOWNLOAD; } get COLUMN_TYPE_BTN_EDIT(): string { return ListComponent.COLUMN_TYPE_BTN_EDIT; } get COLUMN_TYPE_BTN_REMOVE(): string { return ListComponent.COLUMN_TYPE_BTN_REMOVE; } get COLUMN_TYPE_CURRENCY(): string { return ListComponent.COLUMN_TYPE_CURRENCY; } get COLUMN_TYPE_DATE(): string { return ListComponent.COLUMN_TYPE_DATE; } get COLUMN_TYPE_DETAIL(): string { return ListComponent.COLUMN_TYPE_DETAIL; } get COLUMN_TYPE_DETAIL_LINK(): string { return ListComponent.COLUMN_TYPE_DETAIL_LINK; } get COLUMN_TYPE_EMAIL(): string { return ListComponent.COLUMN_TYPE_EMAIL; } get COLUMN_TYPE_POSITION(): string { return ListComponent.COLUMN_TYPE_POSITION; } get COLUMN_TYPE_IMAGE(): string { return ListComponent.COLUMN_TYPE_IMAGE; } get COLUMN_TYPE_COMBINED_IMAGES(): string { return ListComponent.COLUMN_TYPE_COMBINED_IMAGES; } get COLUMN_TYPE_TEXT(): string { return ListComponent.COLUMN_TYPE_TEXT; } get COLUMN_TYPE_NUMBER(): string { return ListComponent.COLUMN_TYPE_NUMBER; } get COLUMN_TYPE_NUMBER_UNFORMATTED(): string { return ListComponent.COLUMN_TYPE_NUMBER_UNFORMATTED; } get COLUMN_TYPE_NUMBER_BOLD(): string { return ListComponent.COLUMN_TYPE_NUMBER_BOLD; } get COLUMN_TYPE_TEXT_BOLD(): string { return ListComponent.COLUMN_TYPE_TEXT_BOLD; } get COLUMN_TYPE_TEXT_LINKED(): string { return ListComponent.COLUMN_TYPE_TEXT_LINKED; } get COLUMN_TYPE_WEBSITE(): string { return ListComponent.COLUMN_TYPE_WEBSITE; } ngOnDestroy(): void { this.clearAutoRefresh(); } }