import { PaginatorComponent, UikitCopyComponent, UikitEmptyStateComponent } from '@/uikit'; import { formatJalali, formatWithCurrency } from '@/utils'; import { CommonModule } from '@angular/common'; import { Component, computed, ContentChild, ElementRef, EventEmitter, Input, Output, Renderer2, signal, TemplateRef, } from '@angular/core'; import { ButtonModule } from 'primeng/button'; import { DrawerModule } from 'primeng/drawer'; import { PaginatorModule } from 'primeng/paginator'; import { SkeletonModule } from 'primeng/skeleton'; import { TableModule } from 'primeng/table'; import { TableActionRowComponent } from '../table-action-row.component'; export interface IColumn { field: T extends object ? keyof T | string : string; header: string; width?: string; minWidth?: string; canCopy?: boolean; type?: 'text' | 'price' | 'boolean' | 'date' | 'nested' | 'index'; nestedPath?: string; customDataModel?: TemplateRef | ((item: T) => string | number | boolean); } @Component({ selector: 'app-page-data-list', templateUrl: './page-data-list.component.html', host: { class: 'block w-full', }, imports: [ CommonModule, TableActionRowComponent, UikitEmptyStateComponent, TableModule, ButtonModule, SkeletonModule, PaginatorModule, DrawerModule, PaginatorComponent, UikitCopyComponent, ], }) export class PageDataListComponent { constructor( private host: ElementRef, private renderer: Renderer2, ) {} @Input({ required: true }) pageTitle!: string; @Input() addNewCtaLabel?: string; @Input({ required: true }) columns!: IColumn[]; @Input({ required: true }) items!: I[]; @Input({ required: true }) loading!: boolean; @Input() emptyPlaceholderTitle: string = 'موردی برای نمایش وجود ندارد'; @Input() emptyPlaceholderDescription?: string = ''; @Input() emptyPlaceholderCtaLabel?: string; @Input() totalRecords!: number; @Input() perPage?: number = 10; @Input() currentPage?: number = 1; @Input() showEdit: boolean = false; @Input() showDelete: boolean = false; @Input() showDetails: boolean = false; @Input() showAdd: boolean = false; @Input() isFiltered: boolean = false; @Input() fullHeight?: boolean = false; @Input() height: string = ''; @ContentChild('filter', { static: true }) filter!: TemplateRef | null; @ContentChild('caption', { static: true }) caption!: TemplateRef | null; @Output() onEdit = new EventEmitter(); @Output() onDelete = new EventEmitter(); @Output() onDetails = new EventEmitter(); @Output() onAdd = new EventEmitter(); @Output() pageChange = new EventEmitter(); filterDrawerVisible = signal(false); ngOnInit() { if (this.height) this.renderer.setStyle( this.host.nativeElement, 'height', this.fullHeight ? '100cqmin' : this.height, ); // if (this.fullHeight) { // } } edit = (item: I) => { this.onEdit.emit(item); }; remove = (item: I) => { this.onDelete.emit(item); }; details = (item: I) => { this.onDetails.emit(item); }; openAddForm = () => { this.onAdd.emit(); }; openFilter = () => { this.filterDrawerVisible.set(true); }; closeFilter = () => { this.filterDrawerVisible.set(false); }; onPage = ($event: any) => { this.pageChange.emit($event); }; get showPaginator() { return !!(this.totalRecords && this.perPage && this.totalRecords > this.perPage); } getCell(item: Record, column: IColumn): any { if (!item || !column) return ''; try { const { field } = column; if (column.customDataModel) { return this.renderCustom(column, item); } const data = item[String(field)]; switch (column.type) { case 'date': if (!data) return '-'; return formatJalali(data); case 'boolean': return data ? 'بله' : 'خیر'; case 'price': return formatWithCurrency(data, false, 'ریال'); case 'nested': { const path = column.nestedPath; if (!path) return '-'; const nestedData = path .split('.') .reduce((obj, key) => (obj && obj[key] !== undefined ? obj[key] : null), item); return nestedData || '-'; } default: break; } if (data === undefined || data === null) { return '-'; } if (typeof data === 'object') { return data.title || '-'; } return data || '-'; } catch (e) { return '-'; } } getTemplate(col: IColumn): TemplateRef | null { const v = col.customDataModel; return v instanceof TemplateRef ? (v as TemplateRef) : null; } renderCustom(column: IColumn, item: any): any { const v = column.customDataModel; if (!v) { return null; } if (typeof v === 'function') { try { return (v as (item: any) => any)(item); } catch { return '-'; } } if (typeof v === 'string') return v; return null; } actionsCount = computed(() => { let totalCount = 0; if (this.showEdit) totalCount += 1; if (this.showDelete) totalCount += 1; if (this.showDetails) totalCount += 1; return totalCount; }); }