Files
your-gold/components/section/ProductBlock.vue

302 lines
11 KiB
Vue

<template>
<UiContainer>
<transition>
<div v-if="showPopup && curImage" ref="dropdownRef" tabindex="0"
class="fixed top-0 left-0 bg-[#000000cc] w-screen h-screen z-50 flex items-center justify-center cursor-pointer p-10"
@keyup.esc="showPopup = false" @keyup.left="goLeft" @keyup.right="goRight" @click.self="showPopup = false">
<div class="border border-white p-4 rounded-2xl shadow-2xl bg-white cursor-auto">
<img :src="`/api/public/file/${curImage}_l.webp`" class="max-h-[80vh] object-contain" alt=""
@load="focusOnImgLoad">
</div>
<button class="absolute top-8 p-8 right-8 text-4xl rotate-45 text-white" @click="showPopup = false">
+
</button>
</div>
</transition>
<div ref="productBlockRef" class="space-y-10 sm:space-y-[75px]">
<div class="flex flex-col items-start xl:flex-row gap-[40px] sm:gap-[100px] border-b-2 border-block pb-[25px]">
<!-- images block -->
<button
class="xl:hidden h-[40px] sm:h-[43px] px-[10px] sm:px-[17px] border border-gray dark:border-button-disabled text-gray dark:text-button-disabled hover:bg-gray transition-all hover:text-white rounded-[8px] cursor-pointer"
@click="menuStore.navigateToItem()">
{{ $t("back_to_home") }}
</button>
<div class="w-full flex-col-reverse sm:flex-row sm:w-[70%] mx-auto justify-center xl:w-auto flex gap-[25px]">
<div class="flex justify-center sm:justify-start sm:flex-col gap-[15px]">
<div v-for="image in product.picture_uuids" :key="image"
:class="['cursor-pointer hover:border-button-disabled transition-all h-[100px] w-[90px] border py-3 flex items-center justify-center rounded-[8px]', image === curImage ? 'border-block' : 'border-button']"
@click="curImage = image">
<img :src="`/api/public/file/${image}_m.webp`" :alt="product.name" srcset="" class="h-full w-auto">
</div>
</div>
<div class="cursor-pointer h-full flex justify-center sm:items-start" @click="showPopup = true">
<img :src="`/api/public/file/${curImage}_m.webp`"
class="sm:min-w-[400px] sm:max-w-[410px] xl:min-w-[300px] xl:max-w-[320px] w-full h-auto" alt="">
</div>
</div>
<!-- info block -->
<div class="w-full space-y-[50px]">
<div class="w-full space-y-[30px]">
<div class="space-y-[15px]">
<div class="flex items-center justify-between w-full">
<h1 class="h4-uppercase-bold-inter max-w-[450px]">
{{ product.name }}
</h1>
<button
class="hidden xl:block h-[40px] sm:h-[43px] px-[10px] sm:px-[17px] border border-gray dark:border-button-disabled text-gray dark:text-button-disabled hover:bg-gray transition-all hover:text-white rounded-[8px] cursor-pointer"
@click="menuStore.navigateToItem()">
{{ $t("back_to_home") }}
</button>
</div>
<p class="text-button font-inter text-base leading-[150%] uppercase sm:text-[20px]">
Title: {{
product.reference }}
</p>
</div>
<div class="flex flex-col gap-5" v-html="product.description" />
<div
class="flex flex-col sm:flex-row sm:items-end gap-[25px] sm:justify-between border-t-2 border-block pt-[15px]">
<div class="space-y-4">
<div class="space-y-2">
<h4 class="text-accent-green-light font-inter text-2xl leading-[150%] font-bold">
{{ formattedPrice }}
</h4>
<p>{{ product.tax_name }}</p>
</div>
<p class="font-medium">
{{ component.front_section_lang[0].data.purchase_price }}<span
class="text-button">{{ formattedBuyOutPrice
}}</span>
</p>
</div>
<div class="mx-auto sm:mx-0 group flex cursor-pointer items-center justify-start gap-2 whitespace-nowrap" @click="productStore.incrementCartItem(product.id)">
<button
class="h-[40px] cursor-pointer min-w-40 rounded-[10px] px-[22px] transition-all sm:h-[50px] md:h-[65px] md:rounded-[15px] md:px-[42px] bg-button text-text-dark group-hover:bg-button-hover">
Add to cart
</button>
<div :disabled="!product.sale_active" class="
flex h-[40px] w-[40px] items-center justify-center rounded-[10px] p-2.5 transition-all sm:h-[50px] sm:w-[50px] md:h-[65px] md:w-[65px] md:rounded-[15px] bg-button text-text-dark group-hover:bg-button-hover
">
<i class="uil uil-shopping-cart text-[23px] sm:text-[33px]"></i>
</div>
</div>
</div>
</div>
<div class="flex flex-col gap-5">
<h1 class="h4-uppercase-bold-inter max-w-[450px]">
{{ component.front_section_lang[0].data.product_details }}
</h1>
<div class="flex flex-col gap-5 mx-[50px]">
<div v-for="feature in product.features" :key="feature.feature" class="flex gap-4 justify-between">
<span class="mx-4">{{ feature.feature }}:</span>
<span class="mx-4">
{{ feature.value }}
</span>
</div>
</div>
</div>
</div>
</div>
<div class="space-y-[55px]">
<h1 class="h4-uppercase-bold-inter max-w-[450px]">
{{ component.front_section_lang[0].data.recommendations }}
</h1>
<!-- products -->
<div class="flex flex-wrap justify-center gap-5 sm:gap-10">
<div v-for="(item, index) in product.recommendations" :key="index">
<nuxt-link :to="{
name: `id-slug___${$i18n.locale}`,
params: {
id: menuStore.getProductMenu()?.id,
slug: menuStore.getProductMenu()?.front_menu_lang[0]
.link_rewrite,
},
query: { prod_id: item?.id, name: item?.link_rewrite },
}" @click.stop.prevent="scrollToTop">
<div
class="w-[200px] sm:w-[260px] md:w-[290px] sm:py-5 sm:px-[15px] py-[15px] px-[10px] bg-block rounded-2xl flex flex-col items-center gap-5 sm:gap-7 h-full">
<img :src="`/api/public/file/${item.cover_picture_uuid}.webp`" alt="pics"
class="max-h-[150px] sm:max-h-[180px] md:max-h-[205px]">
<div class="flex flex-col justify-between h-full">
<div class="flex flex-col gap-[10px] sm:gap-[15px] w-full">
<h3 class="text-[13px] sm:text-base md:text-lg text-xl font-bold leading-[150%] text-bg-dark">
{{ item.name }}
</h3>
<p class="text-[10px] sm:text-[12px] text-sm text-bg-dark">
{{ item.tax_name }}
</p>
</div>
<div class="flex items-center justify-between">
<p class="text-accent-green-light text-bold-24">
{{ item.formatted_price }}
</p>
<button
class="w-9 h-9 md:w-12 md:h-12 rounded-xl bg-button cursor-pointer hover:bg-button-hover transition-all flex items-center justify-center"
@click="productStore.incrementCartItem(item.id)">
<i class="uil uil-shopping-cart text-[25px] md:text-[24px] text-bg-light" />
</button>
</div>
</div>
</div>
</nuxt-link>
</div>
</div>
</div>
</div>
</UiContainer>
</template>
<script setup lang="ts">
import type { GenericResponse, ProductItem } from '~/types'
defineProps<{
component: {
id: number
name: string
img: string[]
component_name: string
is_no_lang: boolean
page_name: string
front_section_lang: {
data: {
purchase_price: string
product_details: string
recommendations: string
}
id_front_section: number
id_lang: number
}[]
}
}>()
const menuStore = useMenuStore()
const { $session } = useNuxtApp()
const productStore = useProductStore()
const dropdownRef = ref()
const productBlockRef = ref()
function scrollToTop() {
productBlockRef.value?.scrollIntoView({ behavior: 'smooth' })
}
const formatPrice = (p: number) => {
const formatdecimal = new Intl.NumberFormat(
$session.cookieData.value.country.iso_code,
{
style: 'decimal',
maximumFractionDigits: $session.cookieData.value.currency.precision,
},
).format(p)
return $session.cookieData.value.currency.suffix
? $session.cookieData.value.currency.sign + ' ' + formatdecimal
: formatdecimal + ' ' + $session.cookieData.value.currency.sign
}
const focusOnImgLoad = () => {
dropdownRef.value.focus()
}
const formattedPrice = computed(() => {
return formatPrice(product.value.price)
})
const formattedBuyOutPrice = computed(() => {
return formatPrice(product.value.buyout_price)
})
const route = useRoute()
// if (!route.query?.prod_id) {
// throw createError({
// statusCode: 404,
// statusMessage: 'Not Found',
// fatal: true
// });
// }
const getProduct = async (): Promise<ProductItem> => {
if (route.query?.prod_id) {
const data = await useMyFetch<GenericResponse<ProductItem>>(
`/api/public/product/${route.query?.prod_id}`,
{},
)
// await new Promise(resolve => setTimeout(resolve, 200));
return data.data
}
return {} as ProductItem
}
const product = ref(await getProduct())
watch(
() => route.query?.prod_id,
async () => {
const data = await getProduct()
product.value = data
if (curImage.value != data.picture_uuids[0]) {
curImage.value = data.cover_picture_uuid
}
},
)
watch($session.cookieData, async () => {
const oldId = product.value.id
const data = await getProduct()
product.value = data
if (oldId != data.id) {
curImage.value = data.cover_picture_uuid
}
})
const showPopup = ref(false)
const curImage = ref(product.value.cover_picture_uuid)
watch(showPopup, (newValue) => {
if (newValue) {
document.body.style.overflow = 'hidden'
}
else {
document.body.style.overflow = 'unset'
}
})
const goLeft = () => {
if (!curImage.value) return
const index = product.value.picture_uuids.indexOf(curImage.value)
if (index > 0) {
curImage.value = product.value.picture_uuids[index - 1]
}
else {
curImage.value
= product.value.picture_uuids[product.value.picture_uuids.length - 1]
}
}
const goRight = () => {
if (!curImage.value) return
const index = product.value.picture_uuids.indexOf(curImage.value)
if (index < product.value.picture_uuids.length - 1) {
curImage.value = product.value.picture_uuids[index + 1]
}
else {
curImage.value = product.value.picture_uuids[0]
}
}
</script>
<style>
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease;
}
.v-enter-from,
.v-leave-to {
opacity: 0;
}
</style>