refactor: remove loading state management from category and search pages

This commit is contained in:
2026-07-29 12:06:29 -03:00
parent da61067c6a
commit 8c2f00bcbc
7 changed files with 42 additions and 25 deletions

View File

@@ -1,8 +1,6 @@
<section class="category-items" aria-labelledby="category-items-title">
@if (error()) {
<p class="alert text-center mb-0 category-items__alert-error">{{ error() }}</p>
} @else if (loading()) {
<p class="alert alert-light border text-center mb-0">Cargando productos...</p>
} @else if (results(); as categoryResults) {
<header class="category-items__header mb-5">
<h1 id="category-items-title" class="category-items__title mb-0">
@@ -19,7 +17,6 @@
[layout]="categoryResults.layout"
[groupLayout]="paginatedLayout"
[items]="categoryResults"
[loading]="loading()"
(buy)="onBuyProduct($event)"
(addToCart)="onAddToCart($event)"
(pageChange)="onPageChange($event)"

View File

@@ -71,9 +71,6 @@ describe('CategoryItemsPageComponent', () => {
provide: CatalogService,
useValue: {
getCategoryItems,
withCustomLoading() {
return this;
},
},
},
{

View File

@@ -50,7 +50,6 @@ export class CategoryItemsPageComponent {
private readonly toastService = inject(ToastService);
protected readonly results = signal<CategoryItemsResponse | null>(null);
protected readonly loading = signal(true);
protected readonly error = signal<string | null>(null);
protected readonly creatingDirectPurchase = signal(false);
protected readonly paginatedLayout: CatalogGroupLayout = 'paginated';
@@ -71,7 +70,6 @@ export class CategoryItemsPageComponent {
tap(() => {
this.results.set(null);
this.error.set(null);
this.loading.set(true);
}),
switchMap(({ categoryId, page }) => {
if (categoryId === 0) {
@@ -80,10 +78,7 @@ export class CategoryItemsPageComponent {
return of(null);
}
return this.catalogService
.withCustomLoading()
.getCategoryItems(categoryId, { page })
.pipe(
return this.catalogService.getCategoryItems(categoryId, { page }).pipe(
catchError(() => {
this.error.set('No pudimos cargar los productos de esta categoría.');
@@ -95,7 +90,6 @@ export class CategoryItemsPageComponent {
)
.subscribe((results) => {
this.results.set(results);
this.loading.set(false);
});
}

View File

@@ -1,8 +1,8 @@
<section class="search-results" aria-labelledby="search-results-title">
<header class="search-results__header mb-4">
<h1 id="search-results-title" class="h3 mb-2">Resultados de búsqueda</h1>
<header class="search-results__header mb-5">
<h1 id="search-results-title" class="search-results__title mb-0">Resultados de búsqueda</h1>
@if (query()) {
<p class="text-body-secondary mb-0">
<p class="search-results__subtitle text-body-secondary mb-0">
Resultados para <strong>“{{ query() }}”</strong>
</p>
}
@@ -10,8 +10,6 @@
@if (error()) {
<p class="alert text-center mb-0 search-results__alert-error">{{ error() }}</p>
} @else if (loading()) {
<p class="alert alert-light border text-center mb-0">Buscando productos...</p>
} @else if (results(); as searchResults) {
@if (!searchResults.data.length) {
<p class="alert alert-light border text-center mb-0">

View File

@@ -1,3 +1,24 @@
.search-results__header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8px;
padding: 16px 28px;
text-align: center;
}
.search-results__title {
color: var(--bs-primary);
font-size: 24px;
font-weight: bold;
line-height: 1.2;
}
.search-results__subtitle {
line-height: 1.2;
}
.search-results__alert-error {
color: var(--tenant-danger-color, var(--bs-danger));
}

View File

@@ -67,9 +67,6 @@ describe('SearchPageComponent', () => {
provide: CatalogService,
useValue: {
searchCatalog,
withCustomLoading() {
return this;
},
},
},
{
@@ -111,6 +108,22 @@ describe('SearchPageComponent', () => {
);
});
it('renders the search title and query subtitle with the category header layout', () => {
const fixture = TestBed.createComponent(SearchPageComponent);
fixture.detectChanges();
const element = fixture.nativeElement as HTMLElement;
const header = element.querySelector('.search-results__header');
const title = element.querySelector('#search-results-title');
const subtitle = element.querySelector('.search-results__subtitle');
expect(header?.classList).toContain('mb-5');
expect(title?.classList).toContain('search-results__title');
expect(title?.classList).toContain('mb-0');
expect(subtitle?.textContent).toContain('Resultados para');
expect(subtitle?.textContent).toContain('running');
});
it('does not search when the query has fewer than three characters', () => {
TestBed.overrideProvider(ActivatedRoute, {
useValue: {

View File

@@ -56,7 +56,6 @@ export class SearchPageComponent {
protected readonly query = signal('');
protected readonly results = signal<ApiPaginatedResponse<CatalogFeaturedItem[]> | null>(null);
protected readonly loading = signal(false);
protected readonly error = signal<string | null>(null);
protected readonly creatingDirectPurchase = signal(false);
@@ -105,14 +104,13 @@ export class SearchPageComponent {
? null
: `Ingresá al menos ${this.minSearchLength} caracteres para buscar.`,
);
this.loading.set(query.length >= this.minSearchLength);
}),
switchMap(({ query, page }) => {
if (query.length < this.minSearchLength) {
return of(null);
}
return this.catalogService.withCustomLoading().searchCatalog({ q: query, page }).pipe(
return this.catalogService.searchCatalog({ q: query, page }).pipe(
catchError(() => {
this.error.set('No pudimos realizar la búsqueda en este momento.');
@@ -124,7 +122,6 @@ export class SearchPageComponent {
)
.subscribe((results) => {
this.results.set(results);
this.loading.set(false);
});
}