Skip to content

Instantly share code, notes, and snippets.

@harshmaur
Last active May 13, 2021 12:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save harshmaur/d88ca14a97c1b5daee334dfe179e20fe to your computer and use it in GitHub Desktop.
Save harshmaur/d88ca14a97c1b5daee334dfe179e20fe to your computer and use it in GitHub Desktop.
Dynamic SEO
const VirtualRefinement = connectRefinementList(() => null)
const ConnectedSort = connectSortBy(({ refine, items, currentRefinement }) => (
<div>
{map(items, item => (
<SortingLink key={item.label} selected={currentRefinement === item.value} onClick={() => refine(item.value)}>
{item.label}
</SortingLink>
))}
</div>
))
class ProductsApp extends PureComponent {
state = {
onlyInstock: true
}
onStockChange = e => {
this.setState({ onlyInstock: e.target.checked })
}
render() {
const {
facets,
searchState,
onSearchStateChange,
resultsState,
brand_slug,
category_slug,
togglePopup,
...props
} = this.props
return (
<IS
appId={ALGOLIA_APPID}
apiKey={ALGOLIA_APIKEY}
indexName={PRODUCTSINDEX}
searchState={searchState}
onSearchStateChange={onSearchStateChange}
resultsState={resultsState}>
<Configure {...props} />
{this.state.onlyInstock && <VirtualRefinement attributeName="instock" defaultRefinement={[1]} />}
<ScrollTo>
<ListingWrapper>
<FilterContainer>
<div>
<FilterRefined>
<p>
Filter By{' '}
<span>
<ClearAll />
</span>
</p>
<RefinedValue>
<CurrentRefinements
transformItems={items =>
filter(
items,
item =>
item.attributeName.indexOf('hierarchical') === -1 &&
item.attributeName !== 'price' &&
item.attributeName !== 'rating' &&
item.attributeName !== 'instock'
)
}
/>
</RefinedValue>
</FilterRefined>
<CustomRefinementList title="Categories" expanded={true}>
<HierarchicalMenu
key="categories"
attributes={[
'hierarchicalCategories.lvl0',
'hierarchicalCategories.lvl1',
'hierarchicalCategories.lvl2',
'hierarchicalCategories.lvl3',
'hierarchicalCategories.lvl4',
'hierarchicalCategories.lvl5',
'hierarchicalCategories.lvl6'
]}
showParentLevel={false}
/>
</CustomRefinementList>
<CustomRefinementList title="Price" expanded contentStyle={{ padding: '0px 15px 10px 16px' }}>
<RangeSlider attributeName="price" />
</CustomRefinementList>
{map(facets, (item, i) => (
<CustomRefinementList key={item} title={item.replace('properties.', '')} expanded={i < 3}>
<RefinementList
attributeName={item}
transformItems={data => orderBy(data, ['count', 'label'], ['desc', 'asc'])}
limitMin={50}
/>
</CustomRefinementList>
))}
<CustomRefinementList key="Rating" title="Rating" expanded>
<StarRating min={0} max={5} attributeName="rating" />
</CustomRefinementList>
<CustomRefinementList key="instock" title="instock" expanded>
<label>
<input type="checkbox" checked={this.state.onlyInstock} onChange={this.onStockChange} />
<span>Exclude Out of Stock</span>
</label>
</CustomRefinementList>
</div>
</FilterContainer>
<ListingContainer>
<TotleResult>
<ResultCountAndSearch>
<Stats
translations={{
stats: n =>
`${
brand_slug ? startCase(brand_slug) + ' ' + startCase(category_slug) : startCase(category_slug)
} (${n} Results)`
}}
/>
<SearchInput>
<SearchBox placeholder="Search Results" togglePopup={togglePopup} />
</SearchInput>
</ResultCountAndSearch>
</TotleResult>
<Sorting>
<div>Sort By:</div>
<ConnectedSort
items={[{ value: PRODUCTSINDEX, label: 'Most Relevant' }]}
defaultRefinement={PRODUCTSINDEX}
/>
</Sorting>
{/* Listing */}
<ProductTupleListing />
{/* Listing */}
<Pagination
showPrevious={false}
showLast
showNext={false}
translations={{ first: 'FIRST PAGE', last: 'LAST PAGE' }}
/>
</ListingContainer>
</ListingWrapper>
</ScrollTo>
</IS>
)
}
}
const updateAfter = 700
const searchStateToUrl = searchState => (searchState ? `${window.location.pathname}?${qs.stringify(searchState)}` : '')
class ProductListing extends PureComponent {
static async getInitialProps({ query, asPath, res, req }) {
const { category_slug = '', brand_slug = '' } = query
const q = query.query
// excluding instock 1
const checkResponseFor404 = await index.search({
query: q,
facets: '*',
filters: `${category_slug ? `category_slug: ${category_slug} ` : ''}${
brand_slug ? `AND brand_slug: ${brand_slug}` : ''
}`
})
let statusCode = 200
if (checkResponseFor404.nbHits === 0) {
const data = await fetch(`${BASE_URL}/redirects?oldUrl=${req.originalUrl}`)
.then(r => r.json())
.catch(() => [])
if (data.length) {
res.writeHead(301, {
Location: data[0].newUrl
})
res.end()
res.finished = true
} else {
statusCode = 404
res.statusCode = 404
return { statusCode }
}
}
const [seoMeta, response, blogResults, dealsResults] = await Promise.all([
fetch(`${BASE_URL}/comparisonSeo?categorySlug=${category_slug}&brandSlug=${brand_slug}`).then(r => r.json()),
index.search({
query: q,
facets: '*',
filters: `instock: 1 OR instock: 0 ${category_slug ? `AND category_slug: ${category_slug}` : ''} ${
brand_slug ? `AND brand_slug: ${brand_slug}` : ''
}`
}),
FR(BlogSlider, { query: `${brand_slug} ${category_slug} ${q}` }),
FR(DealTupleSlider, { query: `${category_slug} ${q}`, filters: 'isExpired: false' })
])
const facets = filter(Object.keys(response.facets), item => item.indexOf('properties') > -1)
// Remove brand facet if brand page
if (brand_slug) {
pull(facets, 'properties.brand')
}
const filters = `instock: 1 OR instock: 0 ${category_slug ? `AND category_slug: ${category_slug}` : ''} ${
brand_slug ? `AND brand_slug: ${brand_slug}` : ''
}`
// send the url for seo
const sub = asPath.indexOf('?') > -1 ? asPath.indexOf('?') : asPath.length
const canonical = asPath.substring(0, sub)
const productsAppState = asPath.indexOf('?') > -1 ? qs.parse(asPath.substring(asPath.indexOf('?') + 1)) : {}
const productsAppResults = await FR(ProductsApp, {
filters,
facets,
brand_slug,
category_slug,
searchState: productsAppState
})
return {
canonical,
facets,
seoMeta: seoMeta[0],
blogResults,
dealsResults,
productsAppResults,
productsAppState,
filters,
category_slug,
brand_slug
}
}
componentDidMount() {
this.setState({ searchState: qs.parse(window.location.search.slice(1)) })
}
componentWillReceiveProps() {
this.setState({ searchState: qs.parse(window.location.search.slice(1)) })
}
onSearchStateChange = searchState => {
clearTimeout(this.debouncedSetState)
const { category_slug = '', brand_slug = '' } = this.props.url.query
this.debouncedSetState = setTimeout(() => {
const href = searchStateToUrl(searchState)
// console.log(href)
Router.replace(
`/product-listing?category_slug=${category_slug}&brand_slug=${brand_slug}&${qs.stringify(searchState)}`,
href,
{
shallow: true
}
)
}, updateAfter)
this.setState({ searchState })
}
render() {
if (this.props.statusCode === 404) return <Error statusCode={404} />
const { canonical, facets, filters, seoMeta = {}, category_slug, brand_slug } = this.props
let seoStr = ''
let hierarchy = []
if (brand_slug) {
seoStr = `${startCase(brand_slug)} ${startCase(category_slug)}`
hierarchy = [
{ name: 'Products', link: 'https://www.comparemunafa.com/search' },
{
name: startCase(category_slug),
link: `https://www.comparemunafa.com/c/${category_slug}`
},
{
name: startCase(brand_slug),
link: `https://www.comparemunafa.com/c/${category_slug}/${brand_slug}`
}
]
} else if (category_slug) {
seoStr = `${startCase(category_slug)}`
hierarchy = [
{ name: 'Products', link: 'https://www.comparemunafa.com/search' },
{
name: startCase(category_slug),
link: `https://www.comparemunafa.com/c/${category_slug}`
}
]
} else {
seoStr = `Products`
hierarchy = [{ name: 'Products', link: 'https://www.comparemunafa.com/search' }]
}
const seoTitle = `Latest ${seoStr} Price List | Compare & Buy ${seoStr} Online ${format(
new Date(),
'YYYY Do MMMM'
)}`
const seoDesc = `Latest ${seoStr} list in India. Shop & compare ${
seoStr
} at lowest price only on Comparemunafa and get best ${seoStr} offers, coupon & deals`
return (
<Layout>
<Head title={seoTitle} description={seoDesc} url={canonical} />
<Breadcrumb hierarchy={hierarchy} />
<ProductsApp
filters={filters}
facets={facets}
brand_slug={brand_slug}
category_slug={category_slug}
togglePopup={this.props.togglePopup}
resultsState={this.props.productsAppResults}
onSearchStateChange={this.onSearchStateChange}
searchState={this.state && this.state.searchState ? this.state.searchState : this.props.productsAppState}
/>
{seoMeta.longDescription && (
<Container backgroundColor="#fff" padding="0px 65px 20px">
<SortDescription>
<h1>{seoTitle}</h1>
<div dangerouslySetInnerHTML={{ __html: seoMeta.longDescription }} />
</SortDescription>
</Container>
)}
<DealTupleSlider
query={`${category_slug}`}
resultsState={this.props.dealsResults}
filters={'isExpired: false'}
/>
<BlogSlider query={`${brand_slug} ${category_slug}`} resultsState={this.props.blogResults} />
</Layout>
)
}
}
export default withRedux(initStore, null, { togglePopup })(ProductListing)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment