658 lines
17 KiB
Vue
658 lines
17 KiB
Vue
<script>
|
|
import { storeToRefs } from "pinia";
|
|
import { useMeta } from "quasar";
|
|
import { useForm } from "vee-validate";
|
|
import { defineComponent, onBeforeMount, reactive, ref, watch } from "vue";
|
|
import { useRoute } from "vue-router";
|
|
|
|
import IconArrowCircleFilledLeft from "components/icons/IconArrowCircleFilledLeft.vue";
|
|
import IconArrowCircleFilledRight from "components/icons/IconArrowCircleFilledRight.vue";
|
|
import IconPencilGreen from "components/icons/IconPencilGreen.vue";
|
|
import IconEmail from "components/icons/social/IconEmail.vue";
|
|
import IconLinkedin from "components/icons/social/IconLinkedin.vue";
|
|
import IconShare from "components/icons/social/IconShare.vue";
|
|
import IconTwitter from "components/icons/social/IconTwitter.vue";
|
|
import IconWhatsapp from "components/icons/social/IconWhatsapp.vue";
|
|
import ProductCarousel from "components/quasar-components/carousel/ProductCarousel.vue";
|
|
import DudasSection from "components/sections/DudasSection.vue";
|
|
import Card from "components/ui/Card.vue";
|
|
import Container from "components/ui/Container.vue";
|
|
import Modal from "components/ui/Modal.vue";
|
|
|
|
import { dedicationSchema } from "src/utils/zod/schemas";
|
|
import { useCartStore } from "stores/cart";
|
|
import { useModalStore } from "stores/modalStore";
|
|
|
|
export default defineComponent({
|
|
name: "ProductPage",
|
|
components: {
|
|
IconPencilGreen,
|
|
IconWhatsapp,
|
|
IconLinkedin,
|
|
IconTwitter,
|
|
IconShare,
|
|
IconEmail,
|
|
DudasSection,
|
|
Container,
|
|
Card,
|
|
IconArrowCircleFilledRight,
|
|
IconArrowCircleFilledLeft,
|
|
ProductCarousel,
|
|
Modal,
|
|
},
|
|
setup() {
|
|
const route = useRoute();
|
|
|
|
const modalStore = useModalStore();
|
|
const { openModal } = modalStore;
|
|
|
|
const cartStore = useCartStore();
|
|
const { getProduct, getProducts, products } = cartStore;
|
|
const { prevProduct, currentProduct, nextProduct, addCartLoadingBtn } =
|
|
storeToRefs(cartStore);
|
|
|
|
onBeforeMount(() => {
|
|
getProduct(route.params.id);
|
|
getProducts();
|
|
});
|
|
|
|
watch(currentProduct.value, (newValue) => {
|
|
useMeta(() => {
|
|
return {
|
|
title: `${newValue.value?.title}`,
|
|
titleTemplate: (title) => `${title} - FloraNet`,
|
|
meta: {
|
|
description: {
|
|
name: "description",
|
|
content: `${newValue.value?.description}`,
|
|
},
|
|
keywords: { name: "keywords", content: `${newValue.value?.title}` },
|
|
equiv: {
|
|
"http-equiv": "Content-Type",
|
|
content: "text/html; charset=UTF-8",
|
|
},
|
|
ogTitle: {
|
|
property: "og:title",
|
|
template(ogTitle) {
|
|
return `${ogTitle} - FloraNet`;
|
|
},
|
|
},
|
|
noscript: {
|
|
default:
|
|
"This is content for browsers with no JS (or disabled JS)",
|
|
},
|
|
},
|
|
};
|
|
});
|
|
});
|
|
|
|
watch(
|
|
() => route.params.id,
|
|
(newId) => {
|
|
getProduct(newId);
|
|
}
|
|
);
|
|
|
|
const currentData = reactive({});
|
|
watch(currentProduct.value, (newData) => {
|
|
if (newData.value) {
|
|
const { id, ...newDataWhithoutId } = newData.value;
|
|
currentData.value = {
|
|
...newDataWhithoutId,
|
|
productId: +route.params.id,
|
|
};
|
|
}
|
|
});
|
|
|
|
const category = reactive({
|
|
1: "Planta",
|
|
2: "Ramos",
|
|
});
|
|
|
|
const { handleSubmit, defineField, handleReset } = useForm({
|
|
validationSchema: dedicationSchema,
|
|
});
|
|
const [dedication, dedicationAttrs] = defineField("dedication");
|
|
/* const onSubmit = handleSubmit(() => {
|
|
openModal({ modal: "availability" });
|
|
// addToCart(currentData.value, dedication);
|
|
// handleReset();
|
|
}); */
|
|
|
|
return {
|
|
slide: ref(1),
|
|
fullscreen: ref(false),
|
|
dedication,
|
|
dedicationAttrs,
|
|
products,
|
|
addCartLoadingBtn,
|
|
prevProduct,
|
|
currentProduct,
|
|
nextProduct,
|
|
currentData,
|
|
category,
|
|
openModal,
|
|
};
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<q-page>
|
|
<Container class="product-container" tag="section">
|
|
<ProductCarousel>
|
|
<template v-for="(img, i) in currentProduct?.images" :key="i">
|
|
<q-carousel-slide
|
|
v-if="img"
|
|
:img-src="img"
|
|
class="product-gallery-item"
|
|
:name="i + 1"
|
|
/>
|
|
|
|
<q-carousel-slide
|
|
v-else
|
|
:img-src="'../assets/empty-img.jpg'"
|
|
class="product-gallery-item"
|
|
:name="1"
|
|
/>
|
|
</template>
|
|
</ProductCarousel>
|
|
|
|
<div class="product-content">
|
|
<header class="product-content-header">
|
|
<h3 class="product-content-title subtitle">
|
|
{{ currentProduct?.name }}
|
|
<q-skeleton type="rect" v-if="!currentProduct?.name" />
|
|
</h3>
|
|
|
|
<div class="product-header-block">
|
|
<p class="product-content-paragraph">
|
|
SKU:
|
|
<span class="green-text" style="display: inline-flex">
|
|
{{ currentProduct?.slug }}
|
|
<q-skeleton
|
|
width="100px"
|
|
type="text"
|
|
v-if="!currentProduct?.slug"
|
|
/>
|
|
</span>
|
|
</p>
|
|
|
|
<p class="product-content-paragraph">
|
|
Categoría:
|
|
<span class="green-text">
|
|
{{ category[currentProduct?.category] }}
|
|
<q-skeleton
|
|
type="text"
|
|
width="50px"
|
|
v-if="!currentProduct?.category"
|
|
/>
|
|
</span>
|
|
</p>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="product-content-body">
|
|
<div class="product-content-paragraphs">
|
|
<p class="product-price green-text">
|
|
{{ currentProduct?.price }}
|
|
<q-skeleton
|
|
type="text"
|
|
height="90px"
|
|
width="80px"
|
|
v-if="!currentProduct?.price"
|
|
/>
|
|
</p>
|
|
<p class="product-delivery green-text">Envío Gratuito</p>
|
|
<p class="product-description">
|
|
{{ currentProduct?.description }}
|
|
<q-skeleton type="text" v-if="!currentProduct?.description" />
|
|
<q-skeleton type="text" v-if="!currentProduct?.description" />
|
|
</p>
|
|
</div>
|
|
|
|
<div class="product-form">
|
|
<div class="product-dedication">
|
|
<header class="product-dedication-header">
|
|
<IconPencilGreen />
|
|
<label
|
|
class="product-dedication-paragraph"
|
|
for="dedication-btn"
|
|
>
|
|
¿Deseas añadir una dedicatoria?
|
|
</label>
|
|
</header>
|
|
|
|
<div class="product-dedication-body">
|
|
<q-input
|
|
v-model="dedication"
|
|
v-bind="dedicationAttrs"
|
|
bg-color="white"
|
|
color="primary"
|
|
outlined
|
|
id="dedication-btn"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<q-btn
|
|
:loading="addCartLoadingBtn"
|
|
color="primary"
|
|
class="btn sm-btn"
|
|
label="AÑADIR AL CARRITO"
|
|
@click="openModal({ modal: 'availability' })"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="product-content-footer">
|
|
<p class="product-share-paragraph">Conmparte con tus amigos</p>
|
|
|
|
<div class="product-share-content">
|
|
<a href="#" class="product-share-item"><IconShare /></a>
|
|
<a href="#" class="product-share-item"><IconLinkedin /></a>
|
|
<a href="#" class="product-share-item"><IconTwitter /></a>
|
|
<a href="#" class="product-share-item"><IconEmail /></a>
|
|
<a href="#" class="product-share-item"><IconWhatsapp /></a>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<div
|
|
class="product-pag-block"
|
|
:style="+$route.params.id === 1 && 'justify-content: flex-end;'"
|
|
>
|
|
<q-btn
|
|
color="white"
|
|
class="btn outlined rounded sm-btn product-pag-item product-prev-btn"
|
|
:to="`${+$route.params.id - 1}`"
|
|
v-if="+$route.params.id > 1"
|
|
@click="currentProduct.value = undefined"
|
|
>
|
|
<IconArrowCircleFilledLeft />
|
|
|
|
<div class="btn-pag-paragraphs">
|
|
<p class="btn-paragraph-top green-text">Produto anterior</p>
|
|
<p
|
|
class="product-paragraph-bottom"
|
|
:title="prevProduct.value?.title"
|
|
>
|
|
{{ prevProduct.value?.title }}
|
|
</p>
|
|
</div>
|
|
</q-btn>
|
|
|
|
<q-btn
|
|
color="white"
|
|
class="btn outlined rounded sm-btn product-pag-item product-next-btn"
|
|
:to="`${+$route.params.id + 1}`"
|
|
v-if="nextProduct.value?.id"
|
|
@click="currentProduct.value = undefined"
|
|
>
|
|
<div class="btn-pag-paragraphs">
|
|
<p class="btn-paragraph-top green-text">Siguiente producto</p>
|
|
<p
|
|
class="product-paragraph-bottom"
|
|
:title="nextProduct.value?.title"
|
|
>
|
|
{{ nextProduct.value?.title }}
|
|
</p>
|
|
</div>
|
|
|
|
<IconArrowCircleFilledRight />
|
|
</q-btn>
|
|
</div>
|
|
</Container>
|
|
|
|
<DudasSection isWhite />
|
|
<Modal modalItem="isOpenAvailability" typeModal="product" />
|
|
|
|
<Container class="like-another-container gray-bg" tag="section">
|
|
<header class="like-another-header">
|
|
<h3 class="like-another-title subtitle">
|
|
Quizás también te gusten estos ramos
|
|
</h3>
|
|
|
|
<p class="like-another-paragraph">
|
|
Descripción SEO: Lorem ipsum dolor sit amet, consectetur adipiscing
|
|
elit, sed do eiusmod tempor incididunt ut labore et dolore magna
|
|
aliqua.
|
|
</p>
|
|
</header>
|
|
|
|
<Container cardContainer class="no-padding">
|
|
<template
|
|
v-for="({ images, discount, isNew, name, price, slug }, i) in products
|
|
.data.products"
|
|
>
|
|
<Card
|
|
v-if="i < 4"
|
|
:price="price"
|
|
:title="name"
|
|
:discount="discount"
|
|
:imgSrc="images[0]"
|
|
:isNew="isNew"
|
|
:key="slug"
|
|
:id="slug"
|
|
/>
|
|
</template>
|
|
</Container>
|
|
</Container>
|
|
</q-page>
|
|
</template>
|
|
|
|
<style lang="scss">
|
|
.product-container {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 136px;
|
|
margin-bottom: 100px;
|
|
& .product-gallery {
|
|
/* flex: 1 0 min(100%, 562px); */
|
|
height: 622px;
|
|
flex: 1 0 min(100%, 500px);
|
|
overflow: initial;
|
|
& .q-carousel__slides-container .q-panel {
|
|
border-radius: 15px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
& .q-carousel__navigation {
|
|
bottom: -127px;
|
|
left: 0;
|
|
right: 0;
|
|
& .q-carousel__navigation-inner {
|
|
justify-content: flex-start;
|
|
gap: 19px;
|
|
& .q-carousel__thumbnail {
|
|
width: 97px;
|
|
height: 107px;
|
|
object-fit: cover;
|
|
border-radius: 10px;
|
|
opacity: 1;
|
|
flex: 0 0 97px;
|
|
position: relative;
|
|
&.q-carousel__thumbnail--active {
|
|
border: 1px solid $primary;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@media only screen and (max-width: $med-md) {
|
|
height: 396px;
|
|
& .q-carousel__navigation {
|
|
bottom: -83px;
|
|
& .q-carousel__navigation-inner {
|
|
gap: 12px;
|
|
& .q-carousel__thumbnail {
|
|
width: 61px;
|
|
height: 68px;
|
|
flex: 0 0 61px;
|
|
&::after {
|
|
content: "";
|
|
}
|
|
|
|
&.q-carousel__thumbnail--active {
|
|
border: 1px solid $primary;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
& .product-content {
|
|
flex: 1 0 min(100%, 466px);
|
|
margin-bottom: 27px;
|
|
& .product-content-header {
|
|
margin-bottom: 20px;
|
|
& .product-content-title {
|
|
margin-bottom: 14px;
|
|
opacity: 0.9;
|
|
}
|
|
|
|
& .product-header-block {
|
|
& .product-content-paragraph {
|
|
font-size: $font-14;
|
|
line-height: 21px;
|
|
letter-spacing: 0.28px;
|
|
color: $text-gray;
|
|
& .green-text {
|
|
display: inline-flex;
|
|
}
|
|
}
|
|
}
|
|
|
|
@media only screen and (max-width: $med-md) {
|
|
margin-bottom: 30px;
|
|
}
|
|
}
|
|
|
|
& .product-content-body {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin-bottom: 36px;
|
|
gap: 60px;
|
|
& .product-content-paragraphs {
|
|
& .product-price {
|
|
font-weight: 500;
|
|
letter-spacing: 0.8px;
|
|
line-height: 21px;
|
|
font-family: $font-lora;
|
|
font-size: $font-40;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
& .product-delivery {
|
|
font-size: $font-14;
|
|
line-height: 24px;
|
|
letter-spacing: 0.28px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
& .product-description {
|
|
font-size: $font-14;
|
|
line-height: 21px;
|
|
letter-spacing: 0.28px;
|
|
color: $text-gray;
|
|
}
|
|
}
|
|
|
|
& .product-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 22px;
|
|
& .product-dedication {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 22px;
|
|
background-color: $secondary-10;
|
|
border-radius: 10px;
|
|
padding: 18px 31px 28px;
|
|
& .product-dedication-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
|
|
& .product-dedication-body {
|
|
border: 1px solid $secondary-40;
|
|
}
|
|
}
|
|
}
|
|
|
|
@media only screen and (max-width: $med-md) {
|
|
margin-bottom: 30px;
|
|
gap: 24px;
|
|
& .product-content-paragraphs {
|
|
& .product-price {
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
& .product-delivery {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
& .product-form .product-dedication {
|
|
padding: 18px 26px 28px;
|
|
}
|
|
}
|
|
}
|
|
|
|
& .product-content-footer {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
& .product-share-paragraph {
|
|
color: $text-muted-one;
|
|
font-size: $font-14;
|
|
line-height: 21px;
|
|
letter-spacing: 0.28px;
|
|
}
|
|
|
|
& .product-share-content {
|
|
display: flex;
|
|
gap: 6px;
|
|
}
|
|
|
|
@media only screen and (max-width: $med-md) {
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
|
|
& .product-pag-block {
|
|
flex: 1 0 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 84px;
|
|
& .product-pag-item {
|
|
margin-top: 76px;
|
|
&::before {
|
|
content: "";
|
|
display: none;
|
|
}
|
|
|
|
& .q-focus-helper {
|
|
display: none;
|
|
}
|
|
|
|
& .q-btn__content {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
text-align: start;
|
|
gap: 14px;
|
|
flex-wrap: nowrap;
|
|
}
|
|
|
|
& .btn-pag-paragraphs {
|
|
& .product-paragraph-bottom {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
width: 15ch;
|
|
}
|
|
}
|
|
|
|
&.product-prev-btn {
|
|
padding: 10px 55px 10px 20px;
|
|
}
|
|
|
|
&.product-next-btn {
|
|
padding: 10px 20px 10px 55px;
|
|
& .q-btn__content {
|
|
text-align: end;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@media only screen and (max-width: $med-md) {
|
|
display: block;
|
|
gap: 130px;
|
|
& .product-gallery {
|
|
margin-bottom: 130px;
|
|
}
|
|
|
|
& .product-pag-block {
|
|
flex: initial;
|
|
& .product-pag-item {
|
|
margin: initial;
|
|
justify-content: center;
|
|
align-items: center;
|
|
padding: 20px 29px 14px 20px !important;
|
|
|
|
& .q-btn__content {
|
|
gap: 12px;
|
|
|
|
& > svg {
|
|
flex: 0 0 23px;
|
|
}
|
|
}
|
|
|
|
&.product-prev-btn,
|
|
&.product-next-btn {
|
|
padding: 17px 29px 14px 20px;
|
|
}
|
|
|
|
& .btn-pag-paragraphs {
|
|
font-size: $font-14;
|
|
& p {
|
|
font-size: inherit;
|
|
}
|
|
|
|
& .product-paragraph-bottom {
|
|
display: none;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.like-another-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 82px;
|
|
padding-block: 129px 126px;
|
|
text-align: center;
|
|
|
|
& .like-another-header {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 25px;
|
|
|
|
& .like-another-title {
|
|
font-weight: 500;
|
|
font-size: $font-35;
|
|
line-height: 30px;
|
|
}
|
|
|
|
& .like-another-paragraph {
|
|
color: $text-default;
|
|
line-height: 26px;
|
|
}
|
|
}
|
|
|
|
@media only screen and (max-width: $med-md) {
|
|
gap: 61px;
|
|
padding-block: 62px 108px;
|
|
|
|
& .like-another-header {
|
|
& .like-another-title {
|
|
font-size: $font-24;
|
|
}
|
|
|
|
& .like-another-paragraph {
|
|
font-size: $font-14;
|
|
line-height: 18px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|