import { ChangeDetectionStrategy, Component, Input, Output, ViewChild, ViewEncapsulation, } from "@angular/core"; import {SharedAbstractPresentational} from "@app/shared/components/presentationals/shared-abstract/shared-abstract.presentational"; import {SharedPaginatorPresentational} from "@app/shared/components/presentationals/shared-paginator/shared-paginator.presentational"; import {FieldTypeEnum} from "@app/shared/enums/field-type.enum"; import {DataTableColumns} from "@app/shared/models/data-table-columns.model"; import {environment} from "@environments/environment"; import {DataTableComponentEnum} from "@shared/enums/data-table-component.enum"; import {DataTableActions} from "@shared/models/data-table-actions.model"; import {LazyLoadEvent} from "primeng/api"; import { BehaviorSubject, Observable, } from "rxjs"; import { BaseResource, DateUtil, isEmptyArray, isEmptyString, isNullOrUndefined, isObservable, isTrue, MappingObjectUtil, ObjectUtil, ObservableUtil, OrderEnum, Paging, QueryParameters, QueryParametersUtil, Sort, StringUtil, } from "solidify-frontend"; @Component({ selector: "dlcm-shared-data-table", templateUrl: "./shared-data-table.presentational.html", styleUrls: ["./shared-data-table.presentational.scss"], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, }) export class SharedDataTablePresentational<TResource extends BaseResource> extends SharedAbstractPresentational { readonly TIME_BEFORE_DISPLAY_TOOLTIP: number = environment.timeBeforeDisplayTooltipOnDataTable; private _datas: TResource[]; defaultSortColumn: DataTableColumns; @Input() set datas(value: TResource[]) { this._datas = value; this.computeIsDatasPresent(); } get datas(): TResource[] { return this._datas; } @Input() skipInitialQuery: boolean = false; @Input() isLoading: boolean = false; private _queryParameters: QueryParameters; @Input() columnsSkippedToClear: string[]; @Input() set queryParameters(value: QueryParameters) { this._queryParameters = QueryParametersUtil.clone(value); if (isNullOrUndefined(value.paging)) { this._queryParameters.paging = new Paging(environment.defaultPageSize); } } get queryParameters(): QueryParameters { return this._queryParameters; } private _columns: DataTableColumns[]; @Input() set columns(value: DataTableColumns[]) { this._columns = value; this.defaultSortColumn = this._columns.find(c => isTrue(c.isSortable) && c.order !== 0); } get columns(): DataTableColumns[] { return this._columns; } private _actions: DataTableActions[] = []; @Input() set actions(value: DataTableActions[]) { this._actions = value; } get actions(): DataTableActions[] { return this._actions; } private _listNewId: string[] = []; @Input() set listNewId(value: string[]) { this._listNewId = value; } get listNewId(): string[] { return this._listNewId; } @Input() scrollHeight: string | undefined = undefined; private readonly _selectBS: BehaviorSubject<TResource | undefined> = new BehaviorSubject<TResource | undefined>(undefined); private readonly _queryParametersBS: BehaviorSubject<QueryParameters | undefined> = new BehaviorSubject<QueryParameters | undefined>(undefined); @Output("selectChange") readonly selectObs: Observable<TResource | undefined> = ObservableUtil.asObservable(this._selectBS); @Output("queryParametersChange") readonly queryParametersObs: Observable<QueryParameters | undefined> = ObservableUtil.asObservable(this._queryParametersBS); @ViewChild("paginator", {static: false}) paginator: SharedPaginatorPresentational; isDatasPresent: boolean; get fieldTypeEnum(): typeof FieldTypeEnum { return FieldTypeEnum; } get dataTableComponentEnum(): typeof DataTableComponentEnum { return DataTableComponentEnum; } private readonly _SEPARATOR: string = "."; constructor() { super(); } private computeIsDatasPresent(): void { let isDatasPresent = false; if (!isNullOrUndefined(this.datas) && this.datas.length > 0) { isDatasPresent = true; } this.isDatasPresent = isDatasPresent; } showDetail(row: TResource): void { this._selectBS.next(row); } getCellData(row: TResource, column: DataTableColumns): any { const data = this.getData(row, column); if (column.translate && column.filterEnum) { const enumValue = column.filterEnum.find(f => f.key === data + ""); if (enumValue) { return enumValue.value; } return data; } return this.applyTypeToData(data, column.type); } getData(row: TResource, column: DataTableColumns): any { const field = column.field; if (field.includes(this._SEPARATOR)) { return ObjectUtil.getNestedValue(row as any, column.field); } return row[field]; } private applyTypeToData(data: any, type: FieldTypeEnum): any { if (isNullOrUndefined(data)) { return StringUtil.stringEmpty; } if (type === FieldTypeEnum.date) { return DateUtil.convertDateToDateString(data); } if (type === FieldTypeEnum.datetime) { return DateUtil.convertDateToDateTimeString(data); } return data; } backendSort($event: LazyLoadEvent): void { if (isTrue(this.skipInitialQuery)) { this.skipInitialQuery = false; return; } const queryParameters = ObjectUtil.clone(this.queryParameters); if (!isNullOrUndefined($event.sortField)) { const sort = this.adaptLazyLoadEventToSortModel($event); queryParameters.sort = sort; } this._queryParametersBS.next(queryParameters); } private adaptLazyLoadEventToSortModel($event: LazyLoadEvent): Sort { return { field: this.getSortableFieldByFieldName($event.sortField), order: $event.sortOrder === -1 ? OrderEnum.descending : OrderEnum.ascending, } as Sort; } pageChange($event: Paging): void { const queryParameters = ObjectUtil.clone(this.queryParameters); queryParameters.paging = $event; this._queryParametersBS.next(queryParameters); } onValueChange(col: DataTableColumns, value: string): void { const fieldName = this.getFilterableField(col); let queryParameters = ObjectUtil.clone(this.queryParameters); queryParameters.search = ObjectUtil.clone(queryParameters.search); queryParameters.search.searchItems = ObjectUtil.clone(queryParameters.search.searchItems); if (isEmptyString(value) || isNullOrUndefined(value)) { MappingObjectUtil.delete(queryParameters.search.searchItems, fieldName); } else { MappingObjectUtil.set(queryParameters.search.searchItems, fieldName, value); } queryParameters = QueryParametersUtil.resetToFirstPage(queryParameters); this._queryParametersBS.next(queryParameters); } getValue(col: DataTableColumns): string | null { const value = MappingObjectUtil.get(this.queryParameters.search.searchItems, this.getFilterableField(col)); if (value === null || value === undefined || value === StringUtil.stringEmpty) { return null; } return value; } private getFilterableField(col: DataTableColumns): string { return isNullOrUndefined(col.filterableField) ? col.field : col.filterableField; } private getSortableField(col: DataTableColumns): string { return isNullOrUndefined(col.sortableField) ? col.field : col.sortableField; } getSortableFieldByFieldName(fieldName: string): string { const column = this.columns.find(col => col.field === fieldName); return this.getSortableField(column); } isFilterApplied(): boolean { let toSkip: boolean = false; if (MappingObjectUtil.size(QueryParametersUtil.getSearchItems(this.queryParameters)) > 0) { if (this.columnsSkippedToClear === undefined || isEmptyArray(this.columnsSkippedToClear)) { return true; } const searchItems: Map<string, string> = MappingObjectUtil.toMap(this.queryParameters.search.searchItems); searchItems.forEach((value: string, key: string) => { if (this.columnsSkippedToClear.some(c => c.includes(key)) === false) { toSkip = true; } }); } return toSkip; } clearFilters(): void { let queryParameters = QueryParametersUtil.clone(this.queryParameters); const searchItems = QueryParametersUtil.getSearchItems(queryParameters); if (this.columnsSkippedToClear === undefined || isEmptyArray(this.columnsSkippedToClear)) { MappingObjectUtil.clear(searchItems); } else { MappingObjectUtil.forEach(searchItems, (value: string, key: string) => { if (this.columnsSkippedToClear.some(c => c.includes(key)) === false) { MappingObjectUtil.delete(searchItems, key); } }); } queryParameters = QueryParametersUtil.resetToFirstPage(queryParameters); this._queryParametersBS.next(queryParameters); } callCallback($event: MouseEvent, callback: (current: TResource) => void, rowData: TResource): void { $event.stopPropagation(); callback(rowData); } isNew(rowData: TResource): boolean { if (isNullOrUndefined(this.listNewId) || isEmptyArray(this.listNewId)) { return false; } return this.listNewId.includes(rowData.resId); } displayActionOnCondition(action: DataTableActions<TResource>, resource: TResource = undefined): boolean | Observable<boolean> { if (isNullOrUndefined(resource) || isNullOrUndefined(action.displayOnCondition)) { return this.atLeastOneActionRowDisplayed(action); } return action.displayOnCondition(resource); } private atLeastOneActionRowDisplayed(action: DataTableActions<TResource>): boolean | Observable<boolean> { if (isNullOrUndefined(this._datas) || isEmptyArray(this._datas)) { return false; } if (isNullOrUndefined(action.displayOnCondition)) { return true; } if (this._datas.some(data => !isObservable(action.displayOnCondition(data)) && isTrue(action.displayOnCondition(data)))) { return true; } // TODO Find a way to merge all observable return action.displayOnCondition(this._datas[0]); } getWidth(column: DataTableColumns<TResource>): string { if (!isNullOrUndefined(column.width)) { return column.width; } if (column.component === DataTableComponentEnum.conformityLevelStar) { return "100px"; } if (column.type === FieldTypeEnum.datetime) { return "145px"; } if (column.type === FieldTypeEnum.date) { return "90px"; } return undefined; } isColumnFilterable(): boolean { return !isNullOrUndefined(this.columns.find(c => isTrue(c.isFilterable))); } }