302 lines
11 KiB
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>
|