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-row>
|
||||||
<v-col cols="12" md="9" lg="7" :class="{ 'pb-mobile-cart': isMobile }">
|
<v-col cols="12" md="9" lg="7" :class="{ 'pb-mobile-cart': isMobile }">
|
||||||
<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">
|
||||||
<span class="headline">Catálogo</span>
|
<v-icon size="28" color="white" class="mr-2 d-sm-none">mdi-store</v-icon>
|
||||||
</v-card-title>
|
<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 -->
|
<!-- Controles de paginación superiores -->
|
||||||
<PaginationControls
|
<PaginationControls
|
||||||
v-if="items.length > 0"
|
v-if="filteredItems.length > 0"
|
||||||
:current-page="currentPage"
|
:current-page="currentPage"
|
||||||
:total-pages="totalPages"
|
:total-pages="totalPages"
|
||||||
:items-per-page="itemsPerPage"
|
:items-per-page="itemsPerPage"
|
||||||
@@ -52,10 +71,18 @@
|
|||||||
>
|
>
|
||||||
No hay productos disponibles en el catálogo
|
No hay productos disponibles en el catálogo
|
||||||
</v-alert>
|
</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 -->
|
<!-- Controles de paginación inferiores -->
|
||||||
<PaginationControls
|
<PaginationControls
|
||||||
v-if="items.length > 0"
|
v-if="filteredItems.length > 0"
|
||||||
:current-page="currentPage"
|
:current-page="currentPage"
|
||||||
:total-pages="totalPages"
|
:total-pages="totalPages"
|
||||||
:items-per-page="itemsPerPage"
|
:items-per-page="itemsPerPage"
|
||||||
@@ -225,6 +252,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
api: inject("api"),
|
api: inject("api"),
|
||||||
items: [],
|
items: [],
|
||||||
|
searchQuery: "",
|
||||||
// Paginación
|
// Paginación
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
itemsPerPage: 20,
|
itemsPerPage: 20,
|
||||||
@@ -257,14 +285,22 @@ export default {
|
|||||||
cartCount() {
|
cartCount() {
|
||||||
return this.cartStore.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
|
// Paginación
|
||||||
paginatedItems() {
|
paginatedItems() {
|
||||||
const start = (this.currentPage - 1) * this.itemsPerPage;
|
const start = (this.currentPage - 1) * this.itemsPerPage;
|
||||||
const end = start + this.itemsPerPage;
|
const end = start + this.itemsPerPage;
|
||||||
return this.items.slice(start, end);
|
return this.filteredItems.slice(start, end);
|
||||||
},
|
},
|
||||||
totalPages() {
|
totalPages() {
|
||||||
return Math.ceil(this.items.length / this.itemsPerPage);
|
return Math.ceil(this.filteredItems.length / this.itemsPerPage);
|
||||||
},
|
},
|
||||||
paginationInfo() {
|
paginationInfo() {
|
||||||
const start = (this.currentPage - 1) * this.itemsPerPage + 1;
|
const start = (this.currentPage - 1) * this.itemsPerPage + 1;
|
||||||
@@ -275,7 +311,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
start,
|
start,
|
||||||
end,
|
end,
|
||||||
total: this.items.length,
|
total: this.filteredItems.length,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// Computed para total-visible dinámico y responsive (usado por ambos PaginationControls)
|
// Computed para total-visible dinámico y responsive (usado por ambos PaginationControls)
|
||||||
@@ -306,6 +342,11 @@ export default {
|
|||||||
this.loadItemsPerPagePreference();
|
this.loadItemsPerPagePreference();
|
||||||
this.fetchProducts();
|
this.fetchProducts();
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
searchQuery() {
|
||||||
|
this.currentPage = 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
fetchProducts() {
|
fetchProducts() {
|
||||||
this.api
|
this.api
|
||||||
@@ -446,7 +487,7 @@ export default {
|
|||||||
},
|
},
|
||||||
scrollToTop() {
|
scrollToTop() {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
const catalogHeader = this.$el?.querySelector(".headline");
|
const catalogHeader = this.$el?.querySelector(".page-header");
|
||||||
if (catalogHeader) {
|
if (catalogHeader) {
|
||||||
catalogHeader.scrollIntoView({
|
catalogHeader.scrollIntoView({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
@@ -468,59 +509,60 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.headline {
|
.page-header {
|
||||||
font-weight: bold;
|
position: sticky;
|
||||||
font-size: 1.25rem;
|
top: 80px;
|
||||||
|
z-index: 10;
|
||||||
|
background: linear-gradient(135deg, #1565C0 0%, #0D47A1 100%) !important;
|
||||||
|
color: white !important;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === LISTADO DE PRODUCTOS === */
|
@media (max-width: 959px) {
|
||||||
.catalog-item {
|
.page-header {
|
||||||
padding: 0;
|
top: 0;
|
||||||
margin-bottom: 12px;
|
border-radius: 0 !important;
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Padding para el contenido del catálogo en mobile */
|
@media (max-width: 559px) {
|
||||||
.pb-mobile-cart {
|
.page-header {
|
||||||
padding-bottom: 76px !important; /* 60px sidebar + 16px margen */
|
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) {
|
@media (min-width: 960px) {
|
||||||
.cart-sidebar {
|
.cart-sidebar {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
@@ -543,18 +585,12 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolución <560px - Mobile extra small */
|
|
||||||
@media (max-width: 559px) {
|
@media (max-width: 559px) {
|
||||||
.headline {
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.catalog-item {
|
.catalog-item {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Resolución 560-959px - Tablet */
|
|
||||||
@media (min-width: 560px) and (max-width: 959px) {
|
@media (min-width: 560px) and (max-width: 959px) {
|
||||||
.catalog-item {
|
.catalog-item {
|
||||||
margin-bottom: 14px;
|
margin-bottom: 14px;
|
||||||
|
|||||||
Reference in New Issue
Block a user