Commit 16869631 authored by Denis Sedura's avatar Denis Sedura

[RNA-802] Extract Store filters component

parent 903be1dd
......@@ -13,10 +13,13 @@ import {
import { createStoreCartStatusSelector } from '../../selectors'
import tabItems from '../../constants/storeTabItems'
import enhanceStoreSearchInput from './StoreSearchInput'
import { enhanceFiltersButton } from './filters/FiltersButton'
import { enhanceFilterModal } from './filters/FiltersModal'
function enhanceStore(ComposedComponent, StoreSearchInput) {
function enhanceStore(ComposedComponent, StoreSearchInput, FiltersModal, FiltersButton) {
const StoreSearchInputHOC = enhanceStoreSearchInput(StoreSearchInput)
const FiltersButtonHOC = enhanceFiltersButton(FiltersButton)
const FiltersModalHOC = enhanceFilterModal(FiltersModal)
class Wrapper extends Component {
static propTypes = {
// route props
......@@ -102,8 +105,10 @@ function enhanceStore(ComposedComponent, StoreSearchInput) {
<ComposedComponent
{...this.props}
{...this.state}
StoreSearchInputHOC={StoreSearchInputHOC}
StoreSearchInput={StoreSearchInputHOC}
StoreTabView={StoreTabView}
FiltersButton={FiltersButtonHOC}
FiltersModal={FiltersModalHOC}
handleCheckoutButtonOnPress={this.handleCheckoutButtonOnPress}
handleChangeTab={this.handleChangeTab}
addInventoryToCart={this.addInventoryToCart}
......
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { createSelector } from 'reselect'
import { debounce, isEqual, union, without, snakeCase, capitalize } from 'lodash'
import { createSearchInventoryListSelector } from 'tipsi_api/selectors'
import { loadSmartFilters, clearPaginationCache } from 'tipsi_api/actions'
import { updateStoreFilters, clearStoreFilters } from '../../../actions'
import tabItems from '../../../constants/storeTabItems'
import {
initialFiltersState,
getActiveFiltersLength,
convertFiltersToQuery,
} from '../../../reducers/storeFilters'
import { WINE_TYPES } from '../../../constants/storeFilters'
function enhanceFilters(ComposedComponent) {
const withInitialFilters = storeFilters => ({
...initialFiltersState,
...storeFilters,
})
const WINE_FILTERS = ['wine_type', 'unit_size', 'varietal', 'country', 'region', 'sub_region']
const DRINK_FILTERS = ['tag', 'country', 'unit_size']
const PAGE_SIZE = 7
class Wrapper extends Component {
static propTypes = {
clearStoreFilters: PropTypes.func.isRequired,
updateStoreFilters: PropTypes.func.isRequired,
isLoading: PropTypes.bool.isRequired,
itemsCount: PropTypes.number,
tabIndex: PropTypes.number.isRequired,
storeFilters: PropTypes.object,
storeId: PropTypes.number.isRequired,
onClose: PropTypes.func.isRequired,
// map dispatch to props
loadSmartFilters: PropTypes.func.isRequired,
clearPaginationCache: PropTypes.func.isRequired,
}
static defaultProps = {
itemsCount: undefined,
storeFilters: undefined,
}
state = withInitialFilters(this.props.storeFilters)
getSmartFilterTypes = (storeFilters = {}, nextStoreFilters = {}) => {
const availableSmartFilterTypes = this.getDrinkType() === 'wine' ? WINE_FILTERS : DRINK_FILTERS
if (!storeFilters && !nextStoreFilters) {
return availableSmartFilterTypes
}
const changedSmartFilterTypes = union(
Object.keys(storeFilters), Object.keys(nextStoreFilters)
)
.filter(key => !isEqual(storeFilters[key], nextStoreFilters[key]))
.map(snakeCase)
.filter(key => availableSmartFilterTypes.includes(key))
return without(availableSmartFilterTypes, ...changedSmartFilterTypes)
}
getInventoryType = () => this.getDrinkType() === 'wine' ? 'wine' : 'drink'
getDrinkType = () => tabItems[this.props.tabIndex].key.toLowerCase()
getSmartFiltersKey = () => `${this.props.storeId}/${this.getDrinkType()}`
resetFilters = nextStoreFilters => (
this.setState(withInitialFilters(nextStoreFilters))
)
clearSmartFiltersCache = (filterTypes = []) => {
const key = this.getSmartFiltersKey()
const smartFilters = filterTypes.map(filterType => `${key}/${filterType}`)
this.props.clearPaginationCache({ smartFilters })
}
loadSmartFilters = (filterTypes = []) => {
const fields = filterTypes.reduce(
(memo, filterType) => ({ ...memo, [filterType]: 'id,name' }),
{ filter: filterTypes.join(',') }
)
this.props.loadSmartFilters(
this.props.storeId,
this.getInventoryType(),
this.getSmartFiltersKey(),
{
fields,
drinkType: this.getDrinkType(),
pageSize: PAGE_SIZE,
page: 1,
filters: convertFiltersToQuery(this.props.storeFilters),
}
)
}
updateFilters = (filters) => {
const { storeId, tabIndex } = this.props
this.setState(filters)
this.debouncedUpdateStoreFilters({
key: `${storeId}/${tabItems[tabIndex].storeKey}`,
filters,
})
}
debouncedUpdateStoreFilters = debounce(this.props.updateStoreFilters, 50)
handleMapWineTypes = wineTypes => (
wineTypes.map(wineType => ({
...wineType,
name: WINE_TYPES[wineType.value] || capitalize(wineType.value),
}))
)
handleBudgetValueChange = budget => this.updateFilters({ budget })
handleRatingValueChange = rating => this.updateFilters({ rating })
handleWineTypeChange = wineType => this.updateFilters({ wineType })
handleColorChange = color => this.updateFilters({ color })
handleVarietalChange = varietal => this.updateFilters({ varietal })
handleCountryChange = country => this.updateFilters({ country })
handleRegionChange = region => this.updateFilters({ region })
handleSubRegionChange = subRegion => this.updateFilters({ subRegion })
handleTagChange = tag => this.updateFilters({ tag })
handleUnitSizeChange = unitSize => this.updateFilters({ unitSize })
handleResetFilters = () => {
const { storeId, tabIndex } = this.props
this.setState(initialFiltersState)
this.props.clearStoreFilters({
keys: `${storeId}/${tabItems[tabIndex].storeKey}`,
})
}
render() {
return (
<ComposedComponent
{...this.props}
{...this.state}
getActiveFiltersLength={getActiveFiltersLength}
getSmartFilterTypes={this.getSmartFilterTypes}
getInventoryType={this.getInventoryType}
getDrinkType={this.getDrinkType}
getSmartFiltersKey={this.getSmartFiltersKey}
resetFilters={this.resetFilters}
clearSmartFiltersCache={this.clearSmartFiltersCache}
loadSmartFilters={this.loadSmartFilters}
updateFilters={this.updateFilters}
handleMapWineTypes={this.handleMapWineTypes}
handleBudgetValueChange={this.handleBudgetValueChange}
handleRatingValueChange={this.handleRatingValueChange}
handleWineTypeChange={this.handleWineTypeChange}
handleColorChange={this.handleColorChange}
handleVarietalChange={this.handleVarietalChange}
handleCountryChange={this.handleCountryChange}
handleRegionChange={this.handleRegionChange}
handleSubRegionChange={this.handleSubRegionChange}
handleTagChange={this.handleTagChange}
handleUnitSizeChange={this.handleUnitSizeChange}
handleResetFilters={this.handleResetFilters}
/>
)
}
}
const storeFiltersSelector = ({ storeFilters }, { storeId, tabIndex }) => (
storeFilters[`${storeId}/${tabItems[tabIndex].storeKey}`]
)
const searchInventoryKeySelector = (state, { storeId, tabIndex }) => (
`${storeId}/${tabItems[tabIndex].storeKey}`
)
const mapStateToProps = createSelector(
createSearchInventoryListSelector(searchInventoryKeySelector),
storeFiltersSelector,
({ result, count, isLoading }, storeFilters) => ({
isLoading,
storeFilters,
itemsCount: count,
storeWines: result,
})
)
return connect(mapStateToProps, {
updateStoreFilters,
clearStoreFilters,
loadSmartFilters,
clearPaginationCache,
})(Wrapper)
}
export default enhanceFilters
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import declensionFilter from '../../../utils/declensionFilter'
import { getActiveFiltersLength } from '../../../reducers/storeFilters'
import tabItems from '../../../constants/storeTabItems'
export function enhanceFiltersButton(ComposedComponent) {
class Wrapper extends Component {
static propTypes = {
activeFiltersCount: PropTypes.number.isRequired,
}
render() {
const title = declensionFilter(this.props.activeFiltersCount, {
0: 'SHOW FILTERS',
1: '@ FILTER',
other: '@ FILTERS',
})
return (
<ComposedComponent {...this.props} title={title} />
)
}
}
const storeFiltersSelector = ({ storeFilters }, { storeId, tabIndex }) => {
const key = `${storeId}/${tabItems[tabIndex].storeKey}`
return {
activeFiltersCount: getActiveFiltersLength(storeFilters[key]),
}
}
return connect(storeFiltersSelector)(Wrapper)
}
import React, { Component } from 'react'
import { Router } from 'tipsi-router'
export function enhanceFilterModal(ComposedComponent) {
return class Wrapper extends Component {
render() {
const Filters = Router.routes.storeFilters.component
return (
<ComposedComponent {...this.props}>
<Filters {...this.props} />
</ComposedComponent>
)
}
}
}
......@@ -11,6 +11,7 @@ import {
} from './Screens'
import { enhanceSearch } from './Screens/Search'
import { enhanceLogin } from './Screens/Login'
import { default as enhanceFilters } from './Screens/Store/filters/Filters'
const routesMap = {
home: {
......@@ -25,6 +26,10 @@ const routesMap = {
path: 'storeTabView',
component: enhanceStoreTabView,
},
storeFilters: {
path: 'storeFilters',
component: enhanceFilters,
},
search: {
path: 'search',
component: enhanceSearch,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment