feat(events): add category-filtered event pages
This commit is contained in:
@@ -2,6 +2,7 @@ import { ApiPaginatedResponse } from '../api-paginated-response.interface';
|
|||||||
|
|
||||||
export interface StoreEvent {
|
export interface StoreEvent {
|
||||||
id: number;
|
id: number;
|
||||||
|
event_category_id: number | null;
|
||||||
title: string;
|
title: string;
|
||||||
subtitle: string | null;
|
subtitle: string | null;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import { StoreEvent, StoreEventPage } from './event.interface';
|
|||||||
export class EventService extends BaseApiService {
|
export class EventService extends BaseApiService {
|
||||||
private readonly tenantService = inject(TenantService);
|
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`, {
|
return this.http.get<StoreEventPage>(`${this.tenantService.getTenantApiUrl()}/events`, {
|
||||||
params: { page, ...(query ? { q: query } : {}) },
|
params: { page, ...(query ? { q: query } : {}), ...(categoryId ? { category_id: categoryId } : {}) },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ChangeDetectionStrategy, Component, DestroyRef, inject, input, OnInit, signal } from '@angular/core';
|
import { ChangeDetectionStrategy, Component, DestroyRef, inject, input, OnInit, signal } from '@angular/core';
|
||||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
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 { EventService } from '../../../../core/services/event/event.service';
|
||||||
import { StoreEventPage } from '../../../../core/services/event/event.interface';
|
import { StoreEventPage } from '../../../../core/services/event/event.interface';
|
||||||
@@ -22,28 +22,30 @@ export class EventListComponent implements OnInit {
|
|||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
readonly search = input(false);
|
readonly search = input(false);
|
||||||
|
readonly categoryId = input<number | null>(null);
|
||||||
protected readonly results = signal<StoreEventPage | null>(null);
|
protected readonly results = signal<StoreEventPage | null>(null);
|
||||||
protected readonly loading = signal(true);
|
protected readonly loading = signal(true);
|
||||||
protected readonly error = signal(false);
|
protected readonly error = signal(false);
|
||||||
protected readonly query = signal('');
|
protected readonly query = signal('');
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.queryParamMap.pipe(
|
combineLatest([this.route.queryParamMap, this.route.paramMap]).pipe(
|
||||||
map((params) => {
|
map(([params, routeParams]) => {
|
||||||
const requestedPage = Number(params.get('page'));
|
const requestedPage = Number(params.get('page'));
|
||||||
return {
|
return {
|
||||||
page: Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1,
|
page: Number.isSafeInteger(requestedPage) && requestedPage > 0 ? requestedPage : 1,
|
||||||
query: this.search() ? (params.get('q') ?? '').trim() : '',
|
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 }) => {
|
tap(({ query }) => {
|
||||||
this.query.set(query);
|
this.query.set(query);
|
||||||
this.loading.set(true);
|
this.loading.set(true);
|
||||||
this.error.set(false);
|
this.error.set(false);
|
||||||
this.results.set(null);
|
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(() => {
|
catchError(() => {
|
||||||
this.error.set(true);
|
this.error.set(true);
|
||||||
return of(null);
|
return of(null);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -98,6 +98,14 @@ export const routes: Routes = [
|
|||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
import('./pages/event-detail-page/event-detail-page.component').then((m) => m.EventDetailPageComponent),
|
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',
|
path: 'categoria/:id',
|
||||||
loadComponent: () =>
|
loadComponent: () =>
|
||||||
|
|||||||
Reference in New Issue
Block a user