INDUSTRY STANDARD IMPLEMENTATION: - Change placeholder images from 300x200 (3:2) to 600x600 (1:1 square) - Set max-height to 300px with aspect-ratio 1:1 for consistent display - Follow e-commerce best practices (Amazon, Shopify, Mercado Libre) TEMPLATE CHANGES: - catalog.vue: Update placeholder URL to 600x600 square images - Card.vue: Add aspect-ratio="1" for perfect square display - Card.vue: Increase max-height from 140px to 300px - Restore padding: pa-2 → pa-3 (8px → 12px) - Restore title size: text-subtitle-2 → text-subtitle-1 - Restore margins: mb-1 → mb-2 - Restore chip size: x-small → small - Restore footer padding: pa-2 → pa-2 pb-3 CSS CHANGES: - Remove fixed height from .product-image-container (use 100% for flexibility) - Product name: 0.85rem → 0.95rem, min-height 2rem → 2.5rem - Price label: 0.6rem → 0.65rem - Price value: 0.9rem → 1.1rem - Price chip: height 22px → 26px, font 0.75rem → 0.9rem - Quantity input: 55px → 65px, font 0.85rem → 0.95rem - Section gaps: 2px → 4px, row gaps: 1px → 2px RESPONSIVE STRATEGY (optimized for 300px max): - Mobile XS (<375px): Natural scaling, compact layout - Mobile (375-559px): Natural scaling, readable text - Tablet (560-959px): Enhanced padding and fonts - Desktop (≥960px): Full 300px display with optimal spacing - Desktop L/XL: Maintain 300px with enhanced typography RESULT: - Square images (1:1) matching industry standard - 600x600 source allows retina displays and zoom - 300px display on desktop (sweet spot for catalogs) - Responsive scaling maintains aspect ratio - Professional e-commerce appearance
732 lines
20 KiB
Vue
732 lines
20 KiB
Vue
<template>
|
|
<v-container fluid>
|
|
<!-- Backdrop para mobile cuando el carrito está expandido -->
|
|
<div
|
|
v-if="isMobile && !cartCollapsed"
|
|
class="cart-backdrop"
|
|
@click="cartCollapsed = true"
|
|
></div>
|
|
|
|
<v-row>
|
|
<v-col cols="12" md="10" lg="9" :class="{ 'pb-mobile-cart': isMobile }">
|
|
<v-sheet
|
|
class="page-header d-flex align-center pa-3 pa-sm-4 pa-md-6 mb-3 mb-sm-4 rounded-lg"
|
|
>
|
|
<v-icon size="28" color="primary" class="mr-2 d-sm-none flex-shrink-0"
|
|
>mdi-store</v-icon
|
|
>
|
|
<v-icon size="36" color="primary" class="mr-3 d-none d-sm-inline flex-shrink-0"
|
|
>mdi-store</v-icon
|
|
>
|
|
<div
|
|
class="d-flex flex-column flex-sm-row align-start align-sm-center w-100 ga-2 ga-sm-4"
|
|
>
|
|
<div class="flex-shrink-0 d-none d-sm-block">
|
|
<h1
|
|
class="text-h6 text-sm-h5 text-md-h4 font-weight-bold text-primary mb-0"
|
|
>
|
|
Catálogo
|
|
</h1>
|
|
<p class="text-body-2 text-medium-emphasis mb-0">
|
|
Explora y agrega productos a tu compra
|
|
</p>
|
|
</div>
|
|
<v-spacer class="d-none d-sm-flex"></v-spacer>
|
|
<v-text-field
|
|
v-model="searchQuery"
|
|
prepend-inner-icon="mdi-magnify"
|
|
label="Buscar producto..."
|
|
variant="solo-filled"
|
|
density="compact"
|
|
clearable
|
|
hide-details
|
|
single-line
|
|
class="search-field flex-grow-1 flex-sm-grow-0"
|
|
/>
|
|
</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
|
|
v-for="item in paginatedItems"
|
|
:key="item.id"
|
|
cols="12"
|
|
sm="6"
|
|
md="6"
|
|
lg="4"
|
|
class="product-col"
|
|
>
|
|
<Card
|
|
:product="item"
|
|
:increase="increase"
|
|
:decrease="decrease"
|
|
:currency="currency"
|
|
:updateQuantity="updateQuantity"
|
|
@add-to-cart="addToCart"
|
|
/>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<!-- Mensaje cuando no hay productos -->
|
|
<v-alert
|
|
v-if="items.length === 0"
|
|
type="info"
|
|
class="my-4"
|
|
variant="tonal"
|
|
>
|
|
No hay productos disponibles en el catálogo
|
|
</v-alert>
|
|
<v-alert
|
|
v-else-if="filteredItems.length === 0"
|
|
type="warning"
|
|
class="my-4"
|
|
variant="tonal"
|
|
>
|
|
No se encontraron productos con ese nombre
|
|
</v-alert>
|
|
|
|
<!-- Controles de paginación inferiores -->
|
|
<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="bottom"
|
|
/>
|
|
</v-col>
|
|
|
|
<v-col cols="12" md="2" lg="3">
|
|
<div
|
|
class="cart-sidebar"
|
|
:class="{ 'cart-is-collapsed': cartCollapsed && isMobile }"
|
|
>
|
|
<Cart
|
|
:cart-items="cartItems"
|
|
:currency="currency"
|
|
:is-collapsed="cartCollapsed"
|
|
:is-mobile="isMobile"
|
|
:window-width="windowWidth"
|
|
@remove="removeFromCart"
|
|
@checkout="goToCheckout"
|
|
@update-quantity="updateCartQuantity"
|
|
@toggle-collapse="toggleCart"
|
|
/>
|
|
</div>
|
|
</v-col>
|
|
</v-row>
|
|
|
|
<!-- Modal 1: Confirmación de productos -->
|
|
<v-dialog v-model="checkoutDialog" max-width="600" persistent>
|
|
<v-card>
|
|
<v-card-title class="headline">Confirmar Compra</v-card-title>
|
|
<v-card-text>
|
|
<v-list v-if="cartItems.length > 0" class="product-list-scroll">
|
|
<v-list-item v-for="item in cartItems" :key="item.id">
|
|
<div class="d-flex justify-space-between align-center">
|
|
<div>
|
|
<div class="font-weight-medium">{{ item.name }}</div>
|
|
<div class="text-caption text-grey">
|
|
{{ currency(item.price) }} x {{ item.quantity }}
|
|
</div>
|
|
</div>
|
|
<div class="font-weight-bold text-success">
|
|
{{ currency(item.price * item.quantity) }}
|
|
</div>
|
|
</div>
|
|
</v-list-item>
|
|
</v-list>
|
|
<v-divider class="my-3"></v-divider>
|
|
<div class="d-flex justify-space-between text-h6">
|
|
<span class="font-weight-bold">Total</span>
|
|
<span class="font-weight-bold text-success">{{
|
|
currency(cartStore.cartTotal)
|
|
}}</span>
|
|
</div>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn variant="text" @click="checkoutDialog = false">Cancelar</v-btn>
|
|
<v-btn color="primary" variant="elevated" @click="onConfirmCheckout"
|
|
>Confirmar</v-btn
|
|
>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
|
|
<!-- Modal 2: Datos personales -->
|
|
<v-dialog v-model="personalDataDialog" max-width="500" persistent>
|
|
<v-card>
|
|
<v-card-title class="headline">Datos de Contacto</v-card-title>
|
|
<v-card-text>
|
|
<v-form ref="personalForm">
|
|
<v-text-field
|
|
v-model="customerName"
|
|
label="Nombre completo"
|
|
:rules="[rules.required]"
|
|
required
|
|
variant="outlined"
|
|
class="mb-3"
|
|
></v-text-field>
|
|
<v-text-field
|
|
v-model="customerAddress"
|
|
label="Dirección"
|
|
variant="outlined"
|
|
class="mb-3"
|
|
></v-text-field>
|
|
<v-text-field
|
|
v-model="customerPhone"
|
|
label="Teléfono"
|
|
variant="outlined"
|
|
class="mb-3"
|
|
></v-text-field>
|
|
<v-select
|
|
v-model="pickupMethod"
|
|
:items="pickupOptions"
|
|
item-title="text"
|
|
item-value="value"
|
|
label="Recogida"
|
|
:rules="[rules.required]"
|
|
required
|
|
variant="outlined"
|
|
></v-select>
|
|
</v-form>
|
|
</v-card-text>
|
|
<v-card-actions>
|
|
<v-spacer></v-spacer>
|
|
<v-btn variant="text" @click="cancelPurchase">Cancelar</v-btn>
|
|
<v-btn
|
|
color="primary"
|
|
variant="elevated"
|
|
@click="onSubmitPurchase"
|
|
:loading="isSubmitting"
|
|
:disabled="isSubmitting"
|
|
>
|
|
Finalizar Compra
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</v-container>
|
|
</template>
|
|
|
|
<script>
|
|
import Card from "@/components/catalog/Card.vue";
|
|
import Cart from "@/components/catalog/Cart.vue";
|
|
import PaginationControls from "@/components/catalog/PaginationControls.vue";
|
|
import { useCartStore } from "@/stores/cart";
|
|
import { inject, ref, computed, onMounted, onUnmounted } from "vue";
|
|
|
|
export default {
|
|
components: {
|
|
Card,
|
|
Cart,
|
|
PaginationControls,
|
|
},
|
|
setup() {
|
|
const cartStore = useCartStore();
|
|
const cartCollapsed = ref(false); // Cambiado a false para que inicie expandido en desktop
|
|
const windowWidth = ref(window.innerWidth);
|
|
|
|
const isMobile = computed(() => windowWidth.value < 960); // Cambiado de 680 a 960
|
|
|
|
const updateWindowWidth = () => {
|
|
windowWidth.value = window.innerWidth;
|
|
};
|
|
|
|
onMounted(() => {
|
|
window.addEventListener("resize", updateWindowWidth);
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
window.removeEventListener("resize", updateWindowWidth);
|
|
});
|
|
|
|
return {
|
|
cartStore,
|
|
cartCollapsed,
|
|
isMobile,
|
|
windowWidth,
|
|
};
|
|
},
|
|
data() {
|
|
return {
|
|
api: inject("api"),
|
|
items: [],
|
|
searchQuery: "",
|
|
// Paginación
|
|
currentPage: 1,
|
|
itemsPerPage: 20,
|
|
itemsPerPageOptions: [10, 20, 50, 100],
|
|
checkoutDialog: false,
|
|
personalDataDialog: false,
|
|
customerName: "",
|
|
customerAddress: "",
|
|
customerPhone: "",
|
|
pickupMethod: "STORE",
|
|
pickupOptions: [
|
|
{ text: "En Sitio", value: "STORE" },
|
|
{ text: "Domicilio", value: "DELIVERY" },
|
|
],
|
|
isSubmitting: false,
|
|
rules: {
|
|
required: (value) => !!value || "Requerido.",
|
|
},
|
|
};
|
|
},
|
|
computed: {
|
|
cartItems: {
|
|
get() {
|
|
return this.cartStore.items;
|
|
},
|
|
set(value) {
|
|
this.cartStore.items = value;
|
|
},
|
|
},
|
|
cartCount() {
|
|
return this.cartStore.cartCount;
|
|
},
|
|
// Búsqueda
|
|
filteredItems() {
|
|
if (!this.searchQuery) return this.items;
|
|
const query = this.searchQuery.toLowerCase();
|
|
return this.items.filter((item) =>
|
|
item.name.toLowerCase().includes(query),
|
|
);
|
|
},
|
|
// Paginación
|
|
paginatedItems() {
|
|
const start = (this.currentPage - 1) * this.itemsPerPage;
|
|
const end = start + this.itemsPerPage;
|
|
return this.filteredItems.slice(start, end);
|
|
},
|
|
totalPages() {
|
|
return Math.ceil(this.filteredItems.length / this.itemsPerPage);
|
|
},
|
|
paginationInfo() {
|
|
const start = (this.currentPage - 1) * this.itemsPerPage + 1;
|
|
const end = Math.min(
|
|
this.currentPage * this.itemsPerPage,
|
|
this.items.length,
|
|
);
|
|
return {
|
|
start,
|
|
end,
|
|
total: this.filteredItems.length,
|
|
};
|
|
},
|
|
// Computed para total-visible dinámico y responsive (usado por ambos PaginationControls)
|
|
totalVisiblePages() {
|
|
// Si hay pocas páginas, mostrarlas todas (IMPORTANTE para mostrar iconos de navegación)
|
|
if (this.totalPages <= 7) {
|
|
return this.totalPages;
|
|
}
|
|
|
|
// Breakpoints responsivos basados en windowWidth
|
|
// OPTIMIZADO: Reducidos para evitar que desaparezcan los iconos de navegación
|
|
const width = this.windowWidth;
|
|
|
|
if (width < 400) {
|
|
return 3; // Extra small mobile
|
|
} else if (width < 680) {
|
|
return 5; // Mobile
|
|
} else if (width < 960) {
|
|
return 5; // Tablet (REDUCIDO de 7 → 5 para evitar overflow)
|
|
} else if (width < 1280) {
|
|
return 7; // Desktop small (REDUCIDO de 9 → 7)
|
|
} else {
|
|
return 9; // Desktop large (REDUCIDO de 11 → 9)
|
|
}
|
|
},
|
|
},
|
|
created() {
|
|
this.loadItemsPerPagePreference();
|
|
this.fetchProducts();
|
|
},
|
|
watch: {
|
|
searchQuery() {
|
|
this.currentPage = 1;
|
|
},
|
|
},
|
|
methods: {
|
|
fetchProducts() {
|
|
this.api
|
|
.getProducts()
|
|
.then((data) => {
|
|
this.items = data.map((product) => ({
|
|
...product,
|
|
quantity: 0,
|
|
img:
|
|
product.img ||
|
|
`https://picsum.photos/600/600?random=${product.id}`,
|
|
}));
|
|
})
|
|
.catch((error) => {
|
|
console.error(error);
|
|
});
|
|
},
|
|
increase(item) {
|
|
item.quantity = Number(item.quantity) + 1;
|
|
this.addToCart(item);
|
|
},
|
|
decrease(item) {
|
|
item.quantity = Math.max(0, Number(item.quantity) - 1);
|
|
if (item.quantity === 0) {
|
|
this.removeFromCart(item.id);
|
|
} else {
|
|
this.addToCart(item);
|
|
}
|
|
},
|
|
updateQuantity(item) {
|
|
if (item.quantity > 0) {
|
|
this.addToCart(item);
|
|
} else {
|
|
this.removeFromCart(item.id);
|
|
}
|
|
},
|
|
addToCart(item) {
|
|
if (item.quantity <= 0) return;
|
|
this.cartStore.addItem(item);
|
|
},
|
|
removeFromCart(itemId) {
|
|
this.cartStore.removeItem(itemId);
|
|
const item = this.items.find((i) => i.id === itemId);
|
|
if (item) {
|
|
item.quantity = 0;
|
|
}
|
|
},
|
|
updateCartQuantity({ itemId, quantity }) {
|
|
this.cartStore.updateQuantity({ itemId, quantity });
|
|
const productItem = this.items.find((i) => i.id === itemId);
|
|
if (productItem) {
|
|
productItem.quantity = quantity;
|
|
}
|
|
},
|
|
goToCheckout() {
|
|
this.checkoutDialog = true;
|
|
},
|
|
onConfirmCheckout() {
|
|
this.checkoutDialog = false;
|
|
this.personalDataDialog = true;
|
|
},
|
|
cancelPurchase() {
|
|
this.checkoutDialog = false;
|
|
this.personalDataDialog = false;
|
|
this.customerName = "";
|
|
this.customerAddress = "";
|
|
this.customerPhone = "";
|
|
this.pickupMethod = "STORE";
|
|
},
|
|
async onSubmitPurchase() {
|
|
const form = this.$refs.personalForm;
|
|
|
|
if (form) {
|
|
const { valid } = await form.validate();
|
|
if (!valid) return;
|
|
}
|
|
this.isSubmitting = true;
|
|
const payload = {
|
|
date: this.getCurrentDate(),
|
|
customer: 1,
|
|
notes: "",
|
|
payment_method: "CASH",
|
|
catalogsaleline_set: this.cartItems.map((item) => ({
|
|
product: item.id,
|
|
unit_price: item.price,
|
|
quantity: item.quantity,
|
|
measuring_unit: item.measuring_unit || "Unidad",
|
|
})),
|
|
customer_name: this.customerName,
|
|
customer_address: this.customerAddress,
|
|
customer_phone: this.customerPhone,
|
|
pickup_method: this.pickupMethod,
|
|
};
|
|
|
|
this.api
|
|
.createCatalogPurchase(payload)
|
|
.then((data) => {
|
|
this.cartStore.clearCart();
|
|
this.personalDataDialog = false;
|
|
this.$router.push({
|
|
path: "/summary_purchase",
|
|
query: { id: parseInt(data.id) },
|
|
});
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error al crear la compra:", error);
|
|
this.isSubmitting = false;
|
|
});
|
|
},
|
|
getCurrentDate() {
|
|
const today = new Date();
|
|
const gmtOffSet = -5;
|
|
const localDate = new Date(today.getTime() + gmtOffSet * 60 * 60 * 1000);
|
|
return localDate.toISOString().slice(0, 16);
|
|
},
|
|
toggleCart() {
|
|
this.cartCollapsed = !this.cartCollapsed;
|
|
},
|
|
// Paginación
|
|
handlePageChange(newPage) {
|
|
this.currentPage = newPage;
|
|
this.scrollToTop();
|
|
},
|
|
handleItemsPerPageChange(newValue) {
|
|
this.itemsPerPage = newValue;
|
|
this.currentPage = 1;
|
|
this.saveItemsPerPagePreference(newValue);
|
|
this.scrollToTop();
|
|
},
|
|
saveItemsPerPagePreference(value) {
|
|
localStorage.setItem("catalog_items_per_page", value);
|
|
},
|
|
loadItemsPerPagePreference() {
|
|
const saved = localStorage.getItem("catalog_items_per_page");
|
|
if (saved && this.itemsPerPageOptions.includes(parseInt(saved))) {
|
|
this.itemsPerPage = parseInt(saved);
|
|
}
|
|
},
|
|
scrollToTop() {
|
|
this.$nextTick(() => {
|
|
const catalogHeader = this.$el?.querySelector(".page-header");
|
|
if (catalogHeader) {
|
|
catalogHeader.scrollIntoView({
|
|
behavior: "smooth",
|
|
block: "start",
|
|
});
|
|
}
|
|
});
|
|
},
|
|
currency(val) {
|
|
if (val == null) return "-";
|
|
return new Intl.NumberFormat("es-CO", {
|
|
style: "currency",
|
|
currency: "COP",
|
|
minimumFractionDigits: 0,
|
|
}).format(val);
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* ============================================
|
|
CABECERA STICKY CON BÚSQUEDA
|
|
============================================ */
|
|
.page-header {
|
|
position: sticky;
|
|
top: 80px;
|
|
z-index: 5;
|
|
background: white !important;
|
|
color: #1565c0 !important;
|
|
overflow: visible;
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1) !important;
|
|
}
|
|
|
|
/* Mobile: Header sticky compensating for NavBar height, z-index menor que el cart */
|
|
@media (max-width: 959px) {
|
|
.page-header {
|
|
top: 64px;
|
|
border-radius: 0 !important;
|
|
z-index: 900;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 559px) {
|
|
.page-header {
|
|
padding: 12px 16px !important;
|
|
}
|
|
}
|
|
|
|
/* Estilos profundos para el campo de búsqueda de Vuetify */
|
|
.page-header :deep(.v-field) {
|
|
background-color: #f5f5f5 !important;
|
|
border-radius: 8px;
|
|
transition: background-color 0.2s ease;
|
|
}
|
|
|
|
.page-header :deep(.v-field:hover),
|
|
.page-header :deep(.v-field--focused) {
|
|
background-color: #e0e0e0 !important;
|
|
}
|
|
|
|
.page-header :deep(.v-field__input) {
|
|
color: #1565c0 !important;
|
|
}
|
|
|
|
.page-header :deep(.v-field__input::placeholder) {
|
|
color: rgba(0, 0, 0, 0.5) !important;
|
|
}
|
|
|
|
@media (max-width: 559px) {
|
|
.page-header .search-field :deep(.v-field__input) {
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.page-header .search-field {
|
|
width: 100%;
|
|
max-width: 100%;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 560px) {
|
|
.page-header .search-field {
|
|
min-width: 260px;
|
|
max-width: 360px;
|
|
}
|
|
}
|
|
|
|
@media (min-width: 960px) {
|
|
.page-header .search-field {
|
|
min-width: 320px;
|
|
max-width: 460px;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
CARRITO FLOTANTE (MOBILE FIRST)
|
|
============================================ */
|
|
.cart-sidebar {
|
|
--footer-height: 40px;
|
|
position: fixed;
|
|
bottom: var(--footer-height);
|
|
left: 0;
|
|
right: 0;
|
|
z-index: 1000;
|
|
transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
border-radius: 16px 16px 0 0;
|
|
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.15);
|
|
}
|
|
|
|
/* Cuando está colapsado en mobile, solo muestra el header (60px) */
|
|
.cart-sidebar.cart-is-collapsed {
|
|
transform: translateY(calc(100% - 60px));
|
|
}
|
|
|
|
.cart-backdrop {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
z-index: 999;
|
|
animation: fadeIn 0.2s ease-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Espacio inferior para evitar que productos queden ocultos bajo el cart */
|
|
.pb-mobile-cart {
|
|
padding-bottom: 100px !important;
|
|
}
|
|
|
|
/* ============================================
|
|
GRID DE PRODUCTOS
|
|
============================================ */
|
|
.product-grid {
|
|
margin: 0 -8px;
|
|
}
|
|
|
|
.product-col {
|
|
padding: 8px;
|
|
display: flex;
|
|
}
|
|
|
|
/* Asegurar que las cards ocupen toda la altura */
|
|
.product-col :deep(.product-card) {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Mobile: mayor espaciado vertical */
|
|
@media (max-width: 559px) {
|
|
.product-grid {
|
|
margin: 0 -6px;
|
|
}
|
|
|
|
.product-col {
|
|
padding: 6px;
|
|
}
|
|
}
|
|
|
|
/* Tablet: espaciado medio */
|
|
@media (min-width: 560px) and (max-width: 959px) {
|
|
.product-grid {
|
|
margin: 0 -8px;
|
|
}
|
|
|
|
.product-col {
|
|
padding: 8px;
|
|
}
|
|
}
|
|
|
|
/* Desktop: espaciado óptimo */
|
|
@media (min-width: 960px) {
|
|
.product-grid {
|
|
margin: 0 -12px;
|
|
}
|
|
|
|
.product-col {
|
|
padding: 12px;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
DISEÑO DESKTOP (>= 960px)
|
|
============================================ */
|
|
@media (min-width: 960px) {
|
|
.cart-sidebar {
|
|
position: sticky;
|
|
top: 96px;
|
|
z-index: 1;
|
|
max-height: calc(100vh - 120px);
|
|
overflow-y: auto;
|
|
box-shadow: none;
|
|
border-radius: 12px;
|
|
transform: none !important;
|
|
}
|
|
|
|
.cart-backdrop {
|
|
display: none;
|
|
}
|
|
|
|
.pb-mobile-cart {
|
|
padding-bottom: 16px !important;
|
|
}
|
|
}
|
|
|
|
/* ============================================
|
|
MODALES
|
|
============================================ */
|
|
.product-list-scroll {
|
|
max-height: 400px;
|
|
overflow-y: auto;
|
|
}
|
|
</style>
|