fix(storefront): release-candidate walkthrough fixes

- Cart item description showed a stray literal "..." when the item had no
  description text (cart.component.html) — now only renders the trailing
  ellipsis when a description is present.
- Compare table showed raw internal stock enum values ("high"/"low"/etc.)
  instead of localized labels (compare-table.component.ts) — now reuses the
  same stock-label mapping used by product cards.
- Search with zero results incorrectly showed the empty-category messaging
  ("browse categories" / "go to parent category") stacked on top of the
  search's own "nothing found" message (catalog-container.component.ts) —
  isEmptyCategoryState now excludes active search queries so only the
  search-appropriate empty state renders.
- Footer "About" link pointed to /about, which 404s; the actual CMS page
  route is /about-us (bootstrap.json mock nav data) — corrected the route.
- Added missing public/assets/images/placeholder.svg, the fallback image
  referenced by getMainImage() for items without photos (previously 404s
  if that fallback path is ever hit).

Investigated and left as-is (not code bugs): /images/*.webp 404s on
product cards are references to a real backend/CDN not present in local
dev (confirmed via mock-data.interceptor.ts and api.service.ts image-URL
resolution) — expected dev-only gap. Footer "Contacts" link (/contacts)
has no corresponding static page content at all in mock data; flagging
for a content decision rather than fabricating copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sdarbinyan
2026-07-24 09:46:51 +04:00
parent f5f431620e
commit 0a1acbd610
5 changed files with 32 additions and 4 deletions

View File

@@ -110,7 +110,10 @@ export class CatalogContainerComponent {
readonly isDrawerLayout = computed(() => this.viewportWidth() <= 1024);
readonly isMobile = computed(() => this.viewportWidth() <= 767);
readonly hasRawProducts = computed(() => this.rawProducts().length > 0);
readonly isEmptyCategoryState = computed(() => this.viewMode() === 'products' && !this.loadingProducts() && this.rawProducts().length === 0 && this.subcategoryChips().length === 0);
// A blank search query with zero raw results is a "nothing found" search result, not an
// empty category — showing category-navigation copy ("browse categories" / "go to parent
// category") here is misleading when the emptiness is caused by the user's search term.
readonly isEmptyCategoryState = computed(() => this.viewMode() === 'products' && !this.loadingProducts() && this.rawProducts().length === 0 && this.subcategoryChips().length === 0 && !this.state().search.trim());
readonly isFilteredEmptyState = computed(() => this.viewMode() === 'products' && !this.loadingProducts() && this.rawProducts().length > 0 && this.products().length === 0);
readonly showDesktopFilters = computed(() => this.viewMode() === 'products' && !this.isEmptyCategoryState() && !this.isDrawerLayout() && !this.offlineState());
readonly showMobileToolbar = computed(() => this.viewMode() === 'products' && !this.isEmptyCategoryState() && this.isMobile() && !this.offlineState());

View File

@@ -10,6 +10,13 @@ interface CompareRow {
identical: boolean;
}
const STOCK_LABEL_KEYS: Record<string, string> = {
high: 'catalog.stockHigh',
medium: 'catalog.stockMedium',
low: 'catalog.stockLow',
out: 'catalog.stockOut'
};
@Component({
selector: 'app-compare-table',
standalone: true,
@@ -34,7 +41,7 @@ export class CompareTableComponent {
const baseRows: CompareRow[] = [
this.toRow('price', this.i18n.t('ux.comparePrice'), products.map(product => `${product.price.toFixed(2)} ${product.currency}`)),
this.toRow('rating', this.i18n.t('ux.compareRating'), products.map(product => `${(product.rating ?? 0).toFixed(1)}`)),
this.toRow('stock', this.i18n.t('ux.compareStock'), products.map(product => product.remainings ?? this.i18n.t('ux.compareUnknown'))),
this.toRow('stock', this.i18n.t('ux.compareStock'), products.map(product => this.stockLabel(product.remainings))),
this.toRow('discount', this.i18n.t('ux.compareDiscount'), products.map(product => `${product.discount ?? 0}%`)),
this.toRow('color', this.i18n.t('ux.compareColor'), products.map(product => product.colour ?? '—')),
this.toRow('size', this.i18n.t('ux.compareSize'), products.map(product => product.size ?? '—'))
@@ -58,6 +65,14 @@ export class CompareTableComponent {
return this.highlightDifferences && !row.identical;
}
private stockLabel(remainings: string | undefined): string {
if (!remainings) {
return this.i18n.t('ux.compareUnknown');
}
const key = STOCK_LABEL_KEYS[remainings.toLowerCase()];
return key ? this.i18n.t(key) : remainings;
}
private toRow(key: string, label: string, values: string[]): CompareRow {
const normalized = values.map(value => value.trim().toLowerCase());
const identical = normalized.every(value => value === normalized[0]);

View File

@@ -40,7 +40,9 @@
</button>
</div>
<p class="item-description">{{ itemDesc(item) || '' }}...</p>
@if (itemDesc(item)) {
<p class="item-description">{{ itemDesc(item) }}...</p>
}
@if (item.colour || (item.size && item.size.toLowerCase() !== 'default')) {
<div class="cart-item-variants">

View File

@@ -201,7 +201,7 @@
{
"id": "footer-about",
"labelKey": "nav.about",
"route": "/about",
"route": "/about-us",
"order": 1
},
{