product page
This commit is contained in:
445
components/ProductCardDetails.vue
Normal file
445
components/ProductCardDetails.vue
Normal file
@@ -0,0 +1,445 @@
|
||||
<template>
|
||||
<div class="productcard_container">
|
||||
<div class="row productcard_container-basic">
|
||||
<div class="image_container col-md-5">
|
||||
<img :src="getProductImg(product)" class="image" alt="" />
|
||||
</div>
|
||||
<div class="info_container col-md-5">
|
||||
<h2 variant="primary">
|
||||
{{ product?.name }}
|
||||
</h2>
|
||||
<span v-if="product?.price" class="price">{{
|
||||
`${product?.price}€`
|
||||
}}</span>
|
||||
<span v-else class="price">Precio a consultar</span>
|
||||
<span v-if="Number(product?.shipping_cost)"
|
||||
>| {{ `Gastos de envío ${product?.shipping_cost}€` }}</span
|
||||
>
|
||||
<span v-else>| Sin gastos de envío</span>
|
||||
|
||||
<span v-if="product?.stock">| {{ `Stock ${product?.stock}€` }} </span>
|
||||
<p
|
||||
v-if="product?.description"
|
||||
class="description"
|
||||
v-html="sanitize(product?.description)"
|
||||
></p>
|
||||
<span v-if="product?.shipping_terms">{{ product?.shipping_terms }}</span>
|
||||
<BCollapse visible accordion="my-accordion">
|
||||
<div class="tags_container">
|
||||
<NuxtLink
|
||||
v-for="n in product?.tags"
|
||||
:key="n"
|
||||
:to="tagRoute(n)"
|
||||
class="tag_container"
|
||||
>
|
||||
<img
|
||||
class="tag_img"
|
||||
alt="tag image"
|
||||
src="@/assets/img/latienda-tag.svg"
|
||||
/>
|
||||
<span>{{ n }}</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class="smlogos_container">
|
||||
<p class="share-text">Comparte:</p>
|
||||
<div class="smlogo_container">
|
||||
<a @click="shareFacebook">
|
||||
<img
|
||||
alt="facebook logo"
|
||||
class="smlogo_img"
|
||||
src="@/assets/img/latienda-smlogo-facebook.svg"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<a :href="shareTwitter()">
|
||||
<div class="smlogo_container">
|
||||
<img
|
||||
alt="twitter logo"
|
||||
class="smlogo_img"
|
||||
src="@/assets/img/latienda-smlogo-twitter.svg"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
:href="shareWhatsApp()"
|
||||
data-action="share/whatsapp/share"
|
||||
target="_blank"
|
||||
title="latiendacoop"
|
||||
>
|
||||
<div class="smlogo_container">
|
||||
<img
|
||||
alt="whatsapp logo"
|
||||
class="smlogo_img"
|
||||
src="@/assets/img/latienda-smlogo-whatsapp.svg"
|
||||
/>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="coop_info">
|
||||
<NuxtLink :to="`/c/${company?.id}`">
|
||||
<h2>{{ company?.company_name }}</h2>
|
||||
</NuxtLink>
|
||||
<p class="description">{{ company?.description }}</p>
|
||||
<a href="#">{{ company?.web_link }}</a>
|
||||
</div>
|
||||
</BCollapse>
|
||||
</div>
|
||||
<div class="col-md-2 button_container-detail" align="center">
|
||||
<button class="button_buy-simple" @click="buyIntent">
|
||||
<img
|
||||
class="button_cart_img"
|
||||
alt="cart"
|
||||
src="@/assets/img/latienda-carrito.svg"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
v-if="product?.discount && product?.discount > 0"
|
||||
class="discount-tag"
|
||||
>
|
||||
{{ `Descuento ${product?.discount}%` }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="related_products">
|
||||
<h2 v-if="related">Productos relacionados</h2>
|
||||
<h2 v-else>Otros productos</h2>
|
||||
<ProductsRelated :related-products="relatedProducts" />
|
||||
</div>
|
||||
<ProductModal v-if="modal" :product="product" @close-modal="closeModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DOMPurify from 'dompurify'
|
||||
import socialShare from '~/utils/socialShare'
|
||||
export default {
|
||||
props: {
|
||||
product: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
company: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
relatedProducts: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
related: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modal: true,
|
||||
productUrl: null,
|
||||
geolocation: null,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.productUrl = window.location.href
|
||||
this.sendLog('view')
|
||||
},
|
||||
|
||||
methods: {
|
||||
getProductImg(product) {
|
||||
if (product && product.image)
|
||||
return product.image
|
||||
return `@/assets/img/latienda-product-default.svg`
|
||||
},
|
||||
tagRoute(tag) {
|
||||
return `/busqueda?tags=${tag}`
|
||||
},
|
||||
openUrl(url) {
|
||||
window.open(url)
|
||||
},
|
||||
closeModal(value) {
|
||||
this.modal = false
|
||||
if (value === 200) {
|
||||
this.$bvToast.toast(`Email enviado correctamente`, {
|
||||
title: 'latienda.coop',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: true,
|
||||
})
|
||||
} else if (value) {
|
||||
this.$bvToast.toast(`Se ha producido un error en el envío`, {
|
||||
title: 'latienda.coop',
|
||||
autoHideDelay: 5000,
|
||||
appendToast: true,
|
||||
variant: 'danger',
|
||||
})
|
||||
}
|
||||
},
|
||||
buyIntent() {
|
||||
this.sendLog('shop')
|
||||
return this.product.url
|
||||
? this.openUrl(this.product.url)
|
||||
: (this.modal = true)
|
||||
},
|
||||
|
||||
async getPosition() {
|
||||
const geoLocation = () =>
|
||||
new Promise((resolve, reject) =>
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(posData) => {
|
||||
resolve(posData)
|
||||
},
|
||||
(error) => {
|
||||
reject(error)
|
||||
}
|
||||
)
|
||||
)
|
||||
try {
|
||||
const position = await geoLocation()
|
||||
const geo = {
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
}
|
||||
return geo
|
||||
} catch {
|
||||
const geo = null
|
||||
return geo
|
||||
}
|
||||
},
|
||||
|
||||
async sendLog(action) {
|
||||
const geo = await this.getPosition()
|
||||
try {
|
||||
const { data } = await this.$axios.get(
|
||||
'https://api.ipify.org?format=json'
|
||||
)
|
||||
const ip = data.ip
|
||||
const object = {
|
||||
action: action,
|
||||
action_object: {
|
||||
model: 'product',
|
||||
id: this.product.id,
|
||||
},
|
||||
}
|
||||
if (ip) object.ip = ip
|
||||
if (geo) object.geo = geo
|
||||
await this.$api.post(`/stats/me/`, object)
|
||||
} catch {}
|
||||
},
|
||||
|
||||
shareFacebook() {
|
||||
const url = socialShare.facebook(this.productUrl)
|
||||
window.open(url, '_blank')
|
||||
},
|
||||
shareTwitter() {
|
||||
return socialShare.twitter(this.productUrl)
|
||||
},
|
||||
shareWhatsApp() {
|
||||
return socialShare.whatsApp(this.productUrl)
|
||||
},
|
||||
|
||||
sanitize(dirtyHtml) {
|
||||
return DOMPurify.sanitize(dirtyHtml, {
|
||||
ALLOWED_TAGS: [
|
||||
'b',
|
||||
'i',
|
||||
'em',
|
||||
'strong',
|
||||
'a',
|
||||
'p',
|
||||
'br',
|
||||
'ul',
|
||||
'li',
|
||||
'ol',
|
||||
'span',
|
||||
'h1',
|
||||
'h2',
|
||||
'h3',
|
||||
],
|
||||
ALLOWED_ATTR: ['href', 'target', 'rel', 'class'],
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.productcard_container {
|
||||
border: 3px solid $color-grey-nav;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.productcard_container-basic {
|
||||
padding: 25px 20px;
|
||||
}
|
||||
|
||||
.image_container {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.info_container {
|
||||
h2 {
|
||||
outline: none;
|
||||
font-weight: medium;
|
||||
color: $color-navy;
|
||||
font-size: $m;
|
||||
}
|
||||
.price {
|
||||
font-weight: bold;
|
||||
color: $color-navy;
|
||||
font-size: $m;
|
||||
}
|
||||
span {
|
||||
color: $color-greytext;
|
||||
}
|
||||
.description {
|
||||
margin-top: 8px;
|
||||
font-family: $font-secondary;
|
||||
font-size: $s;
|
||||
color: $color-greytext;
|
||||
}
|
||||
}
|
||||
|
||||
.button_container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: $m;
|
||||
|
||||
span {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
font-weight: medium;
|
||||
font-size: $m;
|
||||
color: $color-navy;
|
||||
}
|
||||
|
||||
.button_buy {
|
||||
border: 3px solid $color-orange;
|
||||
border-radius: 5px;
|
||||
background-color: $color-light;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 16px rgba(99, 99, 99, 0.2);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
span {
|
||||
color: $color-orange;
|
||||
font-weight: $bold;
|
||||
display: inline-block;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button_buy-simple {
|
||||
border: 3px solid $color-orange;
|
||||
border-radius: 8px;
|
||||
background-color: $color-light;
|
||||
padding: 10px 20px;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 0 4px 16px rgba(99, 99, 99, 0.2);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.button_cart_img {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tag_container {
|
||||
margin: 25px 6px 0 0;
|
||||
border: 2px solid $color-greylayout;
|
||||
border-radius: 5px;
|
||||
padding: 6px 10px;
|
||||
display: inline-block;
|
||||
font-family: $font-secondary;
|
||||
font-size: $xs;
|
||||
color: $color-greytext;
|
||||
|
||||
.tag_img {
|
||||
width: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.share-text {
|
||||
color: $color-navy;
|
||||
font-size: $m;
|
||||
margin-top: 2rem;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.smlogo_container {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
|
||||
.smlogo_img {
|
||||
cursor: pointer;
|
||||
width: 40px;
|
||||
fill: $color-greylayout;
|
||||
}
|
||||
img:hover {
|
||||
transform: scale(1.1);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.button_cart_img {
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.coop_info {
|
||||
margin-top: 25px;
|
||||
p,
|
||||
a {
|
||||
margin-top: 8px;
|
||||
font-family: $font-secondary;
|
||||
font-size: $s;
|
||||
color: $color-greytext;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.related_products {
|
||||
background-color: $color-lighter-green;
|
||||
text-align: center;
|
||||
padding: 0 15px;
|
||||
|
||||
h2 {
|
||||
margin: 35px auto;
|
||||
font-weight: medium;
|
||||
color: $color-navy;
|
||||
font-size: $m;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.discount-tag {
|
||||
margin: 5px;
|
||||
border: none;
|
||||
background-color: $color-green;
|
||||
border-radius: 5px;
|
||||
padding: 6px 10px;
|
||||
display: inline-block;
|
||||
font-family: $font-secondary;
|
||||
font-size: $xs;
|
||||
color: $color-greytext;
|
||||
}
|
||||
|
||||
.content > h2,
|
||||
h3,
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
310
components/ProductModal.vue
Normal file
310
components/ProductModal.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<transition name="modal">
|
||||
<div v-if="product" class="mask">
|
||||
<div class="wrapper" @click.self="handleEmit">
|
||||
<div class="container">
|
||||
<div class="element header">
|
||||
<h2>Interesado en comprar el producto</h2>
|
||||
<img src="@/assets/img/latienda-lineapuntos-2.svg" alt="" />
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<form @submit.prevent="sendForm">
|
||||
<div>
|
||||
<BFormGroup class="element">
|
||||
<BFormInput
|
||||
v-model="form.email"
|
||||
required
|
||||
class="input"
|
||||
size="lg"
|
||||
placeholder="Email"
|
||||
/>
|
||||
</BFormGroup>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BFormGroup class="element">
|
||||
<BFormInput
|
||||
v-model="form.telephone"
|
||||
required
|
||||
class="input"
|
||||
size="lg"
|
||||
placeholder="Teléfono"
|
||||
/>
|
||||
</BFormGroup>
|
||||
</div>
|
||||
<div v-if="!isAuthenticated" class="element">
|
||||
<BButton
|
||||
class="input"
|
||||
size="lg"
|
||||
variant="outline-primary w-100"
|
||||
@click="redirectToLogin"
|
||||
>Login</BButton
|
||||
>
|
||||
</div>
|
||||
<div class="element coop">
|
||||
<!-- <BCard
|
||||
img-src="https://placekitten.com/1000/300"
|
||||
img-alt="Card image"
|
||||
img-left
|
||||
>
|
||||
<BCardText>
|
||||
<h3>Cooperativa</h3>
|
||||
<p>Dirección</p>
|
||||
</BCardText>
|
||||
</BCard> -->
|
||||
<div class="content">
|
||||
<img :src="getImgUrl(product.company.logo)" alt="" />
|
||||
<div class="text">
|
||||
<h3>{{ product?.company?.company_name }}</h3>
|
||||
<p>{{ product?.company?.address }}</p>
|
||||
<br />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="element coop">
|
||||
<!-- <BCard
|
||||
img-src="https://placekitten.com/1000/300"
|
||||
img-alt="Card image"
|
||||
img-left
|
||||
>
|
||||
<BCardText>
|
||||
<h3>Cooperativa</h3>
|
||||
<p>Dirección</p>
|
||||
</BCardText>
|
||||
</BCard> -->
|
||||
<div class="content">
|
||||
<img :src="getImgUrl(product.image)" alt="" />
|
||||
<div class="text">
|
||||
<h3>{{ product?.name }}</h3>
|
||||
<p>{{ product?.price }} €</p>
|
||||
<p class="text-muted">
|
||||
{{ product?.shipping_cost || 'Sin gastos de envío' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="element">
|
||||
<BFormTextarea
|
||||
id="textarea-no-resize"
|
||||
v-model="form.comment"
|
||||
class="input"
|
||||
placeholder="Comentarios"
|
||||
rows="3"
|
||||
no-resize
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<BButton type="submit" class="enviar-button">
|
||||
<v-progress-circular
|
||||
v-if="loading"
|
||||
:size="15"
|
||||
:width="2"
|
||||
indeterminate
|
||||
/>
|
||||
<span v-else>Enviar</span>
|
||||
</BButton>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
export default {
|
||||
props: {
|
||||
product: { type: Object, default: () => ({}) },
|
||||
},
|
||||
emits: ['closeModal'],
|
||||
setup() {
|
||||
const authStore = useAuthStore()
|
||||
return {
|
||||
authStore
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isAuthenticated: true,
|
||||
form: {
|
||||
email: undefined,
|
||||
telephone: undefined,
|
||||
company: undefined,
|
||||
product: undefined,
|
||||
comment: '',
|
||||
},
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.isAuthenticated = this.authStore.isAuthenticated
|
||||
if (this.isAuthenticated) {
|
||||
const email = this.authStore.email
|
||||
this.form.email = email
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
async sendForm() {
|
||||
this.form.company = this.product.company.id
|
||||
this.form.product = this.product.id
|
||||
this.loading = true
|
||||
|
||||
let response
|
||||
let status
|
||||
const config = useRuntimeConfig()
|
||||
try { //TODO: review if its working
|
||||
response = await $fetch(`/purchase_email/`, {
|
||||
baseURL: config.public.baseURL,
|
||||
method: 'POST',
|
||||
body: { ...this.form }
|
||||
})
|
||||
status = response.status
|
||||
} catch {
|
||||
status = 500
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
this.$emit('closeModal', status)
|
||||
},
|
||||
getImgUrl(image) {
|
||||
if (image) return image
|
||||
return `@/assets/img/latienda-product-default.svg`
|
||||
},
|
||||
redirectToLogin() {
|
||||
this.$router.push({
|
||||
name: 'login',
|
||||
})
|
||||
},
|
||||
handleEmit() {
|
||||
this.$emit('closeModal')
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.mask {
|
||||
position: fixed;
|
||||
z-index: 9998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: table;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
display: table-cell;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 500px;
|
||||
margin: 0px auto;
|
||||
padding: 20px 40px;
|
||||
background-color: #fff;
|
||||
border-radius: 7px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.element {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.header > h2 {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 500;
|
||||
color: #374493;
|
||||
}
|
||||
|
||||
.header > img {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.input {
|
||||
font-size: 0.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.input-group-text {
|
||||
width: 48px;
|
||||
border-right: none;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.coop {
|
||||
border: 1px lightgray solid;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.coop > .content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
margin: 5px 10px;
|
||||
}
|
||||
.coop > .content > img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
object-fit: cover;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.coop > .content > .text > h3,
|
||||
p {
|
||||
font-size: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.enviar-button {
|
||||
width: 100%;
|
||||
background: #fd6871;
|
||||
border-color: #fd6871;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="modal" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
*
|
||||
* You can easily play with the modal transition by editing
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.modal-enter {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-enter .modal-container,
|
||||
.modal-leave-active .modal-container {
|
||||
-webkit-transform: scale(1.1);
|
||||
transform: scale(0.8);
|
||||
}
|
||||
</style>
|
||||
86
components/ProductsRelated.vue
Normal file
86
components/ProductsRelated.vue
Normal file
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<div class="row related_products-cards">
|
||||
<NuxtLink
|
||||
v-for="(product, key) in relatedProducts"
|
||||
:key="`related-${key}`"
|
||||
:to="`/productos/${product.id}`"
|
||||
class="col mx-2 related_product-card"
|
||||
>
|
||||
<img
|
||||
v-if="product.image"
|
||||
:src="product.image"
|
||||
alt=""
|
||||
class="related_product-image"
|
||||
/>
|
||||
<img
|
||||
v-else
|
||||
class="image-default"
|
||||
src="@/assets/img/latienda-product-default.svg"
|
||||
alt=""
|
||||
/>
|
||||
<h3>{{ product.name }}</h3>
|
||||
<span v-if="product.price" class="price">{{ `${product.price}€` }}</span>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
relatedProducts: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.related_products {
|
||||
background-color: $color-lighter-green;
|
||||
text-align: center;
|
||||
padding: 0 15px;
|
||||
|
||||
img {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.image-default {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.star {
|
||||
width: 12px;
|
||||
margin-right: 1px;
|
||||
}
|
||||
|
||||
.related_product-card {
|
||||
border: 3px solid $color-grey-nav;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 35px;
|
||||
|
||||
.related_product-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: $regular;
|
||||
color: $color-navy;
|
||||
font-size: $m;
|
||||
display: inline-block;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
span {
|
||||
font-weight: $regular;
|
||||
color: $color-navy;
|
||||
font-size: $m;
|
||||
display: block;
|
||||
font-weight: bolder;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -12,6 +12,7 @@
|
||||
"@nuxt/eslint": "^1.8.0",
|
||||
"@nuxtjs/sitemap": "^7.4.3",
|
||||
"@pinia/nuxt": "^0.11.2",
|
||||
"dompurify": "^3.2.6",
|
||||
"eslint": "^9.32.0",
|
||||
"nuxt": "^3.17.7",
|
||||
"pinia": "^3.0.3",
|
||||
@@ -4321,6 +4322,13 @@
|
||||
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/trusted-types": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||
@@ -7027,6 +7035,15 @@
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
|
||||
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
|
||||
"license": "(MPL-2.0 OR Apache-2.0)",
|
||||
"optionalDependencies": {
|
||||
"@types/trusted-types": "^2.0.7"
|
||||
}
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"@nuxt/eslint": "^1.8.0",
|
||||
"@nuxtjs/sitemap": "^7.4.3",
|
||||
"@pinia/nuxt": "^0.11.2",
|
||||
"dompurify": "^3.2.6",
|
||||
"eslint": "^9.32.0",
|
||||
"nuxt": "^3.17.7",
|
||||
"pinia": "^3.0.3",
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
<SubmitButton text="Actualizar" image-url="" />
|
||||
</div>
|
||||
</form>
|
||||
{{ form }}
|
||||
</BCol>
|
||||
</BRow>
|
||||
</BContainer>
|
||||
|
||||
@@ -1,15 +1,92 @@
|
||||
<template>
|
||||
<div>
|
||||
Producto 1
|
||||
<div class="container">
|
||||
<h1 class="title">Detalles del producto</h1>
|
||||
<ProductCardDetails
|
||||
:product="product"
|
||||
:related="related"
|
||||
:related-products="relatedProducts"
|
||||
:company="company"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
import { useRoute } from 'vue-router'
|
||||
export default {
|
||||
setup() {
|
||||
definePageMeta({
|
||||
layout: 'mainbanner'
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
product: null,
|
||||
company: null,
|
||||
relatedProducts: null,
|
||||
related: false,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
try {
|
||||
const config = useRuntimeConfig()
|
||||
const $route = useRoute()
|
||||
this.product = await $fetch(`/products/${$route.params.id}/`, {
|
||||
baseURL: config.public.baseURL,
|
||||
method: 'GET',
|
||||
})
|
||||
|
||||
}
|
||||
if (this.product.company) {
|
||||
this.company = await $fetch(`/companies/${this.product.company.id}/`, {
|
||||
baseURL: config.public.baseURL,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
this.relatedProducts = await $fetch(`/products/${this.product.id}/related/`, {
|
||||
baseURL: config.public.baseURL,
|
||||
method: 'GET',
|
||||
})
|
||||
this.related = this.relatedProducts.length > 0
|
||||
|
||||
//return { product, company, relatedProducts, related }
|
||||
} catch (error) {
|
||||
console.error('Error fetching product details:', error)
|
||||
}
|
||||
},
|
||||
//TODO: meta data
|
||||
// mounted() {
|
||||
// useHead({
|
||||
// title: `latienda.coop | ${this.product.id}`,
|
||||
// meta: [
|
||||
// {
|
||||
// hid: 'description',
|
||||
// name: 'description',
|
||||
// content: this.product.description,
|
||||
// },
|
||||
// { property: 'og:title', content: this.product.id },
|
||||
// { property: 'og:description', content: this.product.description },
|
||||
// { property: 'og:image', content: this.product.image },
|
||||
// { property: 'og:url', content: this.product.url },
|
||||
// { name: 'twitter:card', content: 'summary_large_image' },
|
||||
// ],
|
||||
// })
|
||||
// },
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
margin-top: 40px;
|
||||
margin-bottom: 80px;
|
||||
@include mobile {
|
||||
margin-top: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-bottom: 40px;
|
||||
font-size: $xl;
|
||||
color: $color-navy;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user