692 lines
17 KiB
Vue
692 lines
17 KiB
Vue
<template>
|
|
<div class="">
|
|
<!-- <HeroWithSearch
|
|
title="Productos y servicios"
|
|
subtitle="Catálogo colectivo de consumo transformador"
|
|
/> -->
|
|
<section class="hero-section">
|
|
<div class="gradient">
|
|
<div class="content">
|
|
<h1>Productos y servicios</h1>
|
|
<p>Catálogo colectivo de consumo transformador</p>
|
|
<div class="container wrapper">
|
|
<form class="search-container" @submit.prevent="search">
|
|
<input
|
|
v-model="searchText"
|
|
class="search-text"
|
|
type="text"
|
|
autocomplete="off"
|
|
placeholder="Encuentra productos o servicios"
|
|
/>
|
|
<div class="search-link">
|
|
<img
|
|
class="search-icon"
|
|
src="@/assets/img/latienda-search.svg"
|
|
alt="consumo-cuidado-search"
|
|
@click="search"
|
|
/>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<div class="c-container row">
|
|
<div class="col-md-3">
|
|
<ProductFilter
|
|
:filters="filters"
|
|
:current-filters="currentFilters"
|
|
:prices="prices"
|
|
:geo="coordinates"
|
|
@apply-filters="updateData"
|
|
/>
|
|
</div>
|
|
<div v-if="loadingProducts" class="col-md-9 loading-spinner">
|
|
<BSpinner />
|
|
<span>Cargando productos...</span>
|
|
</div>
|
|
<!-- <div class="items">
|
|
<div class="title-container">
|
|
<div class="title-lines"></div>
|
|
<h5 class="items-title">Últimos productos</h5>
|
|
<div class="title-lines"></div>
|
|
</div>
|
|
<div class="items-container">
|
|
<ItemsRow :type="`product`" :items="carouselProducts" />
|
|
<NuxtLink to="/busqueda" class="link">
|
|
<ButtonCTA class="button">Ver todos los servicios</ButtonCTA>
|
|
</NuxtLink>
|
|
</div>
|
|
</div> -->
|
|
<div v-if="!loadingProducts" class="col-md-9 container-fluid">
|
|
<div v-if="count > 0" class="carousel">
|
|
<div class="title-container">
|
|
<h5 class="items-title">Últimos productos y servicios</h5>
|
|
<div class="title-lines"></div>
|
|
</div>
|
|
<ItemsRow class="items" :type="`product`" :items="carouselProducts.results" :items-to-show="3" />
|
|
</div>
|
|
<!-- <div v-if="hasFilterTags" class="applied-filters">
|
|
<h2 class="title">FILTROS APLICADOS</h2>
|
|
<div class="filter-buttons">
|
|
<button
|
|
v-if="appliedFilters.hasOwnProperty('shipping_cost')"
|
|
type="button"
|
|
class="btn-tag"
|
|
@click="removeFilter('shipping_cost')"
|
|
>
|
|
<span>Sin gastos de envío</span>
|
|
<img src="@/assets/img/latienda-close.svg" alt="" />
|
|
</button>
|
|
<button
|
|
v-if="appliedFilters.hasOwnProperty('discount')"
|
|
type="button"
|
|
class="btn-tag"
|
|
@click="removeFilter('discount')"
|
|
>
|
|
<span>Descuentos</span>
|
|
<img src="@/assets/img/latienda-close.svg" alt="" />
|
|
</button>
|
|
<div v-if="appliedFilters.hasOwnProperty('category')">
|
|
<button
|
|
v-for="(cat, key) in appliedFilters.category"
|
|
:key="key"
|
|
type="button"
|
|
class="btn-delete-all"
|
|
@click="removeCategory(cat)"
|
|
>
|
|
<span>{{ cat }}</span>
|
|
<img src="@/assets/img/latienda-close.svg" alt="" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
<div class="results">
|
|
<div class="title-container">
|
|
<h5 class="items-title">Catálogo</h5>
|
|
<div class="title-lines"></div>
|
|
</div>
|
|
<p class="count">Resultados de búsqueda: {{ count }} resultados</p>
|
|
<div v-if="products.length !== 0">
|
|
<ProductsRelated :related-products="products" :products-per-page="12" />
|
|
<!-- <div v-for="product in products" :key="product.id">
|
|
<ProductCard :key="product.key" :product="product" />
|
|
</div> -->
|
|
<!-- <BPagination
|
|
v-model="currentPage"
|
|
class="pagination"
|
|
:total-rows="count"
|
|
:per-page="perPage"
|
|
@change="handlePageChange"
|
|
/> -->
|
|
</div>
|
|
<div v-else class="no-results">
|
|
<p class="query-text">
|
|
No encontramos <span v-if="queryText">"{{ queryText }}"</span> en Consumo Cuidado.
|
|
</p>
|
|
<p>
|
|
Prueba un término más general, revisa la ortografía o
|
|
<span class="link" @click="clearFilters">limpia los filtros</span>.
|
|
</p>
|
|
<!-- <div v-for="product in defaultProducts" :key="product.id">
|
|
<ProductCard :key="product.key" :product="product" />
|
|
</div> -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- appliedFilters: {{appliedFilters}} <br>
|
|
filters: {{filters}} <br>
|
|
prices: {{prices}} <br>
|
|
coordinates: {{coordinates}} <br>
|
|
products: {{products}} <br>
|
|
defaultProducts: {{defaultProducts}} <br>
|
|
carouselProducts: {{carouselProducts}} <br>
|
|
count: {{count}} -->
|
|
</template>
|
|
|
|
<script>
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import serverSearch from '~/utils/serverSearch'
|
|
import clientSearch from '~/utils/clientSearch'
|
|
|
|
export default {
|
|
setup(){
|
|
definePageMeta({
|
|
layout: 'mainbanner',
|
|
})
|
|
|
|
const auth = useAuthStore()
|
|
return { auth }
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
searchText: '',
|
|
queryText: '',
|
|
currentPage: 1,
|
|
perPage: 10,
|
|
filterTags: {
|
|
shipping_cost: undefined,
|
|
},
|
|
previousParams: null,
|
|
currentFilters: null,
|
|
mountedProducts: [],
|
|
appliedFilters: {},
|
|
filters: {},
|
|
prices: { min: null, max: null },
|
|
coordinates: null,
|
|
products: [],
|
|
defaultProducts: [],
|
|
carouselProducts: [],
|
|
count: 0,
|
|
loadingProducts: true,
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
hasFilterTags() {
|
|
if (!this.appliedFilters) return false
|
|
return (
|
|
Object.keys(this.appliedFilters).includes('shipping_cost') ||
|
|
Object.keys(this.appliedFilters).includes('discount') ||
|
|
(Array.isArray(this.appliedFilters.category) &&
|
|
this.appliedFilters.category.length > 0)
|
|
)
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
'$route.query'(newValue) {
|
|
console.log('New Value:', newValue)
|
|
//console.log('Route changed:', this.$route.fullPath)
|
|
//console.log('Current params:', this.$route.query)
|
|
this.queryText = newValue.q
|
|
console.log('Updated queryText:', this.queryText)
|
|
this.updateData(newValue)
|
|
//Object.assign(this.$data, this.$options.data())
|
|
}
|
|
},
|
|
|
|
|
|
async beforeCreate() {
|
|
const config = useRuntimeConfig()
|
|
const params = import.meta.client ? clientSearch(this.$route.query) : serverSearch(this.$route.query)
|
|
const data = await $fetch(`/search_products/?`, {
|
|
baseURL: config.public.baseURL,
|
|
method: 'GET',
|
|
params: params,
|
|
headers: {
|
|
Authorization: '/',
|
|
},
|
|
})
|
|
//console.log('data', data)
|
|
|
|
const products = data.products
|
|
//console.log('products', products)
|
|
let defaultProducts = []
|
|
if (products.length === 0) {
|
|
//console.log('no products, fetching default')
|
|
const data = await $fetch(`/search_products/?q=${params.q}`, {
|
|
baseURL: config.public.baseURL,
|
|
method: 'GET',
|
|
params: {
|
|
order: 'newest',
|
|
limit: 10,
|
|
offset: 0
|
|
},
|
|
headers: {
|
|
Authorization: '/',
|
|
},
|
|
})
|
|
defaultProducts = data.products
|
|
}
|
|
|
|
const carouselProducts = await $fetch(`/products/`, {
|
|
baseURL: config.public.baseURL,
|
|
method: 'GET',
|
|
params: {
|
|
limit: 10,
|
|
offset: 0
|
|
},
|
|
headers: {
|
|
Authorization: '/',
|
|
},
|
|
})
|
|
//console.log('carouselProducts', carouselProducts)
|
|
|
|
let coordinates
|
|
if (params.latitude && params.longitude) {
|
|
coordinates = {
|
|
lat: Number(params.latitude),
|
|
lng: Number(params.longitude),
|
|
}
|
|
}
|
|
|
|
let prices
|
|
if (params.price_min || params.price_max) {
|
|
prices = { max: params.price_max, min: params.price_min }
|
|
} else if (data.prices.min || data.prices.max) {
|
|
prices = data.prices
|
|
} else {
|
|
prices = { max: null, min: null }
|
|
}
|
|
|
|
this.appliedFilters = params.q
|
|
console.log('Initial appliedFilters:', this.appliedFilters)
|
|
this.filters = data.filters
|
|
this.prices = prices
|
|
this.coordinates = coordinates
|
|
this.products = products
|
|
this.defaultProducts = defaultProducts
|
|
this.carouselProducts = carouselProducts
|
|
this.count = data.count
|
|
this.loadingProducts = false
|
|
},
|
|
|
|
|
|
mounted() {
|
|
this.currentFilters = this.appliedFilters
|
|
},
|
|
|
|
methods: {
|
|
async handlePageChange(value) {
|
|
const offset = (value - 1) * this.perPage
|
|
this.products = await this.getMoreProducts(offset)
|
|
},
|
|
|
|
async getMoreProducts(offset) {
|
|
const config = useRuntimeConfig()
|
|
const data = await $fetch(`/search_products/`, {
|
|
baseURL: config.public.baseURL,
|
|
method: 'GET',
|
|
params: {
|
|
...this.appliedFilters,
|
|
limit: 10,
|
|
offset: offset
|
|
},
|
|
headers: {
|
|
Authorization: '/',
|
|
},
|
|
})
|
|
return data.products
|
|
},
|
|
|
|
async updateData(value) {
|
|
//console.log('updateData called with:', value)
|
|
const filters = { q: this.appliedFilters.q }
|
|
const query = Object.keys(value).length === 0 ? { ...filters } : { ...value, ...filters }
|
|
|
|
//console.log('Navigating to busqueda with query:', query)
|
|
this.$router.push({ name: 'busqueda', query })
|
|
|
|
const config = useRuntimeConfig()
|
|
const data = await $fetch('/search_products/', {
|
|
baseURL: config.public.baseURL,
|
|
method: 'GET',
|
|
params: query,
|
|
headers: { Authorization: '/' },
|
|
})
|
|
this.products = data.products
|
|
this.count = data.count
|
|
this.loadingProducts = false
|
|
},
|
|
|
|
removeCategory(cat) {
|
|
this.currentFilters = this.appliedFilters
|
|
const categoryArray = this.currentFilters.category
|
|
const newCats = []
|
|
categoryArray.forEach((element) => {
|
|
if (element !== cat) {
|
|
newCats.push(element)
|
|
}
|
|
})
|
|
this.currentFilters.category = newCats
|
|
const noCategory = {}
|
|
Object.entries(this.currentFilters).forEach(([key, value]) => {
|
|
if (key !== 'category') {
|
|
noCategory[key] = value
|
|
}
|
|
})
|
|
if (newCats.length === 0) {
|
|
return this.$router.push({
|
|
path: this.$route.path,
|
|
query: { ...noCategory },
|
|
})
|
|
} else {
|
|
return this.$router.push({
|
|
path: this.$route.path,
|
|
query: { category: newCats, ...noCategory },
|
|
})
|
|
}
|
|
},
|
|
|
|
removeFilter(filter) {
|
|
this.currentFilters = { ...this.appliedFilters }
|
|
this.currentFilters = Object.fromEntries(
|
|
Object.entries(this.currentFilters).filter(([key]) => key !== filter)
|
|
)
|
|
|
|
return this.$router.push({
|
|
name: 'busqueda',
|
|
query: { ...this.currentFilters },
|
|
})
|
|
},
|
|
|
|
|
|
async search() {
|
|
this.currentFilters = { ...this.appliedFilters }
|
|
console.log('Searching for:', this.searchText)
|
|
if (this.searchText) {
|
|
this.appliedFilters = this.searchText
|
|
this.products = await this.updateData({ q: this.searchText })
|
|
}
|
|
},
|
|
|
|
clearFilters() {
|
|
this.currentFilters = null
|
|
this.appliedFilters = null
|
|
this.queryText = ''
|
|
this.$router.push({
|
|
name: 'busqueda'
|
|
})
|
|
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.c-container {
|
|
contain: content;
|
|
width: 100%;
|
|
min-height: 100dvh;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.hero-section {
|
|
width: 100%;
|
|
@include mobile {
|
|
margin-top: 12dvh;
|
|
}
|
|
}
|
|
.gradient {
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
border-radius: 24px;
|
|
min-height: 45dvh;
|
|
overflow: hidden;
|
|
|
|
// Imagen de fondo
|
|
background-image: url('@/assets/img/voluntarios.jpg');
|
|
background-size: cover;
|
|
background-position: center;
|
|
|
|
// Overlay de gradiente
|
|
&::before {
|
|
content: "";
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: 24px;
|
|
background: linear-gradient(
|
|
180deg,
|
|
$color-consumo-base 0%,
|
|
$color-consumo-base-light 100%
|
|
);
|
|
z-index: 1;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.content {
|
|
position: relative;
|
|
z-index: 2;
|
|
text-align: center;
|
|
padding: 2rem;
|
|
|
|
h1 {
|
|
text-align: center;
|
|
font-size: $hero;
|
|
font-weight: $bold;
|
|
@include mobile { max-width: 100%;
|
|
margin: 1.5rem 1rem 0.5rem 1rem;
|
|
}
|
|
}
|
|
p {
|
|
font-size: $xl;
|
|
}
|
|
}
|
|
|
|
@include mobile {
|
|
margin-top: 8dvh;
|
|
}
|
|
}
|
|
|
|
.wrapper {
|
|
margin: 0 auto;
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
align-items: center;
|
|
text-align: center;
|
|
}
|
|
|
|
.search-container {
|
|
background-color: $color-light;
|
|
height: 60px;
|
|
max-width: 100%;
|
|
border-radius: 12px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-top: 48px;
|
|
-webkit-box-shadow: 0px 0px 20px 0px #d1d1d1; /* Android 2.3+, iOS 4.0.2-4.2, Safari 3-4 */
|
|
box-shadow: 0px 0px 20px 0px #d1d1d1;
|
|
@include mobile {
|
|
height: 40px;
|
|
margin: 1rem auto;
|
|
}
|
|
@include tablet {
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.search-link {
|
|
@include mobile {
|
|
width: 40px;
|
|
height: 40px;
|
|
padding: 0.6rem;
|
|
float: right;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
text-decoration: none;
|
|
transition: 0.4s;
|
|
}
|
|
@include tablet {
|
|
width: 4rem;
|
|
margin-right: 1.5rem;
|
|
}
|
|
}
|
|
.search-text {
|
|
outline: none;
|
|
width: 100%;
|
|
padding: 0 1.5rem;
|
|
}
|
|
.search-icon {
|
|
cursor: pointer;
|
|
width: 1.8rem;
|
|
@include mobile {
|
|
margin-left: 0;
|
|
width: 100%;
|
|
}
|
|
@include tablet {
|
|
float: right;
|
|
height: 70%;
|
|
}
|
|
}
|
|
|
|
.ad {
|
|
margin: 40px auto;
|
|
width: 100%;
|
|
height: 100px;
|
|
background-color: $color-grey-nav;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.applied-filters {
|
|
@include mobile {
|
|
display: none;
|
|
}
|
|
.title {
|
|
font-size: $xl;
|
|
color: $color-navy;
|
|
}
|
|
}
|
|
|
|
.filter-buttons {
|
|
margin-bottom: 30px;
|
|
|
|
button {
|
|
margin-right: 5px;
|
|
margin-bottom: 5px;
|
|
border: none;
|
|
border-radius: 5px;
|
|
padding: 8px 10px;
|
|
color: $color-light;
|
|
background-color: $color-dark-green;
|
|
|
|
img {
|
|
width: 18px;
|
|
margin-left: 5px;
|
|
position: relative;
|
|
bottom: 1px;
|
|
}
|
|
}
|
|
|
|
.btn-delete-all {
|
|
background-color: $color-darker-green;
|
|
}
|
|
}
|
|
|
|
.container-fluid {
|
|
background-color: $color-bg-light;
|
|
margin-top: 2rem;
|
|
border-radius: 24px;
|
|
}
|
|
|
|
.carousel {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: 2rem;
|
|
padding: 8rem 8rem 8rem 8rem;
|
|
border-radius: 24px;
|
|
padding: 2rem 0;
|
|
margin-top: 2rem;
|
|
&-title {
|
|
text-align: center;
|
|
}
|
|
@include mobile {
|
|
display: none;
|
|
}
|
|
}
|
|
.title-container {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: start;
|
|
padding: 0 1rem;
|
|
.title-lines {
|
|
width: 34px;
|
|
height: 2px;
|
|
background: $color-consumo-base;
|
|
margin: 0 8px;
|
|
}
|
|
.items-title {
|
|
font-size: $h5;
|
|
text-transform: uppercase;
|
|
padding-top: 10px;
|
|
}
|
|
.title {
|
|
font-size: $xl;
|
|
color: $color-navy;
|
|
@include tablet {
|
|
margin-top: 3rem;
|
|
}
|
|
}
|
|
.items {
|
|
margin: auto;
|
|
}
|
|
}
|
|
.results {
|
|
display: flex;
|
|
flex-direction: column;
|
|
justify-content: center;
|
|
gap: 1rem;
|
|
padding: 8rem8rem;
|
|
border-radius: 24px;
|
|
padding: 2rem 0;
|
|
margin-top: 2rem;
|
|
.count {
|
|
padding: 0 1rem;
|
|
font-size: $s;
|
|
font-weight: $medium;
|
|
}
|
|
}
|
|
.pagination {
|
|
margin-top: 40px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.no-results {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
.query-text {
|
|
font-size: $xxl;
|
|
color: $color-primary;
|
|
font-weight: $medium;
|
|
}
|
|
p {
|
|
text-align: center;
|
|
font-size: $xl;
|
|
color: $color-primary;
|
|
font-weight: $medium;
|
|
}
|
|
.link {
|
|
color: $color-button;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
}
|
|
}
|
|
|
|
.loading-spinner {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
min-height: 100dvh;
|
|
background-color: $color-bg-light;
|
|
border-radius: 1rem;
|
|
padding: 2rem;
|
|
margin-top: 2rem;
|
|
gap: 3rem;
|
|
color: $color-primary;
|
|
|
|
@include mobile {
|
|
margin-top: 7rem;
|
|
}
|
|
}
|
|
</style>
|