Skip to content

Commit

Permalink
Merge pull request #366 from amansinghbais/#360
Browse files Browse the repository at this point in the history
Improved: product filters fetching logic, and added support for client side searching and infinite scrolling (#360)
  • Loading branch information
ravilodhi authored Aug 8, 2024
2 parents eac969e + 56af957 commit 4b81984
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 43 deletions.
105 changes: 65 additions & 40 deletions src/components/AddProductFiltersModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,22 @@
</ion-toolbar>
</ion-header>

<ion-content>
<ion-content ref="contentRef" :scroll-events="true" @ionScroll="enableScrolling()">
<ion-searchbar :placeholder="translate('Search', { label })" v-model="queryString" @keyup.enter="search()"/>
<ion-row>
<ion-chip v-for="filter in selectedValues" outline :key="filter.id">
<ion-label>{{ filter }}</ion-label>
</ion-chip>
</ion-row>

<ion-list v-if="facetOptions.length">
<ion-item v-for="option in facetOptions" :key="option.id" @click="!isAlreadyApplied(option.id) ? updateSelectedValues(option.id) : null">
<div class="empty-state" v-if="isLoading">
<ion-item lines="none">
<ion-spinner name="crescent" slot="start" />
{{ translate("Fetching filters") }}
</ion-item>
</div>
<ion-list v-else-if="filteredOptions.length">
<ion-item v-for="option in filteredOptions" :key="option.id" @click="!isAlreadyApplied(option.id) ? updateSelectedValues(option.id) : null">
<ion-label v-if="isAlreadyApplied(option.id)">{{ option.label }}</ion-label>
<ion-checkbox v-if="!isAlreadyApplied(option.id)" :checked="selectedValues.includes(option.id)">
{{ option.label }}
Expand All @@ -44,7 +50,7 @@
</ion-fab-button>
</ion-fab>

<ion-infinite-scroll @ionInfinite="loadMoreFilters($event)" threshold="100px" :disabled="!isScrollable">
<ion-infinite-scroll @ionInfinite="loadMoreFilters($event)" threshold="100px" v-show="isScrollable" ref="infiniteScrollRef">
<ion-infinite-scroll-content loading-spinner="crescent" :loading-text="translate('Loading')"/>
</ion-infinite-scroll>
</ion-content>
Expand All @@ -70,76 +76,83 @@ import {
IonNote,
IonRow,
IonSearchbar,
IonSpinner,
IonTitle,
IonToolbar,
modalController
} from "@ionic/vue";
import { closeOutline, saveOutline } from 'ionicons/icons';
import { useStore } from "vuex";
import { UtilService } from "@/services/UtilService";
import { translate } from '@hotwax/dxp-components';
import { hasError } from "@/utils";
const queryString = ref('');
const facetOptions = ref([]) as any;
const isScrollable = ref(true);
const queryString = ref('');
const selectedValues = ref([]) as any;
const filteredOptions = ref([]) as any;
const availableOptions = ref([]) as any;
const pageSize = process.env.VUE_APP_VIEW_SIZE || 0;
const currentPage = ref(0);
const isScrollable = ref(true);
const isLoading = ref(false);
const isScrollingEnabled = ref(false);
const contentRef = ref({}) as any;
const infiniteScrollRef = ref({}) as any;
const props = defineProps(["label", "facetToSelect", "searchfield", "type"]);
const store = useStore();
const appliedFilters = computed(() => store.getters["util/getAppliedFilters"]);
const getFacetOptions = computed(() => store.getters["util/getFacetOptions"]);
onMounted(() => {
onMounted(async() => {
isLoading.value = true;
await store.dispatch("util/fetchProductFilters", { facetToSelect: props.facetToSelect, searchfield: props.searchfield })
facetOptions.value = getFacetOptions.value(props.searchfield);
availableOptions.value = JSON.parse(JSON.stringify(facetOptions.value))
getFilters();
selectedValues.value = JSON.parse(JSON.stringify(appliedFilters.value[props.type][props.searchfield]))
isLoading.value = false;
})
function closeModal() {
modalController.dismiss({ dismissed: true });
}
function search() {
getFilters();
}
async function getFilters(vSize?: any, vIndex?: any) {
const viewSize = vSize ? vSize : process.env.VUE_APP_VIEW_SIZE;
const viewIndex = vIndex ? vIndex : 0;
const payload = {
facetToSelect: props.facetToSelect,
docType: 'PRODUCT',
coreName: 'enterpriseSearch',
searchfield: props.searchfield,
jsonQuery: '{"query":"*:*","filter":["docType:PRODUCT"]}',
noConditionFind: 'N',
limit: viewSize,
q: queryString.value,
term: queryString.value,
offset: viewSize * viewIndex,
}
const resp = await UtilService.fetchFacets(payload);
if(!hasError(resp)) {
const results = resp.data.facetResponse.response
facetOptions.value = viewIndex === 0 ? results : [...facetOptions.value , ...results];
isScrollable.value = (facetOptions.value.length % process.env.VUE_APP_VIEW_SIZE) === 0;
isScrollable.value = true;
currentPage.value = 0;
filteredOptions.value = []
if(queryString.value.trim()) {
availableOptions.value = facetOptions.value.filter((option: any) => option.label.toLowerCase().includes(queryString.value.trim().toLowerCase()))
} else {
facetOptions.value = [];
isScrollable.value = false;
availableOptions.value = JSON.parse(JSON.stringify(facetOptions.value))
}
getFilters();
}
async function loadMoreFilters(event: any){
getFilters(
undefined,
Math.ceil(facetOptions.value.length / process.env.VUE_APP_VIEW_SIZE).toString()
).then(() => {
// Added this check here as if added on infinite-scroll component the Loading content does not gets displayed
if(!(isScrollingEnabled.value && isScrollable.value)) {
await event.target.complete();
}
getFilters().then(() => {
event.target.complete();
})
}
async function getFilters() {
const nextPageItems = availableOptions.value.slice(currentPage.value * pageSize, (currentPage.value + 1) * pageSize);
filteredOptions.value = filteredOptions.value.concat(nextPageItems);
currentPage.value += 1;
if(filteredOptions.value.length >= availableOptions.value.length) {
isScrollable.value = false;
}
}
function updateSelectedValues(value: string) {
selectedValues.value.includes(value) ? selectedValues.value.splice(selectedValues.value.indexOf(value), 1) : selectedValues.value.push(value);
}
Expand All @@ -156,6 +169,18 @@ async function saveFilters() {
await store.dispatch('util/updateAppliedFilters', selectedFilters)
modalController.dismiss()
}
function enableScrolling() {
const parentElement = contentRef.value.$el
const scrollEl = parentElement.shadowRoot.querySelector("div[part='scroll']")
let scrollHeight = scrollEl.scrollHeight, infiniteHeight = infiniteScrollRef.value.$el.offsetHeight, scrollTop = scrollEl.scrollTop, threshold = 100, height = scrollEl.offsetHeight
const distanceFromInfinite = scrollHeight - infiniteHeight - scrollTop - threshold - height
if(distanceFromInfinite < 0) {
isScrollingEnabled.value = false;
} else {
isScrollingEnabled.value = true;
}
}
</script>

<style scoped>
Expand Down
1 change: 1 addition & 0 deletions src/store/modules/util/UtilState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ export default interface UtilState {
selectedSegment: any;
pickupGroups: any;
pickupGroupFacilities: any;
facetOptions: any;
}
28 changes: 28 additions & 0 deletions src/store/modules/util/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,34 @@ const actions: ActionTree<UtilState, RootState> = {
return pickupGroupFacilities;
},

async fetchProductFilters({ commit, state }, params) {
const filters = JSON.parse(JSON.stringify(state.facetOptions));

if(filters[params.searchfield]) return;

const payload = {
facetToSelect: params.facetToSelect,
docType: 'PRODUCT',
coreName: 'enterpriseSearch',
jsonQuery: '{"query":"*:*","filter":["docType:PRODUCT"]}',
noConditionFind: 'N',
limit: -1,
term: "",
}

try {
const resp = await UtilService.fetchFacets(payload);
if(!hasError(resp)) {
filters[params.searchfield] = resp.data.facetResponse.response
} else {
throw resp.data;
}
} catch(error: any) {
logger.error(error);
}
commit(types.UTIL_FACET_OPTIONS_UPDATED, filters);
},

async updatePickupGroupFacilities({ commit }, payload) {
commit(types.UTIL_PICKUP_GROUP_FACILITIES , payload);
},
Expand Down
3 changes: 3 additions & 0 deletions src/store/modules/util/getters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ const getters: GetterTree<UtilState, RootState> = {
getPickupGroupFacilities(state) {
return state.pickupGroupFacilities
},
getFacetOptions: (state) => (searchField: any) => {
return state.facetOptions[searchField].length ? state.facetOptions[searchField] : [];
},
}

export default getters;
3 changes: 2 additions & 1 deletion src/store/modules/util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ const utilModule: Module<UtilState, RootState> = {
},
selectedSegment: "",
pickupGroups: [],
pickupGroupFacilities: {}
pickupGroupFacilities: {},
facetOptions: {}
},
getters,
actions,
Expand Down
3 changes: 2 additions & 1 deletion src/store/modules/util/mutation-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export const UTIL_FACILITY_GROUPS_UPDATED = SN_UTIL + '/FACILITY_GROUPS_UPDATED'
export const UTIL_FACILITY_LIST_UPDATED = SN_UTIL + '/FACILITY_LIST_UPDATED'
export const UTIL_SELECTED_SEGMENT_UPDATED = SN_UTIL + '/SELECTED_SEGMENT_UPDATED'
export const UTIL_PICKUP_GROUPS_UPDATED = SN_UTIL + '/PICKUP_GROUPS_UPDATED'
export const UTIL_PICKUP_GROUP_FACILITIES = SN_UTIL + '/PICKUP_GROUP_FACILITIES_UPDATED'
export const UTIL_PICKUP_GROUP_FACILITIES = SN_UTIL + '/PICKUP_GROUP_FACILITIES_UPDATED'
export const UTIL_FACET_OPTIONS_UPDATED = SN_UTIL + '/FACET_OPTIONS_UPDATED'
6 changes: 5 additions & 1 deletion src/store/modules/util/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const mutations: MutationTree <UtilState> = {
tags: [],
productFeatures: []
}
}
},
state.facetOptions = {}
},
[types.UTIL_APPLIED_FILTERS_CLEARED](state) {
state.appliedFilters = {
Expand All @@ -50,5 +51,8 @@ const mutations: MutationTree <UtilState> = {
[types.UTIL_PICKUP_GROUP_FACILITIES](state, payload) {
state.pickupGroupFacilities = payload
},
[types.UTIL_FACET_OPTIONS_UPDATED](state, payload) {
state.facetOptions = payload
},
}
export default mutations;

0 comments on commit 4b81984

Please sign in to comment.