@revova/hydrogen 1.0.1 → 1.0.2
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 +78 -122
- package/dist/index.cjs +100 -143
- package/dist/index.d.cts +45 -57
- package/dist/index.d.ts +45 -57
- package/dist/index.js +99 -142
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,55 +17,53 @@ npm install @revova/hydrogen
|
|
|
17
17
|
|
|
18
18
|
## How it works
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
In Hydrogen, your storefront runs on its own domain (Oxygen, Vercel, etc.), so you must pass the **absolute** proxy URL — not a relative path.
|
|
20
|
+
Components and hooks call your Revova backend directly over HTTPS with a `shop` query parameter and a `Bearer` token:
|
|
23
21
|
|
|
24
22
|
```
|
|
25
|
-
https://yourstore.myshopify.com
|
|
23
|
+
GET https://yourapp.trycloudflare.com/api/reviews?shop=yourstore.myshopify.com&...
|
|
24
|
+
Authorization: Bearer <apiToken>
|
|
26
25
|
```
|
|
27
26
|
|
|
28
|
-
No
|
|
27
|
+
No Shopify App Proxy. No CORS issues. No API keys exposed beyond what you pass in.
|
|
29
28
|
|
|
30
29
|
---
|
|
31
30
|
|
|
32
31
|
## Setup (Hydrogen)
|
|
33
32
|
|
|
34
|
-
### 1 —
|
|
35
|
-
|
|
36
|
-
Same as any merchant. The App Proxy is registered automatically on install.
|
|
37
|
-
|
|
38
|
-
### 2 — Expose your store domain in the Hydrogen environment
|
|
33
|
+
### 1 — Add your credentials to the environment
|
|
39
34
|
|
|
40
35
|
```env
|
|
41
36
|
# .env
|
|
37
|
+
PUBLIC_REVOVA_API_URL=https://yourapp.trycloudflare.com
|
|
42
38
|
PUBLIC_STORE_DOMAIN=yourstore.myshopify.com
|
|
39
|
+
PUBLIC_REVOVA_API_TOKEN=your_token_here
|
|
43
40
|
```
|
|
44
41
|
|
|
45
|
-
|
|
42
|
+
Expose them through your Hydrogen loader:
|
|
46
43
|
|
|
47
44
|
```ts
|
|
48
45
|
// app/root.tsx or any route loader
|
|
49
46
|
export async function loader({ context }: LoaderArgs) {
|
|
50
47
|
return {
|
|
51
|
-
|
|
48
|
+
apiUrl: context.env.PUBLIC_REVOVA_API_URL,
|
|
49
|
+
shop: context.env.PUBLIC_STORE_DOMAIN,
|
|
50
|
+
apiToken: context.env.PUBLIC_REVOVA_API_TOKEN,
|
|
52
51
|
};
|
|
53
52
|
}
|
|
54
53
|
```
|
|
55
54
|
|
|
56
|
-
###
|
|
55
|
+
### 2 — Spread credentials into any component
|
|
57
56
|
|
|
58
|
-
Every component accepts
|
|
57
|
+
Every component accepts `apiUrl`, `shop`, and `apiToken`. The easiest pattern is to spread a single `creds` object:
|
|
59
58
|
|
|
60
59
|
```tsx
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
const creds = {
|
|
61
|
+
apiUrl: env.PUBLIC_REVOVA_API_URL,
|
|
62
|
+
shop: env.PUBLIC_STORE_DOMAIN,
|
|
63
|
+
apiToken: env.PUBLIC_REVOVA_API_TOKEN,
|
|
64
|
+
};
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
<ReviewWidget
|
|
66
|
-
proxyUrl={`https://${storeDomain}/apps/revova`}
|
|
67
|
-
productId={product.id}
|
|
68
|
-
/>
|
|
66
|
+
<ReviewWidget {...creds} productId={product.id} />
|
|
69
67
|
```
|
|
70
68
|
|
|
71
69
|
---
|
|
@@ -80,26 +78,21 @@ export async function loader({ context, params }: LoaderArgs) {
|
|
|
80
78
|
const product = await getProduct(params.handle);
|
|
81
79
|
return {
|
|
82
80
|
product,
|
|
83
|
-
|
|
81
|
+
apiUrl: context.env.PUBLIC_REVOVA_API_URL,
|
|
82
|
+
shop: context.env.PUBLIC_STORE_DOMAIN,
|
|
83
|
+
apiToken: context.env.PUBLIC_REVOVA_API_TOKEN,
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export default function ProductPage() {
|
|
88
|
-
const { product,
|
|
88
|
+
const { product, apiUrl, shop, apiToken } = useLoaderData<typeof loader>();
|
|
89
|
+
const creds = { apiUrl, shop, apiToken };
|
|
89
90
|
|
|
90
91
|
return (
|
|
91
92
|
<>
|
|
92
93
|
<h1>{product.title}</h1>
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
storeDomain={storeDomain}
|
|
96
|
-
productId={product.id}
|
|
97
|
-
/>
|
|
98
|
-
|
|
99
|
-
<FloatingReviewButton
|
|
100
|
-
storeDomain={storeDomain}
|
|
101
|
-
productId={product.id}
|
|
102
|
-
/>
|
|
94
|
+
<ReviewWidget {...creds} productId={product.id} />
|
|
95
|
+
<FloatingReviewButton {...creds} productId={product.id} />
|
|
103
96
|
</>
|
|
104
97
|
);
|
|
105
98
|
}
|
|
@@ -107,9 +100,19 @@ export default function ProductPage() {
|
|
|
107
100
|
|
|
108
101
|
---
|
|
109
102
|
|
|
110
|
-
##
|
|
103
|
+
## API Credentials (`ApiProps`)
|
|
104
|
+
|
|
105
|
+
All components and hooks require these three props. Spread them as a group for convenience.
|
|
106
|
+
|
|
107
|
+
| Prop | Type | Description |
|
|
108
|
+
|---|---|---|
|
|
109
|
+
| `apiUrl` | `string` | Revova backend base URL (e.g. `https://yourapp.trycloudflare.com`) |
|
|
110
|
+
| `shop` | `string` | Store myshopify.com domain (e.g. `yourstore.myshopify.com`) |
|
|
111
|
+
| `apiToken` | `string` | Bearer token for authorization |
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Components
|
|
113
116
|
|
|
114
117
|
### `<ReviewWidget>`
|
|
115
118
|
|
|
@@ -119,7 +122,7 @@ Full review list with pagination, sorting, and an inline submission form.
|
|
|
119
122
|
import { ReviewWidget } from '@revova/hydrogen';
|
|
120
123
|
|
|
121
124
|
<ReviewWidget
|
|
122
|
-
|
|
125
|
+
{...creds}
|
|
123
126
|
productId={product.id}
|
|
124
127
|
pageSize={10}
|
|
125
128
|
showForm
|
|
@@ -129,8 +132,7 @@ import { ReviewWidget } from '@revova/hydrogen';
|
|
|
129
132
|
|
|
130
133
|
| Prop | Type | Default | Description |
|
|
131
134
|
|---|---|---|---|
|
|
132
|
-
| `
|
|
133
|
-
| `proxyUrl` | `string` | — | Explicit proxy URL — use instead of `storeDomain` if preferred |
|
|
135
|
+
| `...creds` | `ApiProps` | — | `apiUrl`, `shop`, `apiToken` |
|
|
134
136
|
| `productId` | `string` | — | Shopify product GID |
|
|
135
137
|
| `locale` | `string` | — | Locale code for translated reviews (e.g. `fr`) |
|
|
136
138
|
| `pageSize` | `number` | `10` | Reviews per page |
|
|
@@ -149,14 +151,14 @@ import { ReviewForm } from '@revova/hydrogen';
|
|
|
149
151
|
|
|
150
152
|
// `form` comes from the ReviewsResponse returned by useReviews
|
|
151
153
|
<ReviewForm
|
|
152
|
-
|
|
154
|
+
{...creds}
|
|
153
155
|
productId={product.id}
|
|
154
156
|
form={data.form}
|
|
155
157
|
onSuccess={() => console.log('submitted')}
|
|
156
158
|
/>
|
|
157
159
|
```
|
|
158
160
|
|
|
159
|
-
**Props:**
|
|
161
|
+
**Props:** `...creds`, `productId`, `form` (required — `ResolvedForm`), `onSuccess?`, `className?`
|
|
160
162
|
|
|
161
163
|
---
|
|
162
164
|
|
|
@@ -181,10 +183,10 @@ Aggregate rating + review count badge. Great for product cards and PDP headers.
|
|
|
181
183
|
```tsx
|
|
182
184
|
import { ReviewCount } from '@revova/hydrogen';
|
|
183
185
|
|
|
184
|
-
<ReviewCount
|
|
186
|
+
<ReviewCount {...creds} starSize={14} />
|
|
185
187
|
```
|
|
186
188
|
|
|
187
|
-
**Props:**
|
|
189
|
+
**Props:** `...creds`, `starColor?`, `starSize?`, `className?`
|
|
188
190
|
|
|
189
191
|
---
|
|
190
192
|
|
|
@@ -195,15 +197,10 @@ Auto-advancing carousel of recent reviews. Includes previous/next arrows and dot
|
|
|
195
197
|
```tsx
|
|
196
198
|
import { ReviewCarousel } from '@revova/hydrogen';
|
|
197
199
|
|
|
198
|
-
<ReviewCarousel
|
|
199
|
-
storeDomain={storeDomain}
|
|
200
|
-
limit={10}
|
|
201
|
-
autoPlay
|
|
202
|
-
intervalMs={4000}
|
|
203
|
-
/>
|
|
200
|
+
<ReviewCarousel {...creds} limit={10} autoPlay intervalMs={4000} />
|
|
204
201
|
```
|
|
205
202
|
|
|
206
|
-
**Props:**
|
|
203
|
+
**Props:** `...creds`, `limit?` (default 10), `autoPlay?` (default `true`), `intervalMs?` (default 4000), `starColor?`, `className?`
|
|
207
204
|
|
|
208
205
|
---
|
|
209
206
|
|
|
@@ -214,14 +211,10 @@ Masonry photo grid of reviews that have images. Clicking any photo opens a light
|
|
|
214
211
|
```tsx
|
|
215
212
|
import { ReviewGallery } from '@revova/hydrogen';
|
|
216
213
|
|
|
217
|
-
<ReviewGallery
|
|
218
|
-
storeDomain={storeDomain}
|
|
219
|
-
columns={3}
|
|
220
|
-
limit={20}
|
|
221
|
-
/>
|
|
214
|
+
<ReviewGallery {...creds} columns={3} limit={20} />
|
|
222
215
|
```
|
|
223
216
|
|
|
224
|
-
**Props:**
|
|
217
|
+
**Props:** `...creds`, `limit?` (default 20), `columns?` (default 3), `starColor?`, `className?`
|
|
225
218
|
|
|
226
219
|
---
|
|
227
220
|
|
|
@@ -232,13 +225,10 @@ Product Q&A — lists questions with answers and includes an ask-a-question form
|
|
|
232
225
|
```tsx
|
|
233
226
|
import { QnAWidget } from '@revova/hydrogen';
|
|
234
227
|
|
|
235
|
-
<QnAWidget
|
|
236
|
-
storeDomain={storeDomain}
|
|
237
|
-
productId={product.id}
|
|
238
|
-
/>
|
|
228
|
+
<QnAWidget {...creds} productId={product.id} />
|
|
239
229
|
```
|
|
240
230
|
|
|
241
|
-
**Props:**
|
|
231
|
+
**Props:** `...creds`, `productId`, `className?`
|
|
242
232
|
|
|
243
233
|
---
|
|
244
234
|
|
|
@@ -249,17 +239,12 @@ Aggregate rating badge in three styles: `pill` (default), `inline`, or `card`.
|
|
|
249
239
|
```tsx
|
|
250
240
|
import { TrustBadge } from '@revova/hydrogen';
|
|
251
241
|
|
|
252
|
-
|
|
253
|
-
<TrustBadge
|
|
254
|
-
|
|
255
|
-
// Card — great for landing pages
|
|
256
|
-
<TrustBadge storeDomain={storeDomain} style="card" />
|
|
257
|
-
|
|
258
|
-
// Inline — great inside sentences or product specs
|
|
259
|
-
<TrustBadge storeDomain={storeDomain} style="inline" />
|
|
242
|
+
<TrustBadge {...creds} style="pill" />
|
|
243
|
+
<TrustBadge {...creds} style="card" />
|
|
244
|
+
<TrustBadge {...creds} style="inline" />
|
|
260
245
|
```
|
|
261
246
|
|
|
262
|
-
**Props:**
|
|
247
|
+
**Props:** `...creds`, `style?` (`'pill' | 'inline' | 'card'`, default `'pill'`), `starColor?`, `className?`
|
|
263
248
|
|
|
264
249
|
---
|
|
265
250
|
|
|
@@ -270,14 +255,10 @@ A continuously scrolling marquee of recent review snippets — ideal for homepag
|
|
|
270
255
|
```tsx
|
|
271
256
|
import { ReviewTicker } from '@revova/hydrogen';
|
|
272
257
|
|
|
273
|
-
<ReviewTicker
|
|
274
|
-
storeDomain={storeDomain}
|
|
275
|
-
limit={20}
|
|
276
|
-
speedSeconds={30}
|
|
277
|
-
/>
|
|
258
|
+
<ReviewTicker {...creds} limit={20} speedSeconds={30} />
|
|
278
259
|
```
|
|
279
260
|
|
|
280
|
-
**Props:**
|
|
261
|
+
**Props:** `...creds`, `limit?` (default 20), `speedSeconds?` (default 30), `starColor?`, `className?`
|
|
281
262
|
|
|
282
263
|
---
|
|
283
264
|
|
|
@@ -289,14 +270,14 @@ A timed floating popup showing recent reviews one at a time. Add once to your ro
|
|
|
289
270
|
import { SocialProofPopup } from '@revova/hydrogen';
|
|
290
271
|
|
|
291
272
|
<SocialProofPopup
|
|
292
|
-
|
|
273
|
+
{...creds}
|
|
293
274
|
position="bottom-left"
|
|
294
275
|
intervalMs={8000}
|
|
295
276
|
displayMs={5000}
|
|
296
277
|
/>
|
|
297
278
|
```
|
|
298
279
|
|
|
299
|
-
**Props:**
|
|
280
|
+
**Props:** `...creds`, `position?` (`'bottom-left' | 'bottom-right'`, default `'bottom-left'`), `intervalMs?` (default 8000), `displayMs?` (default 5000), `starColor?`, `className?`
|
|
300
281
|
|
|
301
282
|
---
|
|
302
283
|
|
|
@@ -307,15 +288,10 @@ A sticky tab fixed to the left or right edge of the viewport. Clicking it slides
|
|
|
307
288
|
```tsx
|
|
308
289
|
import { FloatingReviewsTab } from '@revova/hydrogen';
|
|
309
290
|
|
|
310
|
-
<FloatingReviewsTab
|
|
311
|
-
storeDomain={storeDomain}
|
|
312
|
-
label="Reviews"
|
|
313
|
-
position="right"
|
|
314
|
-
color="#111827"
|
|
315
|
-
/>
|
|
291
|
+
<FloatingReviewsTab {...creds} label="Reviews" position="right" />
|
|
316
292
|
```
|
|
317
293
|
|
|
318
|
-
**Props:**
|
|
294
|
+
**Props:** `...creds`, `label?` (default `'Reviews'`), `position?` (`'left' | 'right'`, default `'right'`), `color?`, `limit?` (default 5), `starColor?`, `className?`
|
|
319
295
|
|
|
320
296
|
---
|
|
321
297
|
|
|
@@ -327,37 +303,30 @@ A fixed "Write a Review" button that opens a modal with the submission form.
|
|
|
327
303
|
import { FloatingReviewButton } from '@revova/hydrogen';
|
|
328
304
|
|
|
329
305
|
<FloatingReviewButton
|
|
330
|
-
|
|
306
|
+
{...creds}
|
|
331
307
|
productId={product.id}
|
|
332
308
|
text="Write a Review"
|
|
333
309
|
position="bottom-right"
|
|
334
|
-
color="#111827"
|
|
335
310
|
/>
|
|
336
311
|
```
|
|
337
312
|
|
|
338
|
-
**Props:**
|
|
313
|
+
**Props:** `...creds`, `productId`, `text?` (default `'Write a Review'`), `color?`, `position?` (`'bottom-left' | 'bottom-right'`, default `'bottom-right'`), `className?`
|
|
339
314
|
|
|
340
315
|
---
|
|
341
316
|
|
|
342
317
|
## Hooks
|
|
343
318
|
|
|
344
|
-
Use hooks to build fully custom UIs while Revova handles data fetching.
|
|
345
|
-
|
|
346
|
-
```ts
|
|
347
|
-
import { resolveProxyUrl } from '@revova/hydrogen';
|
|
348
|
-
|
|
349
|
-
const proxyUrl = resolveProxyUrl({ storeDomain }); // or { proxyUrl }
|
|
350
|
-
```
|
|
319
|
+
Use hooks to build fully custom UIs while Revova handles data fetching.
|
|
351
320
|
|
|
352
321
|
### `useReviews(options)`
|
|
353
322
|
|
|
354
323
|
Paginated reviews for a product with sorting controls.
|
|
355
324
|
|
|
356
325
|
```tsx
|
|
357
|
-
import { useReviews
|
|
326
|
+
import { useReviews } from '@revova/hydrogen';
|
|
358
327
|
|
|
359
328
|
const { data, loading, error, setPage, setSort, currentPage, currentSort, refetch } = useReviews({
|
|
360
|
-
|
|
329
|
+
...creds,
|
|
361
330
|
productId: product.id,
|
|
362
331
|
limit: 5,
|
|
363
332
|
sort: 'helpful', // 'recent' | 'helpful' | 'rating_high' | 'rating_low'
|
|
@@ -367,15 +336,12 @@ const { data, loading, error, setPage, setSort, currentPage, currentSort, refetc
|
|
|
367
336
|
|
|
368
337
|
### `useWidgetGlobals(options)`
|
|
369
338
|
|
|
370
|
-
Shop-level stats and recent reviews. Used internally by
|
|
339
|
+
Shop-level stats and recent reviews. Used internally by most global widgets.
|
|
371
340
|
|
|
372
341
|
```tsx
|
|
373
|
-
import { useWidgetGlobals
|
|
342
|
+
import { useWidgetGlobals } from '@revova/hydrogen';
|
|
374
343
|
|
|
375
|
-
const { data, loading, error } = useWidgetGlobals({
|
|
376
|
-
proxyUrl: resolveProxyUrl({ storeDomain }),
|
|
377
|
-
limit: 20,
|
|
378
|
-
});
|
|
344
|
+
const { data, loading, error } = useWidgetGlobals({ ...creds, limit: 20 });
|
|
379
345
|
|
|
380
346
|
// data.stats.averageRating — "4.8"
|
|
381
347
|
// data.stats.totalReviews — 142
|
|
@@ -383,16 +349,14 @@ const { data, loading, error } = useWidgetGlobals({
|
|
|
383
349
|
// data.config — merchant widget settings
|
|
384
350
|
```
|
|
385
351
|
|
|
386
|
-
### `useSubmitReview(
|
|
352
|
+
### `useSubmitReview(creds)`
|
|
387
353
|
|
|
388
354
|
Submit a new review programmatically.
|
|
389
355
|
|
|
390
356
|
```tsx
|
|
391
|
-
import { useSubmitReview
|
|
357
|
+
import { useSubmitReview } from '@revova/hydrogen';
|
|
392
358
|
|
|
393
|
-
const { submit, submitting, success, error, result, reset } = useSubmitReview(
|
|
394
|
-
resolveProxyUrl({ storeDomain })
|
|
395
|
-
);
|
|
359
|
+
const { submit, submitting, success, error, result, reset } = useSubmitReview(creds);
|
|
396
360
|
|
|
397
361
|
await submit({
|
|
398
362
|
productId: 'gid://shopify/Product/123',
|
|
@@ -411,18 +375,10 @@ await submit({
|
|
|
411
375
|
Fetch and submit Q&A for a product.
|
|
412
376
|
|
|
413
377
|
```tsx
|
|
414
|
-
import { useQnA
|
|
415
|
-
|
|
416
|
-
const {
|
|
417
|
-
|
|
418
|
-
loading,
|
|
419
|
-
setPage,
|
|
420
|
-
submitQuestion,
|
|
421
|
-
submitAnswer,
|
|
422
|
-
submitState,
|
|
423
|
-
resetSubmit,
|
|
424
|
-
} = useQnA({
|
|
425
|
-
proxyUrl: resolveProxyUrl({ storeDomain }),
|
|
378
|
+
import { useQnA } from '@revova/hydrogen';
|
|
379
|
+
|
|
380
|
+
const { data, loading, setPage, submitQuestion, submitAnswer, submitState, resetSubmit } = useQnA({
|
|
381
|
+
...creds,
|
|
426
382
|
productId: product.id,
|
|
427
383
|
});
|
|
428
384
|
|
|
@@ -434,14 +390,14 @@ await submitQuestion({
|
|
|
434
390
|
});
|
|
435
391
|
```
|
|
436
392
|
|
|
437
|
-
### `useHelpfulVote(
|
|
393
|
+
### `useHelpfulVote(creds)`
|
|
438
394
|
|
|
439
395
|
Cast a helpful / not-helpful vote on a review.
|
|
440
396
|
|
|
441
397
|
```tsx
|
|
442
|
-
import { useHelpfulVote
|
|
398
|
+
import { useHelpfulVote } from '@revova/hydrogen';
|
|
443
399
|
|
|
444
|
-
const { vote, loading, voted } = useHelpfulVote(
|
|
400
|
+
const { vote, loading, voted } = useHelpfulVote(creds);
|
|
445
401
|
|
|
446
402
|
<button onClick={() => vote({ reviewId: review.id, vote: 'helpful' })} disabled={voted}>
|
|
447
403
|
Helpful ({review.helpfulCount})
|