#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>
<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
v-for="item in items"
:key="item.title"
@@ -31,9 +39,24 @@
</a>
</div>
</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>
<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue';
const items = [
{
title: 'Vuetify Documentation',
@@ -65,15 +88,48 @@
icon: `mdi-reddit`,
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>
<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))
text-decoration: none
transition: .2s ease-in-out
&:hover
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>

View File

@@ -1,7 +1,13 @@
<template>
<v-dialog v-model="show" max-width="400">
<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-form ref="form" @submit.prevent="onSubmit">
<v-text-field

View File

@@ -63,6 +63,18 @@
<!-- Footer con Controles de Cantidad -->
<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">
<v-btn
icon
@@ -71,7 +83,7 @@
color="error"
class="qty-btn"
@click="decrease(product)"
:disabled="disabled || product.quantity === 0"
:disabled="product.quantity === 0"
>
<v-icon size="20">mdi-minus</v-icon>
</v-btn>
@@ -87,7 +99,6 @@
single-line
flat
aria-label="Cantidad"
:disabled="disabled"
@input="handleQuantityChange"
/>
@@ -98,17 +109,18 @@
color="success"
class="qty-btn"
@click="handleIncrease"
:disabled="disabled"
>
<v-icon size="20">mdi-plus</v-icon>
</v-btn>
</div>
</template>
</v-card-actions>
</v-card>
</template>
<script>
export default {
emits: ['add-to-cart', 'request-login'],
props: {
product: {
type: Object,
@@ -312,6 +324,13 @@ export default {
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) */
@media (max-width: 374px) {
.product-content {

View File

@@ -49,20 +49,6 @@
</div>
</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 -->
<v-row class="product-grid" v-if="paginatedItems.length > 0">
<v-col
@@ -82,6 +68,7 @@
:updateQuantity="updateQuantity"
:disabled="!isAuthenticated"
@add-to-cart="addToCart"
@request-login="showLogin"
/>
</v-col>
</v-row>
@@ -136,7 +123,16 @@
@update-quantity="updateCartQuantity"
@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>
<p class="text-body-2 text-medium-emphasis mb-3">
Inicia sesión para agregar productos al carrito y finalizar tu compra
@@ -274,7 +270,8 @@ export default {
setup() {
const cartStore = useCartStore();
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 isMobile = computed(() => windowWidth.value < 960); // Cambiado de 680 a 960
@@ -295,6 +292,7 @@ export default {
cartStore,
authStore,
cartCollapsed,
showLoginPrompt,
isMobile,
windowWidth,
};
@@ -553,7 +551,14 @@ export default {
openLoginDialog() {
this.$refs.loginDialogRef.open();
},
showLogin() {
this.showLoginPrompt = true;
if (this.isMobile) {
this.cartCollapsed = false;
}
},
onLoginSuccess() {
this.showLoginPrompt = false;
this.api.getCurrentUser().then((user) => {
this.authStore.setUser(user);
});
@@ -769,6 +774,13 @@ export default {
.login-prompt-card {
border: 2px dashed rgba(0, 0, 0, 0.12);
background: #fafafa !important;
position: relative;
}
.login-prompt-close {
position: absolute;
top: 8px;
right: 8px;
}
/* ============================================