@okendo/shopify-hydrogen 2.3.1 → 2.4.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Okendo Hydrogen 2 (Remix) React Components
4
4
 
5
- This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-plus/) to a Shopify Hydrogen store.
5
+ This package brings [Okendo's review widgets](https://www.okendo.io/blog/widget-plus/) and [Loyalty widgets](https://okendo.io/loyalty/) to a Shopify Hydrogen store.
6
6
 
7
7
  ## Requirements
8
8
 
@@ -44,6 +44,8 @@ Follow the instructions on [this page](https://help.shopify.com/en/manual/apps/a
44
44
 
45
45
  #### Using Curl
46
46
 
47
+ > Note: The settings metafields exist within the Okendo App's Reserved Namespace. To access these metafields we need to include the Okendo App ID (`app--1576377`) as a prefix in the `namespace` field.
48
+
47
49
  Open a new terminal or PowerShell window, then:
48
50
 
49
51
  1. Run the following command to expose the `widget_pre_render_style_tags` shop metafield:
@@ -55,22 +57,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
55
57
  -H 'X-Shopify-Access-Token: {access_token}' \
56
58
  -d '
57
59
  mutation {
58
- metafieldDefinitionCreate(
59
- definition: {
60
- name: "WidgetPreRenderStyleTags"
61
- namespace: "$app:reviews"
62
- key: "widget_pre_render_style_tags"
63
- type: "multi_line_text_field"
64
- ownerType: SHOP
65
- access: {
66
- admin: PUBLIC_READ
67
- storefront: PUBLIC_READ
68
- }
69
- }
70
- ) {
71
- createdDefinition { id name }
72
- userErrors { field message code }
73
- }
60
+ metafieldDefinitionCreate(
61
+ definition: {
62
+ name: "WidgetPreRenderStyleTags"
63
+ namespace: "app--1576377--reviews"
64
+ key: "widget_pre_render_style_tags"
65
+ type: "multi_line_text_field"
66
+ ownerType: SHOP
67
+ access: {
68
+ admin: PUBLIC_READ
69
+ storefront: PUBLIC_READ
70
+ }
71
+ }
72
+ ) {
73
+ createdDefinition { id name }
74
+ userErrors { field message code }
75
+ }
74
76
  }
75
77
  '
76
78
  ```
@@ -84,22 +86,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
84
86
  -H 'X-Shopify-Access-Token: {access_token}' \
85
87
  -d '
86
88
  mutation {
87
- metafieldDefinitionCreate(
88
- definition: {
89
- name: "WidgetPreRenderBodyStyleTags"
90
- namespace: "$app:reviews"
91
- key: "widget_pre_render_body_style_tags"
92
- type: "multi_line_text_field"
93
- ownerType: SHOP
94
- access: {
95
- admin: PUBLIC_READ
96
- storefront: PUBLIC_READ
97
- }
98
- }
99
- ) {
100
- createdDefinition { id name }
101
- userErrors { field message code }
102
- }
89
+ metafieldDefinitionCreate(
90
+ definition: {
91
+ name: "WidgetPreRenderBodyStyleTags"
92
+ namespace: "app--1576377--reviews"
93
+ key: "widget_pre_render_body_style_tags"
94
+ type: "multi_line_text_field"
95
+ ownerType: SHOP
96
+ access: {
97
+ admin: PUBLIC_READ
98
+ storefront: PUBLIC_READ
99
+ }
100
+ }
101
+ ) {
102
+ createdDefinition { id name }
103
+ userErrors { field message code }
104
+ }
103
105
  }
104
106
  '
105
107
  ```
@@ -113,22 +115,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
113
115
  -H 'X-Shopify-Access-Token: {access_token}' \
114
116
  -d '
115
117
  mutation {
116
- metafieldDefinitionCreate(
117
- definition: {
118
- name: "ReviewsWidgetSnippet"
119
- namespace: "$app:reviews"
120
- key: "reviews_widget_snippet"
121
- type: "multi_line_text_field"
122
- ownerType: PRODUCT
123
- access: {
124
- admin: PUBLIC_READ
125
- storefront: PUBLIC_READ
126
- }
127
- }
128
- ) {
129
- createdDefinition { id name }
130
- userErrors { field message code }
131
- }
118
+ metafieldDefinitionCreate(
119
+ definition: {
120
+ name: "ReviewsWidgetSnippet"
121
+ namespace: "app--1576377--reviews"
122
+ key: "reviews_widget_snippet"
123
+ type: "multi_line_text_field"
124
+ ownerType: PRODUCT
125
+ access: {
126
+ admin: PUBLIC_READ
127
+ storefront: PUBLIC_READ
128
+ }
129
+ }
130
+ ) {
131
+ createdDefinition { id name }
132
+ userErrors { field message code }
133
+ }
132
134
  }
133
135
  '
134
136
  ```
@@ -142,22 +144,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
142
144
  -H 'X-Shopify-Access-Token: {access_token}' \
143
145
  -d '
144
146
  mutation {
145
- metafieldDefinitionCreate(
146
- definition: {
147
- name: "StarRatingSnippet"
148
- namespace: "$app:reviews"
149
- key: "star_rating_snippet"
150
- type: "multi_line_text_field"
151
- ownerType: PRODUCT
152
- access: {
153
- admin: PUBLIC_READ
154
- storefront: PUBLIC_READ
155
- }
156
- }
157
- ) {
158
- createdDefinition { id name }
159
- userErrors { field message code }
160
- }
147
+ metafieldDefinitionCreate(
148
+ definition: {
149
+ name: "StarRatingSnippet"
150
+ namespace: "app--1576377--reviews"
151
+ key: "star_rating_snippet"
152
+ type: "multi_line_text_field"
153
+ ownerType: PRODUCT
154
+ access: {
155
+ admin: PUBLIC_READ
156
+ storefront: PUBLIC_READ
157
+ }
158
+ }
159
+ ) {
160
+ createdDefinition { id name }
161
+ userErrors { field message code }
162
+ }
161
163
  }
162
164
  '
163
165
  ```
@@ -171,22 +173,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
171
173
  -H 'X-Shopify-Access-Token: {access_token}' \
172
174
  -d '
173
175
  mutation {
174
- metafieldDefinitionCreate(
175
- definition: {
176
- name: "ReviewCount"
177
- namespace: "$app:reviews"
178
- key: "review_count"
179
- type: "number_integer"
180
- ownerType: PRODUCT
181
- access: {
182
- admin: PUBLIC_READ
183
- storefront: PUBLIC_READ
184
- }
185
- }
186
- ) {
187
- createdDefinition { id name }
188
- userErrors { field message code }
189
- }
176
+ metafieldDefinitionCreate(
177
+ definition: {
178
+ name: "ReviewCount"
179
+ namespace: "app--1576377--reviews"
180
+ key: "review_count"
181
+ type: "number_integer"
182
+ ownerType: PRODUCT
183
+ access: {
184
+ admin: PUBLIC_READ
185
+ storefront: PUBLIC_READ
186
+ }
187
+ }
188
+ ) {
189
+ createdDefinition { id name }
190
+ userErrors { field message code }
191
+ }
190
192
  }
191
193
  '
192
194
  ```
@@ -200,22 +202,22 @@ https://{shop}.myshopify.com/admin/api/2024-10/graphql.json \
200
202
  -H 'X-Shopify-Access-Token: {access_token}' \
201
203
  -d '
202
204
  mutation {
203
- metafieldDefinitionCreate(
204
- definition: {
205
- name: "AverageRating"
206
- namespace: "$app:reviews"
207
- key: "average_rating"
208
- type: "rating"
209
- ownerType: PRODUCT
210
- access: {
211
- admin: PUBLIC_READ
212
- storefront: PUBLIC_READ
213
- }
214
- }
215
- ) {
216
- createdDefinition { id name }
217
- userErrors { field message code }
218
- }
205
+ metafieldDefinitionCreate(
206
+ definition: {
207
+ name: "AverageRating"
208
+ namespace: "app--1576377--reviews"
209
+ key: "average_rating"
210
+ type: "rating"
211
+ ownerType: PRODUCT
212
+ access: {
213
+ admin: PUBLIC_READ
214
+ storefront: PUBLIC_READ
215
+ }
216
+ }
217
+ ) {
218
+ createdDefinition { id name }
219
+ userErrors { field message code }
220
+ }
219
221
  }
220
222
  '
221
223
  ```
@@ -231,26 +233,29 @@ Open your GraphQL IDE (such as Postman) and make `POST` requests with the follow
231
233
 
232
234
  ```graphql
233
235
  mutation {
234
- metafieldDefinitionCreate(
235
- definition: {
236
- name: "WidgetPreRenderStyleTags"
237
- namespace: "$app:reviews"
238
- key: "widget_pre_render_style_tags"
239
- type: "multi_line_text_field"
240
- ownerType: SHOP
241
- access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
242
- }
243
- ) {
244
- createdDefinition {
245
- id
246
- name
247
- }
248
- userErrors {
249
- field
250
- message
251
- code
252
- }
253
- }
236
+ metafieldDefinitionCreate(
237
+ definition: {
238
+ name: "WidgetPreRenderStyleTags"
239
+ namespace: "app--1576377--reviews"
240
+ key: "widget_pre_render_style_tags"
241
+ type: "multi_line_text_field"
242
+ ownerType: SHOP
243
+ access: {
244
+ admin: PUBLIC_READ,
245
+ storefront: PUBLIC_READ
246
+ }
247
+ }
248
+ ) {
249
+ createdDefinition {
250
+ id
251
+ name
252
+ }
253
+ userErrors {
254
+ field
255
+ message
256
+ code
257
+ }
258
+ }
254
259
  }
255
260
  ```
256
261
 
@@ -258,26 +263,29 @@ mutation {
258
263
 
259
264
  ```graphql
260
265
  mutation {
261
- metafieldDefinitionCreate(
262
- definition: {
263
- name: "WidgetPreRenderBodyStyleTags"
264
- namespace: "$app:reviews"
265
- key: "widget_pre_render_body_style_tags"
266
- type: "multi_line_text_field"
267
- ownerType: SHOP
268
- access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
269
- }
270
- ) {
271
- createdDefinition {
272
- id
273
- name
274
- }
275
- userErrors {
276
- field
277
- message
278
- code
279
- }
280
- }
266
+ metafieldDefinitionCreate(
267
+ definition: {
268
+ name: "WidgetPreRenderBodyStyleTags"
269
+ namespace: "app--1576377--reviews"
270
+ key: "widget_pre_render_body_style_tags"
271
+ type: "multi_line_text_field"
272
+ ownerType: SHOP
273
+ access: {
274
+ admin: PUBLIC_READ,
275
+ storefront: PUBLIC_READ
276
+ }
277
+ }
278
+ ) {
279
+ createdDefinition {
280
+ id
281
+ name
282
+ }
283
+ userErrors {
284
+ field
285
+ message
286
+ code
287
+ }
288
+ }
281
289
  }
282
290
  ```
283
291
 
@@ -285,26 +293,29 @@ mutation {
285
293
 
286
294
  ```graphql
287
295
  mutation {
288
- metafieldDefinitionCreate(
289
- definition: {
290
- name: "ReviewsWidgetSnippet"
291
- namespace: "$app:reviews"
292
- key: "reviews_widget_snippet"
293
- type: "multi_line_text_field"
294
- ownerType: PRODUCT
295
- access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
296
- }
297
- ) {
298
- createdDefinition {
299
- id
300
- name
301
- }
302
- userErrors {
303
- field
304
- message
305
- code
306
- }
307
- }
296
+ metafieldDefinitionCreate(
297
+ definition: {
298
+ name: "ReviewsWidgetSnippet"
299
+ namespace: "app--1576377--reviews"
300
+ key: "reviews_widget_snippet"
301
+ type: "multi_line_text_field"
302
+ ownerType: PRODUCT
303
+ access: {
304
+ admin: PUBLIC_READ,
305
+ storefront: PUBLIC_READ
306
+ }
307
+ }
308
+ ) {
309
+ createdDefinition {
310
+ id
311
+ name
312
+ }
313
+ userErrors {
314
+ field
315
+ message
316
+ code
317
+ }
318
+ }
308
319
  }
309
320
  ```
310
321
 
@@ -312,26 +323,29 @@ mutation {
312
323
 
313
324
  ```graphql
314
325
  mutation {
315
- metafieldDefinitionCreate(
316
- definition: {
317
- name: "StarRatingSnippet"
318
- namespace: "$app:reviews"
319
- key: "star_rating_snippet"
320
- type: "multi_line_text_field"
321
- ownerType: PRODUCT
322
- access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
323
- }
324
- ) {
325
- createdDefinition {
326
- id
327
- name
328
- }
329
- userErrors {
330
- field
331
- message
332
- code
333
- }
334
- }
326
+ metafieldDefinitionCreate(
327
+ definition: {
328
+ name: "StarRatingSnippet"
329
+ namespace: "app--1576377--reviews"
330
+ key: "star_rating_snippet"
331
+ type: "multi_line_text_field"
332
+ ownerType: PRODUCT
333
+ access: {
334
+ admin: PUBLIC_READ,
335
+ storefront: PUBLIC_READ
336
+ }
337
+ }
338
+ ) {
339
+ createdDefinition {
340
+ id
341
+ name
342
+ }
343
+ userErrors {
344
+ field
345
+ message
346
+ code
347
+ }
348
+ }
335
349
  }
336
350
  ```
337
351
 
@@ -339,26 +353,29 @@ mutation {
339
353
 
340
354
  ```graphql
341
355
  mutation {
342
- metafieldDefinitionCreate(
343
- definition: {
344
- name: "ReviewCount"
345
- namespace: "$app:reviews"
346
- key: "review_count"
347
- type: "number_integer"
348
- ownerType: PRODUCT
349
- access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
350
- }
351
- ) {
352
- createdDefinition {
353
- id
354
- name
355
- }
356
- userErrors {
357
- field
358
- message
359
- code
360
- }
361
- }
356
+ metafieldDefinitionCreate(
357
+ definition: {
358
+ name: "ReviewCount"
359
+ namespace: "app--1576377--reviews"
360
+ key: "review_count"
361
+ type: "number_integer"
362
+ ownerType: PRODUCT
363
+ access: {
364
+ admin: PUBLIC_READ,
365
+ storefront: PUBLIC_READ
366
+ }
367
+ }
368
+ ) {
369
+ createdDefinition {
370
+ id
371
+ name
372
+ }
373
+ userErrors {
374
+ field
375
+ message
376
+ code
377
+ }
378
+ }
362
379
  }
363
380
  ```
364
381
 
@@ -366,26 +383,29 @@ mutation {
366
383
 
367
384
  ```graphql
368
385
  mutation {
369
- metafieldDefinitionCreate(
370
- definition: {
371
- name: "AverageRating"
372
- namespace: "$app:reviews"
373
- key: "average_rating"
374
- type: "rating"
375
- ownerType: PRODUCT
376
- access: { admin: PUBLIC_READ, storefront: PUBLIC_READ }
377
- }
378
- ) {
379
- createdDefinition {
380
- id
381
- name
382
- }
383
- userErrors {
384
- field
385
- message
386
- code
387
- }
388
- }
386
+ metafieldDefinitionCreate(
387
+ definition: {
388
+ name: "AverageRating"
389
+ namespace: "app--1576377--reviews"
390
+ key: "average_rating"
391
+ type: "rating"
392
+ ownerType: PRODUCT
393
+ access: {
394
+ admin: PUBLIC_READ,
395
+ storefront: PUBLIC_READ
396
+ }
397
+ }
398
+ ) {
399
+ createdDefinition {
400
+ id
401
+ name
402
+ }
403
+ userErrors {
404
+ field
405
+ message
406
+ code
407
+ }
408
+ }
389
409
  }
390
410
  ```
391
411
 
@@ -444,8 +464,8 @@ Open `app/root.tsx` and add the following import:
444
464
 
445
465
  ```ts
446
466
  import {
447
- OkendoProvider,
448
- getOkendoProviderData,
467
+ OkendoProvider,
468
+ getOkendoProviderData,
449
469
  } from '@okendo/shopify-hydrogen';
450
470
  ```
451
471
 
@@ -458,10 +478,10 @@ return defer({
458
478
  // ...
459
479
  okendoProviderData:
460
480
  /* place `await` here if you want server-rendered widgets */ getOkendoProviderData(
461
- {
481
+ {
462
482
  context: args.context,
463
483
  subscriberId: '<your-okendo-subscriber-id>',
464
- },
484
+ },
465
485
  ),
466
486
  });
467
487
  ```
@@ -470,10 +490,10 @@ Locate the `Layout` component, add the `meta` tag `oke:subscriber_id` to `head`,
470
490
 
471
491
  ```tsx
472
492
  <head>
473
- <meta charSet="utf-8" />
474
- <meta name="viewport" content="width=device-width,initial-scale=1" />
475
- <meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
476
- ...
493
+ <meta charSet="utf-8" />
494
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
495
+ <meta name="oke:subscriber_id" content="<your-okendo-subscriber-id>" />
496
+ ...
477
497
  </head>
478
498
  ```
479
499
 
@@ -487,10 +507,10 @@ Append `OkendoProvider` to `body`, and pass it the promise — or the data — r
487
507
  cart={data.cart}
488
508
  shop={data.shop}
489
509
  consent={data.consent}
490
- >
510
+ >
491
511
  <PageLayout {...data}>{children}</PageLayout>
492
512
  </Analytics.Provider>
493
- </OkendoProvider>
513
+ </OkendoProvider>
494
514
  ) : (
495
515
  children
496
516
  )}
@@ -512,8 +532,8 @@ const { nonce, header, NonceProvider } = createContentSecurityPolicy({
512
532
  checkoutDomain: context.env.PUBLIC_CHECKOUT_DOMAIN,
513
533
  storeDomain: context.env.PUBLIC_STORE_DOMAIN,
514
534
  },
515
- defaultSrc: [
516
- "'self'",
535
+ defaultSrc: [
536
+ "'self'",
517
537
  'localhost:*',
518
538
  'https://cdn.shopify.com',
519
539
  'https://www.google.com',
@@ -525,9 +545,9 @@ const { nonce, header, NonceProvider } = createContentSecurityPolicy({
525
545
  'https://surveys.okendo.io',
526
546
  'https://api.okendo.io',
527
547
  'data:',
528
- ],
529
- imgSrc: [
530
- "'self'",
548
+ ],
549
+ imgSrc: [
550
+ "'self'",
531
551
  'https://cdn.shopify.com',
532
552
  'data:',
533
553
  'https://d3hw6dc1ow8pp2.cloudfront.net',
@@ -535,26 +555,26 @@ const { nonce, header, NonceProvider } = createContentSecurityPolicy({
535
555
  'https://dov7r31oq5dkj.cloudfront.net',
536
556
  'https://cdn-static.okendo.io',
537
557
  'https://surveys.okendo.io',
538
- ],
539
- mediaSrc: [
540
- "'self'",
558
+ ],
559
+ mediaSrc: [
560
+ "'self'",
541
561
  'https://d3hw6dc1ow8pp2.cloudfront.net',
542
562
  'https://d3g5hqndtiniji.cloudfront.net',
543
563
  'https://dov7r31oq5dkj.cloudfront.net',
544
564
  'https://cdn-static.okendo.io',
545
- ],
565
+ ],
546
566
  styleSrc: [
547
- "'self'",
548
- "'unsafe-inline'",
567
+ "'self'",
568
+ "'unsafe-inline'",
549
569
  'https://cdn.shopify.com',
550
570
  'https://fonts.googleapis.com',
551
571
  'https://fonts.gstatic.com',
552
572
  'https://d3hw6dc1ow8pp2.cloudfront.net',
553
573
  'https://cdn-static.okendo.io',
554
574
  'https://surveys.okendo.io',
555
- ],
556
- scriptSrc: [
557
- "'self'",
575
+ ],
576
+ scriptSrc: [
577
+ "'self'",
558
578
  'https://cdn.shopify.com',
559
579
  'https://d3hw6dc1ow8pp2.cloudfront.net',
560
580
  'https://dov7r31oq5dkj.cloudfront.net',
@@ -563,18 +583,18 @@ const { nonce, header, NonceProvider } = createContentSecurityPolicy({
563
583
  'https://api.okendo.io',
564
584
  'https://www.google.com',
565
585
  'https://www.gstatic.com',
566
- ],
567
- fontSrc: [
568
- "'self'",
586
+ ],
587
+ fontSrc: [
588
+ "'self'",
569
589
  'https://fonts.gstatic.com',
570
590
  'https://d3hw6dc1ow8pp2.cloudfront.net',
571
591
  'https://dov7r31oq5dkj.cloudfront.net',
572
592
  'https://cdn.shopify.com',
573
593
  'https://cdn-static.okendo.io',
574
594
  'https://surveys.okendo.io',
575
- ],
576
- connectSrc: [
577
- "'self'",
595
+ ],
596
+ connectSrc: [
597
+ "'self'",
578
598
  'https://monorail-edge.shopifysvc.com',
579
599
  'localhost:*',
580
600
  'ws://localhost:*',
@@ -585,7 +605,7 @@ const { nonce, header, NonceProvider } = createContentSecurityPolicy({
585
605
  'https://api.raygun.com',
586
606
  'https://www.google.com',
587
607
  'https://www.gstatic.com',
588
- ],
608
+ ],
589
609
  frameSrc: ['https://www.google.com', 'https://www.gstatic.com'],
590
610
  });
591
611
  ```
@@ -602,14 +622,14 @@ Add the following block just before the `RECOMMENDED_PRODUCTS_QUERY` GraphQL que
602
622
 
603
623
  ```ts
604
624
  const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
605
- fragment OkendoStarRatingSnippet on Product {
606
- okendoStarRatingSnippet: metafield(
607
- namespace: "$app:reviews"
608
- key: "star_rating_snippet"
609
- ) {
610
- value
611
- }
612
- }
625
+ fragment OkendoStarRatingSnippet on Product {
626
+ okendoStarRatingSnippet: metafield(
627
+ namespace: "app--1576377--reviews"
628
+ key: "star_rating_snippet"
629
+ ) {
630
+ value
631
+ }
632
+ }
613
633
  ` as const;
614
634
  ```
615
635
 
@@ -617,36 +637,36 @@ Then append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}` and `...OkendoStarRatingSni
617
637
 
618
638
  ```ts
619
639
  const RECOMMENDED_PRODUCTS_QUERY = `#graphql
620
- ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
621
- fragment RecommendedProduct on Product {
622
- id
623
- title
624
- handle
625
- priceRange {
626
- minVariantPrice {
627
- amount
628
- currencyCode
629
- }
630
- }
631
- images(first: 1) {
632
- nodes {
633
- id
634
- url
635
- altText
636
- width
637
- height
638
- }
639
- }
640
- ...OkendoStarRatingSnippet
641
- }
642
- query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
643
- @inContext(country: $country, language: $language) {
644
- products(first: 4, sortKey: UPDATED_AT, reverse: true) {
645
- nodes {
646
- ...RecommendedProduct
647
- }
648
- }
649
- }
640
+ ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
641
+ fragment RecommendedProduct on Product {
642
+ id
643
+ title
644
+ handle
645
+ priceRange {
646
+ minVariantPrice {
647
+ amount
648
+ currencyCode
649
+ }
650
+ }
651
+ images(first: 1) {
652
+ nodes {
653
+ id
654
+ url
655
+ altText
656
+ width
657
+ height
658
+ }
659
+ }
660
+ ...OkendoStarRatingSnippet
661
+ }
662
+ query RecommendedProducts ($country: CountryCode, $language: LanguageCode)
663
+ @inContext(country: $country, language: $language) {
664
+ products(first: 4, sortKey: UPDATED_AT, reverse: true) {
665
+ nodes {
666
+ ...RecommendedProduct
667
+ }
668
+ }
669
+ }
650
670
  ` as const;
651
671
  ```
652
672
 
@@ -656,17 +676,17 @@ Add `OkendoStarRating` to the `RecommendedProducts` component — for instance,
656
676
 
657
677
  ```tsx
658
678
  <Image
659
- data={product.images.nodes[0]}
660
- aspectRatio="1/1"
661
- sizes="(min-width: 45em) 20vw, 50vw"
679
+ data={product.images.nodes[0]}
680
+ aspectRatio="1/1"
681
+ sizes="(min-width: 45em) 20vw, 50vw"
662
682
  />
663
683
  <h4>{product.title}</h4>
664
684
  <OkendoStarRating
665
- productId={product.id}
666
- okendoStarRatingSnippet={product.okendoStarRatingSnippet}
685
+ productId={product.id}
686
+ okendoStarRatingSnippet={product.okendoStarRatingSnippet}
667
687
  />
668
688
  <small>
669
- <Money data={product.priceRange.minVariantPrice} />
689
+ <Money data={product.priceRange.minVariantPrice} />
670
690
  </small>
671
691
  ```
672
692
 
@@ -690,25 +710,25 @@ Add the following block just before the `PRODUCT_FRAGMENT` GraphQL query:
690
710
 
691
711
  ```ts
692
712
  const OKENDO_PRODUCT_STAR_RATING_FRAGMENT = `#graphql
693
- fragment OkendoStarRatingSnippet on Product {
694
- okendoStarRatingSnippet: metafield(
695
- namespace: "$app:reviews"
696
- key: "star_rating_snippet"
697
- ) {
698
- value
699
- }
700
- }
713
+ fragment OkendoStarRatingSnippet on Product {
714
+ okendoStarRatingSnippet: metafield(
715
+ namespace: "app--1576377--reviews"
716
+ key: "star_rating_snippet"
717
+ ) {
718
+ value
719
+ }
720
+ }
701
721
  ` as const;
702
722
 
703
723
  const OKENDO_PRODUCT_REVIEWS_FRAGMENT = `#graphql
704
- fragment OkendoReviewsSnippet on Product {
705
- okendoReviewsSnippet: metafield(
706
- namespace: "$app:reviews"
707
- key: "reviews_widget_snippet"
708
- ) {
709
- value
710
- }
711
- }
724
+ fragment OkendoReviewsSnippet on Product {
725
+ okendoReviewsSnippet: metafield(
726
+ namespace: "app--1576377--reviews"
727
+ key: "reviews_widget_snippet"
728
+ ) {
729
+ value
730
+ }
731
+ }
712
732
  ` as const;
713
733
  ```
714
734
 
@@ -716,35 +736,35 @@ Then append `${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}`, `${OKENDO_PRODUCT_REVIEWS_
716
736
 
717
737
  ```ts
718
738
  const PRODUCT_FRAGMENT = `#graphql
719
- ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
720
- ${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
721
- fragment Product on Product {
722
- id
723
- title
724
- vendor
725
- handle
726
- descriptionHtml
727
- description
728
- options {
729
- name
730
- values
731
- }
732
- selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
733
- ...ProductVariant
734
- }
735
- variants(first: 1) {
736
- nodes {
737
- ...ProductVariant
738
- }
739
- }
740
- seo {
741
- description
742
- title
743
- }
744
- ...OkendoStarRatingSnippet
745
- ...OkendoReviewsSnippet
746
- }
747
- ${PRODUCT_VARIANT_FRAGMENT}
739
+ ${OKENDO_PRODUCT_STAR_RATING_FRAGMENT}
740
+ ${OKENDO_PRODUCT_REVIEWS_FRAGMENT}
741
+ fragment Product on Product {
742
+ id
743
+ title
744
+ vendor
745
+ handle
746
+ descriptionHtml
747
+ description
748
+ options {
749
+ name
750
+ values
751
+ }
752
+ selectedVariant: variantBySelectedOptions(selectedOptions: $selectedOptions) {
753
+ ...ProductVariant
754
+ }
755
+ variants(first: 1) {
756
+ nodes {
757
+ ...ProductVariant
758
+ }
759
+ }
760
+ seo {
761
+ description
762
+ title
763
+ }
764
+ ...OkendoStarRatingSnippet
765
+ ...OkendoReviewsSnippet
766
+ }
767
+ ${PRODUCT_VARIANT_FRAGMENT}
748
768
  ` as const;
749
769
  ```
750
770
 
@@ -754,27 +774,27 @@ Add `OkendoStarRating` and `OkendoReviews` to the `Product` component:
754
774
 
755
775
  ```tsx
756
776
  <>
757
- <div className="product">
758
- <ProductImage image={selectedVariant?.image} />
777
+ <div className="product">
778
+ <ProductImage image={selectedVariant?.image} />
759
779
  <div className="product-main">
760
780
  <h1>{title}</h1>
761
781
  <OkendoStarRating
762
782
  productId={product.id}
763
783
  okendoStarRatingSnippet={product.okendoStarRatingSnippet}
764
- />
784
+ />
765
785
  <ProductPrice
766
786
  price={selectedVariant?.price}
767
787
  compareAtPrice={selectedVariant?.compareAtPrice}
768
788
  />
769
789
  ...
770
- </div>
790
+ </div>
771
791
  ...
772
792
  </div>
773
793
 
774
- <OkendoReviews
775
- productId={product.id}
776
- okendoReviewsSnippet={product.okendoReviewsSnippet}
777
- />
794
+ <OkendoReviews
795
+ productId={product.id}
796
+ okendoReviewsSnippet={product.okendoReviewsSnippet}
797
+ />
778
798
  </>
779
799
  ```
780
800
 
@@ -795,7 +815,7 @@ import { type MetaFunction } from '@remix-run/react';
795
815
  import { OkendoReviews } from '@okendo/shopify-hydrogen';
796
816
 
797
817
  export const meta: MetaFunction = () => {
798
- return [{ title: `Hydrogen | Okendo All Reviews` }];
818
+ return [{title: `Hydrogen | Okendo All Reviews`}];
799
819
  };
800
820
 
801
821
  export default function ReviewsPage() {
@@ -807,3 +827,72 @@ export default function ReviewsPage() {
807
827
  );
808
828
  }
809
829
  ```
830
+
831
+ ### Okendo Reviews Carousel Widget - Client Side Only
832
+ If you would like to include a copy of the Okendo Reviews Carousel Widget which displays reviews by product or group for a given store (to be used on a homepage or featured page for example), please add the `OkendoReviewsCarouselWidget` with or without the the `productId` or `groupId`.
833
+
834
+ Please note the all reviews widget loads on the client not the server.
835
+
836
+ ```tsx
837
+ import { type MetaFunction } from '@remix-run/react';
838
+ import { OkendoReviews } from '@okendo/shopify-hydrogen';
839
+
840
+ export const meta: MetaFunction = () => {
841
+ return [{title: `Hydrogen | Okendo Reviews Carousel`}];
842
+ };
843
+
844
+ export default function AFeaturedPage() {
845
+ return (
846
+ <div className="all-reviews">
847
+ <h1>Reviews Carousel Widget</h1>
848
+ <OkendoReviewsCarousel
849
+ productId={product.id}
850
+ />
851
+ </div>
852
+ );
853
+ }
854
+ ```
855
+
856
+ # Loyalty Widgets
857
+ ## Installation
858
+ To include Loyalty Widgets in your Shopify Hydrogen store, you will need to make the following changes:
859
+
860
+ 1. Add `customerAccessToken: await args.context.customerAccount.getAccessToken(),` to your `loader` function, this will be used to log your customer into the Loyalty App.
861
+
862
+ 2. Add `okendoProducts: ['reviews', 'loyalty'],` as a property to `getOkendoProviderData` in your `loader` function, alongside the existing `context` and `subscriberId` arguments.
863
+
864
+ > Note: If you only wish to use the Loyalty product and not reviews then simply leave out the `'reviews'` from the array like so: `okendoProducts: ['loyalty'],`.
865
+
866
+ The relevant section should now look something like this:
867
+ ```ts
868
+ return defer({
869
+ // ...
870
+ customerAccessToken: await args.context.customerAccount.getAccessToken(),
871
+ okendoProviderData:
872
+ getOkendoProviderData(
873
+ {
874
+ context: args.context,
875
+ subscriberId: '<your-okendo-subscriber-id>',
876
+ okendoProducts: ['reviews', 'loyalty'],
877
+ },
878
+ ),
879
+ });
880
+ ```
881
+
882
+ 3. Add `customerAccessToken={data.customerAccessToken}` to the `OkendoProvider` component, it should now look like:
883
+ ```ts
884
+ <OkendoProvider
885
+ nonce={nonce}
886
+ okendoProviderData={data.okendoProviderData}
887
+ customerAccessToken={data.customerAccessToken}
888
+ >
889
+ ...
890
+ </OkendoProvider>
891
+ ```
892
+
893
+ If your Okendo Loyalty Settings are [correctly set up](https://support.okendo.io/en/collections/8270193-okendo-loyalty) and your program has launched, the Loyalty Floating Widget will now appear on your store.
894
+
895
+ ## Dedicated Loyalty Page
896
+ Add `<OkendoLoyaltyEmbeddedWidget />` to any components/pages where you wish to have the Dedicated Loyalty Page appear.
897
+
898
+ *Make sure you are importing the component from the `okendo-shopify-hydrogen` package: `import {OkendoLoyaltyEmbeddedWidget} from '@okendo/shopify-hydrogen';`*