add generic table
This commit is contained in:
parent
1bfa4dec47
commit
ec44849b31
@ -22,10 +22,6 @@ export class GenericTableSearchForm {
|
||||
|
||||
this.filterForm.valueChanges.pipe(
|
||||
debounceTime(300),
|
||||
filter( value => {
|
||||
console.info(value)
|
||||
return value.term && value.term.length >= 3;
|
||||
}),
|
||||
distinctUntilChanged()
|
||||
).subscribe(values => {
|
||||
const cleanFilter = Object.fromEntries(
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
<div [ngClass]="config.tableCssClass" [class]="'overflow-x-auto'">
|
||||
<app-generic-table-search-form (searchTermChanged)="searchTermChanged($event)" ></app-generic-table-search-form>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
@if (data$ | async; as getDataResponse) {
|
||||
<table class="table w-full table-zebra">
|
||||
<thead>
|
||||
@ -51,6 +54,19 @@
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
@if ((getDataResponse?.data?.meta?.totalPages ?? 1) > 0) {
|
||||
<div class="flex justify-end mt-4 items-center me-3 ">
|
||||
Items: {{getDataResponse.data.data.length}}/{{getDataResponse.data.meta.totalItems}}
|
||||
Page: {{getDataResponse.data.meta.currentPage}}/{{getDataResponse.data.meta.totalPages}}
|
||||
PageSize:
|
||||
<select class="select w-auto" #pageSize (change)="onPageSizeChange(pageSize.value)">
|
||||
<option>10</option>
|
||||
<option>20</option>
|
||||
<option>50</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
}
|
||||
@if ((getDataResponse?.data?.meta?.totalPages ?? 1) > 1) {
|
||||
<div class="flex justify-center mt-4">
|
||||
<div class="join">
|
||||
|
||||
@ -88,4 +88,9 @@ export class GenericTable<T> implements OnInit {
|
||||
this.config.page$.next(1);
|
||||
this.config.filter$.next(searchTerm);
|
||||
}
|
||||
|
||||
protected onPageSizeChange(value: any) {
|
||||
this.config.page$.next(1);
|
||||
this.config.limit$.next(+value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
// dvbooking-cli/src/templates/angular-generic/data-provider.service.ts.tpl
|
||||
|
||||
// Generated by the CLI
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { DataProvider, GetDataOptions, GetDataResponse } from '../../../../components/generic-table/data-provider.interface';
|
||||
import { Product } from '../../models/product.model';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { ProductService } from '../../services/product.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ProductDataProvider implements DataProvider<Product> {
|
||||
private productService = inject(ProductService);
|
||||
|
||||
getData(options?: GetDataOptions): Observable<GetDataResponse<Product>> {
|
||||
const {q,page,limit} = options?.params ?? {};
|
||||
// The generic table's params are compatible with our NestJS Query DTO
|
||||
return this.productService.search(q ?? '',page,limit, ).pipe(
|
||||
map((res) => {
|
||||
// Adapt the paginated response to the GetDataResponse format
|
||||
return { data: res };
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<!-- dvbooking-cli/src/templates/angular-generic/table.component.html.tpl -->
|
||||
|
||||
<!-- Generated by the CLI -->
|
||||
<div class="p-4 md:p-8 space-y-6">
|
||||
<div class="flex justify-between items-center">
|
||||
<h1 class="text-3xl font-bold">Products (Generic Table)</h1>
|
||||
<a routerLink="/products/new" class="btn btn-primary">Create New</a>
|
||||
</div>
|
||||
|
||||
<app-generic-table [config]="tableConfig"></app-generic-table>
|
||||
</div>
|
||||
@ -0,0 +1,112 @@
|
||||
// dvbooking-cli/src/templates/angular-generic/table.component.ts.tpl
|
||||
|
||||
// Generated by the CLI
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { Router, RouterModule } from '@angular/router';
|
||||
import { Product } from '../../models/product.model';
|
||||
import { ProductDataProvider } from './product-data-provider.service';
|
||||
import { ColumnDefinition } from '../../../../components/generic-table/column-definition.interface';
|
||||
import { GenericTable } from '../../../../components/generic-table/generic-table';
|
||||
import { GenericTableConfig } from '../../../../components/generic-table/generic-table.config';
|
||||
import {
|
||||
ActionDefinition,
|
||||
GenericActionColumn,
|
||||
} from '../../../../components/generic-action-column/generic-action-column';
|
||||
import { ProductService } from '../../services/product.service';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-table',
|
||||
standalone: true,
|
||||
imports: [GenericTable, RouterModule],
|
||||
templateUrl: './product-table.component.html',
|
||||
})
|
||||
export class ProductTableComponent implements OnInit {
|
||||
|
||||
private refresh$ = new BehaviorSubject<void>(undefined);
|
||||
private filter$ = new BehaviorSubject<any>({});
|
||||
private page$ = new BehaviorSubject<number>(1);
|
||||
private limit$ = new BehaviorSubject<number>(10);
|
||||
|
||||
router = inject(Router);
|
||||
tableConfig!: GenericTableConfig<Product>;
|
||||
|
||||
productDataProvider = inject(ProductDataProvider);
|
||||
productService = inject(ProductService);
|
||||
|
||||
ngOnInit(): void {
|
||||
const actionHandler = (action: ActionDefinition<Product>, item: Product) => {
|
||||
switch (action.action) {
|
||||
case 'view':
|
||||
this.router.navigate(['/products', item?.id]);
|
||||
break;
|
||||
case 'edit':
|
||||
this.router.navigate(['/products', item?.id, 'edit']);
|
||||
break;
|
||||
case 'delete':
|
||||
this.deleteItem(item.id);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
this.tableConfig = {
|
||||
refresh$: this.refresh$,
|
||||
filter$: this.filter$,
|
||||
page$: this.page$,
|
||||
limit$: this.limit$,
|
||||
dataProvider: this.productDataProvider,
|
||||
columns: [
|
||||
{
|
||||
attribute: 'name',
|
||||
headerCell: true,
|
||||
valueCell: true,
|
||||
},
|
||||
{
|
||||
attribute: 'price',
|
||||
headerCell: true,
|
||||
valueCell: true,
|
||||
},
|
||||
{
|
||||
attribute: 'is_available',
|
||||
headerCell: true,
|
||||
valueCell: {
|
||||
value: item => (item as any)?.is_available ? 'yes' : 'no',
|
||||
},
|
||||
},
|
||||
{
|
||||
attribute: 'actions',
|
||||
headerCell: { value: 'Actions' },
|
||||
valueCell: {
|
||||
component: GenericActionColumn,
|
||||
componentInputs: item => ({
|
||||
item: item,
|
||||
actions: [
|
||||
{ action: 'view', handler: actionHandler },
|
||||
{ action: 'edit', handler: actionHandler },
|
||||
{ action: 'delete', handler: actionHandler },
|
||||
] as ActionDefinition<Product>[],
|
||||
}),
|
||||
},
|
||||
},
|
||||
] as ColumnDefinition<Product>[],
|
||||
tableCssClass: 'product-table-container',
|
||||
};
|
||||
}
|
||||
|
||||
deleteItem(id: number): void {
|
||||
if (confirm('Are you sure you want to delete this item?')) {
|
||||
this.productService.remove(id).subscribe({
|
||||
next: () => {
|
||||
console.log(`Item with ID ${id} deleted successfully.`);
|
||||
this.refresh$.next();
|
||||
},
|
||||
// --- THIS IS THE FIX ---
|
||||
// Explicitly type 'err' to satisfy strict TypeScript rules.
|
||||
error: (err: any) => {
|
||||
console.error(`Error deleting item with ID ${id}:`, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user