@revova/hydrogen 1.0.1 → 1.1.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
@@ -17,55 +17,53 @@ npm install @revova/hydrogen
17
17
 
18
18
  ## How it works
19
19
 
20
- Revova uses Shopify's built-in **App Proxy**. When the Revova app is installed on a store, Shopify automatically registers the route `/apps/revova` on the `myshopify.com` domain.
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/apps/revova
23
+ GET https://yourapp.trycloudflare.com/api/reviews?shop=yourstore.myshopify.com&...
24
+ Authorization: Bearer <apiToken>
26
25
  ```
27
26
 
28
- No API keys. No CORS config. Shopify handles all of it.
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 — Install the Revova app on your Shopify store
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
- Hydrogen exposes this through `context.env` in loaders:
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
- storeDomain: context.env.PUBLIC_STORE_DOMAIN,
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
- ### 3Pass `storeDomain` (or `proxyUrl`) to any component
55
+ ### 2Spread credentials into any component
57
56
 
58
- Every component accepts either prop use whichever is more convenient:
57
+ Every component accepts `apiUrl`, `shop`, and `apiToken`. The easiest pattern is to spread a single `creds` object:
59
58
 
60
59
  ```tsx
61
- // Recommended convenience shorthand
62
- <ReviewWidget storeDomain={storeDomain} productId={product.id} />
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
- // Explicit same result
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
  ---
@@ -74,32 +72,28 @@ Every component accepts either prop — use whichever is more convenient:
74
72
 
75
73
  ```tsx
76
74
  // app/routes/products.$handle.tsx
75
+ import '@revova/hydrogen/styles.css'; // import once in app/root.tsx instead
77
76
  import { ReviewWidget, FloatingReviewButton } from '@revova/hydrogen';
78
77
 
79
78
  export async function loader({ context, params }: LoaderArgs) {
80
79
  const product = await getProduct(params.handle);
81
80
  return {
82
81
  product,
83
- storeDomain: context.env.PUBLIC_STORE_DOMAIN,
82
+ apiUrl: context.env.PUBLIC_REVOVA_API_URL,
83
+ shop: context.env.PUBLIC_STORE_DOMAIN,
84
+ apiToken: context.env.PUBLIC_REVOVA_API_TOKEN,
84
85
  };
85
86
  }
86
87
 
87
88
  export default function ProductPage() {
88
- const { product, storeDomain } = useLoaderData<typeof loader>();
89
+ const { product, apiUrl, shop, apiToken } = useLoaderData<typeof loader>();
90
+ const creds = { apiUrl, shop, apiToken };
89
91
 
90
92
  return (
91
93
  <>
92
94
  <h1>{product.title}</h1>
93
-
94
- <ReviewWidget
95
- storeDomain={storeDomain}
96
- productId={product.id}
97
- />
98
-
99
- <FloatingReviewButton
100
- storeDomain={storeDomain}
101
- productId={product.id}
102
- />
95
+ <ReviewWidget {...creds} productId={product.id} />
96
+ <FloatingReviewButton {...creds} productId={product.id} />
103
97
  </>
104
98
  );
105
99
  }
@@ -107,9 +101,19 @@ export default function ProductPage() {
107
101
 
108
102
  ---
109
103
 
110
- ## Components
104
+ ## API Credentials (`ApiProps`)
105
+
106
+ All components and hooks require these three props. Spread them as a group for convenience.
107
+
108
+ | Prop | Type | Description |
109
+ |---|---|---|
110
+ | `apiUrl` | `string` | Revova backend base URL (e.g. `https://yourapp.trycloudflare.com`) |
111
+ | `shop` | `string` | Store myshopify.com domain (e.g. `yourstore.myshopify.com`) |
112
+ | `apiToken` | `string` | Bearer token for authorization |
113
+
114
+ ---
111
115
 
112
- All components accept either `storeDomain` **or** `proxyUrl` — never both.
116
+ ## Components
113
117
 
114
118
  ### `<ReviewWidget>`
115
119
 
@@ -119,7 +123,7 @@ Full review list with pagination, sorting, and an inline submission form.
119
123
  import { ReviewWidget } from '@revova/hydrogen';
120
124
 
121
125
  <ReviewWidget
122
- storeDomain={storeDomain}
126
+ {...creds}
123
127
  productId={product.id}
124
128
  pageSize={10}
125
129
  showForm
@@ -129,8 +133,7 @@ import { ReviewWidget } from '@revova/hydrogen';
129
133
 
130
134
  | Prop | Type | Default | Description |
131
135
  |---|---|---|---|
132
- | `storeDomain` | `string` | — | Your `myshopify.com` domain (e.g. `yourstore.myshopify.com`) |
133
- | `proxyUrl` | `string` | — | Explicit proxy URL — use instead of `storeDomain` if preferred |
136
+ | `...creds` | `ApiProps` | — | `apiUrl`, `shop`, `apiToken` |
134
137
  | `productId` | `string` | — | Shopify product GID |
135
138
  | `locale` | `string` | — | Locale code for translated reviews (e.g. `fr`) |
136
139
  | `pageSize` | `number` | `10` | Reviews per page |
@@ -149,14 +152,14 @@ import { ReviewForm } from '@revova/hydrogen';
149
152
 
150
153
  // `form` comes from the ReviewsResponse returned by useReviews
151
154
  <ReviewForm
152
- storeDomain={storeDomain}
155
+ {...creds}
153
156
  productId={product.id}
154
157
  form={data.form}
155
158
  onSuccess={() => console.log('submitted')}
156
159
  />
157
160
  ```
158
161
 
159
- **Props:** `storeDomain` or `proxyUrl`, `productId`, `form` (required — `ResolvedForm` from loader), `onSuccess?`, `className?`
162
+ **Props:** `...creds`, `productId`, `form` (required — `ResolvedForm`), `onSuccess?`, `className?`
160
163
 
161
164
  ---
162
165
 
@@ -181,10 +184,10 @@ Aggregate rating + review count badge. Great for product cards and PDP headers.
181
184
  ```tsx
182
185
  import { ReviewCount } from '@revova/hydrogen';
183
186
 
184
- <ReviewCount storeDomain={storeDomain} starSize={14} />
187
+ <ReviewCount {...creds} starSize={14} />
185
188
  ```
186
189
 
187
- **Props:** `storeDomain` or `proxyUrl`, `starColor?`, `starSize?`, `className?`
190
+ **Props:** `...creds`, `starColor?`, `starSize?`, `className?`
188
191
 
189
192
  ---
190
193
 
@@ -195,15 +198,10 @@ Auto-advancing carousel of recent reviews. Includes previous/next arrows and dot
195
198
  ```tsx
196
199
  import { ReviewCarousel } from '@revova/hydrogen';
197
200
 
198
- <ReviewCarousel
199
- storeDomain={storeDomain}
200
- limit={10}
201
- autoPlay
202
- intervalMs={4000}
203
- />
201
+ <ReviewCarousel {...creds} limit={10} autoPlay intervalMs={4000} />
204
202
  ```
205
203
 
206
- **Props:** `storeDomain` or `proxyUrl`, `limit?` (default 10), `autoPlay?` (default `true`), `intervalMs?` (default 4000), `starColor?`, `className?`
204
+ **Props:** `...creds`, `limit?` (default 10), `autoPlay?` (default `true`), `intervalMs?` (default 4000), `starColor?`, `className?`
207
205
 
208
206
  ---
209
207
 
@@ -214,14 +212,10 @@ Masonry photo grid of reviews that have images. Clicking any photo opens a light
214
212
  ```tsx
215
213
  import { ReviewGallery } from '@revova/hydrogen';
216
214
 
217
- <ReviewGallery
218
- storeDomain={storeDomain}
219
- columns={3}
220
- limit={20}
221
- />
215
+ <ReviewGallery {...creds} columns={3} limit={20} />
222
216
  ```
223
217
 
224
- **Props:** `storeDomain` or `proxyUrl`, `limit?` (default 20), `columns?` (default 3), `starColor?`, `className?`
218
+ **Props:** `...creds`, `limit?` (default 20), `columns?` (default 3), `starColor?`, `className?`
225
219
 
226
220
  ---
227
221
 
@@ -232,13 +226,10 @@ Product Q&A — lists questions with answers and includes an ask-a-question form
232
226
  ```tsx
233
227
  import { QnAWidget } from '@revova/hydrogen';
234
228
 
235
- <QnAWidget
236
- storeDomain={storeDomain}
237
- productId={product.id}
238
- />
229
+ <QnAWidget {...creds} productId={product.id} />
239
230
  ```
240
231
 
241
- **Props:** `storeDomain` or `proxyUrl`, `productId`, `className?`
232
+ **Props:** `...creds`, `productId`, `className?`
242
233
 
243
234
  ---
244
235
 
@@ -249,17 +240,12 @@ Aggregate rating badge in three styles: `pill` (default), `inline`, or `card`.
249
240
  ```tsx
250
241
  import { TrustBadge } from '@revova/hydrogen';
251
242
 
252
- // Pill great for footers, headers
253
- <TrustBadge storeDomain={storeDomain} style="pill" />
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" />
243
+ <TrustBadge {...creds} style="pill" />
244
+ <TrustBadge {...creds} style="card" />
245
+ <TrustBadge {...creds} style="inline" />
260
246
  ```
261
247
 
262
- **Props:** `storeDomain` or `proxyUrl`, `style?` (`'pill' | 'inline' | 'card'`, default `'pill'`), `starColor?`, `className?`
248
+ **Props:** `...creds`, `style?` (`'pill' | 'inline' | 'card'`, default `'pill'`), `starColor?`, `className?`
263
249
 
264
250
  ---
265
251
 
@@ -270,14 +256,10 @@ A continuously scrolling marquee of recent review snippets — ideal for homepag
270
256
  ```tsx
271
257
  import { ReviewTicker } from '@revova/hydrogen';
272
258
 
273
- <ReviewTicker
274
- storeDomain={storeDomain}
275
- limit={20}
276
- speedSeconds={30}
277
- />
259
+ <ReviewTicker {...creds} limit={20} speedSeconds={30} />
278
260
  ```
279
261
 
280
- **Props:** `storeDomain` or `proxyUrl`, `limit?` (default 20), `speedSeconds?` (default 30), `starColor?`, `className?`
262
+ **Props:** `...creds`, `limit?` (default 20), `speedSeconds?` (default 30), `starColor?`, `className?`
281
263
 
282
264
  ---
283
265
 
@@ -289,14 +271,14 @@ A timed floating popup showing recent reviews one at a time. Add once to your ro
289
271
  import { SocialProofPopup } from '@revova/hydrogen';
290
272
 
291
273
  <SocialProofPopup
292
- storeDomain={storeDomain}
274
+ {...creds}
293
275
  position="bottom-left"
294
276
  intervalMs={8000}
295
277
  displayMs={5000}
296
278
  />
297
279
  ```
298
280
 
299
- **Props:** `storeDomain` or `proxyUrl`, `position?` (`'bottom-left' | 'bottom-right'`, default `'bottom-left'`), `intervalMs?` (default 8000), `displayMs?` (default 5000), `starColor?`, `className?`
281
+ **Props:** `...creds`, `position?` (`'bottom-left' | 'bottom-right'`, default `'bottom-left'`), `intervalMs?` (default 8000), `displayMs?` (default 5000), `starColor?`, `className?`
300
282
 
301
283
  ---
302
284
 
@@ -307,15 +289,10 @@ A sticky tab fixed to the left or right edge of the viewport. Clicking it slides
307
289
  ```tsx
308
290
  import { FloatingReviewsTab } from '@revova/hydrogen';
309
291
 
310
- <FloatingReviewsTab
311
- storeDomain={storeDomain}
312
- label="Reviews"
313
- position="right"
314
- color="#111827"
315
- />
292
+ <FloatingReviewsTab {...creds} label="Reviews" position="right" />
316
293
  ```
317
294
 
318
- **Props:** `storeDomain` or `proxyUrl`, `label?` (default `'Reviews'`), `position?` (`'left' | 'right'`, default `'right'`), `color?`, `limit?` (default 5), `starColor?`, `className?`
295
+ **Props:** `...creds`, `label?` (default `'Reviews'`), `position?` (`'left' | 'right'`, default `'right'`), `color?`, `limit?` (default 5), `starColor?`, `className?`
319
296
 
320
297
  ---
321
298
 
@@ -327,37 +304,30 @@ A fixed "Write a Review" button that opens a modal with the submission form.
327
304
  import { FloatingReviewButton } from '@revova/hydrogen';
328
305
 
329
306
  <FloatingReviewButton
330
- storeDomain={storeDomain}
307
+ {...creds}
331
308
  productId={product.id}
332
309
  text="Write a Review"
333
310
  position="bottom-right"
334
- color="#111827"
335
311
  />
336
312
  ```
337
313
 
338
- **Props:** `storeDomain` or `proxyUrl`, `productId`, `text?` (default `'Write a Review'`), `color?`, `position?` (`'bottom-left' | 'bottom-right'`, default `'bottom-right'`), `className?`
314
+ **Props:** `...creds`, `productId`, `text?` (default `'Write a Review'`), `color?`, `position?` (`'bottom-left' | 'bottom-right'`, default `'bottom-right'`), `className?`
339
315
 
340
316
  ---
341
317
 
342
318
  ## Hooks
343
319
 
344
- Use hooks to build fully custom UIs while Revova handles data fetching. Hooks take `proxyUrl` as a plain string — use the exported `resolveProxyUrl` helper to build it from `storeDomain`.
345
-
346
- ```ts
347
- import { resolveProxyUrl } from '@revova/hydrogen';
348
-
349
- const proxyUrl = resolveProxyUrl({ storeDomain }); // or { proxyUrl }
350
- ```
320
+ Use hooks to build fully custom UIs while Revova handles data fetching.
351
321
 
352
322
  ### `useReviews(options)`
353
323
 
354
324
  Paginated reviews for a product with sorting controls.
355
325
 
356
326
  ```tsx
357
- import { useReviews, resolveProxyUrl } from '@revova/hydrogen';
327
+ import { useReviews } from '@revova/hydrogen';
358
328
 
359
329
  const { data, loading, error, setPage, setSort, currentPage, currentSort, refetch } = useReviews({
360
- proxyUrl: resolveProxyUrl({ storeDomain }),
330
+ ...creds,
361
331
  productId: product.id,
362
332
  limit: 5,
363
333
  sort: 'helpful', // 'recent' | 'helpful' | 'rating_high' | 'rating_low'
@@ -367,15 +337,12 @@ const { data, loading, error, setPage, setSort, currentPage, currentSort, refetc
367
337
 
368
338
  ### `useWidgetGlobals(options)`
369
339
 
370
- Shop-level stats and recent reviews. Used internally by `<ReviewCount>`, `<TrustBadge>`, `<ReviewCarousel>`, `<ReviewGallery>`, `<ReviewTicker>`, `<SocialProofPopup>`, and `<FloatingReviewsTab>`.
340
+ Shop-level stats and recent reviews. Used internally by most global widgets.
371
341
 
372
342
  ```tsx
373
- import { useWidgetGlobals, resolveProxyUrl } from '@revova/hydrogen';
343
+ import { useWidgetGlobals } from '@revova/hydrogen';
374
344
 
375
- const { data, loading, error } = useWidgetGlobals({
376
- proxyUrl: resolveProxyUrl({ storeDomain }),
377
- limit: 20,
378
- });
345
+ const { data, loading, error } = useWidgetGlobals({ ...creds, limit: 20 });
379
346
 
380
347
  // data.stats.averageRating — "4.8"
381
348
  // data.stats.totalReviews — 142
@@ -383,16 +350,14 @@ const { data, loading, error } = useWidgetGlobals({
383
350
  // data.config — merchant widget settings
384
351
  ```
385
352
 
386
- ### `useSubmitReview(proxyUrl)`
353
+ ### `useSubmitReview(creds)`
387
354
 
388
355
  Submit a new review programmatically.
389
356
 
390
357
  ```tsx
391
- import { useSubmitReview, resolveProxyUrl } from '@revova/hydrogen';
358
+ import { useSubmitReview } from '@revova/hydrogen';
392
359
 
393
- const { submit, submitting, success, error, result, reset } = useSubmitReview(
394
- resolveProxyUrl({ storeDomain })
395
- );
360
+ const { submit, submitting, success, error, result, reset } = useSubmitReview(creds);
396
361
 
397
362
  await submit({
398
363
  productId: 'gid://shopify/Product/123',
@@ -411,18 +376,10 @@ await submit({
411
376
  Fetch and submit Q&A for a product.
412
377
 
413
378
  ```tsx
414
- import { useQnA, resolveProxyUrl } from '@revova/hydrogen';
415
-
416
- const {
417
- data,
418
- loading,
419
- setPage,
420
- submitQuestion,
421
- submitAnswer,
422
- submitState,
423
- resetSubmit,
424
- } = useQnA({
425
- proxyUrl: resolveProxyUrl({ storeDomain }),
379
+ import { useQnA } from '@revova/hydrogen';
380
+
381
+ const { data, loading, setPage, submitQuestion, submitAnswer, submitState, resetSubmit } = useQnA({
382
+ ...creds,
426
383
  productId: product.id,
427
384
  });
428
385
 
@@ -434,14 +391,14 @@ await submitQuestion({
434
391
  });
435
392
  ```
436
393
 
437
- ### `useHelpfulVote(proxyUrl)`
394
+ ### `useHelpfulVote(creds)`
438
395
 
439
396
  Cast a helpful / not-helpful vote on a review.
440
397
 
441
398
  ```tsx
442
- import { useHelpfulVote, resolveProxyUrl } from '@revova/hydrogen';
399
+ import { useHelpfulVote } from '@revova/hydrogen';
443
400
 
444
- const { vote, loading, voted } = useHelpfulVote(resolveProxyUrl({ storeDomain }));
401
+ const { vote, loading, voted } = useHelpfulVote(creds);
445
402
 
446
403
  <button onClick={() => vote({ reviewId: review.id, vote: 'helpful' })} disabled={voted}>
447
404
  Helpful ({review.helpfulCount})
@@ -452,9 +409,15 @@ const { vote, loading, voted } = useHelpfulVote(resolveProxyUrl({ storeDomain })
452
409
 
453
410
  ## Styling
454
411
 
455
- All components are **unstyled by default** they render semantic HTML with inline structural styles only (layout, positioning). Pass a `className` prop and override with Tailwind, CSS Modules, or any CSS-in-JS solution.
412
+ The package ships a bundled CSS file. Import it **once** in your app root (e.g. `app/root.tsx`):
413
+
414
+ ```ts
415
+ import '@revova/hydrogen/styles.css';
416
+ ```
417
+
418
+ This applies the full `rv-*` design system — cards, stars, modal, form, ticker animation, popup, gallery lightbox, and all widget layouts. All class names are prefixed `rv-` so they won't conflict with your theme styles.
456
419
 
457
- The `starColor` prop is available on every component that displays stars and defaults to `#f59e0b` (amber).
420
+ To customise, pass a `className` prop on any component and override the relevant `rv-*` classes in your own CSS. The `starColor` prop (default `#f59e0b`) is available on every component that renders stars.
458
421
 
459
422
  ---
460
423