From c889c20b6179685659e17cd82b6d682fe4c27096 Mon Sep 17 00:00:00 2001 From: Alexandr SaVBaD Waltz Date: Wed, 4 Mar 2026 16:27:52 +0300 Subject: [PATCH] feat(storefront): enhance error handling and navigation across pages Added a global error page to improve user experience during navigation issues, with localized messages and redirect options. Enhanced error handling for brand, product, and category slug composables by introducing explicit 404 responses. - Introduced `/error.vue` template for custom error displays using `NuxtError`. - Updated `useBrandBySlug`, `useProductBySlug`, `useCategoryBySlug` to throw 404 errors when data is not found. - Expanded i18n files (`en-gb.json` and `ru-ru.json`) with additional error-related translations. - Replaced plain text input with a `.search`-scoped class for cleaner styling. Enhances robustness and user feedback during navigation errors. No breaking changes introduced. --- .../app/components/base/header/index.vue | 10 +-- storefront/app/components/ui/title.vue | 2 +- .../app/composables/brands/useBrandBySlug.ts | 8 ++ .../categories/useCategoryBySlug.ts | 8 ++ .../composables/products/useProductBySlug.ts | 8 ++ storefront/app/error.vue | 82 +++++++++++++++++++ storefront/app/pages/brand/[brandSlug].vue | 2 +- storefront/app/pages/brands.vue | 2 +- storefront/i18n/locales/en-gb.json | 25 +++++- storefront/i18n/locales/ru-ru.json | 25 +++++- 10 files changed, 156 insertions(+), 16 deletions(-) create mode 100644 storefront/app/error.vue diff --git a/storefront/app/components/base/header/index.vue b/storefront/app/components/base/header/index.vue index d94bf31f..0563784c 100644 --- a/storefront/app/components/base/header/index.vue +++ b/storefront/app/components/base/header/index.vue @@ -18,35 +18,35 @@ {{ t('header.nav.shop') }} {{ t('header.nav.catalog') }} {{ t('header.nav.brands') }} {{ t('header.nav.blog') }} {{ t('header.nav.contact') }} diff --git a/storefront/app/components/ui/title.vue b/storefront/app/components/ui/title.vue index 8a90db41..db151184 100644 --- a/storefront/app/components/ui/title.vue +++ b/storefront/app/components/ui/title.vue @@ -40,7 +40,7 @@ font-weight: 400; letter-spacing: -0.5px; } -:deep(.title__wrapper div) { +:deep(.title__wrapper .search) { position: relative; & span { diff --git a/storefront/app/composables/brands/useBrandBySlug.ts b/storefront/app/composables/brands/useBrandBySlug.ts index cba9b4da..7dd0cc65 100644 --- a/storefront/app/composables/brands/useBrandBySlug.ts +++ b/storefront/app/composables/brands/useBrandBySlug.ts @@ -8,6 +8,14 @@ export async function useBrandBySlug(slug: string) { slug, }); + if (!data.value?.brands?.edges?.length) { + throw createError({ + status: 404, + statusText: 'Brand not found', + fatal: true + }); + } + watch(error, (err) => { if (err) { console.error('useBrandsBySlug error:', err); diff --git a/storefront/app/composables/categories/useCategoryBySlug.ts b/storefront/app/composables/categories/useCategoryBySlug.ts index 27bacfcb..0a30c961 100644 --- a/storefront/app/composables/categories/useCategoryBySlug.ts +++ b/storefront/app/composables/categories/useCategoryBySlug.ts @@ -13,6 +13,14 @@ export async function useCategoryBySlug(slug: string) { }); const minMaxPrices = computed(() => category.value?.minMaxPrices ?? { minPrice: 0, maxPrice: 50000 }); + if (!data.value?.categories?.edges?.length) { + throw createError({ + status: 404, + statusText: 'Category not found', + fatal: true + }); + } + watch(error, (err) => { if (err) { console.error('useCategoryBySlug error:', err); diff --git a/storefront/app/composables/products/useProductBySlug.ts b/storefront/app/composables/products/useProductBySlug.ts index f30a1af0..ea2f6aa9 100644 --- a/storefront/app/composables/products/useProductBySlug.ts +++ b/storefront/app/composables/products/useProductBySlug.ts @@ -13,6 +13,14 @@ export async function useProductBySlug(slug: string) { product.value = result; } + if (!data.value?.products?.edges?.length) { + throw createError({ + status: 404, + statusText: 'Product not found', + fatal: true + }); + } + watch(error, (err) => { if (err) { console.error('useProductBySlug error:', err); diff --git a/storefront/app/error.vue b/storefront/app/error.vue new file mode 100644 index 00000000..69b79a55 --- /dev/null +++ b/storefront/app/error.vue @@ -0,0 +1,82 @@ + + + + + \ No newline at end of file diff --git a/storefront/app/pages/brand/[brandSlug].vue b/storefront/app/pages/brand/[brandSlug].vue index 7e0bee76..4947516c 100644 --- a/storefront/app/pages/brand/[brandSlug].vue +++ b/storefront/app/pages/brand/[brandSlug].vue @@ -9,7 +9,7 @@ :alt="brand.name" />

{{ brand.name }}

-

{{ brand.description }}

+
diff --git a/storefront/app/pages/brands.vue b/storefront/app/pages/brands.vue index fab39de9..30c4e064 100644 --- a/storefront/app/pages/brands.vue +++ b/storefront/app/pages/brands.vue @@ -3,7 +3,7 @@

{{ t('brands.title') }}

{{ t('brands.text') }}

-