wip migration

This commit is contained in:
María
2025-08-14 15:12:29 +02:00
commit 61d96ac328
148 changed files with 31438 additions and 0 deletions

160
components/BannerCoop.vue Normal file
View File

@@ -0,0 +1,160 @@
<template>
<div class="container-fluid">
<div class="row">
<div class="banner-text col-12" align="center">
<span>
Si quieres formar parte de este gran proyecto
<br class="br" />registrate en el siguiente formulario
</span>
<img
class="sep-dots"
src="@/assets/img/latienda-dots-blue.svg"
alt=""
/>
</div>
</div>
<div class="row banner-links justify-content-center" align="center">
<NuxtLink class="col-2 link out-div" to="/page/info">
<img src="@/assets/img/latienda-info-red.svg" alt="" />
<img
class="dots"
src="@/assets/img/latienda-dots-vertical-red.svg"
alt=""
/>
<span>Saber más</span>
</NuxtLink>
<NuxtLink class="col-2 link fill-div" to="/registro/cooperativa">
<img
class="img-form"
src="@/assets/img/latienda-formulario.svg"
alt=""
/>
<img
class="dots"
src="@/assets/img/latienda-dots-vertical-white.svg"
alt=""
/>
<span>¡, quiero!</span>
</NuxtLink>
</div>
</div>
</template>
<script>
export default {
name: 'BannerCoop',
}
</script>
<style lang="scss" scoped>
.container-fluid {
padding-top: 80px;
padding-bottom: 80px;
background-color: $color-lighter-green;
background-image: url('../assets/img/latienda-banner-imagen.png');
background-repeat: no-repeat;
background-size: contain;
background-position: right;
position: relative;
@include mobile {
background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)),
url('../assets/img/latienda-banner-imagen.png');
background-position: center;
background-size: cover;
background-color: rgba(0, 0, 0, 0.3);
}
}
.banner-text {
@include mobile {
padding: 2em;
}
span {
font-size: $m;
color: $color-navy;
font-weight: $regular;
@include mobile {
font-size: $s;
font-weight: $medium;
color: $color-light;
}
}
img {
margin-top: 20px;
margin-bottom: 20px;
display: block;
width: 60px;
}
}
.sep-dots {
@include mobile {
visibility: hidden;
}
}
.banner-links {
.link {
border-radius: 40px;
padding: 10px;
text-decoration: none;
min-width: 200px;
@include mobile {
min-width: 160px;
padding: 5px;
}
img {
width: 30px;
@include mobile {
width: 25px;
}
}
.dots {
width: 5px;
margin: 0 10px 0 10px;
@include mobile {
width: 3px;
}
}
.img-form {
width: 25px;
@include mobile {
width: 20px;
}
}
span {
text-transform: uppercase;
font-size: $xs;
font-weight: 600;
}
}
}
.out-div {
border: 2px solid $color-orange;
color: $color-orange;
margin-right: 10px;
@include mobile {
display: none;
}
}
.fill-div {
background-color: $color-orange;
color: $color-light;
}
.br {
@include mobile {
display: none;
}
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<ClientOnly>
<BAlert class="cookies" :show="show">
<div class="cookies-container">
<span class="title">Uso de cookies</span>
<p>
Utilizamos cookies propias y de terceros para mejorar la navegación.
Si continúa navegando, consideramos que acepta su uso. Puede obtener
más información, o bien conocer cómo cambiar la configuración, en
nuestra <NuxtLink to="/page/cookies">Política de cookies</NuxtLink>.
</p>
<BButton size="sm" variant="success" @click="close">Aceptar</BButton>
</div>
</BAlert>
</ClientOnly>
</template>
<script>
import { useAuthStore } from '@/stores/auth'
import { mapActions } from 'pinia'
export default {
name: 'CookieUsageNotification',
data() {
return {
show: undefined,
}
},
mounted() {
const auth = useAuthStore()
if (auth.cookiesAccepted === true) {
this.show = false
} else {
this.show = true
}
},
methods: {
...mapActions(useAuthStore, ['acceptCookies']),
close() {
this.show = false
this.acceptCookies()
},
},
}
</script>
<style lang="scss" scoped>
.cookies {
z-index: 999;
position: fixed;
bottom: 0px;
background-color: $color-navy;
border: none;
color: $color-light;
a {
color: $color-light;
text-decoration: underline;
}
span {
font-size: $m;
font-weight: $bold;
}
p {
font-size: $s;
}
}
.cookies-container {
font-weight: $regular;
}
.btn {
background-color: $color-navy;
color: $color-light;
border: 2px solid $color-orange;
padding: 0.5em 1em;
margin: auto;
&:hover {
background-color: $color-orange;
}
}
</style>

212
components/Footer.vue Normal file
View File

@@ -0,0 +1,212 @@
<template>
<div>
<hr />
<div>
<img class="bolsa" src="@/assets/img/footer-bolsa.svg" alt="" />
</div>
<hr />
<div class="social">
<a href="https://www.facebook.com/coceta" target="_blank">
<img
class="social-logo"
src="@/assets/img/footer-facebook.svg"
alt=""
/>
</a>
<a href="https://twitter.com/Coopsdetrabajo" target="_blank">
<img class="social-logo" src="@/assets/img/footer-twitter.svg" alt="" />
</a>
</div>
<hr />
<div class="organization">
<div class="organization-image-container">
<img
class="organization-image"
src="@/assets/img/footer-coceta.svg"
alt=""
/>
</div>
<div>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
<span class="dot"></span>
</div>
<div class="organization-image-container">
<img
class="organization-image"
src="@/assets/img/footer-ministerio.svg"
alt=""
/>
</div>
</div>
<ul class="link-list">
<li>
<small>2021 La Tienda.Coop</small>
</li>
<li>
<a href="https://coceta.coop/" class="footer-link" target="_blank">
<small>Coceta</small>
</a>
</li>
<li>
<NuxtLink to="/page/terminos">
<small>Términos y condiciones</small>
</NuxtLink>
</li>
<li>
<NuxtLink to="/page/legal">
<small>Política de privacidad</small>
</NuxtLink>
</li>
<li>
<NuxtLink to="/page/cookies">
<small>Cookies</small>
</NuxtLink>
</li>
<li>
<a
href="http://enreda.coop/"
class="footer-link enreda"
target="_blank"
>
<small
><mark class="highlight">Sitio desarrollado por Enreda</mark></small
>
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Footer',
}
</script>
<style lang="scss" scoped>
hr {
margin-top: 0;
}
.bolsa {
width: 50px;
height: auto;
margin: 4.375rem auto;
display: block;
@include mobile {
width: 35px;
margin: 3rem auto;
}
}
.social {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
margin: 3.125rem auto;
&-logo {
height: 1.25rem;
margin: auto 1.875rem;
}
}
.organization {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
&-image {
width: 18rem;
margin: 3.125rem;
@include mobile {
width: 6rem;
margin: 0.8rem;
}
}
}
.dot {
height: 3px;
width: 3px;
background-color: #bbb;
border-radius: 50%;
display: block;
margin: 7px;
@include mobile {
height: 2px;
width: 2px;
margin: 5px;
}
}
.link-list {
font-family: 'Noto Sans';
font-size: $xs;
color: $color-grey-darker;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
flex-wrap: nowrap;
margin-top: 1rem;
margin-bottom: 3.125rem;
text-decoration: none;
list-style-type: none;
@include mobile {
display: none;
}
@include tablet {
flex-direction: row;
font-size: $xxs;
}
& small {
margin: auto 1.25rem;
text-align: center;
color: $color-grey-darker;
:hover {
cursor: pointer;
}
}
@include tablet {
li:nth-child(1):after,
li:nth-child(2):after,
li:nth-child(3):after,
li:nth-child(4):after,
li:nth-child(5):after {
color: $color-grey-darker;
content: '|';
margin: 0.5rem;
}
}
.footer-link {
.enreda {
padding: 1.25em;
}
.highlight {
background: $color-greylighter;
border-radius: 5px;
padding: 5px;
}
}
}
</style>

33
components/FormHeader.vue Normal file
View File

@@ -0,0 +1,33 @@
<template>
<div class="header">
<h1 class="title">{{ title }}</h1>
<img src="@/assets/img/latienda-lineapuntos-2.svg" alt="" class="" />
</div>
</template>
<script>
export default {
props: {
title: { type: String, default: '' },
},
}
</script>
<style lang="scss" scoped>
.header {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 40px;
.title {
font-size: $xl;
color: $color-navy;
margin-bottom: 10px;
}
img {
width: 40px;
}
}
</style>

89
components/FormInput.vue Normal file
View File

@@ -0,0 +1,89 @@
<template>
<div class="form-input">
<label
>{{ labelText + (required ? '*' : '') }}
<input
v-model="inputValue"
:required="required"
:type="type"
:step="step"
:placeholder="placeholder"
@input="handleInput"
/>
</label>
</div>
</template>
<script>
export default {
props: {
labelText: {
type: String,
default: '',
},
required: {
type: Boolean,
default: false,
},
type: {
type: String,
default: '',
},
step: {
type: String,
default: '',
},
value: {
type: [Number, String],
default: '',
},
placeholder: {
type: String,
default: '',
},
},
emits: ['input'],
data() {
return {
inputValue: this.value,
}
},
methods: {
handleInput() {
this.$emit('input', this.inputValue)
},
},
}
</script>
<style lang="scss" scoped>
.form-input {
// width: 100%; //Utilizar este width para controlar el ancho desde el elemento padre
width: 45%;
@include mobile {
width: 80%;
}
// @include desktop {
// width: 25%;
// }
}
input {
width: 100%;
background-color: $color-grey-inputs;
border: 1px solid $color-grey-inputs-border;
border-radius: 4px;
padding: 10px 5px;
display: block;
margin-bottom: 10px;
font-weight: $regular;
outline: none;
}
label {
text-align: left;
color: $color-navy;
font-weight: $bold;
font-size: $xs;
display: block;
}
</style>

View File

@@ -0,0 +1,253 @@
<template>
<div class="wrapper">
<label>{{ label }}</label>
<p v-if="value && !place">{{ value }}</p>
<p v-if="place">{{ place.address }}</p>
<p v-show="error" class="error">{{ error }}</p>
<div class="input-and-btn">
<button class="geo-btn" type="button" @click="getGeoLocation">
<v-progress-circular
v-if="loading"
:size="15"
:width="2"
indeterminate
/>
<v-icon v-else small class="geo-icon"> mdi-crosshairs-gps </v-icon>
</button>
<input
ref="googleMap"
:required="!value"
placeholder=""
type="search"
@focus="clear"
/>
<!-- <button class="add-btn" type="button" @click="logPlace">Añadir</button> -->
</div>
</div>
</template>
<script>
import { Loader } from '@googlemaps/js-api-loader'
export default {
props: {
value: {
type: String,
default: '',
},
geo: {
type: Object,
default: null,
},
label: {
type: String,
default: 'Dirección',
},
},
emits: ['addedData'],
data() {
return {
google: null,
autocomplete: null,
geocoder: null,
place: null,
error: null,
loading: false,
}
},
async mounted() {
const config = useRuntimeConfig()
const apiKey = config.public.googleMapsApiKey
if (!apiKey) {
console.error('Google Maps API key is not defined in the runtime config.')
return
}
const googleMapApi = new Loader({
libraries: ['places'],
apiKey: apiKey,
})
try {
await googleMapApi.load()
await this.initializeMap()
} catch (error) {
console.error('Error loading Google Maps API:', error)
this.error = 'Error al cargar el mapa de Google'
}
},
methods: {
initializeMap() {
const mapContainer = this.$refs.googleMap
if (!mapContainer || !window.google) {
this.error = 'Google Maps no disponible'
return
}
this.autocomplete = new window.google.maps.places.PlaceAutocompleteElement(mapContainer)
window.google.maps.event.addListener(this.autocomplete, 'place_changed', () => this.logPlace())
this.geocoder = new window.google.maps.Geocoder()
if (this.geo) {
this.getGeoData(this.geo)
}
},
async getGeoLocation() {
this.place = null
this.error = null
const geoLocation = () =>
new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocalización no soportada'))
return
}
navigator.geolocation.getCurrentPosition(
(posData) => {
resolve(posData)
},
(error) => {
reject(error)
}
)
} )
try {
this.loading = true
const position = await geoLocation()
const geo = {
lat: position.coords.latitude,
lng: position.coords.longitude,
}
this.getGeoData(geo)
} catch (error) {
console.error(error)
this.error = 'No se puede obtener la ubicación'
this.loading = false
}
},
getGeoData(coordinates) {
if (!this.geocoder) {
this.error = 'Geocoder no inicializado'
this.loading = false
return
}
this.geocoder.geocode({ location: coordinates }, (result, status) => {
if (status === 'OK') {
const data = result[0]
const address = data.formatted_address
const geo = {
latitude: Number(data.geometry.location.lat().toFixed(4)),
longitude: Number(data.geometry.location.lng().toFixed(4)),
}
const city = data.address_components.find(
(element) => element.types[0] === 'locality'
).long_name || ''
this.place = { address, geo, city }
if (this.$refs.googleMap) {
this.$refs.googleMap.value = this.place.address
}
this.$emit('addedData', this.place)
this.loading = false
} else {
this.error = 'No se puede obtener la ubicación'
this.loading = false
}
})
},
clear() {
this.place = null
this.error = null
this.$refs.googleMap.value = null
this.$emit('addedData', this.place)
},
async logPlace() {
this.place = null
this.error = null
try {
const googlePlace = await this.autocomplete.getPlace()
console.log('Google Place:', googlePlace)
const address = googlePlace.formatted_address
const geo = {
latitude: Number(googlePlace.geometry.location.lat().toFixed(4)),
longitude: Number(googlePlace.geometry.location.lng().toFixed(4)),
}
const city =
googlePlace.address_components.find(
(element) => element.types[0] === 'locality'
)?.long_name || ''
this.place = { address, geo, city }
this.$emit('addedData', this.place)
} catch {
this.place = null
this.error = 'Debe introducir una dirección correcta'
}
},
},
}
</script>
<style lang="scss" scoped>
label {
text-align: left;
color: $color-navy;
font-weight: $bold;
font-size: $xs;
display: block;
}
input {
width: 100%;
background-color: $color-grey-inputs;
border: 1px solid $color-grey-inputs-border;
border-radius: 4px;
padding: 9px 5px;
display: block;
outline: none;
font-size: $s;
}
.input-and-btn {
width: 100%;
display: flex;
justify-content: space-between;
input {
width: 90%;
}
button {
height: auto;
background-color: $color-navy;
color: $color-light;
border: 1px solid $color-navy;
border-radius: 5px;
text-transform: uppercase;
padding: 5px 5px;
font-size: $xxs;
@include mobile {
font-size: $xxs;
}
}
.geo-btn {
width: 10%;
min-width: 30px;
}
// .add-btn {
// width: 20%;
// }
}
.error {
color: $color-error;
}
.geo-icon {
color: $color-light !important;
}
</style>

View File

@@ -0,0 +1,204 @@
<template>
<div class="box">
<div class="title">
<h5 class="category-title">{{ title }}</h5>
</div>
<div class="container">
<div class="row">
<div class="col-6">
<NuxtLink :to="`/productos/${products[0] ? products[0].id : 1}`">
<div class="item">
<BImg
class="image"
fluid
:src="getProductImg(products[0])"
alt=""
/>
<h6 class="product-name">
{{
products[0]
? products[0].name
.toLowerCase()
.replace(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]/, (c) =>
c.toUpperCase()
)
: ''
}}
</h6>
</div>
</NuxtLink>
</div>
<div class="col-6">
<NuxtLink :to="`/productos/${products[1] ? products[1].id : 1}`">
<div class="item">
<BImg
class="image"
fluid
:src="getProductImg(products[1])"
alt=""
/>
<h6 class="product-name">
{{
products[1]
? products[1].name
.toLowerCase()
.replace(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]/, (c) =>
c.toUpperCase()
)
: ''
}}
</h6>
</div>
</NuxtLink>
</div>
</div>
<div class="row">
<div class="col-6">
<NuxtLink :to="`/productos/${products[2] ? products[2].id : 1}`">
<div class="item">
<BImg
class="image"
fluid
:src="getProductImg(products[2])"
alt=""
/>
<h6 class="product-name">
{{
products[2]
? products[2].name
.toLowerCase()
.replace(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]/, (c) =>
c.toUpperCase()
)
: ''
}}
</h6>
</div>
</NuxtLink>
</div>
<div class="col-6">
<NuxtLink :to="`/productos/${products[3] ? products[3].id : 1}`">
<div class="item">
<BImg
class="image"
fluid
:src="getProductImg(products[3])"
alt=""
/>
<h6 class="product-name">
{{
products[3]
? products[3].name
.toLowerCase()
.replace(/^[a-zA-ZÀ-ÿ\u00f1\u00d1]/, (c) =>
c.toUpperCase()
)
: ''
}}
</h6>
</div>
</NuxtLink>
</div>
</div>
</div>
<nuxt-link class="link" :to="`/busqueda?category=${category}`"
>Ver más &#8594;</nuxt-link
>
</div>
</template>
<script>
export default {
name: 'HighlightsCard',
props: {
title: {
type: String,
default: '',
},
category: {
type: String,
default: '',
},
products: {
type: Array,
default: () => [],
},
},
methods: {
getProductImg(product) {
if (product && product.image) return product.image
return `@/assets/img/latienda-product-default.svg`
},
},
}
</script>
<style lang="scss" scoped>
.box {
border-radius: 7px;
border: 1px solid $color-greylighter;
position: relative;
background-color: $color-light;
margin-top: 30px;
height: 100%;
display: block;
overflow: auto;
padding: 0.8rem;
padding-bottom: 0.4rem;
@include mobile {
margin-top: 15px;
}
}
.category-title {
color: $color-navy;
}
.link {
display: flex;
flex-direction: column;
justify-items: center;
align-items: center;
color: $color-greylink;
font-family: $font-primary;
font-weight: $bold;
font-size: $s;
}
.item {
display: flex;
flex-direction: column;
justify-items: center;
align-items: center;
.product-name {
text-overflow: ellipsis;
overflow: hidden;
text-align: center;
font-family: $font-secondary;
font-size: $s;
font-weight: 400;
display: -webkit-box;
--webkit-line-clamp: 2;
--webkit-box-orient: vertical;
overflow: hidden;
margin-top: 0.6rem;
}
}
.image {
width: 100%;
max-height: 6rem;
max-width: 6rem;
object-fit: cover;
@include mobile {
max-height: 12rem;
max-width: 12rem;
}
}
.title {
width: 100%;
margin: 1.2rem 0 0.5rem;
text-align: center;
}
</style>

157
components/ItemsRow.vue Normal file
View File

@@ -0,0 +1,157 @@
<template>
<div class="row">
<ClientOnly placerholder="Loading...">
<!-- <div class="arrow-left-wrapper col-2 col-sm-1">
<img
class="arrow"
src="@/assets/img/categories-row-arrow-left.svg"
alt=""
@click="slideCarousel('prev')"
/>
</div> -->
<Carousel
ref="productDetails"
class="col-8 col-sm-10"
:items-to-show="3"
:gap="5"
:pagination-enabled="false"
:wrap-around="false"
:mouse-wheel="true"
:breakpoints-enabled="true"
>
<Slide v-for="item in items" :key="item.id">
<NuxtLink :to="formattedItem(item).url" class="slide">
<div class="image-container">
<img class="image" :src="formattedItem(item).image" alt="" />
</div>
<span>{{ formattedItem(item).name }}</span>
</NuxtLink>
</Slide>
<template #addons>
<Navigation />
</template>
</Carousel>
<!-- <div class="arrow-right-wrapper col-2 col-sm-1">
<img
class="arrow"
src="@/assets/img/categories-row-arrow-right.svg"
alt=""
@click="slideCarousel('next')"
/>
</div> -->
</ClientOnly>
</div>
</template>
<script>
import { Carousel, Slide, Navigation } from 'vue3-carousel'
import 'vue3-carousel/dist/carousel.css'
import defaultImage from '@/assets/img/latienda-product-default.png'
export default {
components: {
Carousel,
Slide,
Navigation
},
props: {
type: {
type: String,
required: true,
},
items: {
type: Array,
default: () => [],
},
},
data() {
return {
defaultImage,
config: {
height: 200,
breakpointMode: 'carousel', // o 'viewport'
breakpoints: {
300: {
itemsToShow: 2,
snapAlign: 'center',
},
400: {
itemsToShow: 3,
snapAlign: 'start',
},
500: {
itemsToShow: 4,
snapAlign: 'start',
},
},
}
}
},
methods: {
formattedItem(item) {
if (this.type === 'product') {
return {
name: item.name,
image:
item.image || this.defaultImage,
url: `/productos/${item.id}`,
}
}
if (this.type === 'company') {
return {
name: item.company_name,
image:
item.logo || this.defaultImage,
url: `/c/${item.id}`,
}
}
if (this.type === 'category') {
return {
name: item.name,
image:
item.image || this.defaultImage,
url: `/busqueda?category=${item.name}`,
}
}
},
},
}
</script>
<style lang="scss" scoped>
.slide {
display: flex;
flex-direction: column;
align-items: center;
justify-content: stretch;
padding: 0.3rem;
span {
text-align: center;
display: -webkit-box;
--webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
width: 25vw;
max-width: 110px;
font-weight: $regular;
font-size: $s;
}
}
.image-container {
width: 100%;
height: 5rem;
margin-bottom: 0.7rem;
@include mobile {
max-height: 10rem;
max-width: 10rem;
}
.image {
width: 100%;
height: 100%;
object-fit: contain;
}
}
</style>

204
components/NavBar.vue Normal file
View File

@@ -0,0 +1,204 @@
<template>
<header class="header">
<div class="container wrapper">
<div class="navmenu-container">
<NavMenu @logout="logout" />
</div>
<!-- isAdmin: {{ isAdmin }} <br>
isAuthenticated: {{ isAuthenticated }} <br> -->
<nav>
<NuxtLink to="/">
<img
class="logo"
src="@/assets/img/latienda-logo.png"
alt="latienda.coop"
/>
</NuxtLink>
<SearchInput v-if="$route.name == 'index'" class="search-input"/>
<div class="nav-buttons">
<NuxtLink to="/page/info">
<img
class="nav-buttons-info nav-icon"
src="@/assets/img/latienda-info.svg"
alt="latienda.coop"
/>
</NuxtLink>
<div class="nav-buttons-dots"></div>
<NuxtLink v-if="isAuthenticated & !isAdmin" to="/editar/perfil">
<div class="nav-buttons-acceso">
<img
class="nav-icon"
src="@/assets/img/latienda-acceder.svg"
alt="latienda.coop"
/>
<div class="nav-text">{{ name }}</div>
</div>
</NuxtLink>
<NuxtLink v-else-if="isAuthenticated & isAdmin" to="/admin">
<div class="nav-buttons-acceso">
<img
class="nav-icon"
src="@/assets/img/latienda-acceder.svg"
alt="latienda.coop"
/>
<div class="nav-text">{{ name }}</div>
</div>
</NuxtLink>
<NuxtLink v-else to="/login">
<div class="nav-buttons-acceso">
<img
class="nav-icon"
src="@/assets/img/latienda-acceder.svg"
alt="latienda.coop"
/>
<div class="nav-text">Acceder</div>
</div>
</NuxtLink>
</div>
<!-- <nuxt-link to="/">Búsqueda</nuxt-link>
<nuxt-link to="/auth/registro">Registrarse</nuxt-link>
<nuxt-link to="/auth/login">Login</nuxt-link>
<nuxt-link to="/editar">{{ $store.state.auth.email }}</nuxt-link>
<nuxt-link to="/admin" v-if="isAdmin">Panel de administración</nuxt-link> -->
<!-- <button @click="logout">Logout</button> -->
</nav>
</div>
</header>
</template>
<script>
import { mapActions } from 'pinia'
import { useAuthStore } from '@/stores/auth'
export default {
setup() {
const auth = useAuthStore();
return {
auth,
}
},
computed: {
isAdmin() {
return this.auth.isAdmin
},
isAuthenticated() {
return this.auth.isAuthenticated
},
name() {
return this.auth.getName
},
},
methods: {
...mapActions('auth', ['logout']),
async logout() {
try {
await this.logout()
this.$router.push('/login')
} catch (err) {
console.log(err)
}
},
},
}
</script>
<style lang="scss" scoped>
.header {
background-color: $color-green;
padding: 10px;
@include mobile {
position: fixed;
z-index: 999999;
top: 0;
width: 100%;
}
}
.container {
display: flex;
justify-content: space-evenly;
align-items: center;
}
.wrapper {
width: 100%;
height: 50px;
}
nav {
width: 100%;
list-style: none;
padding: 0;
margin: 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.search-and-login {
width: 100%;
display: flex;
justify-content: flex-end;
}
.logo {
width: 10rem;
margin: 0 0.5rem;
@include mobile {
width: 9rem;
}
}
.nav-icon {
height: 1.25rem;
}
.nav-buttons {
width: auto;
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 0 0.6rem;
margin-left: 1.25rem;
@include mobile {
display: none;
}
&-info {
cursor: pointer;
}
&-dots:after {
color: $color-navy;
content: '\22EE';
margin: 0.5rem;
@include mobile {
display: none;
}
}
&-acceso {
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
}
.nav-buttons-info {
@include mobile {
display: none;
}
}
}
.nav-text {
font-size: $xs;
color: $color-navy;
margin: 0.5rem;
@include mobile {
display: none;
}
}
</style>

115
components/NavBarEditar.vue Normal file
View File

@@ -0,0 +1,115 @@
<template>
<div class="navsearch_container container-fluid">
<NuxtLink to="/editar/perfil">Mi perfil</NuxtLink>
<NuxtLink :class="{ disabled: !coopIsValidated }" to="/editar/cooperativa"
>Cooperativa</NuxtLink
>
<!-- <NuxtLink to="/editar/cooperativa/crear">Crear cooperativa</NuxtLink> -->
<NuxtLink :class="{ disabled: !coopIsValidated }" to="/editar/productos"
>Productos</NuxtLink
>
<NuxtLink
:class="{ disabled: !coopIsValidated }"
to="/editar/productos/importar"
>Importar</NuxtLink
>
<NuxtLink to="/" @click="logout" >Cerrar sesión</NuxtLink>
</div>
</template>
<script>
import { useAuthStore } from '@/stores/auth'
export default {
setup() {
const auth = useAuthStore();
return {
auth,
}
},
data() {
return {
coopIsValidated: false,
}
},
async mounted() {
await this.checkIfCoopValidated()
},
methods: {
async logout() {
await this.auth.logout()
},
//TODO: check if cooperative is validated is working
async checkIfCoopValidated() {
const result = await $fetch('my_company/', {
baseURL: config.public.baseURL,
method: 'GET',
})
this.coopIsValidated = result.data.company.is_validated
},
},
}
</script>
<style lang="scss" scoped>
.navsearch_container {
border-bottom: 3px solid $color-grey-nav;
text-align: center;
padding: 20px 20px;
a {
font-size: $m;
font-weight: $bold;
color: $color-navy;
text-decoration: none;
}
a:nth-child(1):after,
a:nth-child(2):after,
a:nth-child(3):after,
a:nth-child(4):after {
color: $color-navy;
content: '\22EE';
margin: 0.5rem;
}
@include mobile {
display: none;
}
}
.tooltip {
position: relative;
display: inline-block;
border-bottom: 1px dotted black;
}
.tooltip .tooltiptext {
visibility: hidden;
width: 120px;
background-color: #555;
color: #fff;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
bottom: 125%;
left: 50%;
margin-left: -60px;
opacity: 0;
transition: opacity 0.3s;
}
.tooltip .tooltiptext::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -5px;
border-width: 5px;
border-style: solid;
border-color: #555 transparent transparent transparent;
}
.tooltip:hover .tooltiptext {
visibility: visible;
opacity: 1;
}
</style>

View File

@@ -0,0 +1,46 @@
<template>
<div class="navsearch_container container-fluid">
<NuxtLink to="/editar/perfil">Mi perfil</NuxtLink>
<NuxtLink to="/" @click="logout" >Cerrar sesión</NuxtLink>
</div>
</template>
<script>
import { useAuthStore } from '@/stores/auth'
export default {
setup() {
const auth = useAuthStore();
return {
auth,
}
},
methods: {
async logout() {
await this.auth.logout()
},
},
}
</script>
<style lang="scss" scoped>
.navsearch_container {
border-bottom: 3px solid $color-grey-nav;
text-align: center;
padding: 20px 20px;
a {
font-size: $m;
font-weight: $bold;
color: $color-navy;
text-decoration: none;
}
a:nth-child(1):after {
color: $color-navy;
content: '\22EE';
margin: 0.5rem;
}
@include mobile {
display: none;
}
}
</style>

View File

@@ -0,0 +1,40 @@
<template>
<div class="navsearch_container container-fluid">
<NuxtLink to="/busqueda"> Todos</NuxtLink>
<NuxtLink :to="{ name: 'busqueda', query: { order: 'newest' } }">
Últimos productos</NuxtLink
>
<NuxtLink to="/busqueda"> Más buscados</NuxtLink>
<NuxtLink to="/c"> Cooperativas</NuxtLink>
<NuxtLink to="/registro"> Regístrate</NuxtLink>
</div>
</template>
<script></script>
<style lang="scss" scoped>
.navsearch_container {
border-bottom: 3px solid $color-grey-nav;
text-align: center;
padding: 1.25em 1.25em;
@include mobile {
display: none;
}
a {
font-size: $m;
font-weight: $bold;
color: $color-navy;
text-decoration: none;
}
a:nth-child(1):after,
a:nth-child(2):after,
a:nth-child(3):after,
a:nth-child(4):after {
color: $color-navy;
content: '\22EE';
margin: 0.5rem;
}
}
</style>

276
components/NavMenu copy.vue Normal file
View File

@@ -0,0 +1,276 @@
<template>
<div class="nav-menu-container">
<img
class="burger"
src="@/assets/img/latienda-burger-nav.svg"
alt=""
@click="isMenuOpen = !isMenuOpen"
/>
<div :class="isMenuOpen ? `shadow` : ''">
<transition name="slider" mode="out-in">
<div v-if="isMenuOpen" class="nav-menu">
<img
class="close-icon"
src="@/assets/img/latienda-close-nav.svg"
alt=""
@click="isMenuOpen = !isMenuOpen"
/>
<nav class="nav">
<ul class="section-list">
<NuxtLink to="/">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-ubicacion.svg"
alt=""
/>
<span class="section-text">Inicio</span>
</li>
</NuxtLink>
<NuxtLink to="/c">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-tienda-nav.svg"
alt=""
/>
<span class="section-text">Cooperativas</span>
</li>
</NuxtLink>
<NuxtLink to="/page/info">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-bag.svg"
alt=""
/>
<span class="section-text">Sobre nosotros</span>
</li>
</NuxtLink>
<li class="section" @click="isMenuOpen = !isMenuOpen">
<a href="mailto:info@latienda.coop">
<img
class="section-img"
src="@/assets/img/envelope-simple.svg"
alt=""
/>
<span class="section-text">Contacto</span>
</a>
</li>
</ul>
</nav>
<ul class="login-list">
<NuxtLink v-if="!isAuthenticated" to="/login">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-lock.svg"
alt=""
/>
<span class="section-text">Acceder</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/perfil">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-user.svg"
alt=""
/>
<span class="section-text">Perfil</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/cooperativa">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<span class="section-text login">Cooperativa</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/productos">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<span class="section-text login">Productos</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/productos/importar">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<span class="section-text login">Importar</span>
</li>
</NuxtLink>
<NuxtLink v-if="isAuthenticated" @click.native="logout" to="/">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-sign-out.svg"
alt=""
/>
<span class="section-text">Cerrar sesión</span>
</li>
</NuxtLink>
</ul>
<ul class="link-list">
<li class="link">
<a href="https://coceta.coop/" target="_blank">
<span class="link-text">Coceta</span>
</a>
</li>
<NuxtLink to="/page/terminos">
<li class="link" @click="isMenuOpen = !isMenuOpen">
<span class="link-text">Términos y condiciones</span>
</li>
</NuxtLink>
<NuxtLink to="/page/legal">
<li class="link" @click="isMenuOpen = !isMenuOpen">
<span class="link-text">Política de privacidad</span>
</li>
</NuxtLink>
<NuxtLink to="/page/cookies">
<li class="link" @click="isMenuOpen = !isMenuOpen">
<span class="link-text">Cookies</span>
</li>
</NuxtLink>
</ul>
<div class="credits">
<span>2021 La Tienda.Coop</span>
<a href="http://enreda.coop/" target="_blank"
>Sitio desarrollado por Enreda</a
>
</div>
</div>
</transition>
</div>
</div>
</template>
<script>
export default {
data() {
return {
isMenuOpen: false,
}
},
computed: {
isAuthenticated() {
return this.$store.getters['auth/isAuthenticated']
},
isManager() {
return this.$store.getters['auth/isManager']
},
},
methods: {
async logout() {
this.isMenuOpen = false
await this.$store.dispatch('auth/logout')
},
},
}
</script>
<style lang="scss" scoped>
.slider-enter-active,
.slider-leave-active {
transition: all 0.5s ease;
}
.slider-enter,
.slider-leave-to {
transform: translateX(-100%);
}
.nav-menu-container {
@include tablet {
display: none;
}
}
.nav-menu {
font-family: $font-primary;
padding: 30px 0 0 20px;
background-color: $color-green;
height: 100vh;
top: 0;
left: 0;
position: fixed;
z-index: 9999999999;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
overflow: scroll;
@include mobile {
width: 70%;
}
@include tablet {
width: 30%;
}
}
.shadow {
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.2);
position: fixed;
top: 0;
left: 0;
z-index: 999999;
}
.burger,
.close-icon {
width: 1.8rem;
cursor: pointer;
}
.close-icon {
margin-bottom: 3rem;
}
.section {
padding: 0.4em 0;
}
.section-list {
padding-bottom: 1rem;
border-bottom: 1px solid $color-navy;
}
.section-text {
font-weight: $medium;
font-size: $s;
}
.section-img {
width: 1.2rem;
margin-right: 0.8rem;
}
ul {
list-style: none;
}
a {
text-decoration: none;
cursor: pointer;
color: $color-navy;
}
.link-list,
.credits {
margin-top: 1rem;
margin-left: 1rem;
padding: 0.1em;
}
.link {
padding: 0.3em 0;
font-size: $xs;
}
.credits {
margin-top: 1.5rem;
span,
a {
display: block;
color: $color-navy;
font-size: $xs;
padding: 0.3em 0;
}
margin-bottom: 4rem;
}
.login {
padding-left: 2rem;
font-weight: $regular;
}
</style>

287
components/NavMenu.vue Normal file
View File

@@ -0,0 +1,287 @@
<template>
<div class="nav-menu-container">
<img
class="burger"
src="@/assets/img/latienda-burger-nav.svg"
alt=""
@click="isMenuOpen = !isMenuOpen"
/>
<div :class="isMenuOpen ? `shadow` : ''">
<transition name="slider" mode="out-in">
<div v-if="isMenuOpen" class="nav-menu">
<img
class="close-icon"
src="@/assets/img/latienda-close-nav.svg"
alt=""
@click="isMenuOpen = !isMenuOpen"
/>
<nav class="nav">
<ul class="section-list">
<NuxtLink to="/">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-ubicacion.svg"
alt=""
/>
<span class="section-text">Inicio</span>
</li>
</NuxtLink>
<NuxtLink to="/c">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-tienda-nav.svg"
alt=""
/>
<span class="section-text">Cooperativas</span>
</li>
</NuxtLink>
<NuxtLink to="/page/info">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-bag.svg"
alt=""
/>
<span class="section-text">Sobre nosotros</span>
</li>
</NuxtLink>
<li class="section" @click="isMenuOpen = !isMenuOpen">
<a href="mailto:info@latienda.coop">
<img
class="section-img"
src="@/assets/img/envelope-simple.svg"
alt=""
/>
<span class="section-text">Contacto</span>
</a>
</li>
</ul>
</nav>
<ul class="login-list">
<NuxtLink v-if="!isAuthenticated" to="/login">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-lock.svg"
alt=""
/>
<span class="section-text">Acceder</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/perfil">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-user.svg"
alt=""
/>
<span class="section-text">Perfil</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/cooperativa">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<span class="section-text login">Cooperativa</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/productos">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<span class="section-text login">Productos</span>
</li>
</NuxtLink>
<NuxtLink v-if="isManager" to="/editar/productos/importar">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<span class="section-text login">Importar</span>
</li>
</NuxtLink>
<NuxtLink v-if="isAuthenticated" @click="logout" to="/">
<li class="section" @click="isMenuOpen = !isMenuOpen">
<img
class="section-img"
src="@/assets/img/latienda-sign-out.svg"
alt=""
/>
<span class="section-text">Cerrar sesión</span>
</li>
</NuxtLink>
</ul>
<ul class="link-list">
<li class="link">
<a href="https://coceta.coop/" target="_blank">
<span class="link-text">Coceta</span>
</a>
</li>
<NuxtLink to="/page/terminos">
<li class="link" @click="isMenuOpen = !isMenuOpen">
<span class="link-text">Términos y condiciones</span>
</li>
</NuxtLink>
<NuxtLink to="/page/legal">
<li class="link" @click="isMenuOpen = !isMenuOpen">
<span class="link-text">Política de privacidad</span>
</li>
</NuxtLink>
<NuxtLink to="/page/cookies">
<li class="link" @click="isMenuOpen = !isMenuOpen">
<span class="link-text">Cookies</span>
</li>
</NuxtLink>
</ul>
<div class="credits">
<span>2021 La Tienda.Coop</span>
<a href="http://enreda.coop/" target="_blank"
>Sitio desarrollado por Enreda</a
>
</div>
</div>
</transition>
</div>
</div>
</template>
<script>
import { mapActions } from 'pinia'
import { useAuthStore } from '@/stores/auth'
export default {
setup() {
const auth = useAuthStore();
return {
auth,
}
},
data() {
return {
isMenuOpen: false,
}
},
computed: {
isAuthenticated() {
return this.auth.isAuthenticated
},
isManager() {
return this.auth.isManager
},
},
methods: {
...mapActions('auth', ['logout']),
async logout() {
this.isMenuOpen = false
this.$emit('logout')
await this.logout()
},
},
}
</script>
<style lang="scss" scoped>
.slider-enter-active,
.slider-leave-active {
transition: all 0.5s ease;
}
.slider-enter,
.slider-leave-to {
transform: translateX(-100%);
}
.nav-menu-container {
@include tablet {
display: none;
}
}
.nav-menu {
font-family: $font-primary;
padding: 30px 0 0 20px;
background-color: $color-green;
height: 100vh;
top: 0;
left: 0;
position: fixed;
z-index: 9999999999;
box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.2);
overflow: scroll;
@include mobile {
width: 70%;
}
@include tablet {
width: 30%;
}
}
.shadow {
width: 100%;
height: 100vh;
background-color: rgba(0, 0, 0, 0.2);
position: fixed;
top: 0;
left: 0;
z-index: 999999;
}
.burger,
.close-icon {
width: 1.8rem;
cursor: pointer;
}
.close-icon {
margin-bottom: 3rem;
}
.section {
padding: 0.4em 0;
}
.section-list {
padding-bottom: 1rem;
border-bottom: 1px solid $color-navy;
}
.section-text {
font-weight: $medium;
font-size: $s;
}
.section-img {
width: 1.2rem;
margin-right: 0.8rem;
}
ul {
list-style: none;
}
a {
text-decoration: none;
cursor: pointer;
color: $color-navy;
}
.link-list,
.credits {
margin-top: 1rem;
margin-left: 1rem;
padding: 0.1em;
}
.link {
padding: 0.3em 0;
font-size: $xs;
}
.credits {
margin-top: 1.5rem;
span,
a {
display: block;
color: $color-navy;
font-size: $xs;
padding: 0.3em 0;
}
margin-bottom: 4rem;
}
.login {
padding-left: 2rem;
font-weight: $regular;
}
</style>

304
components/SearchHeader.vue Normal file
View File

@@ -0,0 +1,304 @@
<template>
<div class="container wrapper">
<form @submit.prevent="search" class="search-container">
<div class="categorias-wrapper">
<select v-model="selectedCategory" class="categorias">
<option selected value="">Todas las categorías</option>
<option
v-for="(category, key) in categories"
:key="key"
:value="category"
>
{{ category }}
</option>
</select>
</div>
<input
id="searchbox"
@focus="focused"
@blur="focusedOut"
v-model="searchText"
class="search-text"
type="text"
autocomplete="off"
placeholder=""
/>
<div class="search-link">
<img
class="search-icon"
src="@/assets/img/latienda-search.svg"
alt="latienda.coop-search"
@click="search"
/>
</div>
</form>
</div>
</template>
<script>
export default {
name: 'SearchHeader',
data() {
return {
searchText: '',
typing: null,
selectedCategory: '',
categories: [
'Alimentación, bebida y tabaco',
'Arte y ocio',
'Bebés y niños pequeños',
'Bricolaje',
'Cámaras y ópticas',
'Casa y jardín',
'Economía e industria',
'Electrónica',
'Elementos religiosos y ceremoniales',
'Equipamiento deportivo',
'Juegos y juguetes',
'Maletas y bolsos de viaje',
'Material de oficina',
'Mobiliario',
'Multimedia',
'Productos para adultos',
'Productos para mascotas y animales',
'Ropa y accesorios',
'Salud y belleza',
'Software',
'Vehículos y recambios',
],
}
},
mounted() {
this.startTyping()
},
beforeDestroy() {
this.stopTyping()
},
methods: {
focused() {
this.stopTyping()
document.querySelector('#searchbox').setAttribute('placeholder', '')
},
focusedOut() {
document.querySelector('#searchbox').setAttribute('placeholder', '')
},
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
},
startTyping() {
let current = ''
let i = 0
let word = 0
let step = 0
let input = document.querySelector('#searchbox')
let placeholderTexts = [
'Jabón sólido',
'Huertos de libertad',
'Hierbabuena',
'Energía eléctrica',
]
this.typing = setInterval(() => {
if (current === placeholderTexts[word]) {
step++
if (step === 10) {
current = ''
i = 0
word++
input.setAttribute('placeholder', current)
step = 0
if (word === placeholderTexts.length) {
word = 0
}
}
} else {
current += placeholderTexts[word].charAt(i)
input.setAttribute('placeholder', current)
i++
}
}, 100)
},
stopTyping() {
clearInterval(this.typing)
},
search() {
if (this.searchText) {
if (this.selectedCategory !== '') {
return this.$router.push({
name: 'busqueda',
query: {
q: this.searchText,
category: this.selectedCategory,
},
})
} else {
return this.$router.push({
name: 'busqueda',
query: { q: this.searchText },
})
}
}
},
clearSearchText() {
this.searchText = ''
},
},
}
</script>
<style lang="scss" scoped>
.wrapper {
width: 100%;
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;
border-radius: 40px;
overflow: hidden;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin: 15px 0 50px;
-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: 70%;
}
}
.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;
}
}
.categorias-wrapper {
height: 100%;
background: linear-gradient(90deg, #1aceb8, #0bbfba);
padding: 0.4em 0.5em 0.4em 1.2em;
@include mobile {
display: none;
}
}
.categorias {
cursor: pointer;
outline: none;
background: transparent;
color: $color-light;
height: 100%;
width: 18rem;
-moz-appearance: none;
text-align: center;
@include mobile {
display: none;
}
@include tablet {
width: 14rem;
}
}
.search-icon {
cursor: pointer;
@include mobile {
margin-left: 0;
width: 100%;
}
@include tablet {
float: right;
height: 70%;
}
}
.search-text {
outline: none;
width: 100%;
::-webkit-input-placeholder {
display: inline-block;
opacity: 0.2;
transition: all 0.3s ease-in-out;
padding-right: 0.15em;
white-space: nowrap;
}
input:focus::-webkit-input-placeholder {
-webkit-transform: translateY(-125%);
transform: translateY(-125%);
font-size: 75%;
opacity: 0.05;
}
input.imitatefocus::-webkit-input-placeholder {
-webkit-transform: translateY(-125%);
transform: translateY(-125%);
opacity: 0.05;
}
@include mobile {
border: none;
background: none;
outline: none;
float: left;
color: $color-greytext;
font-size: 16px;
transition: 0.4s;
line-height: 2.5rem;
width: 0rem;
}
@include tablet {
padding: 0 2rem;
}
}
select {
-webkit-appearance: auto;
}
.categorias option {
color: $color-grey-darker;
font-family: $font-primary, Arial;
}
@include mobile {
.search-container:matches(:hover, :focus-within) > .search-text {
width: 12rem;
padding: 0 1rem;
}
.search-text:hover > .search-link {
align-self: right;
}
}
</style>

224
components/SearchInput.vue Normal file
View File

@@ -0,0 +1,224 @@
<template>
<form class="search-container" @submit.prevent="search">
<div class="categorias-wrapper">
<select v-model="searchCategory" class="categorias">
<option selected value="">Todas las categorías</option>
<option
v-for="(category, key) in categories"
:key="key"
:value="category"
>
{{ category }}
</option>
</select>
</div>
<input
v-model="searchText"
class="search-text"
type="text"
autocomplete="off"
placeholder="Buscar"
/>
<div class="search-link">
<img
class="search-icon"
src="@/assets/img/latienda-search-blue.svg"
alt="latienda.coop-search"
@click="search"
/>
</div>
</form>
</template>
<script>
export default {
name: 'SearchInput',
data() {
return {
searchText: '',
searchCategory: '',
categories: [
'Alimentación, bebida y tabaco',
'Arte y ocio',
'Bebés y niños pequeños',
'Bricolaje',
'Cámaras y ópticas',
'Casa y jardín',
'Economía e industria',
'Electrónica',
'Elementos religiosos y ceremoniales',
'Equipamiento deportivo',
'Juegos y juguetes',
'Maletas y bolsos de viaje',
'Material de oficina',
'Mobiliario',
'Multimedia',
'Productos para adultos',
'Productos para mascotas y animales',
'Ropa y accesorios',
'Salud y belleza',
'Software',
'Vehículos y recambios',
],
}
},
methods: {
search() {
if (this.searchText) {
if (this.searchCategory) {
return this.$router.push({
name: 'busqueda',
query: { q: this.searchText, category: [this.searchCategory] },
})
} else {
return this.$router.push({
name: 'busqueda',
query: { q: this.searchText },
})
}
}
},
},
}
</script>
<style lang="scss" scoped>
.search-container {
background-color: rgba(255, 255, 255, 0.5);
height: 40px;
border-radius: 40px;
overflow: hidden;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin: 50px 5px;
@include mobile {
}
@include tablet {
width: 40%;
}
@include desktop {
width: 50%;
}
}
.categorias-wrapper {
height: 100%;
padding: 6px, 10px;
border-right: 1px solid rgba(104, 102, 102, 0.5);
@include mobile {
display: none;
}
@include tablet {
display: none;
}
@include desktop {
display: block;
}
}
.categorias {
outline: none;
border: none;
--webkit-appearance: none;
--moz-appearance: none;
background-color: rgba(255, 255, 255, 0.5);
background: transparent;
color: $color-navy;
height: 100%;
width: 200px;
font-size: $s;
padding-left: 1rem;
@include mobile {
display: none;
}
@include tablet {
display: none;
}
@include desktop {
display: block;
}
}
.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-icon {
@include mobile {
margin-left: 0;
width: 100%;
padding: 0.1rem;
}
@include tablet {
float: right;
width: 100%;
padding: 1rem;
}
@include desktop {
margin-left: 0;
width: 100%;
padding: 0.4rem;
}
}
.search-text {
outline: none;
border: none;
width: 100%;
text-align: center;
color: $color-navy;
font-size: $s;
@include mobile {
border: none;
background: none;
float: left;
color: $color-greytext;
font-size: 16px;
transition: 0.4s;
line-height: 2.5rem;
width: 0rem;
}
}
::placeholder {
color: $color-navy;
font-size: $s;
}
select {
--webkit-appearance: auto;
}
.search-container:matches(:hover, :focus-within) > .search-text {
@include mobile {
width: 60px;
padding: 0 0.2rem;
}
}
.search-text:hover > .search-link {
@include mobile {
align-self: right;
}
}
</style>

View File

@@ -0,0 +1,33 @@
<template>
<button class="submit-btn" type="submit">
<span>{{ text }}</span>
<img :src="imageUrl" alt="" />
</button>
</template>
<script>
export default {
props: {
text: {
type: String,
default: ''
},
imageUrl: {
type: String,
default: ''
},
},
}
</script>
<style lang="scss" scoped>
.submit-btn {
background-color: $color-orange;
color: $color-light;
border: none;
border-radius: 5px;
text-transform: uppercase;
padding: 15px 20px;
margin-top: 15px;
}
</style>