Banners

TsBannerHomeDealsAndOffers

Component Code

<template>
  <component
      v-if="props.product && props.product.enableRedirection"
      :is="props.product.enableRedirection || props.landingPageUrl ? NuxtLink : 'div'"
      v-bind="nuxt_link_attrs"
      :id="props.id || `product-banner-${random_id}`"
      :class="twMerge(
          'ts-banner h-full relative font-gilroy rounded-lg overflow-hidden max-w-max bg-primary',
          (props.product?.enableRedirection || props.landingPageUrl) && 'inline-block'
      )"
      :style="root_styles"
  >
    <div ref="bannerHomeDealsAndOfferRef" class="w-full h-full flex flex-col p-4 pt-14 relative max-w-[320px] md:max-w-[300px] min-w-[320px] md:min-w-[300px]">

      <div v-if="props.image && props.image.src" class="w-full relative flex justify-center mb-4">
        <div
            v-if="product_data && product_data.prices && product_data.prices.formatted"
          :class="twMerge(
            'absolute top-0 text-center text-idle-white mt-3 px-2 flex flex-col',
            !rootStore.isIncVat && 'flex-col-reverse'
        )">
          <div :class="twMerge(rootStore.isIncVat ? 'text-sm' : 'text-xs')">{{useTranslation("nowAt","Nu bij")}} <span :class="twMerge(rootStore.isIncVat && 'text-base font-bold')">{{ product_data.prices.formatted.gross }}</span></div>
          <div :class="twMerge(rootStore.isIncVat ? 'text-xs' : 'text-sm')">Excl. {{useTranslation("vat","BTW")}} <span :class="twMerge(!rootStore.isIncVat && 'text-base font-bold')">{{ product_data.prices.formatted.net }}</span></div>
        </div>
        <NuxtImg class="mt-auto"
                 :src="props.image.src"
                 width="147"
        />
      </div>

      <div class="mt-auto">
        <div
            :class="twMerge('text-[14px] text-idle-white leading-tight')"
            :style="sub_title_styles"
            v-html="props.subTitle && props.subTitle.text">
        </div>

        <div
            :class="twMerge('text-[20px] font-bold text-idle-white leading-tight mb-3')"
            :style="title_styles"
            v-html="(props.product && props.product.enableTitle && product_data && product_data?.full_name) ? product_data?.full_name : props.title?.text">
        </div>

        <div
            :class="twMerge('text-[14px] text-white leading-none')"
            :style="description_styles"
            v-html="(props.product && props.product.enableDescription && product_data && product_data?.description
) ? product_data?.description
 : props.description?.text">
        </div>
      </div>
    </div>
  </component>
</template>

<script setup lang="ts">
import {defineProps, onMounted, ref, withDefaults} from "vue";
import { twMerge } from "tailwind-merge";

const rootStore = useRootStore();
const productStore = useProductStore();
type Props = {
  id?: string;
  dataTestid?: string;
  landingPageUrl?: string;
  product?: object;
  image?: object;
  subTitle?: object;
  title?: object;
  description?: object;
  backgroundColor?: object;
}

const bannerHomeDealsAndOfferRef = ref('')

let observer;

const observeElement = (element) => {
  observer = new IntersectionObserver((entries) => {
    if (entries[0].isIntersecting) {
      fetchProduct()
    }
  })
  observer.observe(element)
};

const fetchProduct = async () => {
  // Replace with your actual fetch request
  if(props.product?.id) {
    product_data.value = await productStore.getProductById(product_id.value);
  }
  if (observer && bannerHomeDealsAndOfferRef.value) {
    observer.unobserve(bannerHomeDealsAndOfferRef.value)
  }
}

const props = withDefaults(defineProps<Props>(), {
  dataTestid: 'highlights',
  image: () => ({
    src: '/banners/desktop/images/banner-deals-and-offers-1.png'
  }),
  subTitle: () => ({
    text: 'Tool Up for Savings'
  }),
  title: () => ({
    text: 'Grab Your Gear Now!'
  }),
  description: () => ({
    text: 'Triton JOF001 router 1,010W 6-12mm'
  })
})

const random_id = ref('');
const product_id = computed(() => props.product?.id);
const product_data = ref(null);
const product_price = computed(() => {
  return rootStore.isIncVat ? product_data.value?.prices?.formatted?.gross : product_data.value?.prices?.formatted?.net;
})

const NuxtLink = resolveComponent('NuxtLink');
const localePath=useLocalePath();
const nuxt_link_attrs = computed(() => ({
  ...(props.landingPageUrl && {to: props.landingPageUrl}),
  ...(props.product?.enableRedirection && product_data.value && {to:localePath( `/product/${product_data.value.slug}-${product_data.value.code}`)}),
}))

const sub_title_styles = computed(() => ({
  ...(props.subTitle?.color && {color: props.subTitle?.color}),
  ...(props.subTitle?.fontSize && {fontSize: props.subTitle?.fontSize})
}))

const title_styles = computed(() => ({
  ...(props.title?.color && {color: props.title?.color}),
  ...(props.title?.fontSize && {fontSize: props.title?.fontSize})
}))

const description_styles = computed(() => ({
  ...(props.description?.color && {color: props.description?.color}),
  ...(props.description?.fontSize && {fontSize: props.description?.fontSize}),
}))

const root_styles = computed(() => ({
  ...(props.backgroundColor?.color && {backgroundColor: props.backgroundColor.color }),
  ...(props.backgroundColor?.custom && {backgroundColor: props.backgroundColor.custom }),
}))

onMounted(async () => {
  random_id.value = useRandomUUID();
  if (bannerHomeDealsAndOfferRef.value) {
    observeElement(bannerHomeDealsAndOfferRef.value)
  }
});

onUnmounted(() => {
  if (observer && bannerHomeDealsAndOfferRef.value) {
    observer.unobserve(bannerHomeDealsAndOfferRef.value)
  }
})

watch(product_id, async () => {
  await fetchProduct();
})

</script>

<style scoped>

</style>

Usage in Builder

  1. Go to the insert tab.
  2. Search BannerHomeDealsAndOffers in search bar.
  3. Hold BannerHomeDealsAndOffers component & drag where you want to use.

Props

NameTypeDefaultDescription
idstring""It is used to provide the id to the component.
landingPageUrlstring""It is used to add link to the banner.
productobject""It has keys which are: id - string, enablePrice - boolean, enableTitle - boolean, enableDescription - boolean, enableRedirection - boolean. id is used to provide the productId, enablePrice is used to show the price of product based on id, enableTitle is used to show the title of the product based on id, enableDescription is used to show the description of the product based on id, enableRedirection is used to add link to the banner based on product slug which is dynamic from product data.
backgroundobject""It has keys which are: type - string, src - file, poster - file. type is used to select type either image or video, src is used to add the url of image or video, poster is used in case of video.
overlaystring""It is used to add the overlay color of background
foregroundImageobject""It has keys which are: src - string, altText - string. src is used to add the url of image, altText is used to add alt text to the image.
brandLogoobject""It has keys which are: src - string, altText - string. src is used to add the url of logo image, altText is used to add alt text to the logo image.
tagobject""It has keys which are: label - object, borderColor - object, marginBotton - number. label has four keys: 1 - text value of label, 2 - fontSize - to select the predefined font sizes, 3 - color - to select color from atomic design system for text, 4 - customColor - to add any color using color code. borderColor has two keys: 1 - color - to select color from atomic design system for border , customColor - to add any color using color code. marginBotton - to add bottom margin to the tag.
titleobject""It has keys which are: text - richText, fontSize - string, color - string. text is used to add title text, fontSize is used to add size of title, color is used to add color to the title
descriptionobject""It has keys which are: text - richText, fontSize - string, color - string. text is used to add description text, fontSize is used to add size of description, color is used to add color to the description
buttonobject""It has keys which are: label - object, background - object, marginTop - number. label has four keys: 1 - text value of button, 2 - fontSize - to select the predefined font sizes, 3 - color - to select color from atomic design system for text, 4 - customColor - to add any color using color code. background has two keys: 1 - color - to select color from atomic design system for background , customColor - to add any color using color code. marginTop - to add top margin to the button.

Copyright © 2026