diff --git a/src/app/pages/category/subcategories.component.ts b/src/app/pages/category/subcategories.component.ts index 742483e..948d2a8 100644 --- a/src/app/pages/category/subcategories.component.ts +++ b/src/app/pages/category/subcategories.component.ts @@ -63,10 +63,10 @@ export class SubcategoriesComponent implements OnInit, OnDestroy { // Check for nested subcategories from API response (backOffice format) const nested = parent?.subcategories || []; - const visibleNested = nested.filter(s => s.visible !== false); + const visibleNested = nested.filter(s => this.isDisplayableNestedSubcategory(s)); // Also check flat legacy subcategories - const flatSubs = cats.filter(c => c.parentID === parentID); + const flatSubs = cats.filter(c => c.parentID === parentID && this.isDisplayableFlatSubcategory(c)); if (visibleNested.length > 0) { // Use nested subcategories from API @@ -110,6 +110,20 @@ export class SubcategoriesComponent implements OnInit, OnDestroy { }); } + private isDisplayableFlatSubcategory(category: Category): boolean { + return category.visible !== false + && ((category.itemCount ?? 0) > 0 || (category.subcategories?.length ?? 0) > 0); + } + + private isDisplayableNestedSubcategory(subcategory: Subcategory): boolean { + return subcategory.visible !== false + && ( + (subcategory.itemCount ?? 0) > 0 + || subcategory.hasItems === true + || (subcategory.subcategories?.length ?? 0) > 0 + ); + } + hasSubcategories(): boolean { return this.subcategories().length > 0 || this.nestedSubcategories().length > 0; } diff --git a/src/app/pages/home/home.component.ts b/src/app/pages/home/home.component.ts index 779ed93..f1a1e0d 100644 --- a/src/app/pages/home/home.component.ts +++ b/src/app/pages/home/home.component.ts @@ -28,6 +28,7 @@ export class HomeComponent implements OnInit, OnDestroy { topLevelCategories = computed(() => { return this.categories() .filter(cat => cat.parentID === 0) + .filter(cat => this.isDisplayableTopLevelCategory(cat)) .sort((a, b) => (a.priority ?? Infinity) - (b.priority ?? Infinity)); }); @@ -42,7 +43,7 @@ export class HomeComponent implements OnInit, OnDestroy { private subcategoriesCache = computed(() => { const cache = new Map(); this.categories().forEach(cat => { - if (cat.parentID !== 0) { + if (cat.parentID !== 0 && this.isDisplayableFlatSubcategory(cat)) { if (!cache.has(cat.parentID)) { cache.set(cat.parentID, []); } @@ -90,6 +91,21 @@ export class HomeComponent implements OnInit, OnDestroy { return this.subcategoriesCache().get(parentID) || []; } + private isDisplayableFlatSubcategory(category: Category): boolean { + return category.visible !== false + && ((category.itemCount ?? 0) > 0 || (category.subcategories?.length ?? 0) > 0); + } + + private isDisplayableTopLevelCategory(category: Category): boolean { + return category.visible !== false + && ( + (category.itemCount ?? 0) > 0 + || (category.categoriesCount ?? 0) > 0 + || (category.subcategories?.length ?? 0) > 0 + || this.getSubCategories(category.categoryID).length > 0 + ); + } + isWideCategory(categoryID: number): boolean { return this.wideCategories().has(categoryID); } diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts index d852ca2..9d3f8ba 100644 --- a/src/app/services/api.service.ts +++ b/src/app/services/api.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'; import { Observable, timer } from 'rxjs'; import { map, retry } from 'rxjs/operators'; -import { Category, DeliveryOption, Item } from '../models'; +import { Category, DeliveryOption, Item, Subcategory } from '../models'; import { normalizeDeliveryOption, normalizeOptionalNumber } from '../utils/normalization.utils'; import { environment } from '../../environments/environment'; @@ -125,6 +125,45 @@ export class ApiService { return { options: [], isDigital: false, requiresSelection: false }; } + private normalizeSubcategory(raw: any): Subcategory { + const subcategory: Subcategory = { + id: String(raw.id ?? raw.categoryId ?? raw.categoryID ?? ''), + name: typeof raw.name === 'string' ? raw.name : '', + visible: raw.visible ?? true, + priority: raw.priority ?? 0, + img: raw.img ? this.resolveImageUrl(raw.img) : undefined, + categoryId: String(raw.categoryId ?? raw.categoryID ?? raw.id ?? ''), + parentId: String(raw.parentId ?? raw.parentID ?? ''), + itemCount: raw.itemCount ?? raw.ItemsCount ?? 0, + hasItems: raw.hasItems, + subcategories: Array.isArray(raw.subcategories) + ? raw.subcategories + .map((sub: any) => this.normalizeSubcategory(sub)) + .filter((sub: Subcategory) => this.isDisplayableSubcategory(sub)) + : [], + }; + + return subcategory; + } + + private isDisplayableSubcategory(subcategory: Subcategory): boolean { + if (subcategory.visible === false) { + return false; + } + + return (subcategory.itemCount ?? 0) > 0 + || subcategory.hasItems === true + || (subcategory.subcategories?.length ?? 0) > 0; + } + + private isDisplayableCategory(category: Category): boolean { + return category.visible !== false; + } + + private isDisplayableItem(item: Item): boolean { + return item.visible !== false; + } + /** Resolve relative image URLs (e.g. ./images/x.webp) against site origin */ private resolveImageUrl(url: string): string { if (!url) return ''; @@ -301,7 +340,9 @@ export class ApiService { if (!items || !Array.isArray(items)) { return []; } - return items.map(item => this.normalizeItem(item)); + return items + .map(item => this.normalizeItem(item)) + .filter(item => this.isDisplayableItem(item)); } /** @@ -359,7 +400,9 @@ export class ApiService { cat.name = cat.name || ''; if (raw.subcategories && Array.isArray(raw.subcategories)) { - cat.subcategories = raw.subcategories; + cat.subcategories = raw.subcategories + .map((sub: any) => this.normalizeSubcategory(sub)) + .filter((sub: Subcategory) => this.isDisplayableSubcategory(sub)); } return cat; @@ -367,7 +410,9 @@ export class ApiService { private normalizeCategories(cats: any[] | null | undefined): Category[] { if (!cats || !Array.isArray(cats)) return []; - return cats.map(c => this.normalizeCategory(c)); + return cats + .map(c => this.normalizeCategory(c)) + .filter(category => this.isDisplayableCategory(category)); } // ─── Core Marketplace Endpoints ───────────────────────────