agregar vista publica de vehiculos aun faltan cambios
This commit is contained in:
182
apiferia/src/app/core/services/table.service.ts
Normal file
182
apiferia/src/app/core/services/table.service.ts
Normal file
@@ -0,0 +1,182 @@
|
||||
import type { SortDirection } from '@/app/directive/sortable.directive'
|
||||
import { DecimalPipe } from '@angular/common'
|
||||
import { inject, Injectable, PipeTransform } from '@angular/core'
|
||||
import { BehaviorSubject, Observable, of, Subject } from 'rxjs'
|
||||
import { debounceTime, delay, switchMap, tap } from 'rxjs/operators'
|
||||
|
||||
interface SearchResult<T> {
|
||||
items: T[]
|
||||
total: number
|
||||
}
|
||||
|
||||
interface State<T> {
|
||||
page: number
|
||||
startIndex: number
|
||||
endIndex: number
|
||||
pageSize: number
|
||||
searchTerm: string
|
||||
sortColumn: keyof T | ''
|
||||
sortDirection: SortDirection
|
||||
}
|
||||
|
||||
function matches<T>(items: any, term: string, searchFields: (keyof T)[]) {
|
||||
if (!term) return true
|
||||
term = term.toLowerCase()
|
||||
|
||||
for (const field of searchFields) {
|
||||
const value = (items[field] as unknown as string)?.toString().toLowerCase()
|
||||
if (value?.includes(term)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function compare<T>(v1: T, v2: T): number {
|
||||
return v1 < v2 ? -1 : v1 > v2 ? 1 : 0
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TableService<T extends {}> {
|
||||
private _loading$ = new BehaviorSubject<boolean>(true)
|
||||
private _search$ = new Subject<void>()
|
||||
private _items$ = new BehaviorSubject<T[]>([])
|
||||
private _total$ = new BehaviorSubject<number>(0)
|
||||
|
||||
items: T[] = []
|
||||
private _state: State<T> = {
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
searchTerm: '',
|
||||
startIndex: 1,
|
||||
endIndex: 10,
|
||||
sortColumn: '',
|
||||
sortDirection: '',
|
||||
}
|
||||
|
||||
public pipe = inject(DecimalPipe)
|
||||
constructor() {
|
||||
this._search$
|
||||
.pipe(
|
||||
tap(() => this._loading$.next(true)),
|
||||
debounceTime(200),
|
||||
switchMap(() => this._search()),
|
||||
delay(0),
|
||||
tap(() => this._loading$.next(false))
|
||||
)
|
||||
.subscribe((result) => {
|
||||
this._items$.next(result.items)
|
||||
this._total$.next(result.total)
|
||||
})
|
||||
|
||||
this._search$.next()
|
||||
}
|
||||
|
||||
get items$(): Observable<T[]> {
|
||||
return this._items$.asObservable()
|
||||
}
|
||||
|
||||
get total$(): Observable<number> {
|
||||
return this._total$.asObservable()
|
||||
}
|
||||
|
||||
get loading$(): Observable<boolean> {
|
||||
return this._loading$.asObservable()
|
||||
}
|
||||
|
||||
get page(): number {
|
||||
return this._state.page
|
||||
}
|
||||
|
||||
get startIndex(): number {
|
||||
return this._state.startIndex
|
||||
}
|
||||
|
||||
get endIndex(): number {
|
||||
return this._state.endIndex
|
||||
}
|
||||
|
||||
get pageSize(): number {
|
||||
return this._state.pageSize
|
||||
}
|
||||
|
||||
get searchTerm(): string {
|
||||
return this._state.searchTerm
|
||||
}
|
||||
|
||||
get sortColumn(): keyof T | '' {
|
||||
return this._state.sortColumn
|
||||
}
|
||||
|
||||
get sortDirection(): SortDirection {
|
||||
return this._state.sortDirection
|
||||
}
|
||||
|
||||
set page(page: number) {
|
||||
this._set({ page })
|
||||
}
|
||||
|
||||
set startIndex(startIndex: number) {
|
||||
this._set({ startIndex })
|
||||
}
|
||||
set endIndex(endIndex: number) {
|
||||
this._set({ endIndex })
|
||||
}
|
||||
|
||||
set pageSize(pageSize: number) {
|
||||
this._set({ pageSize })
|
||||
}
|
||||
|
||||
set searchTerm(searchTerm: string) {
|
||||
this._set({ searchTerm })
|
||||
}
|
||||
|
||||
set sortColumn(sortColumn: keyof T | '') {
|
||||
this._set({ sortColumn })
|
||||
}
|
||||
|
||||
set sortDirection(sortDirection: SortDirection) {
|
||||
this._set({ sortDirection })
|
||||
}
|
||||
|
||||
setItems(items: T[], pageSize: number): void {
|
||||
this.items = items
|
||||
this._set({ pageSize })
|
||||
this._set({ endIndex: pageSize })
|
||||
}
|
||||
|
||||
private _set(patch: Partial<State<T>>): void {
|
||||
Object.assign(this._state, patch)
|
||||
this._search$.next()
|
||||
}
|
||||
|
||||
private _search(): Observable<SearchResult<T>> {
|
||||
const { pageSize, page, searchTerm, sortColumn, sortDirection } =
|
||||
this._state
|
||||
const searchableFields = Object.keys(this.items[0]) as (keyof T)[]
|
||||
|
||||
// filter
|
||||
let filteredItems = this.items.filter((item) =>
|
||||
matches(item, searchTerm, searchableFields)
|
||||
)
|
||||
|
||||
// Sort
|
||||
if (sortColumn) {
|
||||
filteredItems = [...filteredItems].sort((a, b) => {
|
||||
const res = compare(a[sortColumn], b[sortColumn])
|
||||
return sortDirection === 'asc' ? res : -res
|
||||
})
|
||||
}
|
||||
|
||||
const total = filteredItems.length
|
||||
|
||||
// Paginate the items
|
||||
this.startIndex = (page - 1) * pageSize
|
||||
this.endIndex = this.startIndex + pageSize
|
||||
const paginatedItems = filteredItems.slice(this.startIndex, this.endIndex)
|
||||
this._loading$.next(false)
|
||||
return of({ items: paginatedItems, total })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user