Implement orders catalog tag filter

This commit is contained in:
Kevin Martinez 2024-01-02 11:40:07 -04:00
parent 4de1b52c30
commit 606f4f0892
3 changed files with 183 additions and 13 deletions

View File

@ -41,7 +41,7 @@ const props = defineProps({
},
});
const emit = defineEmits(['refresh', 'clear', 'search', 'init']);
const emit = defineEmits(['refresh', 'clear', 'search', 'init', 'remove']);
const arrayData = useArrayData(props.dataKey, {
exprBuilder: props.exprBuilder,
@ -116,6 +116,7 @@ const tags = computed(() => {
async function remove(key) {
userParams.value[key] = null;
await search();
emit('remove', key)
}
function formatValue(value) {

View File

@ -6,6 +6,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
import VnSelectFilter from 'components/common/VnSelectFilter.vue';
import axios from 'axios';
import { useRoute } from 'vue-router';
import VnInput from 'components/common/VnInput.vue';
const { t } = useI18n();
const route = useRoute();
@ -14,6 +15,10 @@ const props = defineProps({
type: String,
required: true,
},
tags: {
type: Array,
required: true,
},
});
const categoryList = ref(null);
@ -21,16 +26,27 @@ const selectedCategoryFk = ref(null);
const typeList = ref(null);
const selectedTypeFk = ref(null);
const selectCategory = (params, category) => {
if (params.categoryFk === category?.id) {
const resetCategory = () => {
selectedCategoryFk.value = null;
params.categoryFk = null;
typeList.value = null;
};
const clearFilter = (key) => {
if (key === 'categoryFk') {
resetCategory();
}
};
const selectCategory = (params, category, search) => {
if (params.categoryFk === category?.id) {
resetCategory();
params.categoryFk = null;
} else {
selectedCategoryFk.value = category?.id;
params.categoryFk = category?.id;
loadTypes(category?.id);
}
search();
};
const loadTypes = async (categoryFk) => {
@ -67,6 +83,27 @@ function exprBuilder(param, value) {
return { [param]: value };
}
}
const selectedTag = ref(null);
const tagValues = ref([{}]);
const tagOptions = ref(null);
const applyTagFilter = (params, search) => {
if (!tagValues.value?.length) {
params.tagGroups = null;
search();
return;
}
params.tagGroups = JSON.stringify({
values: tagValues.value,
tagSelection: {
...selectedTag.value,
orgShowField: selectedTag.value.name,
},
tagFk: selectedTag.value.tagFk,
});
search();
};
</script>
<template>
@ -87,13 +124,19 @@ function exprBuilder(param, value) {
/>
<VnFilterPanel
:data-key="props.dataKey"
:search-button="true"
:hidden-tags="['orderFk', 'orderBy']"
:expr-builder="exprBuilder"
@init="onFilterInit"
@remove="clearFilter"
>
<template #tags="{ tag, formatFn }">
<strong v-if="tag.label === 'categoryFk'">
<div v-if="tag.label === 'tagGroups'">
<strong>
{{ JSON.parse(tag?.value).tagSelection?.name }}:
</strong>
<span>{{ (JSON.parse(tag?.value).values || []).map(item => item.value).join(' | ') }}</span>
</div>
<strong v-else-if="tag.label === 'categoryFk'">
{{ t(selectedCategory?.name || '') }}
</strong>
<strong v-else-if="tag.label === 'typeFk'">
@ -104,8 +147,8 @@ function exprBuilder(param, value) {
<span>{{ formatFn(tag.value) }}</span>
</div>
</template>
<template #body="{ params }">
<QList dense>
<template #body="{ params, searchFn }">
<QList dense style="max-width: 256px">
<QItem class="category-filter q-mt-md">
<div
v-for="category in categoryList"
@ -118,7 +161,7 @@ function exprBuilder(param, value) {
<QIcon
:name="category.icon"
class="category-icon"
@click="selectCategory(params, category)"
@click="selectCategory(params, category, searchFn)"
>
<QTooltip>
{{ t(category.name) }}
@ -126,7 +169,7 @@ function exprBuilder(param, value) {
</QIcon>
</div>
</QItem>
<QItem class="q-mt-md">
<QItem class="q-my-md">
<QItemSection>
<VnSelectFilter
:label="t('params.type')"
@ -140,7 +183,12 @@ function exprBuilder(param, value) {
emit-value
use-input
:disable="!selectedCategoryFk"
@update:model-value="(value) => (selectedTypeFk = value)"
@update:model-value="
(value) => {
selectedTypeFk = value;
searchFn();
}
"
>
<template #option="{ itemProps, opt }">
<QItem v-bind="itemProps">
@ -155,6 +203,88 @@ function exprBuilder(param, value) {
</VnSelectFilter>
</QItemSection>
</QItem>
<QSeparator />
<QItem class="q-mt-md">
<QItemSection>
<VnSelectFilter
:label="t('params.tag')"
v-model="selectedTag"
:options="props.tags || []"
option-value="id"
option-label="name"
dense
outlined
rounded
:emit-value="false"
use-input
/>
</QItemSection>
</QItem>
<QItem
v-for="(value, index) in tagValues"
:key="value"
class="q-mt-md filter-value"
>
<VnInput
v-if="selectedTag?.isFree"
v-model="value.value"
:label="t('params.value')"
is-outlined
class="filter-input"
/>
<VnSelectFilter
v-else
:label="t('params.value')"
v-model="value.value"
:options="tagOptions || []"
option-value="value"
option-label="value"
dense
outlined
rounded
emit-value
use-input
:disable="!selectedTag"
class="filter-input"
/>
<FetchData
v-if="selectedTag && !selectedTag.isFree"
:url="`Tags/${selectedTag?.id}/filterValue`"
limit="30"
auto-load
@on-fetch="(data) => (tagOptions = data)"
/>
<QIcon
name="delete"
class="filter-icon"
@click="(tagValues || []).splice(index, 1)"
/>
</QItem>
<QItem class="q-mt-lg">
<QIcon
name="add_circle"
class="filter-icon"
@click="tagValues.push({})"
/>
</QItem>
<QItem>
<QItemSection class="q-py-sm">
<QBtn
:label="t('Search')"
class="full-width"
color="primary"
dense
icon="search"
rounded
type="button"
unelevated
@click.stop="applyTagFilter(params, searchFn)"
/>
</QItemSection>
</QItem>
<QSeparator />
</QList>
</template>
</VnFilterPanel>
@ -188,6 +318,23 @@ function exprBuilder(param, value) {
cursor: pointer;
}
}
.filter-icon {
font-size: 24px;
color: $primary;
padding: 0 4px;
cursor: pointer;
}
.filter-input {
flex-shrink: 1;
min-width: 0;
}
.filter-value {
display: flex;
align-items: center;
}
</style>
<i18n>
@ -195,10 +342,14 @@ en:
params:
type: Type
orderBy: Order By
tag: Tag
value: Value
es:
params:
type: Tipo
orderBy: Ordenar por
tag: Etiqueta
value: Valor
Plant: Planta
Flower: Flor
Handmade: Confección

View File

@ -1,7 +1,7 @@
<script setup>
import { useStateStore } from 'stores/useStateStore';
import { useRoute } from 'vue-router';
import { onMounted, onUnmounted } from 'vue';
import {onMounted, onUnmounted, ref} from 'vue';
import { useI18n } from 'vue-i18n';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnSearchbar from 'components/ui/VnSearchbar.vue';
@ -20,12 +20,29 @@ const catalogParams = {
orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }),
};
const tags = ref([])
function exprBuilder(param, value) {
switch (param) {
case 'search':
return { 'i.name': { like: `%${value}%` } };
}
}
function extractTags(items) {
const resultTags = [];
(items || []).forEach((item) => {
(item.tags || []).forEach((tag) => {
const index = resultTags.findIndex((item) => item.tagFk === tag.tagFk);
if (index === -1) {
resultTags.push({ ...tag, priority: 1 });
} else {
resultTags[index].priority += 1;
}
});
});
tags.value = resultTags
}
</script>
<template>
@ -56,7 +73,7 @@ function exprBuilder(param, value) {
</Teleport>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
<QScrollArea class="fit text-grey-8">
<OrderCatalogFilter data-key="OrderCatalogList" />
<OrderCatalogFilter data-key="OrderCatalogList" :tags="tags" />
</QScrollArea>
</QDrawer>
<QPage class="column items-center q-pa-md">
@ -67,6 +84,7 @@ function exprBuilder(param, value) {
:limit="50"
:user-params="catalogParams"
auto-load
@on-fetch="extractTags"
>
<template #body="{ rows }">
<div class="catalog-list">