#45 feat: clean catalog view on mobile devices

This commit is contained in:
mono
2026-07-11 18:56:45 -05:00
parent 92483a110a
commit 3029e6d4b9
4 changed files with 177 additions and 84 deletions

View File

@@ -1,5 +1,13 @@
<template> <template>
<v-footer height="40" app> <div>
<!-- Footer completo: visible siempre en desktop, toggle en mobile -->
<v-footer
v-if="expanded || !isMobile"
height="40"
app
class="app-footer"
:class="{ 'footer-overlay': isMobile }"
>
<a <a
v-for="item in items" v-for="item in items"
:key="item.title" :key="item.title"
@@ -31,9 +39,24 @@
</a> </a>
</div> </div>
</v-footer> </v-footer>
<!-- Botón flotante: solo en mobile -->
<v-btn
v-if="isMobile"
icon
size="small"
class="footer-toggle-btn"
:color="expanded ? 'primary' : undefined"
@click="expanded = !expanded"
>
<v-icon :icon="expanded ? 'mdi-close' : '$vuetify'" :size="expanded ? 20 : 24" />
</v-btn>
</div>
</template> </template>
<script setup> <script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
const items = [ const items = [
{ {
title: 'Vuetify Documentation', title: 'Vuetify Documentation',
@@ -65,15 +88,48 @@
icon: `mdi-reddit`, icon: `mdi-reddit`,
href: 'https://reddit.com/r/vuetifyjs', href: 'https://reddit.com/r/vuetifyjs',
}, },
] ];
const expanded = ref(false);
const windowWidth = ref(typeof window !== 'undefined' ? window.innerWidth : 1200);
const isMobile = computed(() => windowWidth.value < 960);
const onResize = () => {
windowWidth.value = window.innerWidth;
};
onMounted(() => {
window.addEventListener('resize', onResize);
});
onUnmounted(() => {
window.removeEventListener('resize', onResize);
});
</script> </script>
<style scoped lang="sass"> <style scoped lang="sass">
.social-link :deep(.v-icon) .social-link :deep(.v-icon)
color: rgba(var(--v-theme-on-background), var(--v-disabled-opacity)) color: rgba(var(--v-theme-on-background), var(--v-disabled-opacity))
text-decoration: none text-decoration: none
transition: .2s ease-in-out transition: .2s ease-in-out
&:hover &:hover
color: rgba(25, 118, 210, 1) color: rgba(25, 118, 210, 1)
/* Botón flotante para mobile */
.footer-toggle-btn
position: fixed
bottom: 12px
right: 12px
z-index: 2000
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.25)
/* Overlay del footer en mobile */
.footer-overlay
position: fixed !important
bottom: 0
left: 0
right: 0
z-index: 1999
</style> </style>

View File

@@ -1,7 +1,13 @@
<template> <template>
<v-dialog v-model="show" max-width="400"> <v-dialog v-model="show" max-width="400">
<v-card> <v-card>
<v-card-title class="headline">Iniciar sesión</v-card-title> <v-card-title class="headline d-flex align-center">
Iniciar sesión
<v-spacer />
<v-btn icon size="small" variant="text" @click="show = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-card-title>
<v-card-text> <v-card-text>
<v-form ref="form" @submit.prevent="onSubmit"> <v-form ref="form" @submit.prevent="onSubmit">
<v-text-field <v-text-field

View File

@@ -63,6 +63,18 @@
<!-- Footer con Controles de Cantidad --> <!-- Footer con Controles de Cantidad -->
<v-card-actions class="product-actions pa-2 pb-3 justify-center"> <v-card-actions class="product-actions pa-2 pb-3 justify-center">
<template v-if="disabled">
<v-btn
color="primary"
variant="tonal"
prepend-icon="mdi-cart-plus"
class="login-add-btn"
@click="$emit('request-login')"
>
Agregar
</v-btn>
</template>
<template v-else>
<div class="quantity-controls"> <div class="quantity-controls">
<v-btn <v-btn
icon icon
@@ -71,7 +83,7 @@
color="error" color="error"
class="qty-btn" class="qty-btn"
@click="decrease(product)" @click="decrease(product)"
:disabled="disabled || product.quantity === 0" :disabled="product.quantity === 0"
> >
<v-icon size="20">mdi-minus</v-icon> <v-icon size="20">mdi-minus</v-icon>
</v-btn> </v-btn>
@@ -87,7 +99,6 @@
single-line single-line
flat flat
aria-label="Cantidad" aria-label="Cantidad"
:disabled="disabled"
@input="handleQuantityChange" @input="handleQuantityChange"
/> />
@@ -98,17 +109,18 @@
color="success" color="success"
class="qty-btn" class="qty-btn"
@click="handleIncrease" @click="handleIncrease"
:disabled="disabled"
> >
<v-icon size="20">mdi-plus</v-icon> <v-icon size="20">mdi-plus</v-icon>
</v-btn> </v-btn>
</div> </div>
</template>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</template> </template>
<script> <script>
export default { export default {
emits: ['add-to-cart', 'request-login'],
props: { props: {
product: { product: {
type: Object, type: Object,
@@ -312,6 +324,13 @@ export default {
RESPONSIVE BREAKPOINTS RESPONSIVE BREAKPOINTS
============================================ */ ============================================ */
/* Botón de agregar (no autenticado) */
.login-add-btn {
width: 100%;
font-weight: 600;
letter-spacing: 0.5px;
}
/* Móvil pequeño (< 375px) */ /* Móvil pequeño (< 375px) */
@media (max-width: 374px) { @media (max-width: 374px) {
.product-content { .product-content {

View File

@@ -49,20 +49,6 @@
</div> </div>
</v-sheet> </v-sheet>
<!-- Controles de paginación superiores -->
<PaginationControls
v-if="filteredItems.length > 0"
:current-page="currentPage"
:total-pages="totalPages"
:items-per-page="itemsPerPage"
:items-per-page-options="itemsPerPageOptions"
:pagination-info="paginationInfo"
:total-visible-pages="totalVisiblePages"
@page-change="handlePageChange"
@items-per-page-change="handleItemsPerPageChange"
position="top"
/>
<!-- Grid de productos paginados --> <!-- Grid de productos paginados -->
<v-row class="product-grid" v-if="paginatedItems.length > 0"> <v-row class="product-grid" v-if="paginatedItems.length > 0">
<v-col <v-col
@@ -82,6 +68,7 @@
:updateQuantity="updateQuantity" :updateQuantity="updateQuantity"
:disabled="!isAuthenticated" :disabled="!isAuthenticated"
@add-to-cart="addToCart" @add-to-cart="addToCart"
@request-login="showLogin"
/> />
</v-col> </v-col>
</v-row> </v-row>
@@ -136,7 +123,16 @@
@update-quantity="updateCartQuantity" @update-quantity="updateCartQuantity"
@toggle-collapse="toggleCart" @toggle-collapse="toggleCart"
/> />
<v-card v-else class="login-prompt-card pa-4 text-center"> <v-card v-else-if="!isMobile || showLoginPrompt" class="login-prompt-card pa-4 text-center">
<v-btn
icon
size="x-small"
variant="text"
class="login-prompt-close"
@click="showLoginPrompt = false"
>
<v-icon size="18">mdi-close</v-icon>
</v-btn>
<v-icon size="48" color="primary" class="mb-2">mdi-cart-lock</v-icon> <v-icon size="48" color="primary" class="mb-2">mdi-cart-lock</v-icon>
<p class="text-body-2 text-medium-emphasis mb-3"> <p class="text-body-2 text-medium-emphasis mb-3">
Inicia sesión para agregar productos al carrito y finalizar tu compra Inicia sesión para agregar productos al carrito y finalizar tu compra
@@ -274,7 +270,8 @@ export default {
setup() { setup() {
const cartStore = useCartStore(); const cartStore = useCartStore();
const authStore = useAuthStore(); const authStore = useAuthStore();
const cartCollapsed = ref(false); // Cambiado a false para que inicie expandido en desktop const cartCollapsed = ref(false);
const showLoginPrompt = ref(false);
const windowWidth = ref(window.innerWidth); const windowWidth = ref(window.innerWidth);
const isMobile = computed(() => windowWidth.value < 960); // Cambiado de 680 a 960 const isMobile = computed(() => windowWidth.value < 960); // Cambiado de 680 a 960
@@ -295,6 +292,7 @@ export default {
cartStore, cartStore,
authStore, authStore,
cartCollapsed, cartCollapsed,
showLoginPrompt,
isMobile, isMobile,
windowWidth, windowWidth,
}; };
@@ -553,7 +551,14 @@ export default {
openLoginDialog() { openLoginDialog() {
this.$refs.loginDialogRef.open(); this.$refs.loginDialogRef.open();
}, },
showLogin() {
this.showLoginPrompt = true;
if (this.isMobile) {
this.cartCollapsed = false;
}
},
onLoginSuccess() { onLoginSuccess() {
this.showLoginPrompt = false;
this.api.getCurrentUser().then((user) => { this.api.getCurrentUser().then((user) => {
this.authStore.setUser(user); this.authStore.setUser(user);
}); });
@@ -769,6 +774,13 @@ export default {
.login-prompt-card { .login-prompt-card {
border: 2px dashed rgba(0, 0, 0, 0.12); border: 2px dashed rgba(0, 0, 0, 0.12);
background: #fafafa !important; background: #fafafa !important;
position: relative;
}
.login-prompt-close {
position: absolute;
top: 8px;
right: 8px;
} }
/* ============================================ /* ============================================