mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 11:06:16 -08:00
fix(portal): show search-aware empty state when category search yields no results
When a search filter inside a category produces zero results, the grid
@empty fallback used to render the EMPTY_CATEGORY view ("No content in
this category" with the empty-tv illustration). That message was
misleading — the category itself wasn't empty, only the search filter
excluded everything.
grid-list now takes a searchTerm input and branches the @empty fallback:
when a search is active it renders the NO_SEARCH_RESULTS view with the
parameterised "No results found for {term}" title, otherwise it falls
back to EMPTY_CATEGORY as before. category-content-view derives the
searchTerm signal from the ?q= query param and passes it through.
Also: bump the NO_SEARCH_RESULTS icon from a 64px mat-icon to a clamp()
sized icon (120-180px) at 0.5 opacity so it visually balances with the
SVG illustrations used by the other viewTypes, swap the icon to
search_off (struck-through magnifier) so it reads as "no results"
rather than "search now", and fix the EN i18n typo
("change you search request" -> "change your search request").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 2e1a36e2f0f6
This commit is contained in:
1 parent
d6a91d79e9
commit
d1e4c0d306
8 files changed
+46
-19
No files matched your search
@@ -663,7 +663,7 @@
|
||||
},
|
||||
"EMPTY_LIST_VIEW": {
|
||||
"TITLE": "No results found",
|
||||
"NO_SEARCH_RESULTS": "Nothing found, try to change you search request"
|
||||
"NO_SEARCH_RESULTS": "Nothing found, try to change your search request."
|
||||
},
|
||||
"SEARCH_VIEW": {
|
||||
"INITIAL_TITLE": "Search for content",
|
||||
|
||||
+1
@@ -90,6 +90,7 @@
|
||||
[limit]="limit()"
|
||||
[pageSizeOptions]="pageSizeOptions"
|
||||
[showPaginator]="false"
|
||||
[searchTerm]="searchTerm()"
|
||||
(pageChange)="onPageChange($event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ class MockGridListComponent {
|
||||
readonly limit = input<number>();
|
||||
readonly pageSizeOptions = input<number[]>();
|
||||
readonly showPaginator = input(true);
|
||||
readonly searchTerm = input<string>('');
|
||||
readonly itemClicked = output<unknown>();
|
||||
readonly pageChange = output<unknown>();
|
||||
}
|
||||
|
||||
+6
-1
@@ -7,7 +7,8 @@ import {
|
||||
inject,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { MatIconButton } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
@@ -98,6 +99,10 @@ export class CategoryContentViewComponent implements OnInit {
|
||||
return `${itemCount} ${itemCount === 1 ? 'item' : 'items'}`;
|
||||
});
|
||||
readonly canSortContent = computed(() => this.contentSortMode() !== null);
|
||||
readonly searchTerm = toSignal(
|
||||
this.activatedRoute.queryParamMap.pipe(map((p) => p.get('q') ?? '')),
|
||||
{ initialValue: '' }
|
||||
);
|
||||
readonly selectedDetailComponent = computed(() =>
|
||||
this.selectedItem() ? this.detailComponent : null
|
||||
);
|
||||
|
||||
@@ -125,18 +125,33 @@ export function resolveGridRating(
|
||||
</mat-card>
|
||||
} @empty {
|
||||
<div class="grid-empty-state">
|
||||
<app-playlist-error-view
|
||||
[title]="
|
||||
'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.TITLE'
|
||||
| translate
|
||||
"
|
||||
[description]="
|
||||
'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.DESCRIPTION'
|
||||
| translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'EMPTY_CATEGORY'"
|
||||
/>
|
||||
@if (hasActiveSearch()) {
|
||||
<app-playlist-error-view
|
||||
[title]="
|
||||
'PORTALS.SEARCH_VIEW.NO_RESULTS_FOR'
|
||||
| translate: { term: searchTerm() }
|
||||
"
|
||||
[description]="
|
||||
'PORTALS.EMPTY_LIST_VIEW.NO_SEARCH_RESULTS'
|
||||
| translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'NO_SEARCH_RESULTS'"
|
||||
/>
|
||||
} @else {
|
||||
<app-playlist-error-view
|
||||
[title]="
|
||||
'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.TITLE'
|
||||
| translate
|
||||
"
|
||||
[description]="
|
||||
'PORTALS.ERROR_VIEW.EMPTY_CATEGORY.DESCRIPTION'
|
||||
| translate
|
||||
"
|
||||
[showActionButtons]="false"
|
||||
[viewType]="'EMPTY_CATEGORY'"
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@@ -167,6 +182,7 @@ export class GridListComponent {
|
||||
readonly items = input<GridListItem[]>();
|
||||
readonly isLoading = input<boolean>();
|
||||
readonly showPaginator = input(true);
|
||||
readonly searchTerm = input<string>('');
|
||||
readonly itemClicked = output<GridListItem>();
|
||||
readonly pageChange = output<PageEvent>();
|
||||
|
||||
@@ -175,6 +191,9 @@ export class GridListComponent {
|
||||
readonly limit = input<number>();
|
||||
readonly pageSizeOptions = input<number[]>();
|
||||
protected readonly resolveRating = resolveGridRating;
|
||||
protected readonly hasActiveSearch = computed(
|
||||
() => (this.searchTerm() ?? '').trim().length > 0
|
||||
);
|
||||
|
||||
readonly skeletonRows = computed(() => {
|
||||
const preferredCount = this.limit() ?? 12;
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
src="./assets/images/empty-category.svg"
|
||||
/>
|
||||
} @else if (viewType() === 'NO_SEARCH_RESULTS') {
|
||||
<mat-icon class="icon">search</mat-icon>
|
||||
<mat-icon class="icon">search_off</mat-icon>
|
||||
}
|
||||
}
|
||||
<div class="error-view__copy">
|
||||
|
||||
+4
-3
@@ -44,9 +44,10 @@
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 64px;
|
||||
height: 64px;
|
||||
width: 64px;
|
||||
font-size: clamp(120px, 18vw, 180px);
|
||||
height: clamp(120px, 18vw, 180px);
|
||||
width: clamp(120px, 18vw, 180px);
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.error-view--empty-category .error-view__illustration {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
src="./assets/images/empty-category.svg"
|
||||
/>
|
||||
} @else if (viewType() === 'NO_SEARCH_RESULTS') {
|
||||
<mat-icon class="icon">search</mat-icon>
|
||||
<mat-icon class="icon">search_off</mat-icon>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user