Result List Empty State - Initial Loader - Pending Loader

This commit is contained in:
Nino
2025-03-11 17:06:19 +01:00
parent dcc70745da
commit a364a4f0e0
3 changed files with 66 additions and 18 deletions

View File

@@ -6,7 +6,7 @@
inputKey="qs"
(search)="onSearch()"
></filter-search-bar-input>
<span class="isa-text-body-2-regular"> {{ hits() }} Einträge </span>
<span class="isa-text-body-2-regular"> {{ entityHits() }} Einträge </span>
</div>
<div class="flex flex-row gap-4 items-center">
@@ -15,17 +15,43 @@
</div>
</div>
<div class="flex flex-col gap-4 w-full items-center justify-center">
@for (item of items(); track item.id) {
@defer (on viewport) {
<a [routerLink]="['../', 'details', item.id]" class="w-full">
<lib-return-results-item-list
#listElement
[item]="item"
></lib-return-results-item-list>
</a>
} @placeholder {
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
@let items = entityItems();
@if (items.length > 0) {
<div class="flex flex-col gap-4 w-full items-center justify-center">
@for (item of items; track item.id) {
@defer (on viewport) {
<a [routerLink]="['../', 'details', item.id]" class="w-full">
<lib-return-results-item-list
#listElement
[item]="item"
></lib-return-results-item-list>
</a>
} @placeholder {
<!-- TODO: Den Spinner durch Skeleton Loader Kacheln ersetzen -->
<div class="h-[7.75rem] w-full flex items-center justify-center">
<ui-icon-button
[pending]="true"
[color]="'tertiary'"
></ui-icon-button>
</div>
}
}
}
</div>
@if (entityStatus() === ReturnSearchStatus.Pending) {
<div class="h-[7.75rem] w-full flex items-center justify-center">
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
</div>
}
</div>
} @else if (
items.length === 0 && entityStatus() === ReturnSearchStatus.Pending
) {
<div class="h-[7.75rem] w-full flex items-center justify-center">
<ui-icon-button [pending]="true" [color]="'tertiary'"></ui-icon-button>
</div>
} @else if (entityStatus() !== ReturnSearchStatus.Idle) {
<ui-empty-state
[title]="emptyState().title"
[description]="emptyState().description"
>
</ui-empty-state>
}

View File

@@ -17,6 +17,7 @@ import {
import { injectActivatedProcessId } from '@isa/core/process';
import {
ReturnSearchEntity,
ReturnSearchStatus,
ReturnSearchStore,
} from '@feature/return/services';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
@@ -25,6 +26,12 @@ import { IconButtonComponent } from '@isa/ui/buttons';
import { ViewportScroller } from '@angular/common';
import { toSignal } from '@angular/core/rxjs-interop';
import { debounceTime, fromEvent, map } from 'rxjs';
import { EmptyStateComponent } from '@isa/ui/empty-state';
type EmptyState = {
title: string;
description: string;
};
@Component({
selector: 'lib-return-results-page',
@@ -39,6 +46,7 @@ import { debounceTime, fromEvent, map } from 'rxjs';
ReturnResultsFilterListComponent,
IconButtonComponent,
SearchBarInputComponent,
EmptyStateComponent,
],
})
export class ResultsPageComponent {
@@ -50,6 +58,8 @@ export class ResultsPageComponent {
private _returnSearchStore = inject(ReturnSearchStore);
private _filterService = inject(FilterService);
ReturnSearchStatus = ReturnSearchStatus;
private _entity = computed(() => {
const processId = this._processId();
if (processId) {
@@ -58,14 +68,25 @@ export class ResultsPageComponent {
return undefined;
});
items = computed(() => {
entityItems = computed(() => {
return this._entity()?.items ?? [];
});
hits = computed(() => {
entityHits = computed(() => {
return this._entity()?.hits ?? 0;
});
entityStatus = computed(() => {
return this._entity()?.status ?? ReturnSearchStatus.Idle;
});
emptyState = computed<EmptyState>(() => {
return {
title: 'Keine Suchergebnisse',
description: 'Suchen Sie nach einer Rechnungsnummer oder Kundennamen.',
};
});
listElements =
viewChildren<QueryList<ReturnResultsItemListComponent>>('listElement');
@@ -86,7 +107,8 @@ export class ResultsPageComponent {
if (processId) {
const entity = this._entity();
if (entity) {
const isPending = entity.status === 'pending';
const isPending =
this.entityStatus() === ReturnSearchStatus.Pending;
// Trigger reload search if no search request is already pending and
// 1. List scrolled to bottom
// 2. After Process change AND no items in entity

View File

@@ -12,7 +12,7 @@ import { tapResponse } from '@ngrx/operators';
import { inject } from '@angular/core';
import { ReceiptListItem, QueryTokenSchema, ListResponseArgs } from './schemas';
enum ReturnSearchStatus {
export enum ReturnSearchStatus {
Idle = 'idle',
Pending = 'pending',
Success = 'success',