feat: add search bar and restyle catalog header matching Nueva Compra
- Replace plain title with gradient page-header (icon, title, subtitle) - Add search field with mdi-magnify icon and real-time name filtering - Integrate search into the header as a single sticky unit - Add filteredItems computed for client-side product search - Reset to page 1 on search query change - Show distinct message when search yields no results - Adapt pagination to work with filtered results
This commit is contained in:
@@ -9,13 +9,32 @@
|
||||
|
||||
<v-row>
|
||||
<v-col cols="12" md="9" lg="7" :class="{ 'pb-mobile-cart': isMobile }">
|
||||
<v-card-title>
|
||||
<span class="headline">Catálogo</span>
|
||||
</v-card-title>
|
||||
<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="white" class="mr-2 d-sm-none">mdi-store</v-icon>
|
||||
<v-icon size="36" color="white" class="mr-3 d-none d-sm-inline">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">
|
||||
<h1 class="text-h6 text-sm-h5 text-md-h4 font-weight-bold text-white mb-0">Catálogo</h1>
|
||||
<p class="text-body-2 text-white text-medium-emphasis mb-0">Explora y agrega productos a tu compra</p>
|
||||
</div>
|
||||
<v-spacer></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"
|
||||
/>
|
||||
</div>
|
||||
</v-sheet>
|
||||
|
||||
<!-- Controles de paginación superiores -->
|
||||
<PaginationControls
|
||||
v-if="items.length > 0"
|
||||
v-if="filteredItems.length > 0"
|
||||
:current-page="currentPage"
|
||||
:total-pages="totalPages"
|
||||
:items-per-page="itemsPerPage"
|
||||
@@ -52,10 +71,18 @@
|
||||
>
|
||||
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="items.length > 0"
|
||||
v-if="filteredItems.length > 0"
|
||||
:current-page="currentPage"
|
||||
:total-pages="totalPages"
|
||||
:items-per-page="itemsPerPage"
|
||||
@@ -225,6 +252,7 @@ export default {
|
||||
return {
|
||||
api: inject("api"),
|
||||
items: [],
|
||||
searchQuery: "",
|
||||
// Paginación
|
||||
currentPage: 1,
|
||||
itemsPerPage: 20,
|
||||
@@ -257,14 +285,22 @@ export default {
|
||||
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.items.slice(start, end);
|
||||
return this.filteredItems.slice(start, end);
|
||||
},
|
||||
totalPages() {
|
||||
return Math.ceil(this.items.length / this.itemsPerPage);
|
||||
return Math.ceil(this.filteredItems.length / this.itemsPerPage);
|
||||
},
|
||||
paginationInfo() {
|
||||
const start = (this.currentPage - 1) * this.itemsPerPage + 1;
|
||||
@@ -275,7 +311,7 @@ export default {
|
||||
return {
|
||||
start,
|
||||
end,
|
||||
total: this.items.length,
|
||||
total: this.filteredItems.length,
|
||||
};
|
||||
},
|
||||
// Computed para total-visible dinámico y responsive (usado por ambos PaginationControls)
|
||||
@@ -306,6 +342,11 @@ export default {
|
||||
this.loadItemsPerPagePreference();
|
||||
this.fetchProducts();
|
||||
},
|
||||
watch: {
|
||||
searchQuery() {
|
||||
this.currentPage = 1;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
fetchProducts() {
|
||||
this.api
|
||||
@@ -446,7 +487,7 @@ export default {
|
||||
},
|
||||
scrollToTop() {
|
||||
this.$nextTick(() => {
|
||||
const catalogHeader = this.$el?.querySelector(".headline");
|
||||
const catalogHeader = this.$el?.querySelector(".page-header");
|
||||
if (catalogHeader) {
|
||||
catalogHeader.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
@@ -468,59 +509,60 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.headline {
|
||||
font-weight: bold;
|
||||
font-size: 1.25rem;
|
||||
.page-header {
|
||||
position: sticky;
|
||||
top: 80px;
|
||||
z-index: 10;
|
||||
background: linear-gradient(135deg, #1565C0 0%, #0D47A1 100%) !important;
|
||||
color: white !important;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* === LISTADO DE PRODUCTOS === */
|
||||
.catalog-item {
|
||||
padding: 0;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.catalog-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Mobile First: Estilos base (< 960px) */
|
||||
.cart-sidebar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
transition: all 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.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.3s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
@media (max-width: 959px) {
|
||||
.page-header {
|
||||
top: 0;
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Padding para el contenido del catálogo en mobile */
|
||||
.pb-mobile-cart {
|
||||
padding-bottom: 76px !important; /* 60px sidebar + 16px margen */
|
||||
@media (max-width: 559px) {
|
||||
.page-header {
|
||||
padding: 12px 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.page-header .search-field {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 8px;
|
||||
transition: background 0.2s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.page-header .search-field:hover,
|
||||
.page-header .search-field:focus-within {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
|
||||
@media (max-width: 559px) {
|
||||
.page-header .search-field :deep(.v-field__input) {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Desktop (≥ 960px) - Sidebar sticky con scroll */
|
||||
/* Layout: 960-1023px = md breakpoint (75% productos / 25% cart) */
|
||||
/* Layout: ≥1024px = lg breakpoint (60% productos / 40% cart) */
|
||||
@media (min-width: 960px) {
|
||||
.cart-sidebar {
|
||||
position: sticky;
|
||||
@@ -543,18 +585,12 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolución <560px - Mobile extra small */
|
||||
@media (max-width: 559px) {
|
||||
.headline {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.catalog-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Resolución 560-959px - Tablet */
|
||||
@media (min-width: 560px) and (max-width: 959px) {
|
||||
.catalog-item {
|
||||
margin-bottom: 14px;
|
||||
|
||||
Reference in New Issue
Block a user