Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
de6f52f
chore(VCST-3470): refactor extension points
ivan-kalachikov Nov 19, 2025
8003bc6
Merge branch 'dev' into chore/VCST-3470-refactor-extensions
ivan-kalachikov Nov 19, 2025
ad840ae
fix: resolve comment - refactor missed old extension point composable
ivan-kalachikov Nov 21, 2025
e6a47e7
Merge branch 'dev' into chore/VCST-3470-refactor-extensions
Aleksandra-Mitricheva Nov 24, 2025
d1ae5b1
fix: variations extension points
ivan-kalachikov Nov 25, 2025
178b51c
Merge branch 'dev' into chore/VCST-3470-refactor-extensions
ivan-kalachikov Nov 26, 2025
1efbff1
feat: add EP for variations item
ivan-kalachikov Nov 27, 2025
1304b5a
Merge branch 'dev' into chore/VCST-3470-refactor-extensions
Aleksandra-Mitricheva Nov 27, 2025
9c7cbe1
fix(VCST-4285): filter clipping in select-address-filter (#2079)
goldenmaya Nov 28, 2025
2319292
fix(VCST-4337): add inStock_variations filter to search dropdown (#2084)
goldenmaya Nov 28, 2025
df1249e
fix(VCST-4335): bottom of product cards must have equal heights (#2080)
muller39 Nov 28, 2025
be2c985
feat(VCST-3965): unify display of qty fields and stock alerts in vari…
muller39 Nov 28, 2025
ebec3c5
fix(VCST-4220): long email breaks (#2068)
muller39 Nov 28, 2025
060f3a8
feat(VCST-4232): migrate storyfn to storyobj (#2066)
muller39 Nov 28, 2025
23fa59b
Merge branch 'dev' into chore/VCST-3470-refactor-extensions
ivan-kalachikov Nov 28, 2025
4589e18
chore: update module manifest
ivan-kalachikov Nov 28, 2025
11cab4c
chore: update module manifest
ivan-kalachikov Nov 28, 2025
baad665
chore: update module manifest
ivan-kalachikov Nov 28, 2025
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
17 changes: 7 additions & 10 deletions client-app/modules/back-in-stock/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { defineAsyncComponent } from "vue";
import { useNavigations } from "@/core/composables";
import { useModuleSettings } from "@/core/composables/useModuleSettings";
import { useUser } from "@/shared/account/composables";
import { useCustomProductComponents } from "@/shared/common/composables";
import { CUSTOM_PRODUCT_COMPONENT_IDS } from "@/shared/common/constants";
import { useExtensionRegistry } from "@/shared/common/composables/extensionRegistry/useExtensionRegistry";
import { EXTENSION_NAMES } from "@/shared/common/constants";
import { loadModuleLocale } from "../utils";
import { MODULE_ID, ENABLED_KEY } from "./constants";
import type { MenuType } from "@/core/types";
Expand All @@ -17,7 +17,7 @@ const BackInStockButton = defineAsyncComponent(() => import("./components/back-i

const { isEnabled } = useModuleSettings(MODULE_ID);
const { mergeMenuSchema } = useNavigations();
const { registerComponent } = useCustomProductComponents();
const { register } = useExtensionRegistry();

const route: RouteRecordRaw = {
path: "back-in-stock",
Expand Down Expand Up @@ -64,16 +64,13 @@ export function init(router: Router, i18n: I18n) {
}
if (isAuthenticated.value && isEnabled(ENABLED_KEY)) {
mergeMenuSchema(menuItems);
registerComponent({
id: CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON,
register("productCard", EXTENSION_NAMES.productCard.cardButton, {
component: BackInStockButton,
shouldRender: (product, options) =>
!product.availabilityData.isInStock && (options?.forceProductAsVariation === true || !product.hasVariations),
condition: (product) => !product.availabilityData?.isInStock && !product.hasVariations,
});
registerComponent({
id: CUSTOM_PRODUCT_COMPONENT_IDS.PAGE_SIDEBAR_BUTTON,
register("productPage", EXTENSION_NAMES.productPage.sidebarButton, {
component: BackInStockButton,
shouldRender: (product) => !product.availabilityData.isInStock && !product.hasVariations,
condition: (product) => !product.availabilityData?.isInStock && !product.hasVariations,
});
}
}
30 changes: 16 additions & 14 deletions client-app/modules/push-messages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,11 @@ import { MODULE_ID_PUSH_MESSAGES } from "@/core/constants/modules";
import { loadModuleLocale } from "@/modules/utils";
import { ROUTES } from "@/router/routes/constants";
import { useUser } from "@/shared/account/composables/useUser";
import { useCustomHeaderLinkComponents } from "@/shared/layout/composables/useCustomHeaderLinkComponents";
import { useCustomMobileHeaderComponents } from "@/shared/layout/composables/useCustomMobileHeaderComponents";
import { useCustomMobileMenuLinkComponents } from "@/shared/layout/composables/useCustomMobileMenuLinkComponents";
import { useExtensionRegistry } from "@/shared/common/composables/extensionRegistry/useExtensionRegistry";
import { pushMessagesTypePolices } from "./api/graphql/typePolices";
import { PUSH_MESSAGES_MODULE_ENABLED_KEY, PUSH_MESSAGES_MODULE_FCM_ENABLED_KEY } from "./constants";
import type { MenuType } from "@/core/types";
import type { I18n } from "@/i18n";
import type { ElementType } from "@/shared/layout/composables/useCustomHeaderLinkComponents";
import type { ElementType as HeaderElementType } from "@/shared/layout/composables/useCustomMobileHeaderComponents";
import type { DeepPartial } from "utility-types";
import type { Router, RouteRecordRaw } from "vue-router";

Expand Down Expand Up @@ -67,17 +63,17 @@ const menuItems: DeepPartial<MenuType> = {
const Notifications = () => import("@/modules/push-messages/pages/notifications.vue");
const PushMessage = () => import("@/modules/push-messages/pages/push-message.vue");

const menuLinkCustomElement: ElementType = {
const menuLinkCustomElement = {
id: "push-messages",
component: defineAsyncComponent(() => import("./components/link-push-messages.vue")),
};

const menuLinkCustomElementMobile: ElementType = {
const menuLinkCustomElementMobile = {
id: "push-messages",
component: defineAsyncComponent(() => import("./components/link-push-messages-mobile.vue")),
};

const headerWidgetCustomElementMobile: HeaderElementType = {
const headerWidgetCustomElementMobile = {
id: "push-messages",
component: defineAsyncComponent(() => import("./components/push-messages-mobile.vue")),
};
Expand Down Expand Up @@ -108,9 +104,7 @@ export async function init(router: Router, i18n: I18n) {

if (isModuleEnabled) {
const { mergeMenuSchema } = useNavigations();
const { registerCustomLinkComponent } = useCustomHeaderLinkComponents();
const { registerCustomLinkComponent: registerCustomMobileLinkComponent } = useCustomMobileMenuLinkComponents();
const { registerCustomComponent: registerCustomMobileHeaderComponent } = useCustomMobileHeaderComponents();
const { register } = useExtensionRegistry();
const route: RouteRecordRaw = {
path: "notifications",
name: "Notifications",
Expand All @@ -120,9 +114,17 @@ export async function init(router: Router, i18n: I18n) {
cache.policies.addTypePolicies(pushMessagesTypePolices);
mergeMenuSchema(menuItems);
void loadModuleLocale(i18n, "push-messages");
registerCustomLinkComponent(menuLinkCustomElement);
registerCustomMobileLinkComponent(menuLinkCustomElementMobile);
registerCustomMobileHeaderComponent(headerWidgetCustomElementMobile);

register("headerMenu", "push-messages", {
component: menuLinkCustomElement.component,
});
register("mobileMenu", "push-messages", {
component: menuLinkCustomElementMobile.component,
});
register("mobileHeader", "push-messages", {
component: headerWidgetCustomElementMobile.component,
});

router.addRoute("Account", route); // NOTE: This route must be added before any asynchronous calls. Delaying it can cause a 404 error if accessed prematurely.
}

Expand Down
16 changes: 5 additions & 11 deletions client-app/shared/catalog/components/product-card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,12 @@
:single-line="viewMode === 'grid'"
/>

<component
:is="getComponent(CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON)"
v-if="
isComponentRegistered(CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON) &&
shouldRenderComponent(CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON, product)
"
<ExtensionPoint
v-if="$canRenderExtensionPoint('productCard', EXTENSION_NAMES.productCard.cardButton, product)"
:name="EXTENSION_NAMES.productCard.cardButton"
category="productCard"
:product="product"
is-text-shown
v-bind="getComponentProps(CUSTOM_PRODUCT_COMPONENT_IDS.CARD_BUTTON)"
/>

<VcProductButton
Expand Down Expand Up @@ -158,8 +155,7 @@ import {
import { useProductVariations } from "@/shared/catalog/composables/useProductVariations";
import { useProducts } from "@/shared/catalog/composables/useProducts";
import { PRODUCT_VARIATIONS_LAYOUT_PROPERTY_NAME } from "@/shared/catalog/constants/product";
import { useCustomProductComponents } from "@/shared/common/composables/useCustomProductComponents";
import { CUSTOM_PRODUCT_COMPONENT_IDS } from "@/shared/common/constants";
import { EXTENSION_NAMES } from "@/shared/common/constants";
import { AddToCompareCatalog } from "@/shared/compare/components";
import { AddToList } from "@/shared/wishlists";
import BadgesWrapper from "./badges-wrapper.vue";
Expand Down Expand Up @@ -197,8 +193,6 @@ const productCard = useTemplateRef("productCard");

const { browserTarget: browserTargetFromSetting } = useBrowserTarget();

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

const { isEnabled } = useModuleSettings(CUSTOMER_REVIEWS_MODULE_ID);
const productReviewsEnabled = isEnabled(CUSTOMER_REVIEWS_ENABLED_KEY);

Expand Down
15 changes: 5 additions & 10 deletions client-app/shared/catalog/components/product-price.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,11 @@
</div>

<div class="mt-4 print:hidden">
<component
:is="getComponent(CUSTOM_PRODUCT_COMPONENT_IDS.PAGE_SIDEBAR_BUTTON)"
v-if="
isComponentRegistered(CUSTOM_PRODUCT_COMPONENT_IDS.PAGE_SIDEBAR_BUTTON) &&
shouldRenderComponent(CUSTOM_PRODUCT_COMPONENT_IDS.PAGE_SIDEBAR_BUTTON, product)
"
<ExtensionPoint
v-if="$canRenderExtensionPoint('productPage', EXTENSION_NAMES.productPage.sidebarButton, product)"
:name="EXTENSION_NAMES.productPage.sidebarButton"
category="productPage"
:product="product"
v-bind="getComponentProps(CUSTOM_PRODUCT_COMPONENT_IDS.PAGE_SIDEBAR_BUTTON)"
/>

<component v-else :is="product.isConfigurable ? AddToCart : AddToCartSimple" :product="product">
Expand All @@ -117,8 +114,7 @@ import { useShortCart } from "@/shared/cart/composables";
import { useConfigurableProduct } from "@/shared/catalog/composables";
import { useProductVariationProperties } from "@/shared/catalog/composables/useProductVariationProperties";
import { PRODUCT_VARIATIONS_LAYOUT_PROPERTY_VALUES } from "@/shared/catalog/constants/product";
import { useCustomProductComponents } from "@/shared/common/composables";
import { CUSTOM_PRODUCT_COMPONENT_IDS } from "@/shared/common/constants";
import { EXTENSION_NAMES } from "@/shared/common/constants";
import CountInCart from "./count-in-cart.vue";
import InStock from "./in-stock.vue";
import Price from "./price.vue";
Expand All @@ -139,7 +135,6 @@ const variations = toRef(props, "variations");

const { cart } = useShortCart();
const { configuredLineItem, loading: configuredLineItemLoading } = useConfigurableProduct(product.value.id);
const { getComponent, isComponentRegistered, shouldRenderComponent, getComponentProps } = useCustomProductComponents();
const { variationResult } = useProductVariationProperties(computed(() => variations.value ?? []));

const isDigital = computed<boolean>(() => props.product.productType === ProductType.Digital);
Expand Down
1 change: 1 addition & 0 deletions client-app/shared/common/constants/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./customProductComponents";
export * from "./extensionPointsNames";
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@

<ul class="-mx-2 flex items-center">
<li v-for="item in desktopMainMenuItems" :key="item.id">
<component :is="(item.id && customLinkComponents[item.id]) || LinkDefault" :item="item" />
<ExtensionPoint category="headerMenu" :name="item.id" :item="item">
<LinkDefault :item="item" />
</ExtensionPoint>
</li>
</ul>
</nav>
Expand Down Expand Up @@ -79,7 +81,6 @@ import { useRoute, useRouter } from "vue-router";
import { useNavigations, useWhiteLabeling } from "@/core/composables";
import { useUser } from "@/shared/account/composables/useUser";
import { SearchBar } from "@/shared/layout";
import { useCustomHeaderLinkComponents } from "@/shared/layout/composables/useCustomHeaderLinkComponents";
import CatalogMenu from "./catalog-menu.vue";
import type { StyleValue } from "vue";
import LinkDefault from "@/shared/layout/components/header/_internal/link-components/link-default.vue";
Expand All @@ -94,7 +95,6 @@ const router = useRouter();
const { organization } = useUser();
const { logoUrl } = useWhiteLabeling();
const { catalogMenuItems, desktopMainMenuItems } = useNavigations();
const { customLinkComponents } = useCustomHeaderLinkComponents();

const bottomHeader = ref<HTMLElement | null>(null);
const catalogMenuElement = shallowRef<HTMLElement | null>(null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<VcIcon class="fill-primary" name="search" :size="28" />
</button>

<component :is="item" v-for="(item, index) in customComponents" :key="index" class="px-1 py-2 xs:px-2" />
<ExtensionPointList category="mobileHeader" class="px-1 py-2 xs:px-2" />

<router-link
:to="{ name: ROUTES.CART.NAME }"
Expand Down Expand Up @@ -148,7 +148,6 @@ import { QueryParamName } from "@/core/enums";
import { ROUTES } from "@/router/routes/constants";
import { useShortCart } from "@/shared/cart";
import { useNestedMobileHeader } from "@/shared/layout";
import { useCustomMobileHeaderComponents } from "@/shared/layout/composables/useCustomMobileHeaderComponents";
import { useSearchBar } from "@/shared/layout/composables/useSearchBar";
import { useSearchScore } from "@/shared/layout/composables/useSearchScore";
import { ShipToSelector } from "@/shared/ship-to-location";
Expand All @@ -159,7 +158,6 @@ import BarcodeScanner from "@/shared/layout/components/search-bar/barcode-scanne

const router = useRouter();

const { customComponents } = useCustomMobileHeaderComponents();
const searchPhrase = ref("");
const searchPhraseInUrl = useRouteQueryParam<string>(QueryParamName.SearchPhrase);
const mobileMenuVisible = ref(false);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,42 +1,38 @@
<template>
<ul class="mt-4 flex grow flex-col gap-y-2">
<li v-for="item in items" :key="item.title">
<template v-if="item.id && customLinkComponents[item.id]">
<component
:is="item.id && customLinkComponents[item.id]"
:item="item"
@close="$emit('close')"
@select-item="$emit('selectItem', item)"
/>
</template>
<MobileMenuLink
v-else
:link="item"
:format-text-function="capitalize"
class="py-1 text-lg"
<ExtensionPoint
category="mobileMenu"
:name="item.id"
:item="item"
@close="$emit('close')"
@select="$emit('selectItem', item)"
@select-item="$emit('selectItem', item)"
>
<template #default="{ formattedText }">
{{ formattedText }}
</template>
</MobileMenuLink>
<MobileMenuLink
:link="item"
:format-text-function="capitalize"
class="py-1 text-lg"
@close="$emit('close')"
@select="$emit('selectItem', item)"
>
<template #default="{ formattedText }">
{{ formattedText }}
</template>
</MobileMenuLink>
</ExtensionPoint>
</li>
</ul>
</template>

<script setup lang="ts">
import { capitalize } from "lodash";
import { useCustomMobileMenuLinkComponents } from "@/shared/layout/composables/useCustomMobileMenuLinkComponents";
import type { ExtendedMenuLinkType } from "@/core/types";
import MobileMenuLink from "@/shared/layout/components/header/_internal/mobile-menu/mobile-menu-link.vue";

defineEmits<IEmits>();

defineProps<IProps>();

const { customLinkComponents } = useCustomMobileMenuLinkComponents();

interface IProps {
items: ExtendedMenuLinkType[];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@
{{ menuItem.title }}
</MobileMenuLink>
</li>

<li v-for="item in mobileMainMenuItems" :key="item.title">
<component
:is="(item.id && customLinkComponents[item.id]) || LinkDefault"
<ExtensionPoint
category="mobileMenu"
:name="item.id"
:item="item"
@close="$emit('close')"
@select-item="$emit('selectItem', item)"
/>
>
<LinkDefault :item="item" @close="$emit('close')" @select-item="$emit('selectItem', item)" />
</ExtensionPoint>
</li>
</ul>

Expand All @@ -24,6 +28,7 @@
class="flex size-12 shrink-0 items-center justify-center overflow-hidden rounded-full ring-2 ring-accent-300"
>
<VcImage v-if="user.photoUrl" :src="user.photoUrl" :alt="user.contact?.fullName" class="size-12" lazy />

<VcIcon v-else name="user" />
</div>

Expand Down Expand Up @@ -112,10 +117,7 @@ import { useI18n } from "vue-i18n";
import { useCurrency, useNavigations } from "@/core/composables";
import { ROUTES } from "@/router/routes/constants";
import { useSignMeOut, useUser } from "@/shared/account";
import { useCustomMobileMenuLinkComponents } from "@/shared/layout/composables/useCustomMobileMenuLinkComponents";
import type { ExtendedMenuLinkType } from "@/core/types";
import LinkCart from "@/shared/layout/components/header/_internal/mobile-menu/link-components/link-cart.vue";
import LinkCompare from "@/shared/layout/components/header/_internal/mobile-menu/link-components/link-compare.vue";
import LinkDefault from "@/shared/layout/components/header/_internal/mobile-menu/link-components/link-default.vue";
import MobileMenuLink from "@/shared/layout/components/header/_internal/mobile-menu/mobile-menu-link.vue";

Expand All @@ -136,10 +138,6 @@ const { user, operator, isAuthenticated, isCorporateMember } = useUser();
const { mobileMainMenuItems, mobileCorporateMenuItem, mobileAccountMenuItem } = useNavigations();
const { t } = useI18n();
const { supportedCurrencies } = useCurrency();
const { registerCustomLinkComponent, customLinkComponents } = useCustomMobileMenuLinkComponents();

registerCustomLinkComponent({ id: "cart", component: LinkCart });
registerCustomLinkComponent({ id: "compare", component: LinkCompare });

const unauthorizedMenuItems: ExtendedMenuLinkType[] = [
{ route: { name: ROUTES.SIGN_IN.NAME }, title: t("shared.layout.header.link_sign_in") },
Expand Down

This file was deleted.

Loading