refactor: remove loading state management from category and search pages
This commit is contained in:
@@ -1,8 +1,6 @@
|
|||||||
<section class="category-items" aria-labelledby="category-items-title">
|
<section class="category-items" aria-labelledby="category-items-title">
|
||||||
@if (error()) {
|
@if (error()) {
|
||||||
<p class="alert text-center mb-0 category-items__alert-error">{{ error() }}</p>
|
<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) {
|
} @else if (results(); as categoryResults) {
|
||||||
<header class="category-items__header mb-5">
|
<header class="category-items__header mb-5">
|
||||||
<h1 id="category-items-title" class="category-items__title mb-0">
|
<h1 id="category-items-title" class="category-items__title mb-0">
|
||||||
@@ -19,7 +17,6 @@
|
|||||||
[layout]="categoryResults.layout"
|
[layout]="categoryResults.layout"
|
||||||
[groupLayout]="paginatedLayout"
|
[groupLayout]="paginatedLayout"
|
||||||
[items]="categoryResults"
|
[items]="categoryResults"
|
||||||
[loading]="loading()"
|
|
||||||
(buy)="onBuyProduct($event)"
|
(buy)="onBuyProduct($event)"
|
||||||
(addToCart)="onAddToCart($event)"
|
(addToCart)="onAddToCart($event)"
|
||||||
(pageChange)="onPageChange($event)"
|
(pageChange)="onPageChange($event)"
|
||||||
|
|||||||
@@ -71,9 +71,6 @@ describe('CategoryItemsPageComponent', () => {
|
|||||||
provide: CatalogService,
|
provide: CatalogService,
|
||||||
useValue: {
|
useValue: {
|
||||||
getCategoryItems,
|
getCategoryItems,
|
||||||
withCustomLoading() {
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ export class CategoryItemsPageComponent {
|
|||||||
private readonly toastService = inject(ToastService);
|
private readonly toastService = inject(ToastService);
|
||||||
|
|
||||||
protected readonly results = signal<CategoryItemsResponse | null>(null);
|
protected readonly results = signal<CategoryItemsResponse | null>(null);
|
||||||
protected readonly loading = signal(true);
|
|
||||||
protected readonly error = signal<string | null>(null);
|
protected readonly error = signal<string | null>(null);
|
||||||
protected readonly creatingDirectPurchase = signal(false);
|
protected readonly creatingDirectPurchase = signal(false);
|
||||||
protected readonly paginatedLayout: CatalogGroupLayout = 'paginated';
|
protected readonly paginatedLayout: CatalogGroupLayout = 'paginated';
|
||||||
@@ -71,7 +70,6 @@ export class CategoryItemsPageComponent {
|
|||||||
tap(() => {
|
tap(() => {
|
||||||
this.results.set(null);
|
this.results.set(null);
|
||||||
this.error.set(null);
|
this.error.set(null);
|
||||||
this.loading.set(true);
|
|
||||||
}),
|
}),
|
||||||
switchMap(({ categoryId, page }) => {
|
switchMap(({ categoryId, page }) => {
|
||||||
if (categoryId === 0) {
|
if (categoryId === 0) {
|
||||||
@@ -80,10 +78,7 @@ export class CategoryItemsPageComponent {
|
|||||||
return of(null);
|
return of(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.catalogService
|
return this.catalogService.getCategoryItems(categoryId, { page }).pipe(
|
||||||
.withCustomLoading()
|
|
||||||
.getCategoryItems(categoryId, { page })
|
|
||||||
.pipe(
|
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
this.error.set('No pudimos cargar los productos de esta categoría.');
|
this.error.set('No pudimos cargar los productos de esta categoría.');
|
||||||
|
|
||||||
@@ -95,7 +90,6 @@ export class CategoryItemsPageComponent {
|
|||||||
)
|
)
|
||||||
.subscribe((results) => {
|
.subscribe((results) => {
|
||||||
this.results.set(results);
|
this.results.set(results);
|
||||||
this.loading.set(false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<section class="search-results" aria-labelledby="search-results-title">
|
<section class="search-results" aria-labelledby="search-results-title">
|
||||||
<header class="search-results__header mb-4">
|
<header class="search-results__header mb-5">
|
||||||
<h1 id="search-results-title" class="h3 mb-2">Resultados de búsqueda</h1>
|
<h1 id="search-results-title" class="search-results__title mb-0">Resultados de búsqueda</h1>
|
||||||
@if (query()) {
|
@if (query()) {
|
||||||
<p class="text-body-secondary mb-0">
|
<p class="search-results__subtitle text-body-secondary mb-0">
|
||||||
Resultados para <strong>“{{ query() }}”</strong>
|
Resultados para <strong>“{{ query() }}”</strong>
|
||||||
</p>
|
</p>
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,6 @@
|
|||||||
|
|
||||||
@if (error()) {
|
@if (error()) {
|
||||||
<p class="alert text-center mb-0 search-results__alert-error">{{ error() }}</p>
|
<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) {
|
} @else if (results(); as searchResults) {
|
||||||
@if (!searchResults.data.length) {
|
@if (!searchResults.data.length) {
|
||||||
<p class="alert alert-light border text-center mb-0">
|
<p class="alert alert-light border text-center mb-0">
|
||||||
|
|||||||
@@ -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 {
|
.search-results__alert-error {
|
||||||
color: var(--tenant-danger-color, var(--bs-danger));
|
color: var(--tenant-danger-color, var(--bs-danger));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,9 +67,6 @@ describe('SearchPageComponent', () => {
|
|||||||
provide: CatalogService,
|
provide: CatalogService,
|
||||||
useValue: {
|
useValue: {
|
||||||
searchCatalog,
|
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', () => {
|
it('does not search when the query has fewer than three characters', () => {
|
||||||
TestBed.overrideProvider(ActivatedRoute, {
|
TestBed.overrideProvider(ActivatedRoute, {
|
||||||
useValue: {
|
useValue: {
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ export class SearchPageComponent {
|
|||||||
|
|
||||||
protected readonly query = signal('');
|
protected readonly query = signal('');
|
||||||
protected readonly results = signal<ApiPaginatedResponse<CatalogFeaturedItem[]> | null>(null);
|
protected readonly results = signal<ApiPaginatedResponse<CatalogFeaturedItem[]> | null>(null);
|
||||||
protected readonly loading = signal(false);
|
|
||||||
protected readonly error = signal<string | null>(null);
|
protected readonly error = signal<string | null>(null);
|
||||||
protected readonly creatingDirectPurchase = signal(false);
|
protected readonly creatingDirectPurchase = signal(false);
|
||||||
|
|
||||||
@@ -105,14 +104,13 @@ export class SearchPageComponent {
|
|||||||
? null
|
? null
|
||||||
: `Ingresá al menos ${this.minSearchLength} caracteres para buscar.`,
|
: `Ingresá al menos ${this.minSearchLength} caracteres para buscar.`,
|
||||||
);
|
);
|
||||||
this.loading.set(query.length >= this.minSearchLength);
|
|
||||||
}),
|
}),
|
||||||
switchMap(({ query, page }) => {
|
switchMap(({ query, page }) => {
|
||||||
if (query.length < this.minSearchLength) {
|
if (query.length < this.minSearchLength) {
|
||||||
return of(null);
|
return of(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.catalogService.withCustomLoading().searchCatalog({ q: query, page }).pipe(
|
return this.catalogService.searchCatalog({ q: query, page }).pipe(
|
||||||
catchError(() => {
|
catchError(() => {
|
||||||
this.error.set('No pudimos realizar la búsqueda en este momento.');
|
this.error.set('No pudimos realizar la búsqueda en este momento.');
|
||||||
|
|
||||||
@@ -124,7 +122,6 @@ export class SearchPageComponent {
|
|||||||
)
|
)
|
||||||
.subscribe((results) => {
|
.subscribe((results) => {
|
||||||
this.results.set(results);
|
this.results.set(results);
|
||||||
this.loading.set(false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user