Use searchbar and debouncer

This commit is contained in:
William Buezas 2024-09-23 08:08:24 -03:00
parent 72cd9e6cd6
commit e39cab82f6
3 changed files with 144 additions and 188 deletions

View File

@ -12,7 +12,7 @@ const props = defineProps({
},
placeholder: {
type: String,
default: 'Search'
default: ''
},
sqlQuery: {
type: String,

View File

@ -1,25 +1,7 @@
<template>
<Teleport v-if="isHeaderMounted" to="#actions">
<!-- <QInput
:placeholder="$t('search')"
v-model="search"
debounce="500"
class="search q-mr-sm"
rounded
dark
dense
standout
>
<template #prepend>
<QIcon v-if="search === ''" name="search" />
<QIcon
v-else
name="clear"
class="cursor-pointer"
@click="search = ''"
/>
</template>
</QInput> -->
<div class="q-gutter-x-sm row">
<VnSearchBar :search-term="search" @on-search-error="items = []" />
<QBtn
:icon="viewTypeButtonContent.icon"
:label="viewTypeButtonContent.label"
@ -42,6 +24,7 @@
{{ t('shoppingCart') }}
</QTooltip>
</QBtn>
</div>
</Teleport>
<div style="padding-bottom: 5em">
<QDrawer v-model="rightDrawerOpen" side="right" :width="250">
@ -96,7 +79,7 @@
</QBtn>
</div>
</div>
<div class="q-mt-md" v-if="category || search">
<div class="q-mt-md" v-if="selectedCategory">
<div class="q-mb-xs text-grey-7">
{{ t('filterBy') }}
</div>
@ -157,69 +140,6 @@
Elige una categoria
</span>
</div>
<!-- <div class="q-pa-md" v-if="typeId || search">
<div class="q-mb-md" v-for="tag in tags" :key="tag.uid">
<div class="q-mb-xs text-caption text-grey-7">
{{ tag.name }}
<QIcon
v-if="tag.hasFilter"
style="font-size: 1.3em"
name="cancel"
:title="$t('deleteFilter')"
class="cursor-pointer"
@click="onResetTagFilterClick(tag)"
/>
</div>
<div v-if="!tag.useRange">
<div
v-for="value in tag.values.slice(0, tag.showCount)"
:key="value"
>
<QCheckbox
v-model="tag.filter"
:dense="true"
:val="value"
:label="value"
@input="onCheck(tag)"
/>
</div>
<div v-if="tag.values.length > tag.showCount">
<span
class="cursor-pointer text-blue"
@click="tag.showCount = Infinity"
>
<QIcon name="keyboard_arrow_down" />
{{ $t('viewMore') }}
</span>
</div>
<div v-if="tag.showCount == Infinity">
<span
class="cursor-pointer text-blue"
@click="tag.showCount = tag.initialCount"
>
<QIcon name="keyboard_arrow_up" />
{{ $t('viewLess') }}
</span>
</div>
</div>
<div class="q-mx-md">
<QRange
class="q-mt-lg"
v-if="tag.useRange"
v-model="tag.filter"
:min="tag.min"
:max="tag.max"
:step="tag.step"
:color="tag.hasFilter ? 'primary' : 'grey-6'"
@input="onRangeChange(tag, true)"
@change="onRangeChange(tag)"
label-always
markers
snap
/>
</div>
</div>
</div> -->
</QDrawer>
<QInfiniteScroll>
<div
@ -342,17 +262,19 @@
<script setup>
import { useI18n } from 'vue-i18n';
import { inject, onBeforeMount, ref, computed } from 'vue';
import { inject, onBeforeMount, ref, computed, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import VnImg from 'src/components/ui/VnImg.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import CatalogCard from 'src/pages/Ecomerce/CatalogCard.vue';
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
import { useAppStore } from 'stores/app';
import { storeToRefs } from 'pinia';
import { formatDateTitle, currency } from 'src/lib/filters.js';
import useNotify from 'src/composables/useNotify.js';
import debounce from 'src/utils/debouncer.js';
const jApi = inject('jApi');
const { t } = useI18n();
@ -378,6 +300,7 @@ const itemFamilies = ref([]);
const itemProducers = ref([]);
const itemOrigins = ref([]);
const itemSubcategories = ref([]);
const search = ref(null);
// Filters values
const category = ref(null);
const type = ref(null);
@ -437,11 +360,10 @@ const selectedCategory = computed({
},
async set(value) {
category.value = value;
router.push({ params: { category: category.value, type: null } });
onCategoryChange();
if (!value) return;
getFilters();
getItems();
debouncedGetFilters();
debouncedGetItems();
refreshTitle();
}
});
@ -452,10 +374,13 @@ const selectedType = computed({
},
set(value) {
type.value = value;
router.push({ params: { category: category.value, type: type.value } });
router.push({
params: { category: category.value, type: type.value },
query: { ...route.query }
});
if (!selectedCategory.value) return;
getFilters();
getItems();
debouncedGetFilters();
debouncedGetItems();
refreshTitle();
}
});
@ -467,8 +392,8 @@ const selectedColor = computed({
set(value) {
color.value = value;
if (!selectedCategory.value) return;
getFilters();
getItems();
debouncedGetFilters();
debouncedGetItems();
}
});
@ -479,8 +404,8 @@ const selectedProducer = computed({
set(value) {
producer.value = value;
if (!value || !selectedCategory.value) return;
getFilters();
getItems();
debouncedGetFilters();
debouncedGetItems();
}
});
@ -491,8 +416,8 @@ const selectedOrigin = computed({
set(value) {
origin.value = value;
if (!value || !selectedCategory.value) return;
getFilters();
getItems();
debouncedGetFilters();
debouncedGetItems();
}
});
@ -503,8 +428,8 @@ const selectedSubcategory = computed({
set(value) {
subcategory.value = value;
if (!value && !selectedCategory.value) return;
getFilters();
getItems();
debouncedGetFilters();
debouncedGetItems();
}
});
@ -518,7 +443,7 @@ const selectedOrderBy = computed({
} else {
orderBy.value = 'i.relevancy DESC, longName';
}
getItems();
debouncedGetItems();
}
});
@ -530,6 +455,13 @@ const queryFilter = computed(() => {
filters.push(`(t.categoryFk = ${selectedCategory.value})`);
}
// Filtro búsqueda
if (search.value) {
filters.push(
`((i.longName LIKE '%${search.value}%') OR (i.subname LIKE '%${search.value}%'))`
);
}
const otherFilters = {
'i.typeFk': selectedType.value,
'i.inkFk': selectedColor?.value ? `'${selectedColor.value}'` : null,
@ -550,6 +482,7 @@ const queryFilter = computed(() => {
const isSomeFilterSelected = computed(() => {
return (
search.value ||
selectedType.value ||
selectedColor.value ||
selectedProducer.value ||
@ -565,6 +498,67 @@ const viewTypeButtonContent = computed(() => {
};
});
const getFilters = async () => {
console.log('getFilters');
const promises = [
getItemFamilies(),
getItemColors(),
getProducers(),
getOrigins(),
getSubcategories()
];
await Promise.allSettled(promises);
};
const getItems = async () => {
console.log('getItems');
try {
if (!basketOrderId.value || !isSomeFilterSelected.value) return;
loading.value = true;
const res = await jApi.execQuery(
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
CREATE TEMPORARY TABLE tmp.item
(INDEX (itemFk))
ENGINE = MEMORY
SELECT i.id itemFk
FROM vn.item i
JOIN vn.itemType t ON t.id = i.typeFk
WHERE (${queryFilter.value});
CALL myOrder_calcCatalogFull(#orderId);
SELECT i.id, i.longName item, i.subName,
i.tag5, i.value5, i.tag6, i.value6,
i.tag7, i.value7, i.tag8, i.value8,
i.relevancy, i.size, i.category, b.minQuantity,
k.name ink, p.name producer, o.name origin,
b.available, b.price, b.grouping,
i.image, im.updated
FROM tmp.ticketCalculateItem b
JOIN vn.item i ON i.id = b.itemFk
LEFT JOIN vn.ink k ON k.id = i.inkFk
LEFT JOIN vn.producer p ON p.id = i.producerFk
LEFT JOIN vn.origin o ON o.id = i.originFk
LEFT JOIN image im ON im.collectionFk = 'catalog'
AND im.name = i.image
WHERE b.available > 0
ORDER BY ${selectedOrderBy.value}
LIMIT 5000;
DROP TEMPORARY TABLE tmp.item;
CALL vn.ticketCalculatePurge();`,
{ orderId: basketOrderId.value }
);
items.value = res.results[3].data;
await onItemsFetched();
loading.value = false;
} catch (error) {
console.error('Error getting items:', error);
}
};
const debouncedGetItems = debounce(getItems, 400);
const debouncedGetFilters = debounce(getFilters, 400);
const getOrder = async () => {
try {
const [data] = await jApi.query(
@ -653,55 +647,6 @@ const onItemsFetched = async () => {
});
};
const getItems = async () => {
try {
if (
!selectedCategory.value ||
!basketOrderId.value ||
!isSomeFilterSelected.value
)
return;
loading.value = true;
const res = await jApi.execQuery(
`DROP TEMPORARY TABLE IF EXISTS tmp.item;
CREATE TEMPORARY TABLE tmp.item
(INDEX (itemFk))
ENGINE = MEMORY
SELECT i.id itemFk
FROM vn.item i
JOIN vn.itemType t ON t.id = i.typeFk
WHERE (${queryFilter.value});
CALL myOrder_calcCatalogFull(#orderId);
SELECT i.id, i.longName item, i.subName,
i.tag5, i.value5, i.tag6, i.value6,
i.tag7, i.value7, i.tag8, i.value8,
i.relevancy, i.size, i.category, b.minQuantity,
k.name ink, p.name producer, o.name origin,
b.available, b.price, b.grouping,
i.image, im.updated
FROM tmp.ticketCalculateItem b
JOIN vn.item i ON i.id = b.itemFk
LEFT JOIN vn.ink k ON k.id = i.inkFk
LEFT JOIN vn.producer p ON p.id = i.producerFk
LEFT JOIN vn.origin o ON o.id = i.originFk
LEFT JOIN image im ON im.collectionFk = 'catalog'
AND im.name = i.image
WHERE b.available > 0
ORDER BY ${selectedOrderBy.value}
LIMIT 5000;
DROP TEMPORARY TABLE tmp.item;
CALL vn.ticketCalculatePurge();`,
{ orderId: basketOrderId.value }
);
items.value = res.results[3].data;
await onItemsFetched();
loading.value = false;
} catch (error) {
console.error('Error getting items:', error);
}
};
const getProducers = async () => {
try {
const res = await jApi.execQuery(
@ -762,18 +707,6 @@ const getSubcategories = async () => {
}
};
const getFilters = async () => {
const promises = [
getItemFamilies(),
getItemColors(),
getProducers(),
getOrigins(),
getSubcategories()
];
await Promise.allSettled(promises);
};
const showItem = async item => {
if (isGuest.value) return;
@ -796,7 +729,9 @@ const onCategoryChange = () => {
selectedProducer.value = null;
selectedOrigin.value = null;
selectedSubcategory.value = null;
search.value = '';
items.value = [];
router.push({ params: { category: category.value, type: null } });
};
const getItemTags = async itemFk => {
@ -950,6 +885,16 @@ const onViewModeClick = () => {
viewMode.value = viewMode.value === 'list' ? 'grid' : 'list';
};
watch(
() => route.query.search,
val => {
if (val) {
search.value = val;
debouncedGetItems();
}
}
);
onBeforeMount(async () => {
if (!isGuest.value) {
await appStore.check('catalog');
@ -1021,13 +966,10 @@ en-US:
added: Added
listView: List view
gridView: Grid view
filterBy: Filter by
es-ES:
warehouse: Almacén
agency: Agencia
modify: Modificar
category: Categoría
deleteFilter: Quitar filtro
filterBy: Filtrar por
family: Familia
search: Buscar
color: Color
@ -1045,6 +987,7 @@ es-ES:
added: Añadido
listView: Vista de lista
gridView: Vista de rejilla
filterBy: Filtrar por
ca-ES:
category: Categoría
deleteFilter: Eliminar filtro
@ -1063,6 +1006,7 @@ ca-ES:
add: Afegir
listView: Vista de llista
gridView: Vista de graella
filterBy: Filtrar per
fr-FR:
category: Catégorie
deleteFilter: Supprimer le filtre
@ -1081,6 +1025,7 @@ fr-FR:
add: Ajouter
listView: Vue en liste
gridView: Vue en grille
filterBy: Filtrer par
pt-PT:
category: Categoria
deleteFilter: Apagar filtro
@ -1099,4 +1044,5 @@ pt-PT:
add: Adicionar
listView: Vista de lista
gridView: Vista de grade
filterBy: Filtrar por
</i18n>

10
src/utils/debouncer.js Normal file
View File

@ -0,0 +1,10 @@
export default function debounce(callback, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
// eslint-disable-next-line
callback(...args);
}, delay);
};
}