Skip to content

Commit

Permalink
Merge pull request #222 from tzhf/handle-product-not-found
Browse files Browse the repository at this point in the history
Handle product not found
  • Loading branch information
scottyzen authored Aug 18, 2024
2 parents fe163a2 + 787ffa5 commit e2a0be5
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 88 deletions.
2 changes: 1 addition & 1 deletion woonuxt_base/app/components/filtering/PriceFilter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ watch(isFiltersActive, () => {
<label for="price-to" class="leading-none px-2 text-gray-400 absolute" v-html="currencySymbol" />
</div>
<div class="mx-1 mt-1 col-span-full">
<Slider v-model="price" :tooltips="false" :min="0" :max="maxPrice" ariaLabelledby="price-from price-to" @change="applyPrice" />
<Slider v-model="price" :tooltips="false" :min="0" :max="maxPrice" ariaLabelledby="price-from price-to" @update="applyPrice" />
</div>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion woonuxt_base/app/locales/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"searchProducts": "Produkte suchen...",
"couponCode": "Gutscheincode",
"downloadsRemaining": "Verbleibende Downloads",
"expires": "Läuft ab"
"expires": "Läuft ab",
"productNotFound": "Produkt nicht gefunden"
},
"billing": {
"billing": "Rechnung",
Expand Down
3 changes: 2 additions & 1 deletion woonuxt_base/app/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"searchProducts": "Search Products...",
"couponCode": "Coupon Code",
"downloadsRemaining": "Downloads remaining",
"expires": "Expires"
"expires": "Expires",
"productNotFound": "Product not found"
},
"billing": {
"billing": "Billing",
Expand Down
3 changes: 2 additions & 1 deletion woonuxt_base/app/locales/es-ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"searchProducts": "Buscar productos...",
"couponCode": "Código de cupón",
"downloadsRemaining": "Descargas restantes",
"expires": "Expira"
"expires": "Expira",
"productNotFound": "Producto no encontrado"
},
"billing": {
"billing": "Facturación",
Expand Down
3 changes: 2 additions & 1 deletion woonuxt_base/app/locales/fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"searchProducts": "Rechercher par produit...",
"couponCode": "Code du coupon de réduction",
"downloadsRemaining": "Téléchargements restants",
"expires": "Expire le"
"expires": "Expire le",
"productNotFound": "Ce produit n'existe pas"
},
"billing": {
"billing": "Facturation",
Expand Down
3 changes: 2 additions & 1 deletion woonuxt_base/app/locales/it-IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"searchProducts": "Cerca prodotti...",
"couponCode": "Codice coupon",
"downloadsRemaining": "Download rimanenti",
"expires": "Scade"
"expires": "Scade",
"productNotFound": "Prodotto non trovato"
},
"billing": {
"billing": "Fatturazione",
Expand Down
3 changes: 2 additions & 1 deletion woonuxt_base/app/locales/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@
"searchProducts": "Pesquisar Produtos...",
"couponCode": "Código do Cupom",
"downloadsRemaining": "Downloads restantes",
"expires": "Expira"
"expires": "Expira",
"productNotFound": "Produto não encontrado"
},
"billing": {
"billing": "Cobrança",
Expand Down
170 changes: 89 additions & 81 deletions woonuxt_base/app/pages/product/[slug].vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ const route = useRoute();
const { storeSettings } = useAppConfig();
const { arraysEqual, formatArray, checkForVariationTypeOfAny } = useHelpers();
const { addToCart, isUpdatingCart } = useCart();
const { t } = useI18n();
const slug = route.params.slug as string;
const { data } = (await useAsyncGql('getProduct', { slug })) as { data: { value: { product: Product } } };
const product = ref<Product>(data?.value?.product);
// If the product is not found, throw an error
if (!product.value) {
throw createError({ statusCode: 404, statusMessage: t('messages.shop.productNotFound') });
}
const quantity = ref<number>(1);
const activeVariation = ref<Variation | null>(null);
const variation = ref<Attribute[]>([]);
Expand Down Expand Up @@ -39,7 +45,7 @@ onMounted(async () => {
const errorMessage = error?.gqlErrors?.[0].message;
if (errorMessage) console.error(errorMessage);
}
if (product.value.variations) indexOfTypeAny.value.push(...checkForVariationTypeOfAny(product.value));
if (product.value?.variations) indexOfTypeAny.value.push(...checkForVariationTypeOfAny(product.value));
});
const updateSelectedVariations = (variations: Attribute[]): void => {
Expand Down Expand Up @@ -72,97 +78,99 @@ const disabledAddToCart = computed(() => {
</script>

<template>
<main class="container relative py-6 xl:max-w-7xl" v-if="product">
<SEOHead :info="product" />
<Breadcrumb :product class="mb-6" v-if="storeSettings.showBreadcrumbOnSingleProduct" />

<div class="flex flex-col gap-10 md:flex-row md:justify-between lg:gap-24">
<ProductImageGallery
v-if="product.image"
class="relative flex-1"
:main-image="product.image"
:gallery="product.galleryImages!"
:node="type"
:activeVariation="activeVariation || {}" />
<NuxtImg v-else class="relative flex-1 skeleton" src="/images/placeholder.jpg" :alt="product?.name || 'Product'" />

<div class="lg:max-w-md xl:max-w-lg md:py-2 w-full">
<div class="flex justify-between mb-4">
<div class="flex-1">
<h1 class="flex flex-wrap items-center gap-2 mb-2 text-2xl font-sesmibold">
{{ type.name }}
<WPAdminLink :link="`/wp-admin/post.php?post=${product.databaseId}&action=edit`">Edit</WPAdminLink>
</h1>
<StarRating :rating="product.averageRating || 0" :count="product.reviewCount || 0" v-if="storeSettings.showReviews" />
<main class="container relative py-6 xl:max-w-7xl">
<div v-if="product">
<SEOHead :info="product" />
<Breadcrumb :product class="mb-6" v-if="storeSettings.showBreadcrumbOnSingleProduct" />

<div class="flex flex-col gap-10 md:flex-row md:justify-between lg:gap-24">
<ProductImageGallery
v-if="product.image"
class="relative flex-1"
:main-image="product.image"
:gallery="product.galleryImages!"
:node="type"
:activeVariation="activeVariation || {}" />
<NuxtImg v-else class="relative flex-1 skeleton" src="/images/placeholder.jpg" :alt="product?.name || 'Product'" />

<div class="lg:max-w-md xl:max-w-lg md:py-2 w-full">
<div class="flex justify-between mb-4">
<div class="flex-1">
<h1 class="flex flex-wrap items-center gap-2 mb-2 text-2xl font-sesmibold">
{{ type.name }}
<WPAdminLink :link="`/wp-admin/post.php?post=${product.databaseId}&action=edit`">Edit</WPAdminLink>
</h1>
<StarRating :rating="product.averageRating || 0" :count="product.reviewCount || 0" v-if="storeSettings.showReviews" />
</div>
<ProductPrice class="text-xl" :sale-price="type.salePrice" :regular-price="type.regularPrice" />
</div>
<ProductPrice class="text-xl" :sale-price="type.salePrice" :regular-price="type.regularPrice" />
</div>

<div class="grid gap-2 my-8 text-sm">
<div class="flex items-center gap-2">
<span class="text-gray-400">{{ $t('messages.shop.availability') }}: </span>
<StockStatus :stockStatus @updated="mergeLiveStockStatus" />
</div>
<div class="flex items-center gap-2" v-if="storeSettings.showSKU">
<span class="text-gray-400">{{ $t('messages.shop.sku') }}: </span>
<span>{{ product.sku || 'N/A' }}</span>
<div class="grid gap-2 my-8 text-sm">
<div class="flex items-center gap-2">
<span class="text-gray-400">{{ $t('messages.shop.availability') }}: </span>
<StockStatus :stockStatus @updated="mergeLiveStockStatus" />
</div>
<div class="flex items-center gap-2" v-if="storeSettings.showSKU">
<span class="text-gray-400">{{ $t('messages.shop.sku') }}: </span>
<span>{{ product.sku || 'N/A' }}</span>
</div>
</div>
</div>

<div class="mb-8 font-light prose" v-html="product.shortDescription || product.description" />

<hr />

<form @submit.prevent="addToCart(selectProductInput)">
<AttributeSelections
v-if="product.type == 'VARIABLE' && product.attributes && product.variations"
class="mt-4 mb-8"
:attributes="product.attributes.nodes"
:defaultAttributes="product.defaultAttributes"
:variations="product.variations.nodes"
@attrs-changed="updateSelectedVariations" />
<div class="fixed bottom-0 left-0 z-10 flex items-center w-full gap-4 p-4 mt-12 bg-white md:static md:bg-transparent bg-opacity-90 md:p-0">
<input
v-model="quantity"
type="number"
min="1"
aria-label="Quantity"
class="bg-white border rounded-lg flex text-left p-2.5 w-20 gap-4 items-center justify-center focus:outline-none" />
<AddToCartButton class="flex-1 w-full md:max-w-xs" :disabled="disabledAddToCart" :class="{ loading: isUpdatingCart }" />
</div>
</form>
<div class="mb-8 font-light prose" v-html="product.shortDescription || product.description" />

<div v-if="storeSettings.showProductCategoriesOnSingleProduct && product.productCategories">
<div class="grid gap-2 my-8 text-sm">
<div class="flex items-center gap-2">
<span class="text-gray-400">{{ $t('messages.shop.category', 2) }}:</span>
<div class="product-categories">
<NuxtLink
v-for="category in product.productCategories.nodes"
:key="category.slug"
:to="`/product-category/${decodeURIComponent(category.slug)}`"
class="hover:text-primary"
:title="category.name"
>{{ category.name }}<span class="comma">, </span>
</NuxtLink>
<hr />

<form @submit.prevent="addToCart(selectProductInput)">
<AttributeSelections
v-if="product.type == 'VARIABLE' && product.attributes && product.variations"
class="mt-4 mb-8"
:attributes="product.attributes.nodes"
:defaultAttributes="product.defaultAttributes"
:variations="product.variations.nodes"
@attrs-changed="updateSelectedVariations" />
<div class="fixed bottom-0 left-0 z-10 flex items-center w-full gap-4 p-4 mt-12 bg-white md:static md:bg-transparent bg-opacity-90 md:p-0">
<input
v-model="quantity"
type="number"
min="1"
aria-label="Quantity"
class="bg-white border rounded-lg flex text-left p-2.5 w-20 gap-4 items-center justify-center focus:outline-none" />
<AddToCartButton class="flex-1 w-full md:max-w-xs" :disabled="disabledAddToCart" :class="{ loading: isUpdatingCart }" />
</div>
</form>

<div v-if="storeSettings.showProductCategoriesOnSingleProduct && product.productCategories">
<div class="grid gap-2 my-8 text-sm">
<div class="flex items-center gap-2">
<span class="text-gray-400">{{ $t('messages.shop.category', 2) }}:</span>
<div class="product-categories">
<NuxtLink
v-for="category in product.productCategories.nodes"
:key="category.slug"
:to="`/product-category/${decodeURIComponent(category.slug)}`"
class="hover:text-primary"
:title="category.name"
>{{ category.name }}<span class="comma">, </span>
</NuxtLink>
</div>
</div>
</div>
<hr />
</div>
<hr />
</div>

<div class="flex flex-wrap gap-4">
<WishlistButton :product />
<ShareButton :product />
<div class="flex flex-wrap gap-4">
<WishlistButton :product />
<ShareButton :product />
</div>
</div>
</div>
</div>
<div v-if="product.description || product.reviews" class="my-32">
<ProductTabs :product />
</div>
<div class="my-32" v-if="product.related && storeSettings.showRelatedProducts">
<div class="mb-4 text-xl font-semibold">{{ $t('messages.shop.youMayLike') }}</div>
<ProductRow :products="product.related.nodes" class="grid-cols-2 md:grid-cols-4 lg:grid-cols-5" />
<div v-if="product.description || product.reviews" class="my-32">
<ProductTabs :product />
</div>
<div class="my-32" v-if="product.related && storeSettings.showRelatedProducts">
<div class="mb-4 text-xl font-semibold">{{ $t('messages.shop.youMayLike') }}</div>
<ProductRow :products="product.related.nodes" class="grid-cols-2 md:grid-cols-4 lg:grid-cols-5" />
</div>
</div>
</main>
</template>
Expand Down

0 comments on commit e2a0be5

Please sign in to comment.