-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Refactor catalog module #2890
Merged
Merged
Changes from all commits
Commits
Show all changes
90 commits
Select commit
Hold shift + click to select a range
5d2827c
prepare module draft
patzick 3889092
add default values
patzick 9d2462a
init module actions
patzick 16ca33e
move helper from old module
patzick 0207857
getting available filters
patzick 087d45e
invoke search with current filters
patzick e834b21
current category from getter
patzick d372696
default value as string
patzick 59f2130
add todo for multiple filters
patzick 8a9d28d
switching search filters
patzick d2b4664
switching filters query
patzick 23e70d7
multiple dilters draft and reseting filters
patzick c60508d
migrate filter components
patzick 54a231b
migrate category page
patzick f45fe5b
filters and currentCategory as getters
patzick d027a4c
active filters
patzick 8e71fdf
recomputing getters
patzick 8f9e795
ui active filters
patzick f5cdd82
add sorty by property
patzick 0a9f7d5
system filters not visible on search filters
patzick f979051
get turned off by default
patzick fece9d5
add category breadcrumbs
patzick fb380e7
stop using of sidebar mixin
patzick bd32c8c
add multiple filters support
patzick 9084808
merge develop
patzick 487a847
moved filter logic to mixin
patzick a819f6f
refactor calculate getters
patzick 25eabd7
move logic to getters
patzick 652afe0
move logic to directory
patzick 767aa6d
change loadCategory invocation
patzick 4ee0279
change router as separate action
patzick 97e0981
add category service
patzick adc60ad
loading categories
patzick d97b2a1
change filter method with tests
patzick eab2243
extract getFiltersFromQuery with tests
patzick 8fd4f42
add support for multiple prices with tests
patzick af8db3a
Merge branch 'develop' into magento-catalog
patzick 98034ef
Merge branch 'magento-catalog' of https://github.com/patzick/vue-stor…
patzick 35293e3
add support for filters as query and path params
patzick b47b34b
filters per category
patzick 7d4633b
refactor -magento to -next
patzick e7bdcf2
merge develop
patzick 93d2588
support finding configurable child with multiple filters
patzick 771d734
configure fetched products
patzick fa52a84
load category filters before picking filters
patzick f0a7259
Merge branch 'develop' into magento-catalog
patzick 738ddce
test caching on server
patzick 6208010
improve service worker catalog cache
patzick 4a687c5
use filters on product page
patzick abfa197
cleaning code and dependencies
patzick ed6492f
Merge branch 'magento-catalog' of https://github.com/patzick/vue-stor…
patzick 0549431
merge develop
patzick 447130b
Merge branch 'develop' into magento-catalog
patzick 6867930
linter errors
patzick c648c3c
correct getter
patzick e4665e0
default argument
patzick 2e9e140
filter event
patzick 541add0
Merge branch 'develop' into magento-catalog
patzick c908fdb
cleanup and add types to category service
patzick 02f542a
linter and category interface
patzick 07a52d1
categories as map
patzick 9d6909e
add missing translation
patzick bce5a2d
cleanup
patzick d1ca817
recurrent breadcrumbs
patzick 67087ea
mock configuration for configureProductAsync
patzick 867f582
additional test to check buildProductsQuery
patzick 37aca5e
categories hierarchy map and loading categories to breadcrumbs by one…
patzick 64aeff6
Change caegoryFilters to object
patzick 0fc7b6d
breadcrumbs from categories hierarchy map
patzick ba9f93b
do no t duplicate optionLabel helper
patzick 94e5d23
composition function for proper SSR/CSR caching
patzick 3da38c0
code cleanup
patzick 35638d3
add default breadcrumb value
patzick 7329923
improve filters reactivity on product page
patzick 097eebb
fix problem with available options
patzick cfafe84
merge develop
patzick b868176
sorting filters
patzick dee2ead
lnter
patzick e345a04
Merge branch 'develop' into magento-catalog
patzick a0e993d
add infinite scroll pagination
patzick 9659e6f
categoryHierarchyMap
patzick 0f61390
Merge branch 'develop' into magento-catalog
patzick aa41c09
remove hierarchyMap - build from specific category
patzick 8545d57
Fix problem with products caching on SSR
patzick 52e8854
refactor category page getters names
patzick 61ae322
Merge branch 'develop' into magento-catalog
patzick 6153d69
remove unused hook
patzick 779ce67
fix for product option values incomplete
patzick f31ac67
prefetch stock for cached products
patzick ddc3cde
Merge branch 'develop' into magento-catalog
patzick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import { quickSearchByQuery } from '@vue-storefront/core/lib/search'; | ||
import SearchQuery from '@vue-storefront/core/lib/search/searchQuery'; | ||
import config from 'config'; | ||
import { DataResolver } from './types/DataResolver'; | ||
import { Category } from 'core/modules/catalog-next/types/Category'; | ||
|
||
const getCategories = async ({ | ||
parentId = null, | ||
filters = {}, | ||
level = null, | ||
onlyActive = true, | ||
onlyNotEmpty = false, | ||
size = 4000, | ||
start = 0, | ||
sort = 'position:asc', | ||
includeFields = config.entities.optimize ? config.entities.category.includeFields : null, | ||
excludeFields = config.entities.optimize ? config.entities.category.excludeFields : null | ||
}: DataResolver.CategorySearchOptions = {}): Promise<Category[]> => { | ||
let searchQuery = new SearchQuery() | ||
if (parentId) { | ||
searchQuery = searchQuery.applyFilter({key: 'parent_id', value: {'eq': parentId}}) | ||
} | ||
if (level) { | ||
searchQuery = searchQuery.applyFilter({key: 'level', value: {'eq': level}}) | ||
} | ||
|
||
for (var [key, value] of Object.entries(filters)) { | ||
if (Array.isArray(value)) { | ||
searchQuery = searchQuery.applyFilter({key: key, value: {'in': value}}) | ||
} else { | ||
searchQuery = searchQuery.applyFilter({key: key, value: {'eq': value}}) | ||
} | ||
} | ||
|
||
if (onlyActive === true) { | ||
searchQuery = searchQuery.applyFilter({key: 'is_active', value: {'eq': true}}) | ||
} | ||
|
||
if (onlyNotEmpty === true) { | ||
searchQuery = searchQuery.applyFilter({key: 'product_count', value: {'gt': 0}}) | ||
} | ||
const response = await quickSearchByQuery({ entityType: 'category', query: searchQuery, sort: sort, size: size, start: start, includeFields: includeFields, excludeFields: excludeFields }) | ||
return response.items as Category[] | ||
} | ||
|
||
export const CategoryService: DataResolver.CategoryService = { | ||
getCategories | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { CategoryService } from './CategoryService'; | ||
|
||
export { | ||
CategoryService | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Category } from 'core/modules/catalog-next/types/Category'; | ||
|
||
declare namespace DataResolver { | ||
|
||
interface CategorySearchOptions { | ||
parentId?: number, | ||
filters?: { [key: string]: string[] | string }, | ||
level?: number, | ||
onlyActive?: boolean, | ||
onlyNotEmpty?: boolean, | ||
size?: number, | ||
start?: number, | ||
sort?: string, | ||
includeFields?: string[], | ||
excludeFields?: string[] | ||
} | ||
|
||
interface CategoryService { | ||
getCategories: (searchRequest?: CategorySearchOptions) => Promise<Category[]> | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { Category, ChildrenData } from '../types/Category'; | ||
|
||
export const compareByLabel = (a, b) => { | ||
if (a.label < b.label) { | ||
return -1 | ||
} | ||
if (a.label > b.label) { | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
|
||
export const _prepareCategoryPathIds = (category: Category): string[] => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. _ indicates private fn yet you export it, why? |
||
if (!category || !category.path) return [] | ||
return category.path.split('/') | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import FilterVariant from 'core/modules/catalog-next/types/FilterVariant'; | ||
|
||
export const getSystemFilterNames: string[] = ['sort'] | ||
|
||
/** | ||
* Creates new filtersQuery (based on currentQuery) by modifying specific filter variant. | ||
*/ | ||
export const changeFilterQuery = ({currentQuery = {}, filterVariant}: {currentQuery?: any, filterVariant?: FilterVariant} = {}) => { | ||
const newQuery = JSON.parse(JSON.stringify(currentQuery)) | ||
if (!filterVariant) return newQuery | ||
if (getSystemFilterNames.includes(filterVariant.type)) { | ||
if (newQuery[filterVariant.type] && newQuery[filterVariant.type] === filterVariant.id) { | ||
delete newQuery[filterVariant.type] | ||
} else { | ||
newQuery[filterVariant.type] = filterVariant.id | ||
} | ||
} else { | ||
let queryFilter = newQuery[filterVariant.type] || [] | ||
if (!Array.isArray(queryFilter)) queryFilter = [queryFilter] | ||
if (queryFilter.includes(filterVariant.id)) { | ||
queryFilter = queryFilter.filter(value => value !== filterVariant.id) | ||
} else { | ||
queryFilter.push(filterVariant.id) | ||
} | ||
newQuery[filterVariant.type] = queryFilter | ||
} | ||
return newQuery | ||
} | ||
|
||
export const getFiltersFromQuery = ({filtersQuery = {}, availableFilters = {}} = {}) => { | ||
const searchQuery = { | ||
filters: {} | ||
} | ||
Object.keys(filtersQuery).forEach(filterKey => { | ||
const filter = availableFilters[filterKey] | ||
const queryValue = filtersQuery[filterKey] | ||
if (!filter) return | ||
if (getSystemFilterNames.includes(filterKey)) { | ||
searchQuery[filterKey] = queryValue | ||
} else if (Array.isArray(queryValue)) { | ||
queryValue.map(singleValue => { | ||
const variant = filter.find(filterVariant => filterVariant.id === singleValue) | ||
if (!variant) return | ||
if (!searchQuery.filters[filterKey] || !Array.isArray(searchQuery.filters[filterKey])) searchQuery.filters[filterKey] = [] | ||
searchQuery.filters[filterKey].push({...variant, attribute_code: filterKey}) | ||
}) | ||
} else { | ||
const variant = filter.find(filterVariant => filterVariant.id === queryValue) | ||
if (!variant) return | ||
searchQuery.filters[filterKey] = {...variant, attribute_code: filterKey} | ||
} | ||
}) | ||
return searchQuery | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import { optionLabel } from '@vue-storefront/core/modules/catalog/helpers/optionLabel' | ||
|
||
// TODO in future move old helper here, add tests and refactor | ||
export { optionLabel } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { categoryModule } from './store/category' | ||
import { createModule } from '@vue-storefront/core/lib/module' | ||
|
||
export const KEY = 'catalog-next' | ||
export default createModule({ | ||
key: KEY, | ||
store: { modules: [ | ||
{ key: 'category-next', module: categoryModule } | ||
] } | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Category } from '../../types/Category'; | ||
import Product from 'core/modules/catalog/types/Product'; | ||
|
||
export default interface CategoryState { | ||
categoriesMap: { [id: string]: Category }, | ||
availableFilters: any, | ||
products: Product[], | ||
searchProductsStats: any | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
// import Vue from 'vue' | ||
import { ActionTree } from 'vuex' | ||
import * as types from './mutation-types' | ||
import RootState from '@vue-storefront/core/types/RootState' | ||
import CategoryState from './CategoryState' | ||
import { quickSearchByQuery } from '@vue-storefront/core/lib/search' | ||
import { buildFilterProductsQuery } from '@vue-storefront/core/helpers' | ||
import { router } from '@vue-storefront/core/app' | ||
import FilterVariant from '../../types/FilterVariant' | ||
import { CategoryService } from '@vue-storefront/core/data-resolver' | ||
import { changeFilterQuery } from '../../helpers/filterHelpers' | ||
import { products } from 'config' | ||
import { configureProductAsync } from '@vue-storefront/core/modules/catalog/helpers' | ||
import { DataResolver } from 'core/data-resolver/types/DataResolver'; | ||
import { Category } from '../../types/Category'; | ||
import { _prepareCategoryPathIds } from '../../helpers/categoryHelpers'; | ||
import chunk from 'lodash-es/chunk' | ||
|
||
const actions: ActionTree<CategoryState, RootState> = { | ||
async loadCategoryProducts ({ commit, getters, dispatch, rootState }, { route, category } = {}) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imho products should be cached by default. No additional work should be required to do this |
||
const searchCategory = category || getters.getCategoryFrom(route.path) | ||
await dispatch('loadCategoryFilters', searchCategory) | ||
const searchQuery = getters.getCurrentFiltersFrom(route[products.routerFiltersSource]) | ||
let filterQr = buildFilterProductsQuery(searchCategory, searchQuery.filters) | ||
const {items, perPage, start, total} = await quickSearchByQuery({ query: filterQr, sort: searchQuery.sort }) | ||
commit(types.CATEGORY_SET_SEARCH_PRODUCTS_STATS, { perPage, start, total }) | ||
let configuredProducts = items.map(product => { | ||
const configuredProductVariant = configureProductAsync({rootState, state: {current_configuration: {}}}, {product, configuration: searchQuery.filters, selectDefaultVariant: false, fallbackToDefaultWhenNoAvailable: true, setProductErorrs: false}) | ||
return Object.assign(product, configuredProductVariant) | ||
}) | ||
commit(types.CATEGORY_SET_PRODUCTS, configuredProducts) | ||
// await dispatch('loadAvailableFiltersFrom', searchResult) | ||
|
||
return items | ||
}, | ||
async loadMoreCategoryProducts ({ commit, getters, rootState }) { | ||
const { perPage, start, total } = getters.getCategorySearchProductsStats | ||
if (start >= total || total < perPage) return | ||
|
||
const searchQuery = getters.getCurrentSearchQuery | ||
let filterQr = buildFilterProductsQuery(getters.getCurrentCategory, searchQuery.filters) | ||
const searchResult = await quickSearchByQuery({ query: filterQr, sort: searchQuery.sort, start: start + perPage, size: perPage }) | ||
commit(types.CATEGORY_SET_SEARCH_PRODUCTS_STATS, { | ||
perPage: searchResult.perPage, | ||
start: searchResult.start, | ||
total: searchResult.total | ||
}) | ||
let configuredProducts = searchResult.items.map(product => { | ||
const configuredProductVariant = configureProductAsync({rootState, state: {current_configuration: {}}}, {product, configuration: searchQuery.filters, selectDefaultVariant: false, fallbackToDefaultWhenNoAvailable: true, setProductErorrs: false}) | ||
return Object.assign(product, configuredProductVariant) | ||
}) | ||
commit(types.CATEGORY_ADD_PRODUCTS, configuredProducts) | ||
|
||
return searchResult.items | ||
}, | ||
async cacheProducts ({ commit, getters, dispatch, rootState }, { route } = {}) { | ||
const searchCategory = getters.getCategoryFrom(route.path) | ||
const searchQuery = getters.getCurrentFiltersFrom(route[products.routerFiltersSource]) | ||
let filterQr = buildFilterProductsQuery(searchCategory, searchQuery.filters) | ||
|
||
const cachedProductsResponse = await dispatch('product/list', { | ||
query: filterQr, | ||
sort: searchQuery.sort, | ||
updateState: false // not update the product listing - this request is only for caching | ||
}, { root: true }) | ||
if (products.filterUnavailableVariants && products.configurableChildrenStockPrefetchStatic) { // prefetch the stock items | ||
const skus = [] | ||
let prefetchIndex = 0 | ||
cachedProductsResponse.items.map(i => { | ||
if (products.configurableChildrenStockPrefetchStaticPrefetchCount > 0) { | ||
if (prefetchIndex > products.configurableChildrenStockPrefetchStaticPrefetchCount) return | ||
} | ||
skus.push(i.sku) // main product sku to be checked anyway | ||
if (i.type_id === 'configurable' && i.configurable_children && i.configurable_children.length > 0) { | ||
for (const confChild of i.configurable_children) { | ||
const cachedItem = rootState.stock.cache[confChild.id] | ||
if (typeof cachedItem === 'undefined' || cachedItem === null) { | ||
skus.push(confChild.sku) | ||
} | ||
} | ||
prefetchIndex++ | ||
} | ||
}) | ||
for (const chunkItem of chunk(skus, 15)) { | ||
dispatch('stock/list', { skus: chunkItem }, { root: true }) // store it in the cache | ||
} | ||
} | ||
}, | ||
async findCategories (context, categorySearchOptions: DataResolver.CategorySearchOptions): Promise<Category[]> { | ||
return CategoryService.getCategories(categorySearchOptions) | ||
}, | ||
async loadCategories ({ commit }, categorySearchOptions: DataResolver.CategorySearchOptions): Promise<Category[]> { | ||
const categories = await CategoryService.getCategories(categorySearchOptions) | ||
commit(types.CATEGORY_ADD_CATEGORIES, categories) | ||
return categories | ||
}, | ||
async loadCategory ({ commit }, categorySearchOptions: DataResolver.CategorySearchOptions): Promise<Category> { | ||
const categories: Category[] = await CategoryService.getCategories(categorySearchOptions) | ||
const category: Category = categories && categories.length ? categories[0] : null | ||
commit(types.CATEGORY_ADD_CATEGORY, category) | ||
return category | ||
}, | ||
/** | ||
* Fetch and process filters from current category and sets them in available filters. | ||
*/ | ||
async loadCategoryFilters ({ dispatch, getters }, category) { | ||
const searchCategory = category || getters.getCurrentCategory | ||
let filterQr = buildFilterProductsQuery(searchCategory) | ||
const searchResult = await quickSearchByQuery({ query: filterQr }) | ||
await dispatch('loadAvailableFiltersFrom', searchResult) | ||
}, | ||
async loadAvailableFiltersFrom ({ commit, getters }, {aggregations}) { | ||
const filters = getters.getAvailableFiltersFrom(aggregations) | ||
commit(types.CATEGORY_SET_AVAILABLE_FILTERS, filters) | ||
}, | ||
async switchSearchFilter ({ dispatch }, filterVariant: FilterVariant) { | ||
const newQuery = changeFilterQuery({currentQuery: router.currentRoute[products.routerFiltersSource], filterVariant}) | ||
await dispatch('changeRouterFilterParameters', newQuery) | ||
}, | ||
async resetSearchFilters ({dispatch}) { | ||
await dispatch('changeRouterFilterParameters', {}) | ||
}, | ||
async changeRouterFilterParameters (context, query) { | ||
router.push({[products.routerFiltersSource]: query}) | ||
}, | ||
async loadCategoryBreadcrumbs ({ dispatch, getters }, category: Category) { | ||
if (!category) return | ||
const categoryHierarchyIds = _prepareCategoryPathIds(category) // getters.getCategoriesHierarchyMap.find(categoryMapping => categoryMapping.includes(category.id)) | ||
const categoryFilters = { 'id': categoryHierarchyIds } | ||
await dispatch('loadCategories', {filters: categoryFilters}) | ||
} | ||
} | ||
|
||
export default actions |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
set sideEffects: false for this in package.json