@mezereon/bigcommerce-stencil
Version:
BigCommerce UI components for Mezereon Xperience
504 lines (482 loc) • 13.3 kB
JavaScript
/* eslint-disable eqeqeq */
import { isArray, isEmpty } from '@/helpers'
export const namespaced = true
export const state = () => ({
hits: [],
queryId: '',
segments: [],
keyword: '',
pageId: 0,
aggregations: [],
pagination: {},
selections: [],
filters: undefined,
sorting: [],
paging: [],
banners: [],
layout: null,
toggleLayout: false,
sort: undefined,
pageSize: undefined,
page: 1,
overlay: false,
primaryKey: null,
events: [],
complete: '',
suggestResult: {},
suggestItems: {},
currentSuggestion: null,
context: {},
fallbackMode: false,
gridItem: '',
listItem: '',
css: '',
config: {},
queryParam: 'q',
translation: undefined,
filterLayout: 'vertical',
redirect: undefined
})
export const mutations = {
LOAD_BANNERS(state, data) {
state.banners = data
},
LOAD_CONFIG(state, data) {
state.config = data.autocomplete
state.translation = data.translation
},
RUN_AUTOCOMPLETE(state, payload) {
if (payload.includeSuggestResult) {
state.suggestResult = payload.data.suggestResult
state.currentSuggestion = payload.data.suggestion
state.complete = payload.data.complete
}
state.suggestItems = payload.data.searchResult
state.primaryKey = payload.primaryKey
state.redirect = payload.data.searchResult.redirect
},
RUN_QUERY(state, data) {
const pagingType = data.pagination.pagingType
if (data.pagination.current_page == 1) {
state.hits = data.hits
} else {
if (pagingType === 'more' || pagingType === 'infinite') {
// avoid duplicates
const existingIds = new Set(
state.hits.items.map((item) => item.item.id)
)
const uniqueItems = data.hits.items.filter(
(item) => !existingIds.has(item.item.id)
)
if (uniqueItems.length > 0) {
state.hits.items.push(...uniqueItems)
}
} else {
state.hits = data.hits
}
}
state.queryId = data.queryId
state.segments = data.segments
state.primaryKey = data.primaryKey
// add .state and .open properties
data.aggregations.forEach((agg) => {
agg.state = {}
agg.open = {}
})
// flatten tree aggregations to simplify value lookup
const flat = {}
const flatten = (name, item) => {
flat[name] = flat[name] || {}
flat[name][item.key] = item
if (item.children) {
item.children.forEach((child) => flatten(name, child))
}
}
data.aggregations
.filter((y) => y.control === 'tree')
.forEach((tree) => tree.items.forEach((item) => flatten(tree.name, item)))
// helper function to get updated selection
const getSelection = (agg, x, v) => {
const item =
agg.control === 'tree'
? flat[agg.name][v]
: agg.items.find((a) => a.key == v)
const value = item ? item.value : v
return {
...x,
action: 'add',
key: v,
label: agg.label,
value
}
}
// replace initial selections with new objects that contain updated action, label, key and value
state.selections
.filter((x) => x.action === 'init')
.forEach((x) => {
const agg = data.aggregations.find((y) => y.name === x.name)
if (!agg) {
return
}
if (agg.control === 'slider') {
state.selections.push({
...x,
action: 'add',
label: agg.label
})
} else if (isArray(x.value)) {
x.value.forEach((v) => state.selections.push(getSelection(agg, x, v)))
} else {
state.selections.push(getSelection(agg, x, x.value))
}
})
state.selections = state.selections.filter((x) => x.action !== 'init')
state.aggregations = data.aggregations
state.pagination = data.pagination
if (data.banners) {
state.banners = data.banners
}
if (data.paging) {
state.paging = data.paging
}
if (data.sorting) {
state.sorting = data.sorting
}
if (data.grid) {
state.gridItem = data.grid
}
if (data.list) {
state.listItem = data.list
}
if (data.css) {
state.css = data.css
}
if (data.filterLayout) {
state.filterLayout = data.filterLayout
}
if (data.redirect) {
state.redirect = data.redirect
}
},
SET_LAYOUT(state, data) {
state.layout = data
},
SET_SORT(state, data) {
state.sort = data
},
SET_PAGE_SIZE(state, data) {
state.pageSize = data
},
SET_PAGE(state, data) {
state.page = data
},
SET_OVERLAY(state, data) {
state.overlay = data
},
SET_KEYWORD(state, data) {
state.keyword = data
},
SET_TOGGLE_LAYOUT(state, data) {
state.toggleLayout = data
},
SET_PAGE_ID(state, data) {
state.pageId = data
},
SET_COMPLETE(state, data) {
state.complete = data
},
SET_CURRENT_SUGGESTION(state, data) {
state.currentSuggestion = data
},
CLEAR_AUTOCOMPLETE(state) {
state.complete = ''
state.currentSuggestion = ''
state.suggestItems = {}
state.suggestResult = {}
},
SET_STATE(state, data) {
state.page = data.page
state.sort = data.sort
state.pageSize = data.pageSize
state.keyword = data.keyword
state.filters = data.filters
state.selections = data.selections
},
SET_QUERY_PARAM(state, data) {
state.queryParam = data
},
SET_CONTEXT(state, data) {
state.context = data
},
SET_FALLBACK_MODE(state, data) {
state.fallbackMode = data
},
UPDATE_AGGREGATION(state, agg) {
const aggregation = state.aggregations.find((x) => x.name === agg.name)
if (aggregation) {
aggregation.selected = agg.value
}
},
UPDATE_SELECTIONS(state, last) {
switch (last.action) {
case 'remove-all':
state.selections = state.selections.filter((x) => x.name !== last.name)
if (state.filters && state.filters[last.name]) {
delete state.filters[last.name]
}
break
case 'replace':
state.selections = state.selections.filter((x) => x.name !== last.name)
state.selections.push(last)
if (last.key == '_Keyword') {
break
}
state.filters = state.filters || {}
if (last.value && isArray(last.value)) {
state.filters[last.name] = last.value
} else {
state.filters[last.name] = [last.key]
}
break
case 'add':
state.selections.push(last)
if (last.key == '_Keyword') {
break
}
state.filters = state.filters || {}
state.filters[last.name] = state.filters[last.name] || []
if (last.value && isArray(last.value)) {
state.filters[last.name] = last.value
} else {
state.filters[last.name].push(last.key)
}
break
default:
state.selections = state.selections.filter(
(x) => x.name !== last.name || x.key !== last.key
)
if (state.filters && state.filters[last.name]) {
state.filters[last.name] = state.filters[last.name].filter(
(x) => x !== last.key
)
if (state.filters[last.name].length === 0) {
delete state.filters[last.name]
}
}
break
}
},
CLEAR_ONE_SELECTION(state, toClear) {
state.selections = state.selections.filter(
(x) => x.name !== toClear.name || x.key !== toClear.key
)
if (state.filters && state.filters[toClear.name]) {
if (toClear.value && isArray(toClear.value)) {
delete state.filters[toClear.name]
} else {
state.filters[toClear.name] = state.filters[toClear.name].filter(
(x) => x !== toClear.key
)
if (state.filters[toClear.name].length === 0) {
delete state.filters[toClear.name]
}
}
}
// special case for keyword selection
if (toClear.key === '_Keyword') {
state.keyword = ''
// clear autocomplete
state.complete = ''
state.currentSuggestion = ''
state.suggestItems = {}
state.suggestResult = {}
}
const aggregation = state.aggregations.find((x) => x.name === toClear.name)
if (aggregation) {
if (aggregation.control === 'slider') {
aggregation.selected = []
} else {
aggregation.selected = aggregation.selected.filter(
(x) => x !== toClear.key
)
}
}
},
CLEAR_ALL_SELECTIONS(state, payload) {
state.keyword = ''
state.selections = []
state.aggregations.forEach((agg) => {
agg.selected = []
})
state.filters = undefined
},
TRACK_EVENT(state, payload) {
state.events.push(payload)
},
CLEAR_EVENTS(state) {
state.events = []
}
}
export const actions = {
setLayout({ commit }, payload) {
commit('SET_LAYOUT', payload)
},
setSort({ commit }, payload) {
commit('SET_SORT', payload)
},
setPageSize({ commit }, payload) {
commit('SET_PAGE_SIZE', payload)
},
setPage({ commit }, payload) {
commit('SET_PAGE', payload)
},
setOverlay({ commit }, payload) {
commit('SET_OVERLAY', payload)
},
setKeyword({ commit }, payload) {
commit('SET_KEYWORD', payload)
},
setToggleLayout({ commit }, payload) {
commit('SET_TOGGLE_LAYOUT', payload)
},
setComplete({ commit }, payload) {
commit('SET_COMPLETE', payload)
},
setCurrentSuggestion({ commit }, payload) {
commit('SET_CURRENT_SUGGESTION', payload)
},
clearAutocomplete({ commit }, payload) {
commit('CLEAR_AUTOCOMPLETE', payload)
},
setPageId({ commit }, payload) {
commit('SET_PAGE_ID', payload)
},
setState({ commit }, payload) {
commit('SET_STATE', payload)
},
setQueryParam({ commit }, payload) {
commit('SET_QUERY_PARAM', payload)
},
setContext({ commit }, payload) {
commit('SET_CONTEXT', payload)
},
updateAggregation({ commit }, payload) {
commit('UPDATE_AGGREGATION', payload)
},
updateSelections({ commit }, payload) {
commit('UPDATE_SELECTIONS', payload)
},
clearOneSelection({ commit }, payload) {
commit('CLEAR_ONE_SELECTION', payload)
},
clearAllSelections({ commit }, payload) {
commit('CLEAR_ALL_SELECTIONS', payload)
},
trackEvent({ commit }, payload) {
commit('TRACK_EVENT', payload)
},
getEvents({ commit, state }) {
const events = state.events
commit('CLEAR_EVENTS')
return events
},
async loadConfig({ commit }, payload) {
try {
const { data } = await this.$axios.get('/search/config')
commit('LOAD_CONFIG', data)
} catch {
commit('SET_FALLBACK_MODE', true)
}
},
async runAutocomplete({ commit }, payload) {
const { data } = await this.$axios.post('/search/autocomplete/', payload)
commit('RUN_AUTOCOMPLETE', {
includeSuggestResult: payload.autocompleteConfig.includeSuggestResult,
primaryKey: data.searchResult.primaryKey,
data
})
},
async runQuery({ commit, state }, payload) {
commit('SET_PAGE_ID', payload.query.pageId || 0)
try {
const res = await this.$axios.post('/search/query', payload)
// set default pagination
for (const paging of res.data.paging) {
if (paging.default && !state.pageSize) {
commit('SET_PAGE_SIZE', paging.value)
break
}
}
// set default sorting
for (const sorting of res.data.sorting) {
if (sorting.default && !state.sort) {
commit('SET_SORT', sorting.key)
break
}
}
const skip = (payload.query.page - 1) * state.pageSize
const total = res.data.total || 0
const pagination = {
pagingType: res.data.pagingType || 'standard',
current_page: payload.query.page,
per_page: state.pageSize,
total,
from: skip + 1,
to: skip + state.pageSize,
last_page: Math.ceil(total / state.pageSize)
}
const data = {
hits: res.data,
primaryKey: res.data.primaryKey,
queryId: res.data.queryId,
segments: res.data.segments,
pagination,
aggregations: res.data.aggregations,
banners: res.data.banners,
paging: res.data.paging,
sorting: res.data.sorting,
grid: res.data.grid,
list: res.data.list,
css: res.data.css,
filterLayout: res.data.filterLayout,
redirect: res.data.redirect
}
commit('RUN_QUERY', data)
} catch {
commit('SET_FALLBACK_MODE', true)
}
}
}
export const getters = {
getFacetBanners: (state) => {
const banners = { ...state.banners }
for (const banner in banners) {
if (!banner.startsWith('facet')) {
delete banners[banner]
}
if (isEmpty(banners[banner])) {
delete banners[banner]
}
}
return banners
},
getBannerByZone: (state) => (zone) => {
return state.banners[zone]
},
getQuery: (state) => () => {
return {
page: state.page,
sort: state.sort,
pageSize: state.pageSize,
keyword: state.keyword,
filters: state.filters
}
}
}
export const search = {
namespaced,
state,
getters,
mutations,
actions
}