Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@
:button-text="$t('pages.catalog.variations_button', [(product.variations?.length || 0) + 1])"
/>

<AddToCart v-else :product="product" reserved-space />
<AddToCartSimple v-else :product="product" reserved-space />
</VcProductCard>
</template>

<script setup lang="ts">
import { computed } from "vue";
import { useBrowserTarget } from "@/core/composables";
import { getProductRoute } from "@/core/utilities";
import { AddToCart } from "@/shared/cart";
import { AddToCompareCatalog } from "@/shared/compare";
import { AddToList } from "@/shared/wishlists";
import type { Product } from "@/core/api/graphql/types";
import type { RouteLocationRaw } from "vue-router";
import AddToCartSimple from "@/shared/cart/components/add-to-cart-simple.vue";

interface IEmits {
(event: "linkClick", globalEvent: MouseEvent): void;
Expand Down
14 changes: 10 additions & 4 deletions client-app/shared/catalog/components/product-sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,19 @@
</div>

<div class="mt-4 print:hidden">
<AddToCart v-if="variationResult && variationResult.price?.actual?.amount > 0" :product="variationResult">
<component
v-if="variationResult && variationResult.price?.actual?.amount > 0"
:is="product.isConfigurable ? AddToCart : AddToCartSimple"
:product="variationResult"
>
<InStock
:is-in-stock="variationResult.availabilityData?.isInStock"
:is-digital="isDigital"
:quantity="variationResult.availabilityData?.availableQuantity"
/>

<CountInCart :product-id="variationResult.id" />
</AddToCart>
</component>

<div v-else>
<VcButton
Expand Down Expand Up @@ -97,15 +101,16 @@
:product="product"
v-bind="getComponentProps(CUSTOM_PRODUCT_COMPONENT_IDS.PAGE_SIDEBAR_BUTTON)"
/>
<AddToCart v-else :product="product">

<component v-else :is="product.isConfigurable ? AddToCart : AddToCartSimple" :product="product">
<InStock
:is-in-stock="product.availabilityData?.isInStock"
:is-digital="isDigital"
:quantity="product.availabilityData?.availableQuantity"
/>

<CountInCart :product-id="product.id" />
</AddToCart>
</component>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Missing Component Import Causes Runtime Error

The component uses a dynamic component with product.isConfigurable ? AddToCart : AddToCartSimple, but AddToCart is not imported in the script section. While AddToCartSimple is imported at line 142, the AddToCart component import is missing, which would cause a runtime error when product.isConfigurable is true.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's imported in line 131: import { AddToCart } from "@/shared/cart";

</div>
</template>
</ProductPriceBlock>
Expand Down Expand Up @@ -134,6 +139,7 @@ import CountInCart from "./count-in-cart.vue";
import InStock from "./in-stock.vue";
import ProductPriceBlock from "./product-price-block.vue";
import type { MoneyType, PriceType, Product } from "@/core/api/graphql/types";
import AddToCartSimple from "@/shared/cart/components/add-to-cart-simple.vue";
import VcAlert from "@/ui-kit/components/molecules/alert/vc-alert.vue";

interface IProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@
v-bind="getComponentProps(CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON)"
/>

<AddToCart v-else :product="variation">
<AddToCartSimple v-else :product="variation">
<InStock
:is-in-stock="variation.availabilityData.isInStock"
:quantity="variation.availabilityData.availableQuantity"
/>

<CountInCart :product-id="variation.id" />
</AddToCart>
</AddToCartSimple>
</VcLineItem>

<VcPagination
Expand All @@ -56,13 +56,13 @@ import { toRef } from "vue";
import { PropertyType } from "@/core/api/graphql/types";
import { useBrowserTarget } from "@/core/composables";
import { getPropertiesGroupedByName } from "@/core/utilities";
import { AddToCart } from "@/shared/cart";
import { PRODUCT_VARIATIONS_LAYOUT_PROPERTY_NAME } from "@/shared/catalog/constants/product";
import { useCustomProductComponents } from "@/shared/common/composables";
import { CUSTOM_PRODUCT_COMPONENT_IDS } from "@/shared/common/constants";
import CountInCart from "../count-in-cart.vue";
import InStock from "../in-stock.vue";
import type { Product } from "@/core/api/graphql/types";
import AddToCartSimple from "@/shared/cart/components/add-to-cart-simple.vue";

interface IEmits {
(event: "changePage", page: number): void;
Expand Down
118 changes: 6 additions & 112 deletions client-app/shared/catalog/components/product/variations-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,50 +85,9 @@
v-bind="getComponentProps(CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON)"
/>

<QuantityControl
v-else
:mode="$cfg.product_quantity_control"
:model-value="mappedLineItems[variation.id]?.quantity ?? 0"
:name="variation.id"
:disabled="
!variation.availabilityData?.isInStock ||
!variation.availabilityData?.isAvailable ||
!variation.availabilityData?.isBuyable
"
:error="!!getItemErrors(variation)"
hide-button
:timeout="DEFAULT_DEBOUNCE_IN_MS"
:validate-on-mount="false"
:pack-size="variation.packSize"
:min-quantity="variation.minQuantity"
:max-quantity="variation.maxQuantity"
:available-quantity="variation.availabilityData?.availableQuantity"
:is-in-stock="variation.availabilityData?.isInStock"
:is-active="variation.availabilityData?.isActive"
:is-available="variation.availabilityData?.isAvailable"
:is-buyable="variation.availabilityData?.isBuyable"
:allow-zero="$cfg.product_quantity_control === 'stepper'"
@update:model-value="changeCart(variation, $event)"
@update:validation="handleClientValidation(variation.id, $event)"
>
<template v-if="!!getItemErrors(variation)" #append>
<VcTooltip placement="bottom-end">
<template #trigger>
<VcIcon class="variations-table__quantity-icon" name="warning" />
</template>

<template #content>
<div class="w-max rounded-sm bg-additional-50 px-3.5 py-1.5 text-xs text-danger">
<div v-for="(error, index) in getItemErrors(variation)" :key="index">
{{ error }}
</div>
</div>
</template>
</VcTooltip>
</template>
</QuantityControl>

<CountInCart :product-id="variation.id" class="variations-table__in-cart" />
<AddToCartSimple v-else :product="variation">
<CountInCart :product-id="variation.id" />
</AddToCartSimple>
</td>
</tr>
</template>
Expand All @@ -138,21 +97,17 @@

<script setup lang="ts">
import { flatten, sortBy, uniqBy } from "lodash";
import { computed, ref, watchEffect } from "vue";
import { computed } from "vue";
import { useI18n } from "vue-i18n";
import { PropertyType } from "@/core/api/graphql/types";
import { useErrorsTranslator, useHistoricalEvents } from "@/core/composables";
import { useAnalyticsUtils } from "@/core/composables/useAnalyticsUtils";
import { MAX_DISPLAY_IN_STOCK_QUANTITY } from "@/core/constants";
import { getPropertyValue, getPropertiesGroupedByName } from "@/core/utilities";
import { DEFAULT_DEBOUNCE_IN_MS } from "@/shared/cart";
import { useShortCart } from "@/shared/cart/composables";
import { useCustomProductComponents } from "@/shared/common/composables";
import { CUSTOM_PRODUCT_COMPONENT_IDS } from "@/shared/common/constants";
import CountInCart from "../count-in-cart.vue";
import type { Product, ShortLineItemFragment, ValidationErrorType } from "@/core/api/graphql/types";
import type { Product } from "@/core/api/graphql/types";
import type { ISortInfo } from "@/core/types";
import QuantityControl from "@/shared/common/components/quantity-control.vue";
import AddToCartSimple from "@/shared/cart/components/add-to-cart-simple.vue";

interface IEmits {
(event: "applySorting", item: ISortInfo): void;
Expand All @@ -178,16 +133,9 @@ const emit = defineEmits<IEmits>();
const props = defineProps<IProps>();

const { t } = useI18n();
const { cart, addToCart, changeItemQuantityBatched } = useShortCart();
const { localizedItemsErrors: serverValidationErrors, setErrors } =
useErrorsTranslator<ValidationErrorType>("validation_error");
const { trackAddItemToCart } = useAnalyticsUtils();
const { pushHistoricalEvent } = useHistoricalEvents();

const { isComponentRegistered, getComponent, shouldRenderComponent, getComponentProps } = useCustomProductComponents();

const clientValidation = ref<Record<string, { isValid: boolean; messages?: string[] }>>({});

const variations = computed(() => props.variations);
const productProperties = computed<IProductProperties[]>(() => {
const properties: IProductProperties[] = [];
Expand Down Expand Up @@ -258,12 +206,6 @@ const columns = computed<ITableColumn[]>(() => [
},
]);

const mappedLineItems = computed(() => {
const mapped: Record<string, ShortLineItemFragment | undefined> = {};
variations.value?.forEach((variation) => (mapped[variation.id] = getLineItem(variation)));
return mapped;
});

function getStockQuantity(variation: Product) {
return variation.availabilityData.availableQuantity &&
variation.availabilityData.availableQuantity > MAX_DISPLAY_IN_STOCK_QUANTITY
Expand All @@ -277,61 +219,13 @@ function getProperties(variation: Product) {
);
}

function getLineItem(variation: Product): ShortLineItemFragment | undefined {
return cart.value?.items?.find((item) => item.productId === variation.id);
}

function getItemErrors(variation: Product) {
if (clientValidation.value[variation.id]?.isValid === false) {
return clientValidation.value[variation.id]?.messages;
}

if (serverValidationErrors.value[variation.id]) {
return serverValidationErrors.value[variation.id];
}

const lineItem = getLineItem(variation);
if (lineItem?.id) {
return serverValidationErrors.value[lineItem.id];
}
}

async function changeCart(variation: Product, quantity: number) {
if (!clientValidation.value[variation.id]?.isValid) {
return;
}
const lineItem = getLineItem(variation);

if (lineItem) {
await changeItemQuantityBatched(lineItem.id, quantity);
} else {
await addToCart(variation.id, quantity);
trackAddItemToCart(variation, quantity);
void pushHistoricalEvent({ eventType: "addToCart", productId: variation.id });
}
}

function applySorting(sortInfo: ISortInfo): void {
emit("applySorting", sortInfo);
}

function changePage(page: number): void {
emit("changePage", page);
}

function handleClientValidation(id: string, validation: { isValid: true } | { isValid: false; errorMessage: string }) {
if (!validation.isValid) {
clientValidation.value[id] = { isValid: false, messages: [validation.errorMessage] };
} else {
clientValidation.value[id] = { isValid: true };
}
}

watchEffect(() => {
if (cart.value?.validationErrors) {
setErrors(cart.value?.validationErrors);
}
});
</script>

<style lang="scss">
Expand Down