feat(events): add category-filtered event pages

This commit is contained in:
2026-09-21 09:16:44 -03:00
parent 14bda25c56
commit 865893b96b
5 changed files with 49 additions and 7 deletions

View File

@@ -2,6 +2,7 @@ import { ApiPaginatedResponse } from '../api-paginated-response.interface';
export interface StoreEvent {
id: number;
event_category_id: number | null;
title: string;
subtitle: string | null;
description: string | null;

View File

@@ -10,9 +10,9 @@ import { StoreEvent, StoreEventPage } from './event.interface';
export class EventService extends BaseApiService {
private readonly tenantService = inject(TenantService);
list(page: number, query = ''): Observable<StoreEventPage> {
list(page: number, query = '', categoryId?: number): Observable<StoreEventPage> {
return this.http.get<StoreEventPage>(`${this.tenantService.getTenantApiUrl()}/events`, {
params: { page, ...(query ? { q: query } : {}) },
params: { page, ...(query ? { q: query } : {}), ...(categoryId ? { category_id: categoryId } : {}) },
});
}

View File

@@ -1,7 +1,7 @@
import { ChangeDetectionStrategy, Component, DestroyRef, inject, input, OnInit, signal } from '@angular/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { ActivatedRoute, Router } from '@angular/router';
import { catchError, distinctUntilChanged, map, of, switchMap, tap } from 'rxjs';
import { catchError, combineLatest, distinctUntilChanged, map, of, switchMap, tap } from 'rxjs';
import { EventService } from '../../../../core/services/event/event.service';
import { StoreEventPage } from '../../../../core/services/event/event.interface';
@@ -22,28 +22,30 @@ export class EventListComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);
readonly search = input(false);
readonly categoryId = input<number | null>(null);
protected readonly results = signal<StoreEventPage | null>(null);
protected readonly loading = signal(true);
protected readonly error = signal(false);
protected readonly query = signal('');
ngOnInit(): void {
this.route.queryParamMap.pipe(
map((params) => {
combineLatest([this.route.queryParamMap, this.route.paramMap]).pipe(
map(([params, routeParams]) => {
const requestedPage = Number(params.get('page'));
return {
page: Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1,
query: this.search() ? (params.get('q') ?? '').trim() : '',
categoryId: this.categoryId() === null ? null : Number(routeParams.get('id')),
};
}),
distinctUntilChanged((a, b) => a.page === b.page && a.query === b.query),
distinctUntilChanged((a, b) => a.page === b.page && a.query === b.query && a.categoryId === b.categoryId),
tap(({ query }) => {
this.query.set(query);
this.loading.set(true);
this.error.set(false);
this.results.set(null);
}),
switchMap(({ page, query }) => this.eventService.list(page, query).pipe(
switchMap(({ page, query, categoryId }) => this.eventService.list(page, query, categoryId ?? undefined).pipe(
catchError(() => {
this.error.set(true);
return of(null);

View File

@@ -0,0 +1,31 @@
import { ChangeDetectionStrategy, Component, computed, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { toSignal } from '@angular/core/rxjs-interop';
import { TenantService } from '../../../../core/services/tenant.service';
import { EventListComponent } from '../../components/event-list/event-list.component';
@Component({
selector: 'app-event-category-page',
imports: [EventListComponent],
template: `
@if (category(); as selectedCategory) {
<section aria-labelledby="event-category-title">
<h1 id="event-category-title" class="h3 mb-4">{{ selectedCategory.nombre }}</h1>
<app-event-list [categoryId]="selectedCategory.id" />
</section>
} @else {
<p role="alert">La categoría no existe.</p>
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class EventCategoryPageComponent {
private readonly route = inject(ActivatedRoute);
private readonly tenant = inject(TenantService).tenant;
private readonly params = toSignal(this.route.paramMap, { initialValue: this.route.snapshot.paramMap });
protected readonly category = computed(() => {
const id = Number(this.params().get('id'));
return this.tenant()?.categories.find((category) => category.id === id) ?? null;
});
}

View File

@@ -98,6 +98,14 @@ export const routes: Routes = [
loadComponent: () =>
import('./pages/event-detail-page/event-detail-page.component').then((m) => m.EventDetailPageComponent),
},
{
path: 'categoria/:id',
canMatch: [isMultiEventStorefront],
loadComponent: () =>
import('./pages/event-category-page/event-category-page.component').then(
(m) => m.EventCategoryPageComponent,
),
},
{
path: 'categoria/:id',
loadComponent: () =>