Feature: Paged customer list.
This commit is contained in:
@ -2,11 +2,12 @@ import uuid
|
||||
|
||||
from collections.abc import Sequence
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Security, status
|
||||
from sqlalchemy import delete, or_, select
|
||||
from sqlalchemy import asc, delete, desc, func, or_, select
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy.orm import InstrumentedAttribute, Session, contains_eager
|
||||
|
||||
from ..core.security import get_current_active_user as get_user
|
||||
from ..db.session import SessionFuture
|
||||
@ -97,29 +98,99 @@ def show_blank(
|
||||
return blank_customer_info(sc)
|
||||
|
||||
|
||||
@router.get("/list", response_model=list[schemas.Customer])
|
||||
def show_list(user: UserToken = Depends(get_user)) -> list[schemas.Customer]:
|
||||
@router.get("/list", response_model=schemas.PagedCustomers)
|
||||
def show_list(
|
||||
q: str | None = None,
|
||||
p: int = 0, # Page number
|
||||
s: int = 50, # Page size
|
||||
f: str | None = None, # Sort field
|
||||
d: str | None = None, # Sort direction
|
||||
user: UserToken = Depends(get_user),
|
||||
) -> schemas.PagedCustomers:
|
||||
# Handle dynamic sorting safely
|
||||
sort_field = ALLOWED_SORT_FIELDS.get(f or "name", Customer.name) # Default to name
|
||||
sort_direction = desc if (d or "").lower() == "desc" else asc
|
||||
|
||||
filter = [] if q is None else [x.strip() for x in q.split()]
|
||||
with SessionFuture() as db:
|
||||
sc = db.execute(select(SaleCategory).order_by(SaleCategory.name)).scalars().all()
|
||||
return [
|
||||
customer_info(item, sc) for item in db.execute(select(Customer).order_by(Customer.name)).scalars().all()
|
||||
]
|
||||
customers, total = customer_list(filter, p, s, sort_field, sort_direction, db)
|
||||
return schemas.PagedCustomers(
|
||||
items=[customer_info(item, sc) for item in customers],
|
||||
page=p,
|
||||
page_size=s,
|
||||
total=total,
|
||||
field=f,
|
||||
sort_direction=d,
|
||||
)
|
||||
|
||||
|
||||
ALLOWED_SORT_FIELDS: dict[str, InstrumentedAttribute[Any]] = {
|
||||
"name": Customer.name,
|
||||
"phone": Customer.phone,
|
||||
"address": Customer.address,
|
||||
"printInBill": Customer.print_in_bill,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/query", response_model=list[schemas.Customer])
|
||||
def show_term(
|
||||
q: str,
|
||||
q: str | None,
|
||||
p: int = 0, # Page number
|
||||
s: int = 25, # Page size
|
||||
f: str | None = None, # Sort field
|
||||
d: str | None = None, # Sort direction
|
||||
current_user: UserToken = Depends(get_user),
|
||||
) -> list[schemas.Customer]:
|
||||
query = select(Customer)
|
||||
if q is not None:
|
||||
for item in q.split():
|
||||
query = query.where(or_(Customer.name.ilike(f"%{item}%"), Customer.phone.ilike(f"%{item}%")))
|
||||
# Handle dynamic sorting safely
|
||||
sort_field = ALLOWED_SORT_FIELDS.get(f or "name", Customer.name) # Default to name
|
||||
sort_direction = desc if (d or "").lower() == "desc" else asc
|
||||
|
||||
query = query.order_by(Customer.name)
|
||||
filter = [] if q is None else [x.strip() for x in q.split()]
|
||||
with SessionFuture() as db:
|
||||
sc = db.execute(select(SaleCategory).order_by(SaleCategory.name)).scalars().all()
|
||||
return [customer_info(item, sc) for item in db.execute(query).scalars().all()]
|
||||
customers, _ = customer_list(filter, p, s, sort_field, sort_direction, db)
|
||||
return [customer_info(item, sc) for item in customers]
|
||||
|
||||
|
||||
def customer_list(
|
||||
filter: list[str],
|
||||
page: int | None,
|
||||
page_size: int | None,
|
||||
sort_field: InstrumentedAttribute[Any] | None,
|
||||
sort_direction: Any | None,
|
||||
db: Session,
|
||||
) -> tuple[Sequence[Customer], int]:
|
||||
# Handle dynamic sorting safely
|
||||
sort_field = sort_field or Customer.name # Default to name
|
||||
sort_direction = sort_direction or asc
|
||||
|
||||
print(
|
||||
f"Filter: {filter}, Page: {page}, Page Size: {page_size}, Sort Field: {sort_field}, Sort Direction: {sort_direction}"
|
||||
)
|
||||
id_query = select(Customer.id)
|
||||
for item in filter:
|
||||
id_query = id_query.where(or_(Customer.name.ilike(f"%{item}%"), Customer.phone.ilike(f"%{item}%")))
|
||||
id_query = id_query.order_by(sort_direction(sort_field))
|
||||
if page_size: # Paging
|
||||
if page:
|
||||
id_query = id_query.offset(page * page_size)
|
||||
id_query = id_query.limit(page_size)
|
||||
ids = db.execute(id_query).scalars().all()
|
||||
|
||||
total_query = select(func.count(Customer.id))
|
||||
for item in filter:
|
||||
total_query = total_query.where(or_(Customer.name.ilike(f"%{item}%"), Customer.phone.ilike(f"%{item}%")))
|
||||
total = db.execute(total_query).scalar_one()
|
||||
|
||||
query = (
|
||||
select(Customer)
|
||||
.join(Customer.discounts, isouter=True)
|
||||
.where(Customer.id.in_(ids))
|
||||
.order_by(sort_direction(sort_field))
|
||||
.options(contains_eager(Customer.discounts))
|
||||
)
|
||||
return db.execute(query).unique().scalars().all(), total
|
||||
|
||||
|
||||
@router.get("/{id_}", response_model=schemas.Customer)
|
||||
|
||||
@ -40,3 +40,17 @@ class CustomerLink(BaseModel):
|
||||
class CustomerBlank(CustomerIn):
|
||||
name: str
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
|
||||
class PagedCustomers(BaseModel):
|
||||
items: list[Customer]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
field: str | None
|
||||
sort_direction: str | None
|
||||
|
||||
model_config = ConfigDict(str_strip_whitespace=True, alias_generator=to_camel, populate_by_name=True)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.items)
|
||||
|
||||
17
bookie/src/app/core/paged-result.ts
Normal file
17
bookie/src/app/core/paged-result.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export class PagedResult<T> {
|
||||
items: T[];
|
||||
page: number; // current page (zero-based)
|
||||
pageSize: number; // items per page
|
||||
total: number; // total items in all pages
|
||||
totalPages: number;
|
||||
field?: string; // active sort column
|
||||
sortDirection?: 'asc' | 'desc'; // sort direction
|
||||
|
||||
constructor(items: T[], page: number, pageSize: number, total: number) {
|
||||
this.items = items;
|
||||
this.page = page;
|
||||
this.pageSize = pageSize;
|
||||
this.total = total;
|
||||
this.totalPages = Math.ceil(total / pageSize);
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,14 @@ import { inject } from '@angular/core';
|
||||
import { ResolveFn } from '@angular/router';
|
||||
|
||||
import { Customer } from '../core/customer';
|
||||
import { PagedResult } from '../core/paged-result';
|
||||
import { CustomerService } from './customer.service';
|
||||
|
||||
export const customerListResolver: ResolveFn<Customer[]> = () => {
|
||||
return inject(CustomerService).list();
|
||||
export const customerListResolver: ResolveFn<PagedResult<Customer>> = (route) => {
|
||||
const q = route.queryParamMap.get('q');
|
||||
const page = Number(route.queryParamMap.get('p') ?? 0);
|
||||
const size = Number(route.queryParamMap.get('s') ?? 50);
|
||||
const field = route.queryParamMap.get('f') ?? 'name';
|
||||
const direction = route.queryParamMap.get('d') ?? 'asc';
|
||||
return inject(CustomerService).list(q, page, size, field, direction);
|
||||
};
|
||||
|
||||
@ -1,16 +1,161 @@
|
||||
import { DataSource } from '@angular/cdk/collections';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { Observable, of as observableOf } from 'rxjs';
|
||||
import { PagedResult } from 'src/app/core/paged-result';
|
||||
|
||||
import { Customer } from '../../core/customer';
|
||||
|
||||
export class CustomerListDatasource extends DataSource<Customer> {
|
||||
constructor(public data: Customer[]) {
|
||||
constructor(
|
||||
public data: PagedResult<Customer>,
|
||||
private paginator?: MatPaginator,
|
||||
private sort?: MatSort,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
connect(): Observable<Customer[]> {
|
||||
return observableOf(this.data);
|
||||
if (this.paginator) {
|
||||
this.paginator.length = this.data.total;
|
||||
if (this.data.pageSize !== undefined) {
|
||||
this.paginator.pageSize = this.data.pageSize;
|
||||
}
|
||||
if (this.data.page !== undefined) {
|
||||
this.paginator.pageIndex = this.data.page;
|
||||
}
|
||||
}
|
||||
if (this.sort) {
|
||||
if (this.data.field !== undefined) {
|
||||
this.sort.active = this.data.field;
|
||||
}
|
||||
if (this.data.sortDirection !== undefined) {
|
||||
this.sort.direction = this.data.sortDirection;
|
||||
}
|
||||
}
|
||||
return observableOf(this.data.items);
|
||||
}
|
||||
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
// import { DataSource } from '@angular/cdk/collections';
|
||||
// import { map, merge, Observable, of as observableOf, tap } from 'rxjs';
|
||||
|
||||
// import { Customer } from '../../core/customer';
|
||||
// import { MatPaginator, PageEvent } from '@angular/material/paginator';
|
||||
// import { MatSort, Sort } from '@angular/material/sort';
|
||||
// import { EventEmitter } from '@angular/core';
|
||||
|
||||
// export class CustomerListDatasource extends DataSource<Customer> {
|
||||
// private filterValue = '';
|
||||
// constructor(public data: Customer[],
|
||||
// private readonly filter: Observable<string>,
|
||||
// private paginator?: MatPaginator,
|
||||
// private sort?: MatSort,
|
||||
|
||||
// ) {
|
||||
// super();
|
||||
// this.filter = filter.pipe(
|
||||
// tap((x) => {
|
||||
// this.filterValue = x;
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
|
||||
// connect(): Observable<Customer[]> {
|
||||
// const dataMutations: (EventEmitter<PageEvent> | EventEmitter<Sort>)[] = [];
|
||||
// if (this.paginator) {
|
||||
// dataMutations.push((this.paginator as MatPaginator).page);
|
||||
// }
|
||||
// if (this.sort) {
|
||||
// dataMutations.push((this.sort as MatSort).sortChange);
|
||||
// }
|
||||
|
||||
// return merge(observableOf(this.data), this.filter, ...dataMutations).pipe(
|
||||
// map(() => this.getFilteredData([...this.data])),
|
||||
// tap((x: Customer[]) => {
|
||||
// if (this.paginator) {
|
||||
// this.paginator.length = x.length;
|
||||
// }
|
||||
// }),
|
||||
// map((x: Customer[]) => this.getPagedData(this.getSortedData(x))),
|
||||
// );
|
||||
// }
|
||||
|
||||
// disconnect() {}
|
||||
// private getFilteredData(data: Customer[]): Customer[] {
|
||||
// return this.filterValue.toLowerCase().split(' ').reduce(
|
||||
// (p: Customer[], c: string) =>
|
||||
// p.filter((x) => {
|
||||
// const productString = `${x.name} ${x.phone}${x.address}`.toLowerCase();
|
||||
// return productString.indexOf(c) !== -1;
|
||||
// }),
|
||||
// Object.assign([], data),
|
||||
// );
|
||||
// }
|
||||
// private getPagedData(data: Customer[]) {
|
||||
// if (this.paginator === undefined) {
|
||||
// return data;
|
||||
// }
|
||||
// const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
|
||||
// return data.splice(startIndex, this.paginator.pageSize);
|
||||
// }
|
||||
|
||||
// private getSortedData(data: Customer[]) {
|
||||
// if (this.sort === undefined) {
|
||||
// return data;
|
||||
// }
|
||||
// if (!this.sort.active || this.sort.direction === '') {
|
||||
// return data;
|
||||
// }
|
||||
|
||||
// const sort = this.sort as MatSort;
|
||||
// console.log('Sorting by', sort.active, sort.direction);
|
||||
// return data.sort(sortBy(sort.active as keyof Customer, sort.direction === 'asc'));
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// function compareValues<T>(a: T, b: T, isAsc: boolean): number {
|
||||
// if (a === b) return 0;
|
||||
// if (a == null) return isAsc ? -1 : 1;
|
||||
// if (b == null) return isAsc ? 1 : -1;
|
||||
|
||||
// if (typeof a === "string" && typeof b === "string") {
|
||||
// return isAsc ? a.localeCompare(b) : b.localeCompare(a);
|
||||
// }
|
||||
// if (typeof a === "number" && typeof b === "number") {
|
||||
// return isAsc ? a - b : b - a;
|
||||
// }
|
||||
// if (typeof a === "boolean" && typeof b === "boolean") {
|
||||
// return isAsc ? (Number(a) - Number(b)) : (Number(b) - Number(a));
|
||||
// }
|
||||
// // Fallback
|
||||
// const aStr = String(a);
|
||||
// const bStr = String(b);
|
||||
// return isAsc ? aStr.localeCompare(bStr) : bStr.localeCompare(aStr);
|
||||
// }
|
||||
|
||||
// function sortBy<T, K extends keyof T>(
|
||||
// key: K,
|
||||
// isAsc = true
|
||||
// ): (a: T, b: T) => number {
|
||||
// return (a, b) => compareValues(a[key], b[key], isAsc);
|
||||
// }
|
||||
|
||||
// // function getNestedValue<T>(obj: T, path: string | string[]): any {
|
||||
// // const keys = Array.isArray(path) ? path : path.split(".");
|
||||
// // return keys.reduce((acc, key) => (acc == null ? undefined : acc[key]), obj);
|
||||
// // }
|
||||
|
||||
// // function sortByNested<T>(
|
||||
// // path: string | string[],
|
||||
// // isAsc = true
|
||||
// // ): (a: T, b: T) => number {
|
||||
// // return (a, b) => {
|
||||
// // const aValue = getNestedValue(a, path);
|
||||
// // const bValue = getNestedValue(b, path);
|
||||
// // return compareValues(aValue, bValue, isAsc);
|
||||
// // };
|
||||
// // }
|
||||
|
||||
@ -9,10 +9,18 @@
|
||||
</mat-card-title-group>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<mat-table #table [dataSource]="dataSource" aria-label="Elements">
|
||||
<form [formGroup]="form" class="flex flex-col">
|
||||
<div class="flex flex-row justify-around content-start items-start">
|
||||
<mat-form-field class="flex-auto">
|
||||
<mat-label>Filter</mat-label>
|
||||
<input type="text" matInput #filterElement formControlName="filter" autocomplete="off" />
|
||||
</mat-form-field>
|
||||
</div>
|
||||
</form>
|
||||
<mat-table #table [dataSource]="dataSource" matSort aria-label="Elements">
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<mat-header-cell *matHeaderCellDef>Name</mat-header-cell>
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Name</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row"
|
||||
><a [routerLink]="['/customers', row.id]">{{ row.name }}</a></mat-cell
|
||||
>
|
||||
@ -20,19 +28,19 @@
|
||||
|
||||
<!-- Phone Column -->
|
||||
<ng-container matColumnDef="phone">
|
||||
<mat-header-cell *matHeaderCellDef>Phone</mat-header-cell>
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Phone</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.phone }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Address Column -->
|
||||
<ng-container matColumnDef="address">
|
||||
<mat-header-cell *matHeaderCellDef>Address</mat-header-cell>
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Address</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.address }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
<!-- Print In Bill Column -->
|
||||
<ng-container matColumnDef="printInBill">
|
||||
<mat-header-cell *matHeaderCellDef>Print in Bill</mat-header-cell>
|
||||
<mat-header-cell *matHeaderCellDef mat-sort-header>Print in Bill</mat-header-cell>
|
||||
<mat-cell *matCellDef="let row">{{ row.printInBill }}</mat-cell>
|
||||
</ng-container>
|
||||
|
||||
@ -51,5 +59,13 @@
|
||||
<mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row>
|
||||
<mat-row *matRowDef="let row; columns: displayedColumns"></mat-row>
|
||||
</mat-table>
|
||||
<mat-paginator
|
||||
#paginator
|
||||
[length]="dataSource.data.total"
|
||||
[pageIndex]="0"
|
||||
[pageSize]="50"
|
||||
[pageSizeOptions]="[25, 50, 100, 250, 5000]"
|
||||
>
|
||||
</mat-paginator>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
import { PercentPipe } from '@angular/common';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ElementRef, OnInit, ViewChild, inject, AfterViewInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatSort, MatSortModule } from '@angular/material/sort';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
|
||||
import { debounceTime, distinctUntilChanged, Observable } from 'rxjs';
|
||||
import { PagedResult } from 'src/app/core/paged-result';
|
||||
|
||||
import { Customer } from '../../core/customer';
|
||||
import { CustomerListDatasource } from './customer-list-datasource';
|
||||
@ -13,22 +19,88 @@ import { CustomerListDatasource } from './customer-list-datasource';
|
||||
selector: 'app-customer-list',
|
||||
templateUrl: './customer-list.component.html',
|
||||
styleUrls: ['./customer-list.component.css'],
|
||||
imports: [MatButtonModule, MatCardModule, MatIconModule, MatTableModule, PercentPipe, RouterLink],
|
||||
imports: [
|
||||
MatButtonModule,
|
||||
MatCardModule,
|
||||
MatIconModule,
|
||||
MatTableModule,
|
||||
PercentPipe,
|
||||
RouterLink,
|
||||
MatInputModule,
|
||||
MatPaginatorModule,
|
||||
MatSortModule,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
})
|
||||
export class CustomerListComponent implements OnInit {
|
||||
export class CustomerListComponent implements OnInit, AfterViewInit {
|
||||
private route = inject(ActivatedRoute);
|
||||
private router = inject(Router);
|
||||
private cdr = inject(ChangeDetectorRef);
|
||||
|
||||
@ViewChild('filterElement', { static: true }) filterElement!: ElementRef<HTMLInputElement>;
|
||||
@ViewChild(MatPaginator, { static: true }) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort, { static: true }) sort!: MatSort;
|
||||
|
||||
data: PagedResult<Customer> = new PagedResult<Customer>([], 0, 0, 0);
|
||||
filter: Observable<string>;
|
||||
dataSource: CustomerListDatasource;
|
||||
form: FormGroup<{
|
||||
filter: FormControl<string>;
|
||||
}>;
|
||||
|
||||
dataSource: CustomerListDatasource = new CustomerListDatasource([]);
|
||||
list: Customer[] = [];
|
||||
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
|
||||
displayedColumns = ['name', 'phone', 'address', 'printInBill', 'discounts'];
|
||||
|
||||
ngOnInit() {
|
||||
this.route.data.subscribe((value) => {
|
||||
const data = value as { list: Customer[] };
|
||||
data.list.forEach((c) => (c.discounts = c.discounts.filter((d) => d.discount !== 0)));
|
||||
this.list = data.list;
|
||||
constructor() {
|
||||
this.form = new FormGroup({
|
||||
filter: new FormControl<string>('', { nonNullable: true }),
|
||||
});
|
||||
this.dataSource = new CustomerListDatasource(this.list);
|
||||
this.filter = this.form.controls.filter.valueChanges.pipe(debounceTime(150), distinctUntilChanged());
|
||||
this.dataSource = new CustomerListDatasource(this.data, this.paginator, this.sort);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const q = this.route.snapshot.queryParamMap.get('q');
|
||||
if (q) {
|
||||
this.form.controls.filter.setValue(q, { emitEvent: false }); // Prevent infinite loop
|
||||
}
|
||||
this.route.data.subscribe((value) => {
|
||||
const data = value as { data: PagedResult<Customer> };
|
||||
data.data.items.forEach((c) => (c.discounts = c.discounts.filter((d) => d.discount !== 0)));
|
||||
this.data = data.data;
|
||||
this.dataSource = new CustomerListDatasource(this.data, this.paginator, this.sort);
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
this.sort.sortChange.subscribe({
|
||||
next: () =>
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: { f: this.sort?.active ?? 'name', d: this.sort?.direction },
|
||||
queryParamsHandling: 'merge',
|
||||
}),
|
||||
});
|
||||
this.paginator.page.subscribe({
|
||||
next: () =>
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: { s: this.paginator.pageSize, p: this.paginator.pageIndex },
|
||||
queryParamsHandling: 'merge',
|
||||
}),
|
||||
});
|
||||
this.filter.subscribe({
|
||||
next: (value) => {
|
||||
this.router.navigate([], {
|
||||
relativeTo: this.route,
|
||||
queryParams: { q: value },
|
||||
queryParamsHandling: 'merge',
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
setTimeout(() => {
|
||||
this.filterElement.nativeElement.focus();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,9 @@ export const routes: Routes = [
|
||||
permission: 'Customers',
|
||||
},
|
||||
resolve: {
|
||||
list: customerListResolver,
|
||||
data: customerListResolver,
|
||||
},
|
||||
runGuardsAndResolvers: 'always',
|
||||
},
|
||||
{
|
||||
path: 'new',
|
||||
|
||||
@ -5,6 +5,7 @@ import { catchError } from 'rxjs/operators';
|
||||
|
||||
import { Customer } from '../core/customer';
|
||||
import { ErrorLoggerService } from '../core/error-logger.service';
|
||||
import { PagedResult } from '../core/paged-result';
|
||||
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' }),
|
||||
@ -26,10 +27,18 @@ export class CustomerService {
|
||||
.pipe(catchError(this.log.handleError(serviceName, `get id=${id}`))) as Observable<Customer>;
|
||||
}
|
||||
|
||||
list(): Observable<Customer[]> {
|
||||
list(q: string | null, page = 0, size = 50, field = 'name', direction = 'asc'): Observable<PagedResult<Customer>> {
|
||||
const params = {
|
||||
...(q ? { q } : {}),
|
||||
p: page,
|
||||
s: size,
|
||||
f: field,
|
||||
d: direction,
|
||||
};
|
||||
console.log('CustomerService.list', params);
|
||||
return this.http
|
||||
.get<Customer[]>(`${url}/list`)
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<Customer[]>;
|
||||
.get<PagedResult<Customer>>(`${url}/list`, { params })
|
||||
.pipe(catchError(this.log.handleError(serviceName, 'list'))) as Observable<PagedResult<Customer>>;
|
||||
}
|
||||
|
||||
save(customer: Customer): Observable<Customer> {
|
||||
|
||||
Reference in New Issue
Block a user