import { Maybe } from '@/core'; import { PaginatorComponent, UikitEmptyStateComponent } from '@/uikit'; import { jalaliDiff } from '@/utils'; import { CommonModule } from '@angular/common'; import { Component, computed, ContentChild, ElementRef, EventEmitter, HostListener, Input, Output, Renderer2, signal, TemplateRef, } from '@angular/core'; import { RouterLink } from '@angular/router'; 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 { AppPageDataListGridView } from './page-data-list-grid-view.component'; import { AppPageDataListTableView } from './page-data-list-table-view.component'; export type TDataType = | 'simple' | 'price' | 'boolean' | 'date' | 'nested' | 'index' | 'id' | 'thumbnail' | 'badge' | 'number' | 'dateTime' | 'duration' | 'active' | 'has'; export interface IColumn { field: T extends object ? keyof T | string : string; header: string; width?: string; minWidth?: string; canCopy?: boolean; type?: TDataType; variant?: 'text' | 'tag'; tagOptions?: { severity?: 'success' | 'secondary' | 'info' | 'warn' | 'danger' | 'contrast'; }; nestedOption?: { path: string; type?: TDataType; }; thumbnailOptions?: { editable: boolean; deletable: boolean; showPreview: boolean; }; dateOption?: { expiredMode?: boolean; dateTime?: boolean; onlyTime?: boolean; }; customDataModel?: TemplateRef | ((item: T) => string | number | boolean); } @Component({ selector: 'app-page-data-list', templateUrl: './page-data-list.component.html', host: { class: 'block w-full h-full overflow-hidden', }, imports: [ CommonModule, TableModule, ButtonModule, SkeletonModule, PaginatorModule, DrawerModule, PaginatorComponent, AppPageDataListTableView, AppPageDataListGridView, UikitEmptyStateComponent, RouterLink, ], }) 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!: any[]; @Input({ required: true }) loading!: boolean; @Input() emptyPlaceholderTitle: string = 'موردی برای نمایش وجود ندارد'; @Input() emptyPlaceholderDescription?: string = ''; @Input() emptyPlaceholderCtaLabel?: string; @Input() totalPages?: number = 1; @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() showRefresh: boolean = true; @Input() isFiltered: boolean = false; @Input() fullHeight?: boolean = false; @Input() height: string = ''; @Input() expandable?: boolean = false; @Input() expandableItemKey?: string = ''; @Input() expandColumns?: Maybe = null; @Input() dataKey?: string = 'items'; @Input() showIndex?: boolean = true; @Input() showAll?: boolean = false; @Input() allItemsPageRoute?: string; @ContentChild('filter', { static: true }) filter!: TemplateRef | null; @ContentChild('caption', { static: true }) caption!: TemplateRef | null; @ContentChild('moreActions', { static: true }) moreActions!: TemplateRef | null; @ContentChild('expandableTemp', { static: false }) expandableTemp!: TemplateRef | null; @Output() onEdit = new EventEmitter(); @Output() onDelete = new EventEmitter(); @Output() onDetails = new EventEmitter(); @Output() onAdd = new EventEmitter(); @Output() onChangePage = new EventEmitter(); @Output() onThumbnailClick = new EventEmitter(); @Output() onRefresh = new EventEmitter(); filterDrawerVisible = signal(false); expandedRows: { [key: string]: boolean } = {}; hasCaption = computed( () => !!(this.pageTitle || this.showAdd || this.filter || this.showRefresh || this.moreActions) ); ngOnInit() { if (this.height) this.renderer.setStyle( this.host.nativeElement, 'height', this.fullHeight ? '100cqmin' : this.height ); // if (this.fullHeight) { // } this.updateViewportMode(); } edit = (item: any) => { this.onEdit.emit(item); }; remove = (item: any) => { this.onDelete.emit(item); }; details = (item: any) => { this.onDetails.emit(item); }; openAddForm = () => { this.onAdd.emit(); }; openFilter = () => { this.filterDrawerVisible.set(true); }; closeFilter = () => { this.filterDrawerVisible.set(false); }; onPage = ($event: any) => { this.onChangePage.emit($event); }; openThumbnailModal = (item: I) => { // this. }; get showPaginator() { return !!(this.totalPages && this.perPage && this.totalPages > this.perPage); } getPreviewClasses(item: Record, column: IColumn): any { if (!item || !column || column.customDataModel) return ''; try { const { field } = column; const data = item[String(field)]; switch (column.type) { case 'date': if (column.dateOption?.expiredMode) { if (jalaliDiff(new Date(), data) > 0) { return 'text-error'; } } return ''; default: return; } } catch (e) { return ''; } } refresh() { this.onRefresh.emit(); } isMobile = false; @HostListener('window:resize') onResize() { this.updateViewportMode(); } private updateViewportMode() { this.isMobile = typeof window !== 'undefined' && window.innerWidth <= 768; } }