You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

483 lines
17 KiB

  1. import {AfterViewInit, Component, Input, OnDestroy, OnInit, Type, ViewChild} from '@angular/core';
  2. import {MatSort, Sort} from "@angular/material/sort";
  3. import {PagingComponent} from "@app/_components/paging/paging.component";
  4. import {MatTableDataSource} from "@angular/material/table";
  5. import {ListColDefinition} from "@app/_components/list/list-col-definition";
  6. import {AppHelperService} from "@app/_helpers/app-helper.service";
  7. import {ListGetDataFunctionType} from "@app/_components/list/list-get-data-function-type";
  8. import {ListUpdateElementFunctionType} from "@app/_components/list/list-update-element-function-type";
  9. import {FilterBarComponent} from "@app/_components/filter-bar/filter-bar.component";
  10. import { Router } from '@angular/router';
  11. import {interval, Subscription} from "rxjs";
  12. import {AbstractCreateDataComponent} from "@app/_interfaces/AbstractCreateDataComponent";
  13. @Component({
  14. selector: 'app-list',
  15. templateUrl: './list.component.html',
  16. styleUrl: './list.component.scss'
  17. })
  18. export class ListComponent implements OnInit, AfterViewInit, OnDestroy {
  19. @Input() public listId!: string;
  20. @Input() public getDataFunction!: ListGetDataFunctionType;
  21. @Input() public onSortFunction!: Function;
  22. @Input() public onNavigateToDetailsFunction!: Function;
  23. @Input() public onRemoveItemFunction!: Function;
  24. @Input() public onEditFunction!: Function;
  25. @Input() public onDownloadFunction!: Function;
  26. @Input() public onRowSelectedFunction!: Function;
  27. @Input() public onUpdateBooleanStateFunction!: ListUpdateElementFunctionType;
  28. @Input() public createDataComponent!: Type<AbstractCreateDataComponent<any>>;
  29. @Input() public searchable: boolean;
  30. @Input() public showDetailButton: boolean;
  31. @Input() public showPosition: boolean;
  32. @Input() public showFilterBar: boolean;
  33. @Input() public listColDefinitions!: ListColDefinition[];
  34. @Input() public hidePageSize: boolean;
  35. @Input() public displayOptions!: { [key: string]: string };
  36. @Input() public defaultDisplayOption!: string;
  37. @Input() public refreshIntervalSeconds?: number;
  38. @ViewChild(MatSort) sort;
  39. @ViewChild("pagingComponent", {static: false}) protected pagingComponent!: PagingComponent;
  40. @ViewChild("filterBarComponent", {static: false}) protected filterBarComponent!: FilterBarComponent;
  41. public static COLUMN_TYPE_ADDRESS: string = 'address';
  42. public static COLUMN_TYPE_BOOLEAN: string = 'boolean';
  43. public static COLUMN_TYPE_BTN_DOWNLOAD: string = 'btn_download';
  44. public static COLUMN_TYPE_BTN_EDIT: string = 'btn_edit';
  45. public static COLUMN_TYPE_BTN_REMOVE: string = 'btn_remove';
  46. public static COLUMN_TYPE_CURRENCY: string = 'euro';
  47. public static COLUMN_TYPE_DATE: string = 'date';
  48. public static COLUMN_TYPE_DETAIL: string = 'detail';
  49. public static COLUMN_TYPE_DETAIL_LINK: string = 'detail_link';
  50. public static COLUMN_TYPE_EMAIL: string = 'email';
  51. public static COLUMN_TYPE_IMAGE: string = 'image';
  52. public static COLUMN_TYPE_COMBINED_IMAGES: string = 'combined_images';
  53. public static COLUMN_TYPE_NUMBER: string = 'number';
  54. public static COLUMN_TYPE_NUMBER_UNFORMATTED: string = 'number_unformatted';
  55. public static COLUMN_TYPE_NUMBER_BOLD: string = 'number_bold';
  56. public static COLUMN_TYPE_POSITION: string = 'position';
  57. public static COLUMN_TYPE_TEXT: string = 'text';
  58. public static COLUMN_TYPE_TEXT_BOLD: string = 'text_bold';
  59. public static COLUMN_TYPE_TEXT_LINKED: string = 'text_linked';
  60. public static COLUMN_TYPE_WEBSITE: string = 'website';
  61. public activeFilterCount: number = 0;
  62. protected displayedColumns!: string[];
  63. protected selectedRowIndex: number | null = null;
  64. protected dataSource;
  65. protected currentGroup!: string;
  66. protected filterExists!: boolean;
  67. protected sortObj!: any;
  68. protected filterObj!: any;
  69. protected listColDefinitionsByField!: any;
  70. protected filterConfig: string | null;
  71. private refreshSubscription?: Subscription;
  72. constructor(
  73. protected appHelperService: AppHelperService,
  74. private router: Router,
  75. ) {
  76. this.searchable = true;
  77. this.showDetailButton = true;
  78. this.showPosition = true;
  79. this.showFilterBar = true;
  80. this.filterExists = false;
  81. this.filterObj = {};
  82. this.sort = new MatSort();
  83. this.hidePageSize = false;
  84. this.dataSource = new MatTableDataSource<any>();
  85. this.filterConfig = null;
  86. }
  87. ngOnInit(): void {
  88. this.loadColumnConfig();
  89. if (this.showPosition) {
  90. this.listColDefinitions.unshift(ListComponent.getDefaultColPosition());
  91. }
  92. if (this.showDetailButton) {
  93. // this.listColDefinitions.unshift(ListComponent.getDefaultColDetailBtn());
  94. this.listColDefinitions.unshift(ListComponent.getDefaultColDetailBtnLink(this.router.routerState.snapshot.url));
  95. }
  96. if (this.displayOptions !== undefined) {
  97. this.currentGroup = this.defaultDisplayOption || Object.keys(this.displayOptions)[0] || '';
  98. }
  99. this.listColDefinitionsByField = {};
  100. this.listColDefinitions.forEach((value, index) => {
  101. if (value.visible === undefined) {
  102. value.visible = true;
  103. }
  104. this.listColDefinitionsByField[value['name']] = value;
  105. if (value.filterType !== undefined) {
  106. this.filterExists = true;
  107. }
  108. })
  109. this.updateDisplayedColumns();
  110. this.filterConfig = this.loadFilterConfig();
  111. this.setupAutoRefresh();
  112. }
  113. private setupAutoRefresh(): void {
  114. this.clearAutoRefresh();
  115. if (this.refreshIntervalSeconds && this.refreshIntervalSeconds > 0) {
  116. this.refreshSubscription = interval(this.refreshIntervalSeconds * 1000).subscribe(() => {
  117. this.getData();
  118. });
  119. }
  120. }
  121. private clearAutoRefresh(): void {
  122. if (this.refreshSubscription) {
  123. this.refreshSubscription.unsubscribe();
  124. }
  125. }
  126. saveFilterConfig(): void {
  127. localStorage.setItem(`filterConfig_${this.listId}`, this.getFilterJsonString());
  128. }
  129. loadFilterConfig(): string | null {
  130. return localStorage.getItem(`filterConfig_${this.listId}`);
  131. }
  132. saveColumnConfig(): void {
  133. const config = this.listColDefinitions.map(col => ({
  134. name: col.name,
  135. visible: col.visible
  136. }));
  137. localStorage.setItem(`listConfig_${this.listId}`, JSON.stringify(config));
  138. }
  139. loadColumnConfig(): void {
  140. const savedConfig = localStorage.getItem(`listConfig_${this.listId}`);
  141. if (savedConfig) {
  142. const config = JSON.parse(savedConfig);
  143. this.listColDefinitions.forEach(col => {
  144. const savedCol = config.find((c: any) => c.name === col.name);
  145. if (savedCol) {
  146. col.visible = savedCol.visible;
  147. }
  148. });
  149. this.updateDisplayedColumns();
  150. }
  151. }
  152. updateDisplayedColumns(): void {
  153. this.displayedColumns = this.listColDefinitions
  154. .filter(col => col.visible !== false &&
  155. (this.displayOptions === undefined ||
  156. col.groups?.includes(this.currentGroup) ||
  157. col.type === ListComponent.COLUMN_TYPE_DETAIL ||
  158. col.type === ListComponent.COLUMN_TYPE_POSITION))
  159. .map(col => col.name);
  160. }
  161. onDisplayOptionChange(option: string): void {
  162. this.currentGroup = option;
  163. this.updateDisplayedColumns();
  164. }
  165. onToggleColumnVisibility(columnName: string): void {
  166. const column = this.listColDefinitions.find(col => col.name === columnName);
  167. if (column) {
  168. column.visible = !column.visible;
  169. this.updateDisplayedColumns();
  170. }
  171. }
  172. showAllColumns() {
  173. this.listColDefinitions.forEach((value, index) => {
  174. value.visible = true;
  175. });
  176. this.updateDisplayedColumns();
  177. }
  178. getColumnVisibility(): { [key: string]: boolean } {
  179. const visibility: { [key: string]: boolean } = {};
  180. this.listColDefinitions.forEach(col => {
  181. visibility[col.name] = col.visible !== false;
  182. });
  183. return visibility;
  184. }
  185. ngAfterViewInit(): void {
  186. }
  187. getData = (): void => {
  188. this.getDataFunction(
  189. this.pagingComponent.getPageIndex(),
  190. this.pagingComponent.getPageSize(),
  191. this.pagingComponent.getSearchValue()
  192. ).subscribe(
  193. data => {
  194. this.dataSource = new MatTableDataSource<any>(data['member']);
  195. this.pagingComponent.setDataLength(data["totalItems"]);
  196. }
  197. )
  198. }
  199. onSortChange = (sortState: Sort) => {
  200. let listColDefinition: any = this.listColDefinitionsByField[sortState.active];
  201. this.sortObj = sortState;
  202. this.sortObj['listColDefinition'] = listColDefinition;
  203. this.pagingComponent.resetPageIndex();
  204. this.onSortFunction(sortState);
  205. this.getData();
  206. }
  207. onRowSelected(row: any, index: number) {
  208. this.selectedRowIndex = index;
  209. if (this.onRowSelectedFunction !== undefined) {
  210. this.onRowSelectedFunction(row, index);
  211. }
  212. }
  213. getElementValue(element: any, column: ListColDefinition, multipleFieldIndex?: number): any | null {
  214. element = column.subResource !== undefined ? element[column.subResource] : element;
  215. if (element === undefined) {
  216. return null;
  217. }
  218. if (column.field !== undefined) {
  219. if (
  220. column.displayedLength !== undefined &&
  221. element[column.field] !== undefined &&
  222. element[column.field].length > column.displayedLength
  223. ) {
  224. return element[column.field]?.slice(0, column.displayedLength) + '...';
  225. }
  226. return element[column.field];
  227. }
  228. if (column.multipleFields !== undefined) {
  229. if (multipleFieldIndex !== undefined) {
  230. return element[column.multipleFields[multipleFieldIndex]];
  231. }
  232. let res: any[] = [];
  233. column.multipleFields.forEach((field, index) => {
  234. res.push(element[field]);
  235. })
  236. return res;
  237. }
  238. if (column.address !== undefined) {
  239. const field = column.address;
  240. let addressString = '';
  241. if (element[field.street] !== undefined && element[field.street] !== null) {
  242. addressString += `${element[field.street].trim()} `;
  243. }
  244. if (element[field.streetNo] !== undefined && element[field.streetNo] !== null) {
  245. addressString += `${element[field.streetNo].trim()} `;
  246. }
  247. addressString += ' <br/> ';
  248. if (element[field.zip] !== undefined && element[field.zip] !== null) {
  249. addressString += `${element[field.zip].trim()} `;
  250. }
  251. if (element[field.city] !== undefined && element[field.city] !== null) {
  252. addressString += `${element[field.city].trim()}`;
  253. }
  254. addressString += ' <br/> ';
  255. if (element[field.country] !== undefined && element[field.country] !== null) {
  256. addressString += `${element[field.country].trim()}`;
  257. }
  258. return addressString;
  259. }
  260. return element;
  261. }
  262. getElementImage(element: any, column: ListColDefinition): any {
  263. let elementValue = this.getElementValue(element, column);
  264. if (elementValue !== undefined && elementValue !== null) {
  265. return elementValue;
  266. }
  267. return "/assets/images/icons/dummy-product.png"
  268. }
  269. getColCssClass(column: ListColDefinition): string {
  270. switch (column.type) {
  271. case ListComponent.COLUMN_TYPE_DETAIL:
  272. return "spt-button-td";
  273. case ListComponent.COLUMN_TYPE_BTN_REMOVE:
  274. return "spt-button-td text-end";
  275. case ListComponent.COLUMN_TYPE_TEXT:
  276. return "spt-version-td";
  277. default:
  278. return "";
  279. }
  280. }
  281. public getPageIndex() {
  282. return this.pagingComponent.getPageIndex();
  283. }
  284. public getPageSize() {
  285. return this.pagingComponent.getPageSize();
  286. }
  287. public static getDefaultColDetailBtn(): ListColDefinition {
  288. return {
  289. name: 'detail',
  290. text: '',
  291. type: ListComponent.COLUMN_TYPE_DETAIL
  292. } as ListColDefinition;
  293. }
  294. public static getDefaultColDetailBtnLink(currentUrl: string): ListColDefinition {
  295. return {
  296. name: 'detaillink',
  297. text: '',
  298. url: currentUrl,
  299. type: ListComponent.COLUMN_TYPE_DETAIL_LINK
  300. } as ListColDefinition;
  301. }
  302. public static getDefaultColPosition(): ListColDefinition {
  303. return {
  304. name: 'pos',
  305. text: 'basic.number',
  306. type: ListComponent.COLUMN_TYPE_POSITION
  307. } as ListColDefinition;
  308. }
  309. public getSortingJsonString(): any
  310. {
  311. return JSON.stringify(this.sortObj);
  312. }
  313. public updateBooleanState = (element: any, index: number, column: ListColDefinition) => {
  314. if (this.onUpdateBooleanStateFunction === undefined) {
  315. throw new Error('no onUpdateBooleanStateFunction given');
  316. }
  317. if (column.field !== undefined) {
  318. element[column.field] = !element[column.field];
  319. } else {
  320. throw new Error('column.field is undefined');
  321. }
  322. this.onUpdateBooleanStateFunction(element).subscribe(
  323. data => {
  324. this.updateRow(data, index);
  325. }
  326. )
  327. }
  328. public updateRow(element: any, index: number) {
  329. const data = this.dataSource.data as any;
  330. data[index] = element;
  331. this.dataSource.data = data;
  332. }
  333. public onFilterInit(filterData: {filters: any, activeCount: number}) {
  334. this.filterObj = filterData.filters;
  335. this.activeFilterCount = filterData.activeCount;
  336. }
  337. public onFilterChanged(filterData: {filters: any, activeCount: number}) {
  338. console.log(filterData);
  339. const filterJson = JSON.stringify(filterData.filters);
  340. const currentFilterJson = JSON.stringify(this.filterObj);
  341. if (filterJson !== currentFilterJson) {
  342. this.filterObj = filterData.filters;
  343. this.activeFilterCount = filterData.activeCount;
  344. this.getData();
  345. }
  346. }
  347. public onCreateData() {
  348. this.appHelperService.openModal(
  349. this.createDataComponent,
  350. null,
  351. this.getData
  352. );
  353. }
  354. public getFilterJsonString(): any {
  355. return JSON.stringify(this.filterObj);
  356. }
  357. get COLUMN_TYPE_ADDRESS(): string {
  358. return ListComponent.COLUMN_TYPE_ADDRESS;
  359. }
  360. get COLUMN_TYPE_BOOLEAN(): string {
  361. return ListComponent.COLUMN_TYPE_BOOLEAN;
  362. }
  363. get COLUMN_TYPE_BTN_DOWNLOAD(): string {
  364. return ListComponent.COLUMN_TYPE_BTN_DOWNLOAD;
  365. }
  366. get COLUMN_TYPE_BTN_EDIT(): string {
  367. return ListComponent.COLUMN_TYPE_BTN_EDIT;
  368. }
  369. get COLUMN_TYPE_BTN_REMOVE(): string {
  370. return ListComponent.COLUMN_TYPE_BTN_REMOVE;
  371. }
  372. get COLUMN_TYPE_CURRENCY(): string {
  373. return ListComponent.COLUMN_TYPE_CURRENCY;
  374. }
  375. get COLUMN_TYPE_DATE(): string {
  376. return ListComponent.COLUMN_TYPE_DATE;
  377. }
  378. get COLUMN_TYPE_DETAIL(): string {
  379. return ListComponent.COLUMN_TYPE_DETAIL;
  380. }
  381. get COLUMN_TYPE_DETAIL_LINK(): string {
  382. return ListComponent.COLUMN_TYPE_DETAIL_LINK;
  383. }
  384. get COLUMN_TYPE_EMAIL(): string {
  385. return ListComponent.COLUMN_TYPE_EMAIL;
  386. }
  387. get COLUMN_TYPE_POSITION(): string {
  388. return ListComponent.COLUMN_TYPE_POSITION;
  389. }
  390. get COLUMN_TYPE_IMAGE(): string {
  391. return ListComponent.COLUMN_TYPE_IMAGE;
  392. }
  393. get COLUMN_TYPE_COMBINED_IMAGES(): string {
  394. return ListComponent.COLUMN_TYPE_COMBINED_IMAGES;
  395. }
  396. get COLUMN_TYPE_TEXT(): string {
  397. return ListComponent.COLUMN_TYPE_TEXT;
  398. }
  399. get COLUMN_TYPE_NUMBER(): string {
  400. return ListComponent.COLUMN_TYPE_NUMBER;
  401. }
  402. get COLUMN_TYPE_NUMBER_UNFORMATTED(): string {
  403. return ListComponent.COLUMN_TYPE_NUMBER_UNFORMATTED;
  404. }
  405. get COLUMN_TYPE_NUMBER_BOLD(): string {
  406. return ListComponent.COLUMN_TYPE_NUMBER_BOLD;
  407. }
  408. get COLUMN_TYPE_TEXT_BOLD(): string {
  409. return ListComponent.COLUMN_TYPE_TEXT_BOLD;
  410. }
  411. get COLUMN_TYPE_TEXT_LINKED(): string {
  412. return ListComponent.COLUMN_TYPE_TEXT_LINKED;
  413. }
  414. get COLUMN_TYPE_WEBSITE(): string {
  415. return ListComponent.COLUMN_TYPE_WEBSITE;
  416. }
  417. ngOnDestroy(): void {
  418. this.clearAutoRefresh();
  419. }
  420. }