@graphcommerce/algolia-search 7.0.0-canary.13 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,13 +1,217 @@
1
1
  # @graphcommerce/algolia-search
2
2
 
3
- ## 7.0.0-canary.13
3
+ ## 7.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [`e55d8c390`](https://github.com/graphcommerce-org/graphcommerce/commit/e55d8c390d90b4bb7bab11c6a99027ac72bd7e3e) - Created a new sidebar layout system, can be configured with productFiltersLayout in the graphcommerce.config.js ([@paales](https://github.com/paales))
8
+
9
+ ### Minor Changes
10
+
11
+ - [#1909](https://github.com/graphcommerce-org/graphcommerce/pull/1909) [`7a1f1bb38`](https://github.com/graphcommerce-org/graphcommerce/commit/7a1f1bb382ece4167bd3816d6f2cc41ffae56710) - New Algolia search implementation as an experimental package. Currenly only supports the classic filters. ([@mikekeehnen](https://github.com/mikekeehnen))
4
12
 
5
13
  ### Patch Changes
6
14
 
7
- - [#1921](https://github.com/graphcommerce-org/graphcommerce/pull/1921) [`27a7bf7fa`](https://github.com/graphcommerce-org/graphcommerce/commit/27a7bf7fada89f4e0d37d45515b9597f313ab112) - Bump ([@paales](https://github.com/paales))
15
+ - [#2026](https://github.com/graphcommerce-org/graphcommerce/pull/2026) [`c1b76eb4d`](https://github.com/graphcommerce-org/graphcommerce/commit/c1b76eb4d70cf5cef9f768bb1f4986e639b31932) - Improved documentation @graphcommerce/algolia-search ([@mikekeehnen](https://github.com/mikekeehnen))
8
16
 
9
- ## 7.0.0-canary.12
17
+ ## 6.2.0-canary.98
10
18
 
11
- ### Major Changes
19
+ ## 6.2.0-canary.97
20
+
21
+ ## 6.2.0-canary.96
22
+
23
+ ## 6.2.0-canary.95
24
+
25
+ ## 6.2.0-canary.94
26
+
27
+ ## 6.2.0-canary.93
28
+
29
+ ## 6.2.0-canary.92
30
+
31
+ ### Patch Changes
32
+
33
+ - [#2026](https://github.com/graphcommerce-org/graphcommerce/pull/2026) [`c1b76eb4d`](https://github.com/graphcommerce-org/graphcommerce/commit/c1b76eb4d70cf5cef9f768bb1f4986e639b31932) - Improved documentation @graphcommerce/algolia-search ([@mikekeehnen](https://github.com/mikekeehnen))
34
+
35
+ ## 6.2.0-canary.91
36
+
37
+ ## 6.2.0-canary.90
38
+
39
+ ## 6.2.0-canary.89
40
+
41
+ ## 6.2.0-canary.88
42
+
43
+ ## 6.2.0-canary.87
44
+
45
+ ## 6.2.0-canary.86
46
+
47
+ ## 6.2.0-canary.85
48
+
49
+ ## 6.2.0-canary.84
50
+
51
+ ## 6.2.0-canary.83
52
+
53
+ ## 6.2.0-canary.82
54
+
55
+ ## 6.2.0-canary.81
56
+
57
+ ## 6.2.0-canary.80
58
+
59
+ ## 6.2.0-canary.79
60
+
61
+ ## 6.2.0-canary.78
62
+
63
+ ### Minor Changes
64
+
65
+ - [#1988](https://github.com/graphcommerce-org/graphcommerce/pull/1988) [`af8e0d176`](https://github.com/graphcommerce-org/graphcommerce/commit/af8e0d176af8197a0c13b9a29b438cb54cc29ce4) - Multi website with multiple duplicates locales support. Use `en-us-website1`, `en-us-website2` as the locale declaration. ([@hnsr](https://github.com/hnsr))
66
+
67
+ ## 6.2.0-canary.77
68
+
69
+ ## 6.2.0-canary.76
70
+
71
+ ## 6.2.0-canary.75
72
+
73
+ ## 6.2.0-canary.74
74
+
75
+ ## 6.2.0-canary.73
76
+
77
+ ## 6.2.0-canary.72
78
+
79
+ ## 6.2.0-canary.71
80
+
81
+ ## 6.2.0-canary.70
82
+
83
+ ## 6.2.0-canary.69
84
+
85
+ ## 6.2.0-canary.68
86
+
87
+ ### Minor Changes
88
+
89
+ - [#1990](https://github.com/graphcommerce-org/graphcommerce/pull/1990) [`0729105cd`](https://github.com/graphcommerce-org/graphcommerce/commit/0729105cdb84b9f73d1fda7dac7daf867f4e0b9b) - Add CategorySearchResults component that can be injected with a plugin instead of SearchForm, fix various styling issues with search ([@Giovanni-Schroevers](https://github.com/Giovanni-Schroevers))
90
+
91
+ ## 6.2.0-canary.67
92
+
93
+ ## 6.2.0-canary.66
94
+
95
+ ## 6.2.0-canary.65
96
+
97
+ ## 6.2.0-canary.64
98
+
99
+ ## 6.2.0-canary.63
100
+
101
+ ## 6.2.0-canary.62
102
+
103
+ ## 6.2.0-canary.61
104
+
105
+ ## 6.2.0-canary.60
106
+
107
+ ## 6.2.0-canary.59
108
+
109
+ ## 6.2.0-canary.58
110
+
111
+ ### Patch Changes
112
+
113
+ - [#1981](https://github.com/graphcommerce-org/graphcommerce/pull/1981) [`5936ea5f3`](https://github.com/graphcommerce-org/graphcommerce/commit/5936ea5f3f77fa303ca6d57187cb921dafeffb4d) - Fix algolia autofocus ([@StefanAngenent](https://github.com/StefanAngenent))
114
+
115
+ ## 6.2.0-canary.57
116
+
117
+ ## 6.2.0-canary.56
118
+
119
+ ### Patch Changes
120
+
121
+ - [#1971](https://github.com/graphcommerce-org/graphcommerce/pull/1971) [`1c77da7f9`](https://github.com/graphcommerce-org/graphcommerce/commit/1c77da7f951e34a28df586fcaedf7a0a2c97ba60) - Bug fixes for Algolia Search. Linking via indexed categories is now possible, Mobile Header wil now render results correctly and server side data hydration will no longer be reliant on the locale provided by Lingui. ([@mikekeehnen](https://github.com/mikekeehnen))
122
+
123
+ ## 6.2.0-canary.55
124
+
125
+ ## 6.2.0-canary.54
126
+
127
+ ## 6.2.0-canary.53
128
+
129
+ ## 6.2.0-canary.52
130
+
131
+ ## 6.2.0-canary.51
132
+
133
+ ## 6.2.0-canary.50
134
+
135
+ ### Minor Changes
136
+
137
+ - [`e55d8c390`](https://github.com/graphcommerce-org/graphcommerce/commit/e55d8c390d90b4bb7bab11c6a99027ac72bd7e3e) - Created a new sidebar layout system, can be configured with productFiltersLayout in the graphcommerce.config.js ([@paales](https://github.com/paales))
138
+
139
+ ## 6.2.0-canary.49
140
+
141
+ ## 6.2.0-canary.48
142
+
143
+ ## 6.2.0-canary.47
144
+
145
+ ## 6.2.0-canary.46
146
+
147
+ ## 6.2.0-canary.45
148
+
149
+ ## 6.2.0-canary.44
150
+
151
+ ## 6.2.0-canary.43
152
+
153
+ ## 6.2.0-canary.42
154
+
155
+ ## 6.2.0-canary.41
156
+
157
+ ## 6.2.0-canary.40
158
+
159
+ ## 6.2.0-canary.39
160
+
161
+ ## 6.2.0-canary.38
162
+
163
+ ## 6.2.0-canary.37
164
+
165
+ ## 6.2.0-canary.36
166
+
167
+ ## 6.2.0-canary.35
168
+
169
+ ## 6.2.0-canary.34
170
+
171
+ ## 6.2.0-canary.33
172
+
173
+ ## 6.2.0-canary.32
174
+
175
+ ## 6.2.0-canary.31
176
+
177
+ ## 6.2.0-canary.30
178
+
179
+ ## 6.2.0-canary.29
180
+
181
+ ## 6.2.0-canary.28
182
+
183
+ ## 6.2.0-canary.27
184
+
185
+ ## 6.2.0-canary.26
186
+
187
+ ## 6.2.0-canary.25
188
+
189
+ ## 6.2.0-canary.24
190
+
191
+ ## 6.2.0-canary.23
192
+
193
+ ## 6.2.0-canary.22
194
+
195
+ ## 6.2.0-canary.21
196
+
197
+ ## 6.2.0-canary.20
198
+
199
+ ## 6.2.0-canary.19
200
+
201
+ ## 6.2.0-canary.18
202
+
203
+ ## 6.2.0-canary.17
204
+
205
+ ## 6.2.0-canary.16
206
+
207
+ ## 6.2.0-canary.15
208
+
209
+ ## 6.2.0-canary.14
210
+
211
+ ## 6.2.0-canary.13
212
+
213
+ ## 6.2.0-canary.12
214
+
215
+ ### Minor Changes
12
216
 
13
217
  - [#1909](https://github.com/graphcommerce-org/graphcommerce/pull/1909) [`7a1f1bb38`](https://github.com/graphcommerce-org/graphcommerce/commit/7a1f1bb382ece4167bd3816d6f2cc41ffae56710) - New Algolia search package! ([@mikekeehnen](https://github.com/mikekeehnen))
package/Config.graphqls CHANGED
@@ -69,4 +69,14 @@ extend input GraphCommerceConfig {
69
69
  Configures algolia search debounce time. This will slow down the search response.
70
70
  """
71
71
  algoliaSearchDebounceTime: Int
72
+
73
+ """
74
+ Configure your Algolia HyGraph index. This will create a new index for HyGraph data if it doesn't exist in Algolia.
75
+ """
76
+ algoliaHygraphIndex: String
77
+
78
+ """
79
+ Configure your Algolia HyGraph secret. This will be used to authenticate the HyGraph data.
80
+ """
81
+ hygraphSecret: String
72
82
  }
package/README.md CHANGED
@@ -26,9 +26,77 @@ Storefront configuration values:
26
26
  - filterAttributes (containing a list of the following values)
27
27
  - aggregation
28
28
  - toAlgoliaAttribute
29
+ - sortOptions
30
+ - label
31
+ - value
32
+
33
+ ## When to use filterAttributes?
34
+
35
+ The filterAttributes configuration is used to map the filter attributes from the
36
+ Magento 2 API to Algolia API. This is needed because some of the attributes in
37
+ Algolia don't match with the indexed attributes in Algolia. We currently support
38
+ some default attributes that are Magento 2 native, which means you don't have to
39
+ add them to the filterAttributes configuration. These attributes are:
40
+
41
+ - price
42
+ - category_uid
43
+
44
+ If you want to map other attributes, you can add them to the filterAttributes
45
+ configuration. For example, if you want to map the `color` attribute, you can
46
+ add the following configuration:
47
+
48
+ ```
49
+ filterAttributes: [
50
+ {
51
+ aggregation: 'color',
52
+ toAlgoliaAttribute: 'color',
53
+ },
54
+ ]
55
+ ```
56
+
57
+ Filling in the aggregation (Magento 2) enables this plugin to read the
58
+ aggregation properties, such as the label and the options. The
59
+ toAlgoliaAttribute (Algolia) is the attribute that is used in the Algolia index
60
+ and connects the correct values to the aggregation.
61
+
62
+ ## When to use sortOptions?
63
+
64
+ You can use the sortOptions to define new sorting options inside the Algolia
65
+ plugin. The label is the name of the sorting option that is shown in the UI. The
66
+ value is the value that is used to find the sort index in the Algolia API. For
67
+ example, if you want to add a new sorting option called `Newest`, you can add
68
+ the following configuration:
69
+
70
+ ```
71
+ sortOptions: [
72
+ {
73
+ label: 'Newest',
74
+ value: 'newest-product-index',
75
+ },
76
+ ]
77
+ ```
78
+
79
+ ## What is the recommended algoliaSearchDebounceTime?
80
+
81
+ We've added a debounce time to the search feature to prevent it from being
82
+ called too frequently. This means that the search function will wait for a
83
+ specified amount of time before executing, which reduces the number of queries
84
+ sent to the Algolia API and can reduce cost. The default debounce time is 0
85
+ milliseconds for optimal responsiveness, but you can adjust it by adding the
86
+ `algoliaSearchDebounceTime` parameter to your Graphcommerce configuration as
87
+ following:
88
+
89
+ ```
90
+ algoliaSearchDebounceTime: 500
91
+
92
+ ```
29
93
 
30
94
  ## Add server side hydration to Algolia Search
31
95
 
96
+ **_NOTE:_** Server side hydration is currently not supported due to the current
97
+ requirement of `useRouter` inside this plugin. We are working on a solution to
98
+ this problem.
99
+
32
100
  1. Add `react-instantsearch-hooks-server` package to your project
33
101
 
34
102
  ```
@@ -56,12 +124,15 @@ type SearchResultProps = DefaultPageQuery &
56
124
  ```
57
125
 
58
126
  3. Add the `getServerState` method from the `react-instantsearch-hooks-server`
59
- package to the imports of your search page
127
+ package and the `renderToString` method from the `react-dom/server` package
128
+ to the imports of your search page.
60
129
 
61
130
  ```
62
131
  ...
63
132
  import { getServerState } from 'react-instantsearch-hooks-server'
133
+ import {renderToString } from 'react-dom/server'
64
134
  ...
135
+
65
136
  ```
66
137
 
67
138
  4. Assign the result of the `getServerState` method to the `serverState`
@@ -79,7 +150,7 @@ return {
79
150
  params: productListParams,
80
151
  up: { href: '/', title: 'Home' },
81
152
  apolloState: await conf.then(() => client.cache.extract()),
82
- + serverState: await getServerState(<SearchContext />, {
153
+ + serverState: await getServerState(<SearchContext rendersInsideNextjs={false} />, {
83
154
  + renderToString,
84
155
  + }),
85
156
  },
@@ -109,3 +180,66 @@ return {
109
180
 
110
181
  + <SearchContext serverProps={serverState}>
111
182
  ```
183
+
184
+ ## Add Algolia indexing to Magento 2
185
+
186
+ 1. Follow the algolia docs to enable the Algolia integration in Magento 2
187
+ [here](https://www.algolia.com/doc/integration/magento-2/getting-started/quick-start/?client=php).
188
+
189
+ ## Add Algolia indexing to HyGraph
190
+
191
+ 1. Add the `algoliasearch` package to your project
192
+
193
+ ```
194
+ yarn add algoliasearch
195
+ ```
196
+
197
+ 2. Add the `@hygraph/utils` package to your project
198
+
199
+ ```
200
+ yarn add @hygraph/utils
201
+ ```
202
+
203
+ 3. Create two new api routes in your project, one for adding an entry and one
204
+ for removing an entry from the Algolia index.
205
+
206
+ ```
207
+ - pages
208
+ - api
209
+ - algolia
210
+ - add.ts
211
+ - remove.ts
212
+ ```
213
+
214
+ **_NOTE:_** Both the `add.ts` and `remove.ts` files are implemented with
215
+ signature verification. This is to prevent unwanted requests to your api routes.
216
+ You can read more about signature verification for webhooks
217
+ [here](https://hygraph.com/blog/introducing-signed-webhooks).
218
+
219
+ 4. The following code is an example of how the `add.ts` file could look like.
220
+ This file is responsible for adding an entry to the Algolia index.
221
+
222
+ ```ts
223
+ import { addHygraphRecord } from '@graphcommerce/algolia-search'
224
+ import { NextApiRequest, NextApiResponse } from 'next'
225
+
226
+ export default (req: NextApiRequest, res: NextApiResponse) =>
227
+ addHygraphRecord(req, res)
228
+ ```
229
+
230
+ 5. The following code is an example of how the `remove.ts` file could look like.
231
+ This file is responsible for removing an entry from the Algolia index.
232
+
233
+ ```ts
234
+ import { removeHygraphRecord } from '@graphcommerce/algolia-search'
235
+ import { NextApiRequest, NextApiResponse } from 'next'
236
+
237
+ export default (req: NextApiRequest, res: NextApiResponse) =>
238
+ await removeHygraphRecord(req, res)
239
+ ```
240
+
241
+ 6. Follow the webhook setup guide to add the webhooks to your HyGraph project.
242
+
243
+ You can follow
244
+ [these](https://hygraph.com/docs/guides/webhooks/webhooks-overview#configure-webhooks)
245
+ instructions to configure the webhooks in your HyGraph project.
@@ -0,0 +1,60 @@
1
+ import { Page } from '@graphcommerce/graphql-mesh'
2
+ import { verifyWebhookSignature } from '@hygraph/utils'
3
+ import algoliasearch from 'algoliasearch'
4
+ import { NextApiRequest, NextApiResponse } from 'next'
5
+
6
+ type AlgoliaResult = {
7
+ objectID: string
8
+ slug: string
9
+ name: string
10
+ content: string
11
+ url: string
12
+ algoliaLastUpdateAtCET: string
13
+ }
14
+
15
+ const algolia = algoliasearch(
16
+ import.meta.graphCommerce.algoliaApplicationId,
17
+ import.meta.graphCommerce.algoliaSearchOnlyApiKey,
18
+ )
19
+
20
+ const indexName = import.meta.graphCommerce.algoliaHygraphIndex
21
+ const index = typeof indexName === 'string' ? algolia.initIndex(indexName) : undefined
22
+
23
+ export function addHygraphRecord(req: NextApiRequest, res: NextApiResponse) {
24
+ if (req.method !== 'POST') res.end()
25
+
26
+ const { body } = req
27
+ const gcms = req.headers['gcms-signature']
28
+ const signature = Array.isArray(gcms) ? gcms[0] : gcms
29
+ const secret = import.meta.graphCommerce.hygraphSecret
30
+
31
+ if (!index) return res.status(400).send('Missing "algoliaHygraphIndex" config variable')
32
+ if (!secret) return res.status(400).send('Missing "hygraphSecret" config variable')
33
+ if (!signature) return res.status(400).send('Missing gcms-signature header')
34
+
35
+ if (!verifyWebhookSignature({ body, signature, secret })) res.status(401).end()
36
+
37
+ try {
38
+ const { data } = body
39
+
40
+ const { id: objectID, localizations } = data
41
+
42
+ const objects = localizations.map((localization: Page & { updatedAt: string }) => {
43
+ const url = `${import.meta.graphCommerce.canonicalBaseUrl}/${localization?.url}`
44
+ const algoliaObject: AlgoliaResult = {
45
+ name: localization?.title ?? '',
46
+ content: localization?.metaDescription,
47
+ url,
48
+ slug: localization?.url,
49
+ algoliaLastUpdateAtCET: localization?.updatedAt,
50
+ objectID,
51
+ }
52
+
53
+ return index.saveObject(algoliaObject)
54
+ })
55
+
56
+ return res.status(201).send(objects)
57
+ } catch (err) {
58
+ return res.status(400).send(err)
59
+ }
60
+ }
@@ -0,0 +1,36 @@
1
+ import { verifyWebhookSignature } from '@hygraph/utils'
2
+ import algoliasearch from 'algoliasearch'
3
+ import { NextApiRequest, NextApiResponse } from 'next'
4
+
5
+ const algolia = algoliasearch(
6
+ import.meta.graphCommerce.algoliaApplicationId,
7
+ import.meta.graphCommerce.algoliaSearchOnlyApiKey,
8
+ )
9
+
10
+ const indexName = import.meta.graphCommerce.algoliaHygraphIndex
11
+ const index = indexName ? algolia.initIndex(indexName) : undefined
12
+
13
+ export async function removeHygraphRecord(req: NextApiRequest, res: NextApiResponse) {
14
+ if (req.method !== 'DELETE') return res.end()
15
+
16
+ const { body } = req
17
+ const gcms = req.headers['gcms-signature']
18
+ const signature = Array.isArray(gcms) ? gcms[0] : gcms
19
+ const secret = import.meta.graphCommerce.hygraphSecret
20
+
21
+ if (!index) return res.status(400).send('Missing "algoliaHygraphIndex" config variable')
22
+ if (!secret) return res.status(400).send('Missing "hygraphSecret" config variable')
23
+ if (!signature) return res.status(400).send('Missing gcms-signature header')
24
+
25
+ if (!verifyWebhookSignature({ body, signature, secret })) res.status(401).end()
26
+
27
+ try {
28
+ const { id: objectID, ...rest } = body
29
+
30
+ await index.deleteObject(objectID as string)
31
+
32
+ return res.status(201).send(rest)
33
+ } catch (err) {
34
+ return res.status(400).send(err)
35
+ }
36
+ }
@@ -1,9 +1,15 @@
1
- import { ChipMenu, extendableComponent, responsiveVal } from '@graphcommerce/next-ui'
1
+ import {
2
+ ChipMenu,
3
+ extendableComponent,
4
+ responsiveVal,
5
+ useStorefrontConfig,
6
+ } from '@graphcommerce/next-ui'
2
7
  import { SxProps, Theme } from '@mui/material'
3
8
  import Box from '@mui/material/Box'
4
9
  import Checkbox from '@mui/material/Checkbox'
5
10
  import ListItem from '@mui/material/ListItem'
6
11
  import ListItemText from '@mui/material/ListItemText'
12
+ import { useSortBy } from 'react-instantsearch-hooks-web'
7
13
 
8
14
  const name = 'SortChip' as const
9
15
  const parts = ['menu', 'item'] as const
@@ -22,13 +28,19 @@ export type SortByRenderState = {
22
28
  canRefine: boolean
23
29
  }
24
30
 
25
- export interface SortChipProps extends SortByRenderState {
31
+ export type SortChipProps = {
26
32
  title: string
27
33
  sx?: SxProps<Theme>
28
34
  }
29
35
 
30
36
  export function SortChip(props: SortChipProps) {
31
- const { initialIndex, currentRefinement, options, refine, canRefine, title, sx } = props
37
+ const { title, sx } = props
38
+
39
+ const { initialIndex, currentRefinement, options, refine, canRefine } = useSortBy({
40
+ items: useStorefrontConfig().sortOptions ?? [],
41
+ })
42
+
43
+ if (options.length < 1) return null
32
44
 
33
45
  const selectedOption = options.find((option) => option.value === currentRefinement)
34
46
 
@@ -1,15 +1,14 @@
1
+ import { SearchFormProps } from '@graphcommerce/magento-search'
1
2
  import { Trans } from '@lingui/react'
2
3
  import { Box, debounce } from '@mui/material'
3
4
  import TextField from '@mui/material/TextField'
4
5
  import { ChangeEvent, useCallback, useEffect, useRef } from 'react'
5
6
  import { useHits, useSearchBox, UseSearchBoxProps } from 'react-instantsearch-hooks-web'
6
7
 
7
- type SearchBoxProps = {
8
- defaultValue?: string
9
- } & UseSearchBoxProps
8
+ type SearchBoxProps = UseSearchBoxProps & SearchFormProps
10
9
 
11
10
  export function SearchBox(props: SearchBoxProps) {
12
- const { defaultValue } = props
11
+ const { search, textFieldProps, sx = [] } = props
13
12
  const searchInputElement = useRef<HTMLInputElement>(null)
14
13
 
15
14
  const { refine } = useSearchBox()
@@ -25,8 +24,8 @@ export function SearchBox(props: SearchBoxProps) {
25
24
  )
26
25
 
27
26
  useEffect(() => {
28
- if (defaultValue) refine(defaultValue)
29
- }, [defaultValue, refine])
27
+ if (search) refine(search)
28
+ }, [search, refine])
30
29
 
31
30
  const totalResults = results?.nbHits ?? 0
32
31
 
@@ -45,6 +44,7 @@ export function SearchBox(props: SearchBoxProps) {
45
44
 
46
45
  return (
47
46
  <TextField
47
+ autoFocus
48
48
  variant='outlined'
49
49
  type='text'
50
50
  name='search'
@@ -52,7 +52,8 @@ export function SearchBox(props: SearchBoxProps) {
52
52
  inputRef={searchInputElement}
53
53
  onChange={debounceSearch}
54
54
  fullWidth
55
- sx={{ mt: 1 }}
55
+ sx={[{ mt: 1, mb: 1 }, ...(Array.isArray(sx) ? sx : [sx])]}
56
+ {...textFieldProps}
56
57
  />
57
58
  )
58
59
  }
@@ -1,14 +1,16 @@
1
+ import { useQuery } from '@graphcommerce/graphql'
2
+ import { StoreConfigDocument } from '@graphcommerce/magento-store'
1
3
  import { useHits } from 'react-instantsearch-hooks-web'
2
4
  import { AlgoliaCategoryHit } from '../lib/types'
3
5
 
4
- function hitToCategory(hits: AlgoliaCategoryHit[]) {
6
+ function hitToCategory(hits: AlgoliaCategoryHit[], categoryUrlSuffix?: string | null) {
5
7
  return hits.map((h) => {
6
8
  const urlSplit = h.url.split('/')
7
9
  const categoryUrl = urlSplit.reduce((prev, curr, currIndex) => {
8
10
  if (currIndex > 2) return `${prev}/${curr}`
9
11
  return ''
10
12
  })
11
- const url_key = categoryUrl.substring(0, categoryUrl.length - 5)
13
+ const url_key = categoryUrl.substring(1, categoryUrl.length - (categoryUrlSuffix?.length ?? 0))
12
14
  return {
13
15
  category_uid: h.objectID,
14
16
  category_level: h.level,
@@ -20,6 +22,7 @@ function hitToCategory(hits: AlgoliaCategoryHit[]) {
20
22
 
21
23
  export function useAlgoliaCategoryResults() {
22
24
  const { hits, results } = useHits<AlgoliaCategoryHit>()
23
- const categories = hitToCategory(hits)
25
+ const { data } = useQuery(StoreConfigDocument)
26
+ const categories = hitToCategory(hits, data?.storeConfig?.category_url_suffix)
24
27
  return { categories, search: results?.query }
25
28
  }
@@ -4,10 +4,11 @@ import { AlgoliaPageHit } from '../lib/types'
4
4
  function hitToPage(hits: AlgoliaPageHit[]) {
5
5
  return hits.map((h) => {
6
6
  const urlSplit = h.url.split('/')
7
- const url = urlSplit.reduce((prev, curr, currIndex) => {
7
+ const prep = urlSplit.reduce((prev, curr, currIndex) => {
8
8
  if (currIndex > 2) return `${prev}/${curr}`
9
9
  return ''
10
10
  })
11
+ const url = prep.substring(1, prep.length)
11
12
  return {
12
13
  objectID: h.objectID,
13
14
  name: h.name,
@@ -1,8 +1,9 @@
1
1
  import { storefrontConfig } from '@graphcommerce/next-ui'
2
- import { i18n } from '@lingui/core'
2
+ import { useRouter } from 'next/router'
3
3
 
4
4
  export function useAlgoliaSearchIndexConfig(suffix: string) {
5
- return storefrontConfig(i18n.locale)?.algoliaSearchIndexConfig.find((ai) =>
5
+ const { locale } = useRouter()
6
+ return storefrontConfig(locale)?.algoliaSearchIndexConfig.find((ai) =>
6
7
  ai.searchIndex.includes(suffix),
7
8
  )
8
9
  }
package/index.ts CHANGED
@@ -0,0 +1,2 @@
1
+ export * from './api/add-hygraph-record'
2
+ export * from './api/remove-hygraph-record'
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@graphcommerce/algolia-search",
3
3
  "homepage": "https://www.graphcommerce.org/",
4
4
  "repository": "github:graphcommerce-org/graphcommerce",
5
- "version": "7.0.0-canary.13",
5
+ "version": "7.0.0",
6
6
  "sideEffects": false,
7
7
  "prettier": "@graphcommerce/prettier-config-pwa",
8
8
  "eslintConfig": {
@@ -12,29 +12,30 @@
12
12
  }
13
13
  },
14
14
  "devDependencies": {
15
- "@graphcommerce/eslint-config-pwa": "7.0.0-canary.13",
16
- "@graphcommerce/next-config": "^7.0.0-canary.13",
17
- "@graphcommerce/prettier-config-pwa": "7.0.0-canary.13",
18
- "@graphcommerce/typescript-config-pwa": "7.0.0-canary.13"
15
+ "@graphcommerce/eslint-config-pwa": "7.0.0",
16
+ "@graphcommerce/next-config": "^7.0.0",
17
+ "@graphcommerce/prettier-config-pwa": "7.0.0",
18
+ "@graphcommerce/typescript-config-pwa": "7.0.0"
19
19
  },
20
20
  "dependencies": {
21
- "@graphcommerce/graphql": "7.0.0-canary.13",
22
- "@graphcommerce/ecommerce-ui": "7.0.0-canary.13",
23
- "@graphcommerce/magento-search": "7.0.0-canary.13",
24
- "@graphcommerce/next-config": "^7.0.0-canary.13",
25
- "@graphcommerce/next-ui": "7.0.0-canary.13",
26
- "@graphcommerce/magento-product": "7.0.0-canary.13",
27
- "@graphcommerce/graphql-mesh": "7.0.0-canary.13",
28
- "@graphcommerce/magento-store": "7.0.0-canary.13",
21
+ "@graphcommerce/graphql": "7.0.0",
22
+ "@graphcommerce/ecommerce-ui": "7.0.0",
23
+ "@graphcommerce/magento-search": "7.0.0",
24
+ "@graphcommerce/next-config": "^7.0.0",
25
+ "@graphcommerce/next-ui": "7.0.0",
26
+ "@graphcommerce/magento-product": "7.0.0",
27
+ "@graphcommerce/graphql-mesh": "7.0.0",
28
+ "@graphcommerce/magento-store": "7.0.0",
29
29
  "algoliasearch": "^4.15.0",
30
30
  "react-instantsearch-hooks-web": "^6.41.0"
31
31
  },
32
32
  "peerDependencies": {
33
- "@lingui/core": "^3.13.2",
34
- "@lingui/react": "^3.13.2",
33
+ "@lingui/core": "^4.2.1",
34
+ "@lingui/react": "^4.2.1",
35
+ "@lingui/macro": "^4.2.1",
35
36
  "@mui/material": "^5.10.16",
36
37
  "next": "^13.2.0",
37
38
  "react": "^18.2.0",
38
39
  "react-dom": "^18.2.0"
39
40
  }
40
- }
41
+ }
@@ -1,10 +1,10 @@
1
- import { CategorySearchResult, SearchFormProps } from '@graphcommerce/magento-search'
1
+ import { CategorySearchResult, CategorySearchResultsProps } from '@graphcommerce/magento-search'
2
2
  import { IfConfig, PluginProps } from '@graphcommerce/next-config'
3
3
  import { Index } from 'react-instantsearch-hooks-web'
4
4
  import { useAlgoliaCategoryResults } from '../hooks/useAlgoliaCategoryResults'
5
5
  import { useAlgoliaSearchIndexConfig } from '../hooks/useAlgoliaSearchIndexConfig'
6
6
 
7
- export const component = 'SearchForm'
7
+ export const component = 'CategorySearchResults'
8
8
  export const exported = '@graphcommerce/magento-search'
9
9
  export const ifConfig: IfConfig = 'algoliaApplicationId'
10
10
 
@@ -17,6 +17,7 @@ function CategoryHits() {
17
17
  <>
18
18
  {categories.map((category) => (
19
19
  <CategorySearchResult
20
+ key={category.category_uid}
20
21
  breadcrumbs={[category]}
21
22
  search={search}
22
23
  url_path={category.category_url_path}
@@ -26,19 +27,19 @@ function CategoryHits() {
26
27
  )
27
28
  }
28
29
 
29
- function AlgoliaCategorySearchPlugin(props: PluginProps<SearchFormProps>) {
30
- const { Prev, ...rest } = props
30
+ function AlgoliaCategorySearchPlugin(props: PluginProps<CategorySearchResultsProps>) {
31
+ const { Prev, children, ...rest } = props
31
32
  const searchIndex = useAlgoliaSearchIndexConfig('_categories')?.searchIndex
32
33
 
33
- if (!searchIndex) return <Prev {...rest} />
34
+ if (!searchIndex) return <Prev {...rest}>{children}</Prev>
34
35
 
35
36
  return (
36
- <>
37
- <Prev {...rest} />
37
+ <Prev {...rest}>
38
+ {children}
38
39
  <Index indexName={searchIndex}>
39
40
  <CategoryHits />
40
41
  </Index>
41
- </>
42
+ </Prev>
42
43
  )
43
44
  }
44
45
 
@@ -2,7 +2,7 @@ import { ProductFiltersProps } from '@graphcommerce/magento-product'
2
2
  import { IfConfig, PluginProps } from '@graphcommerce/next-config'
3
3
  import { AlgoliaFilters } from '../components/Filters/AlgoliaFilters'
4
4
 
5
- export const component = 'ProductListFiltersSearch'
5
+ export const component = 'ProductListFilters'
6
6
  export const exported = '@graphcommerce/magento-search'
7
7
  export const ifConfig: IfConfig = 'algoliaApplicationId'
8
8
 
@@ -1,10 +1,10 @@
1
- import { CategorySearchResult, SearchFormProps } from '@graphcommerce/magento-search'
1
+ import { CategorySearchResult, CategorySearchResultsProps } from '@graphcommerce/magento-search'
2
2
  import { IfConfig, PluginProps } from '@graphcommerce/next-config'
3
3
  import { Index } from 'react-instantsearch-hooks-web'
4
4
  import { useAlgoliaPageResults } from '../hooks/useAlgoliaPageResults'
5
5
  import { useAlgoliaSearchIndexConfig } from '../hooks/useAlgoliaSearchIndexConfig'
6
6
 
7
- export const component = 'SearchForm'
7
+ export const component = 'CategorySearchResults'
8
8
  export const exported = '@graphcommerce/magento-search'
9
9
  export const ifConfig: IfConfig = 'algoliaApplicationId'
10
10
 
@@ -17,6 +17,7 @@ function PageHits() {
17
17
  <>
18
18
  {pages.map((page) => (
19
19
  <CategorySearchResult
20
+ key={page.objectID}
20
21
  breadcrumbs={[
21
22
  {
22
23
  category_uid: page.objectID,
@@ -33,19 +34,19 @@ function PageHits() {
33
34
  )
34
35
  }
35
36
 
36
- function AlgoliaPageSearchPlugin(props: PluginProps<SearchFormProps>) {
37
+ function AlgoliaPageSearchPlugin(props: PluginProps<CategorySearchResultsProps>) {
37
38
  const { Prev, ...rest } = props
38
39
  const searchIndex = useAlgoliaSearchIndexConfig('_pages')?.searchIndex
39
40
 
40
41
  if (!searchIndex) return <Prev {...rest} />
41
42
 
42
43
  return (
43
- <>
44
- <Prev {...rest} />
44
+ <Prev {...rest}>
45
+ {rest.children}
45
46
  <Index indexName={searchIndex}>
46
47
  <PageHits />
47
48
  </Index>
48
- </>
49
+ </Prev>
49
50
  )
50
51
  }
51
52
 
@@ -3,7 +3,7 @@ import { IfConfig, PluginProps } from '@graphcommerce/next-config'
3
3
  import { useRouter } from 'next/router'
4
4
  import { AlgoliaPagination } from '../components/Pagination/AlgoliaPagination'
5
5
 
6
- export const component = 'ProductListPaginationSearch'
6
+ export const component = 'ProductListPagination'
7
7
  export const exported = '@graphcommerce/magento-search'
8
8
  export const ifConfig: IfConfig = 'algoliaApplicationId'
9
9
 
@@ -3,7 +3,7 @@ import { IfConfig, PluginProps } from '@graphcommerce/next-config'
3
3
  import { Index, usePagination } from 'react-instantsearch-hooks-web'
4
4
  import { useAlgoliaSearchIndexConfig } from '../hooks/useAlgoliaSearchIndexConfig'
5
5
 
6
- export const component = 'ProductListCountSearch'
6
+ export const component = 'ProductListCount'
7
7
  export const exported = '@graphcommerce/magento-search'
8
8
  export const ifConfig: IfConfig = 'algoliaApplicationId'
9
9
 
@@ -5,7 +5,7 @@ import { Index } from 'react-instantsearch-hooks-web'
5
5
  import { useAlgoliaProductResults } from '../hooks/useAlgoliaProductResults'
6
6
  import { useAlgoliaSearchIndexConfig } from '../hooks/useAlgoliaSearchIndexConfig'
7
7
 
8
- export const component = 'ProductListItemsSearch'
8
+ export const component = 'ProductListItemsBase'
9
9
  export const exported = '@graphcommerce/magento-search'
10
10
  export const ifConfig: IfConfig = 'algoliaApplicationId'
11
11
 
@@ -2,19 +2,24 @@ import { ProductFiltersProps } from '@graphcommerce/magento-product'
2
2
  import { IfConfig, PluginProps } from '@graphcommerce/next-config'
3
3
  import { storefrontConfig } from '@graphcommerce/next-ui'
4
4
  import { i18n } from '@lingui/core'
5
+ import { useRouter } from 'next/router'
5
6
  import { useSortBy } from 'react-instantsearch-hooks-web'
6
7
  import { RenderChip } from '../components/Chip/RenderChip'
7
8
 
8
- export const component = 'ProductListSortSearch'
9
+ export const component = 'ProductListSort'
9
10
  export const exported = '@graphcommerce/magento-search'
10
11
  export const ifConfig: IfConfig = 'algoliaApplicationId'
11
12
 
12
13
  function AlgoliaProductSortPlugin(props: PluginProps<ProductFiltersProps>) {
13
14
  const { Prev, ...rest } = props
15
+ const { locale } = useRouter()
16
+ const options = storefrontConfig(locale)?.sortOptions
14
17
  const sort = useSortBy({
15
- items: storefrontConfig(i18n.locale)?.sortOptions ?? [],
18
+ items: options ?? [],
16
19
  })
17
20
 
21
+ if (!options) return null
22
+
18
23
  return (
19
24
  <RenderChip
20
25
  __typename='Sort'
@@ -12,7 +12,7 @@ export const ifConfig: IfConfig = 'algoliaApplicationId'
12
12
  const searchClient = algoliasearch(applicationId, searchOnlyApiKey)
13
13
 
14
14
  function AlgoliaSearchContextPlugin(props: PluginProps<SearchContextProps>) {
15
- const { Prev, serverProps, ...rest } = props
15
+ const { Prev, serverProps, rendersInsideNextjs = true, ...rest } = props
16
16
  const searchIndex = useAlgoliaSearchIndexConfig('_products')?.searchIndex
17
17
 
18
18
  if (!searchIndex)
@@ -23,7 +23,7 @@ function AlgoliaSearchContextPlugin(props: PluginProps<SearchContextProps>) {
23
23
  return (
24
24
  <InstantSearchSSRProvider {...(typeof serverProps === 'object' ? serverProps : {})}>
25
25
  <InstantSearch searchClient={searchClient} indexName={searchIndex}>
26
- <Prev {...rest} />
26
+ {rendersInsideNextjs && <Prev {...rest} />}
27
27
  </InstantSearch>
28
28
  </InstantSearchSSRProvider>
29
29
  )
@@ -7,8 +7,7 @@ export const exported = '@graphcommerce/magento-search'
7
7
  export const ifConfig: IfConfig = 'algoliaApplicationId'
8
8
 
9
9
  function AlgoliaSearchFieldPlugin(props: PluginProps<SearchFormProps>) {
10
- const { search } = props
11
- return <SearchBox defaultValue={search} />
10
+ return <SearchBox {...props} />
12
11
  }
13
12
 
14
13
  export const Plugin = AlgoliaSearchFieldPlugin