@product7/product7-js 0.3.2 → 0.3.4

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.
@@ -1,14 +1,14 @@
1
1
  # Framework Integrations
2
2
 
3
- Use this document to integrate `@product7/product7-js` in supported frontend frameworks.
3
+ Use this guide to integrate `@product7/product7-js` in modern frontend frameworks.
4
4
 
5
- This guide includes:
5
+ This guide covers:
6
6
 
7
7
  1. Next.js
8
8
  2. React
9
- 3. Vue + Nuxt
9
+ 3. Vue and Nuxt
10
10
  4. Angular
11
- 5. Svelte + SvelteKit
11
+ 5. Svelte and SvelteKit
12
12
  6. Astro
13
13
 
14
14
  ## Before You Start
@@ -19,173 +19,57 @@ Install the SDK:
19
19
  npm install @product7/product7-js
20
20
  ```
21
21
 
22
- Prepare your runtime values:
22
+ Prepare runtime values:
23
23
 
24
- - `workspace` (required)
25
- - `boardName` (recommended for feedback button)
26
- - `metadata` (recommended for personalization and messenger)
24
+ - `workspace` is required
25
+ - `boardName` is recommended for feedback widgets
26
+ - user identity should be passed to `identify()` after `init()`
27
27
 
28
- Example configuration:
28
+ Recommended integration rules:
29
29
 
30
- ```ts
31
- const sdkConfig = {
32
- workspace: 'your-workspace',
33
- boardName: 'feature-requests',
34
- debug: false,
35
- metadata: {
36
- user_id: 'user_123',
37
- email: 'user@example.com',
38
- name: 'Jane Doe',
39
- custom_fields: {
40
- plan: 'pro',
41
- role: 'admin',
42
- },
43
- company: {
44
- id: 'company_123',
45
- name: 'Acme Inc',
46
- },
47
- },
48
- };
49
- ```
50
-
51
- Integration rules:
52
-
53
- - Run SDK initialization on the client side only.
30
+ - Initialize on the client side only.
54
31
  - Always call `await sdk.init()` before creating widgets.
55
- - Prefer a single shared SDK instance per app session.
56
- - Destroy widgets and SDK on app teardown where applicable.
57
-
58
- ## Framework-Specific Configuration
59
-
60
- Use framework-native environment variables so values stay deployment-safe.
32
+ - Call `await sdk.identify(user)` after `init()` when you have a logged-in user.
33
+ - Prefer one shared SDK instance per app session.
34
+ - Destroy widgets and the SDK on teardown where appropriate.
61
35
 
62
- ### Next.js
36
+ ### Shared Config Pattern
63
37
 
64
- - Use `NEXT_PUBLIC_*` for client-side values.
65
- - Build `metadata` from your auth/session payload.
66
- - When logged-in user changes, call `sdk.setMetadata(nextMetadata)`.
38
+ Keep workspace-level SDK config separate from user identity:
67
39
 
68
40
  ```ts
69
41
  const sdkConfig = {
70
- workspace: process.env.NEXT_PUBLIC_PRODUCT7_WORKSPACE!,
71
- boardName: process.env.NEXT_PUBLIC_PRODUCT7_BOARD_ID || 'feature-requests',
72
- debug: process.env.NEXT_PUBLIC_PRODUCT7_DEBUG === 'true',
73
- metadata: {
74
- user_id: session.user.id,
75
- email: session.user.email,
76
- name: session.user.name,
77
- },
78
- };
79
- ```
80
-
81
- ### React (Vite / CRA)
82
-
83
- - Vite: use `import.meta.env.VITE_*`.
84
- - CRA: use `process.env.REACT_APP_*`.
85
- - Keep config in one helper so all components use the same values.
86
-
87
- ```ts
88
- const sdkConfig = {
89
- workspace: import.meta.env.VITE_PRODUCT7_WORKSPACE,
90
- boardName: import.meta.env.VITE_PRODUCT7_BOARD_ID || 'feature-requests',
91
- debug: import.meta.env.DEV,
92
- metadata: currentUser
93
- ? {
94
- user_id: currentUser.id,
95
- email: currentUser.email,
96
- name: currentUser.name,
97
- }
98
- : undefined,
99
- };
100
- ```
101
-
102
- ### Vue + Nuxt
103
-
104
- - Vue (Vite): use `import.meta.env.VITE_*`.
105
- - Nuxt: use `useRuntimeConfig().public`.
106
- - In Nuxt, initialize in a `.client` plugin and expose SDK through `provide`.
107
-
108
- ```ts
109
- // Nuxt example
110
- const runtime = useRuntimeConfig();
111
- const sdkConfig = {
112
- workspace: runtime.public.product7Workspace,
113
- boardName: runtime.public.product7BoardId || 'feature-requests',
114
- debug: runtime.public.product7Debug === 'true',
115
- metadata,
42
+ workspace: 'your-workspace',
43
+ boardName: 'feature-requests',
44
+ debug: false,
116
45
  };
117
- ```
118
46
 
119
- ### Angular
120
-
121
- - Keep SDK values in `environment.ts` and `environment.prod.ts`.
122
- - Map app auth user into `metadata` in your singleton service.
123
-
124
- ```ts
125
- const sdkConfig = {
126
- workspace: environment.product7.workspace,
127
- boardName: environment.product7.boardName || 'feature-requests',
128
- debug: !environment.production,
129
- metadata: {
130
- user_id: user.id,
131
- email: user.email,
132
- name: user.fullName,
47
+ const identifyPayload = {
48
+ user_id: currentUser.id,
49
+ email: currentUser.email,
50
+ name: currentUser.name,
51
+ custom_fields: {
52
+ plan: currentUser.plan,
53
+ role: currentUser.role,
133
54
  },
134
55
  };
135
56
  ```
136
57
 
137
- ### Svelte + SvelteKit
138
-
139
- - Svelte: use `import.meta.env.VITE_*`.
140
- - SvelteKit: use `$env/static/public` (or `$env/dynamic/public`) values.
141
- - Reuse one config builder in `+layout.svelte` to avoid repeated setup.
142
-
143
- ```ts
144
- const sdkConfig = {
145
- workspace: PUBLIC_PRODUCT7_WORKSPACE,
146
- boardName: PUBLIC_PRODUCT7_BOARD_ID || 'feature-requests',
147
- debug: PUBLIC_PRODUCT7_DEBUG === 'true',
148
- metadata,
149
- };
150
- ```
151
-
152
- ### Astro
153
-
154
- - Expose client-safe values with `PUBLIC_*`.
155
- - Run SDK code only in client-loaded components (`client:load` / `client:visible`).
156
-
157
- ```ts
158
- const sdkConfig = {
159
- workspace: import.meta.env.PUBLIC_PRODUCT7_WORKSPACE,
160
- boardName: import.meta.env.PUBLIC_PRODUCT7_BOARD_ID || 'feature-requests',
161
- debug: import.meta.env.PUBLIC_PRODUCT7_DEBUG === 'true',
162
- metadata,
163
- };
164
- ```
165
-
166
- ### Optional SDK Keys You Can Add Per Framework
167
-
168
- - `apiUrl`: custom API endpoint override.
169
- - `env`: force `'staging'` or `'production'`.
170
- - `mock`: enable mock responses for local development.
171
- - `theme`: default `'light'` or `'dark'`.
172
- - `position`: default widget position.
58
+ ---
173
59
 
174
- ## 1) Next.js
60
+ ## 1. Next.js
175
61
 
176
62
  ### App Router
177
63
 
178
- Create a client provider:
179
-
180
64
  `app/providers.tsx`
181
65
 
182
66
  ```tsx
183
67
  'use client';
184
68
 
185
69
  import { useEffect, useRef } from 'react';
186
- import Product7 from '@product7/product7-js';
70
+ import { Product7 } from '@product7/product7-js';
187
71
 
188
- export function FeedbackProvider() {
72
+ export function Product7Provider() {
189
73
  const sdkRef = useRef<any>(null);
190
74
  const widgetRef = useRef<any>(null);
191
75
 
@@ -193,24 +77,26 @@ export function FeedbackProvider() {
193
77
  let cancelled = false;
194
78
 
195
79
  (async () => {
196
- const sdk = Product7.create({
197
- workspace: 'your-workspace',
198
- boardName: 'feature-requests',
199
- metadata: {
200
- user_id: 'user_123',
201
- email: 'user@example.com',
202
- name: 'Jane Doe',
203
- },
80
+ const sdk = new Product7({
81
+ workspace: process.env.NEXT_PUBLIC_PRODUCT7_WORKSPACE!,
82
+ boardName:
83
+ process.env.NEXT_PUBLIC_PRODUCT7_BOARD_ID || 'feature-requests',
204
84
  });
205
85
 
206
86
  await sdk.init();
87
+ await sdk.identify({
88
+ user_id: 'user_123',
89
+ email: 'user@example.com',
90
+ name: 'Jane Doe',
91
+ });
92
+
207
93
  if (cancelled) {
208
94
  sdk.destroy();
209
95
  return;
210
96
  }
211
97
 
212
98
  sdkRef.current = sdk;
213
- widgetRef.current = sdk.createWidget('button', {
99
+ widgetRef.current = sdk.createFeedbackWidget({
214
100
  position: 'bottom-right',
215
101
  });
216
102
  widgetRef.current.mount();
@@ -227,12 +113,10 @@ export function FeedbackProvider() {
227
113
  }
228
114
  ```
229
115
 
230
- Render provider in layout:
231
-
232
116
  `app/layout.tsx`
233
117
 
234
118
  ```tsx
235
- import { FeedbackProvider } from './providers';
119
+ import { Product7Provider } from './providers';
236
120
 
237
121
  export default function RootLayout({
238
122
  children,
@@ -242,7 +126,7 @@ export default function RootLayout({
242
126
  return (
243
127
  <html lang="en">
244
128
  <body>
245
- <FeedbackProvider />
129
+ <Product7Provider />
246
130
  {children}
247
131
  </body>
248
132
  </html>
@@ -257,20 +141,27 @@ export default function RootLayout({
257
141
  ```tsx
258
142
  import type { AppProps } from 'next/app';
259
143
  import { useEffect } from 'react';
260
- import Product7 from '@product7/product7-js';
144
+ import { Product7 } from '@product7/product7-js';
261
145
 
262
146
  export default function App({ Component, pageProps }: AppProps) {
263
147
  useEffect(() => {
264
- const sdk = Product7.create({
265
- workspace: 'your-workspace',
148
+ const sdk = new Product7({
149
+ workspace: process.env.NEXT_PUBLIC_PRODUCT7_WORKSPACE!,
266
150
  boardName: 'feature-requests',
267
- metadata: { user_id: 'user_123', email: 'user@example.com' },
268
151
  });
269
152
 
270
153
  let widget: any;
154
+
271
155
  (async () => {
272
156
  await sdk.init();
273
- widget = sdk.createWidget('button', { position: 'bottom-right' });
157
+ await sdk.identify({
158
+ user_id: 'user_123',
159
+ email: 'user@example.com',
160
+ });
161
+
162
+ widget = sdk.createFeedbackWidget({
163
+ position: 'bottom-right',
164
+ });
274
165
  widget.mount();
275
166
  })();
276
167
 
@@ -284,42 +175,51 @@ export default function App({ Component, pageProps }: AppProps) {
284
175
  }
285
176
  ```
286
177
 
287
- ## 2) React
178
+ ---
179
+
180
+ ## 2. React
288
181
 
289
182
  `src/App.tsx`
290
183
 
291
184
  ```tsx
292
185
  import { useEffect, useRef } from 'react';
293
- import Product7 from '@product7/product7-js';
186
+ import { Product7 } from '@product7/product7-js';
294
187
 
295
188
  export default function App() {
296
189
  const sdkRef = useRef<any>(null);
190
+ const widgetRef = useRef<any>(null);
297
191
 
298
192
  useEffect(() => {
299
- let widget: any;
193
+ let disposed = false;
300
194
 
301
195
  (async () => {
302
- const sdk = Product7.create({
303
- workspace: 'your-workspace',
304
- boardName: 'feature-requests',
305
- metadata: {
306
- user_id: 'user_123',
307
- email: 'user@example.com',
308
- name: 'Jane Doe',
309
- },
196
+ const sdk = new Product7({
197
+ workspace: import.meta.env.VITE_PRODUCT7_WORKSPACE,
198
+ boardName: import.meta.env.VITE_PRODUCT7_BOARD_ID || 'feature-requests',
310
199
  });
311
200
 
312
201
  await sdk.init();
313
- sdkRef.current = sdk;
202
+ await sdk.identify({
203
+ user_id: 'user_123',
204
+ email: 'user@example.com',
205
+ name: 'Jane Doe',
206
+ });
314
207
 
315
- widget = sdk.createWidget('button', {
208
+ if (disposed) {
209
+ sdk.destroy();
210
+ return;
211
+ }
212
+
213
+ sdkRef.current = sdk;
214
+ widgetRef.current = sdk.createFeedbackWidget({
316
215
  position: 'bottom-right',
317
216
  });
318
- widget.mount();
217
+ widgetRef.current.mount();
319
218
  })();
320
219
 
321
220
  return () => {
322
- widget?.destroy();
221
+ disposed = true;
222
+ widgetRef.current?.destroy();
323
223
  sdkRef.current?.destroy();
324
224
  };
325
225
  }, []);
@@ -328,7 +228,9 @@ export default function App() {
328
228
  }
329
229
  ```
330
230
 
331
- ## 3) Vue + Nuxt
231
+ ---
232
+
233
+ ## 3. Vue and Nuxt
332
234
 
333
235
  ### Vue
334
236
 
@@ -337,24 +239,25 @@ export default function App() {
337
239
  ```vue
338
240
  <script setup lang="ts">
339
241
  import { onMounted, onUnmounted, ref } from 'vue';
340
- import Product7 from '@product7/product7-js';
242
+ import { Product7 } from '@product7/product7-js';
341
243
 
342
244
  const sdk = ref<any>(null);
343
245
  const widget = ref<any>(null);
344
246
 
345
247
  onMounted(async () => {
346
- sdk.value = Product7.create({
347
- workspace: 'your-workspace',
248
+ sdk.value = new Product7({
249
+ workspace: import.meta.env.VITE_PRODUCT7_WORKSPACE,
348
250
  boardName: 'feature-requests',
349
- metadata: {
350
- user_id: 'user_123',
351
- email: 'user@example.com',
352
- name: 'Jane Doe',
353
- },
354
251
  });
355
252
 
356
253
  await sdk.value.init();
357
- widget.value = sdk.value.createWidget('button', {
254
+ await sdk.value.identify({
255
+ user_id: 'user_123',
256
+ email: 'user@example.com',
257
+ name: 'Jane Doe',
258
+ });
259
+
260
+ widget.value = sdk.value.createFeedbackWidget({
358
261
  position: 'bottom-right',
359
262
  });
360
263
  widget.value.mount();
@@ -365,30 +268,21 @@ export default function App() {
365
268
  sdk.value?.destroy();
366
269
  });
367
270
  </script>
368
-
369
- <template>
370
- <div>My App</div>
371
- </template>
372
271
  ```
373
272
 
374
- ### Nuxt
273
+ ### Nuxt Plugin
375
274
 
376
- Create a client plugin:
377
-
378
- `plugins/feedback.client.ts`
275
+ `plugins/product7.client.ts`
379
276
 
380
277
  ```ts
381
- import Product7 from '@product7/product7-js';
278
+ import { Product7 } from '@product7/product7-js';
382
279
 
383
280
  export default defineNuxtPlugin(async () => {
384
- const sdk = Product7.create({
385
- workspace: 'your-workspace',
386
- boardName: 'feature-requests',
387
- metadata: {
388
- user_id: 'user_123',
389
- email: 'user@example.com',
390
- name: 'Jane Doe',
391
- },
281
+ const runtime = useRuntimeConfig();
282
+
283
+ const sdk = new Product7({
284
+ workspace: runtime.public.product7Workspace,
285
+ boardName: runtime.public.product7BoardId || 'feature-requests',
392
286
  });
393
287
 
394
288
  await sdk.init();
@@ -401,104 +295,125 @@ export default defineNuxtPlugin(async () => {
401
295
  });
402
296
  ```
403
297
 
404
- Mount a widget in `app.vue` (or a layout):
298
+ When your auth state is ready, identify the user:
405
299
 
406
- ```vue
407
- <script setup lang="ts">
408
- const { $product7 } = useNuxtApp();
409
- let widget: any;
300
+ ```ts
301
+ const { $product7 } = useNuxtApp();
410
302
 
411
- onMounted(() => {
412
- widget = $product7.createWidget('button', {
413
- position: 'bottom-right',
414
- boardName: 'feature-requests',
303
+ await $product7.identify({
304
+ user_id: user.id,
305
+ email: user.email,
306
+ name: user.name,
307
+ });
308
+ ```
309
+
310
+ ### Nuxt Headless Composable
311
+
312
+ `composables/useFeedbackWidget.ts`
313
+
314
+ ```ts
315
+ import { onMounted, onUnmounted, ref } from 'vue';
316
+
317
+ interface FeedbackWidgetOptions {
318
+ boardName?: string;
319
+ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
320
+ headless?: boolean;
321
+ }
322
+
323
+ export function useFeedbackWidget(options: FeedbackWidgetOptions = {}) {
324
+ const widget = ref<any>(null);
325
+ const isReady = ref(false);
326
+
327
+ onMounted(async () => {
328
+ const { $product7 } = useNuxtApp();
329
+
330
+ widget.value = $product7.createFeedbackWidget({
331
+ headless: true,
332
+ ...options,
415
333
  });
416
- widget.mount();
334
+ widget.value.mount();
335
+ isReady.value = true;
417
336
  });
418
337
 
419
- onBeforeUnmount(() => {
420
- widget?.destroy();
338
+ onUnmounted(() => {
339
+ widget.value?.destroy();
421
340
  });
422
- </script>
341
+
342
+ return {
343
+ isReady,
344
+ open: () => widget.value?.open(),
345
+ close: () => widget.value?.close(),
346
+ toggle: () => widget.value?.toggle(),
347
+ };
348
+ }
423
349
  ```
424
350
 
425
- For larger Nuxt apps, use a Pinia store with `isReady` checks to avoid duplicate widget creation.
351
+ Usage:
352
+
353
+ ```vue
354
+ <script setup>
355
+ const { open, isReady } = useFeedbackWidget({
356
+ boardName: 'feature-requests',
357
+ });
358
+ </script>
359
+
360
+ <template>
361
+ <button :disabled="!isReady" @click="open">Leave feedback</button>
362
+ </template>
363
+ ```
426
364
 
427
- ## 4) Angular
365
+ ---
428
366
 
429
- Create a singleton service:
367
+ ## 4. Angular
430
368
 
431
369
  `feedback.service.ts`
432
370
 
433
371
  ```ts
434
372
  import { Injectable } from '@angular/core';
435
- import Product7 from '@product7/product7-js';
373
+ import { Product7 } from '@product7/product7-js';
436
374
 
437
375
  @Injectable({ providedIn: 'root' })
438
376
  export class FeedbackService {
439
377
  private sdk: any = null;
440
- private buttonWidget: any = null;
378
+ private feedbackWidget: any = null;
441
379
 
442
- async init() {
380
+ async init(user: { id: string; email: string; name: string }) {
443
381
  if (this.sdk) return;
444
382
 
445
- this.sdk = Product7.create({
383
+ this.sdk = new Product7({
446
384
  workspace: 'your-workspace',
447
385
  boardName: 'feature-requests',
448
- metadata: {
449
- user_id: 'user_123',
450
- email: 'user@example.com',
451
- name: 'Jane Doe',
452
- },
453
386
  });
454
387
 
455
388
  await this.sdk.init();
389
+ await this.sdk.identify({
390
+ user_id: user.id,
391
+ email: user.email,
392
+ name: user.name,
393
+ });
456
394
  }
457
395
 
458
- mountButton() {
459
- if (!this.sdk || this.buttonWidget) return;
460
- this.buttonWidget = this.sdk.createWidget('button', {
396
+ mountFeedback() {
397
+ if (!this.sdk || this.feedbackWidget) return;
398
+
399
+ this.feedbackWidget = this.sdk.createFeedbackWidget({
461
400
  position: 'bottom-right',
462
401
  });
463
- this.buttonWidget.mount();
402
+ this.feedbackWidget.mount();
464
403
  }
465
404
 
466
405
  destroy() {
467
- this.buttonWidget?.destroy();
468
- this.buttonWidget = null;
406
+ this.feedbackWidget?.destroy();
407
+ this.feedbackWidget = null;
469
408
  this.sdk?.destroy();
470
409
  this.sdk = null;
471
410
  }
472
411
  }
473
412
  ```
474
413
 
475
- Use it in the root component:
476
-
477
- `app.component.ts`
478
-
479
- ```ts
480
- import { Component, OnDestroy, OnInit } from '@angular/core';
481
- import { FeedbackService } from './feedback.service';
482
-
483
- @Component({
484
- selector: 'app-root',
485
- template: '<router-outlet></router-outlet>',
486
- })
487
- export class AppComponent implements OnInit, OnDestroy {
488
- constructor(private feedback: FeedbackService) {}
489
-
490
- async ngOnInit() {
491
- await this.feedback.init();
492
- this.feedback.mountButton();
493
- }
494
-
495
- ngOnDestroy() {
496
- this.feedback.destroy();
497
- }
498
- }
499
- ```
414
+ ---
500
415
 
501
- ## 5) Svelte + SvelteKit
416
+ ## 5. Svelte and SvelteKit
502
417
 
503
418
  ### Svelte
504
419
 
@@ -507,7 +422,7 @@ export class AppComponent implements OnInit, OnDestroy {
507
422
  ```svelte
508
423
  <script lang="ts">
509
424
  import { onMount } from 'svelte';
510
- import Product7 from '@product7/product7-js';
425
+ import { Product7 } from '@product7/product7-js';
511
426
 
512
427
  let sdk: any = null;
513
428
  let widget: any = null;
@@ -516,20 +431,23 @@ export class AppComponent implements OnInit, OnDestroy {
516
431
  let disposed = false;
517
432
 
518
433
  (async () => {
519
- sdk = Product7.create({
434
+ sdk = new Product7({
520
435
  workspace: 'your-workspace',
521
436
  boardName: 'feature-requests',
522
- metadata: {
523
- user_id: 'user_123',
524
- email: 'user@example.com',
525
- name: 'Jane Doe',
526
- },
527
437
  });
528
438
 
529
439
  await sdk.init();
440
+ await sdk.identify({
441
+ user_id: 'user_123',
442
+ email: 'user@example.com',
443
+ name: 'Jane Doe',
444
+ });
445
+
530
446
  if (disposed) return;
531
447
 
532
- widget = sdk.createWidget('button', { position: 'bottom-right' });
448
+ widget = sdk.createFeedbackWidget({
449
+ position: 'bottom-right',
450
+ });
533
451
  widget.mount();
534
452
  })();
535
453
 
@@ -540,8 +458,6 @@ export class AppComponent implements OnInit, OnDestroy {
540
458
  };
541
459
  });
542
460
  </script>
543
-
544
- <main>My App</main>
545
461
  ```
546
462
 
547
463
  ### SvelteKit
@@ -552,7 +468,7 @@ export class AppComponent implements OnInit, OnDestroy {
552
468
  <script lang="ts">
553
469
  import { browser } from '$app/environment';
554
470
  import { onMount } from 'svelte';
555
- import Product7 from '@product7/product7-js';
471
+ import { Product7 } from '@product7/product7-js';
556
472
 
557
473
  let sdk: any;
558
474
  let widget: any;
@@ -561,17 +477,20 @@ export class AppComponent implements OnInit, OnDestroy {
561
477
  if (!browser) return;
562
478
 
563
479
  (async () => {
564
- sdk = Product7.create({
480
+ sdk = new Product7({
565
481
  workspace: 'your-workspace',
566
482
  boardName: 'feature-requests',
567
- metadata: {
568
- user_id: 'user_123',
569
- email: 'user@example.com',
570
- },
571
483
  });
572
484
 
573
485
  await sdk.init();
574
- widget = sdk.createWidget('button', { position: 'bottom-right' });
486
+ await sdk.identify({
487
+ user_id: 'user_123',
488
+ email: 'user@example.com',
489
+ });
490
+
491
+ widget = sdk.createFeedbackWidget({
492
+ position: 'bottom-right',
493
+ });
575
494
  widget.mount();
576
495
  })();
577
496
 
@@ -581,134 +500,93 @@ export class AppComponent implements OnInit, OnDestroy {
581
500
  };
582
501
  });
583
502
  </script>
584
-
585
- <slot />
586
503
  ```
587
504
 
588
- ## 6) Astro
505
+ ---
506
+
507
+ ## 6. Astro
589
508
 
590
- Astro renders on the server by default, so initialize the SDK from a client-loaded component.
509
+ Astro renders on the server by default, so initialize Product7 from a client-loaded component.
591
510
 
592
- `src/components/FeedbackWidget.astro`
511
+ `src/components/Product7Widget.astro`
593
512
 
594
513
  ```astro
595
514
  <script>
596
- import Product7 from '@product7/product7-js';
515
+ import { Product7 } from '@product7/product7-js';
597
516
 
598
517
  let sdk;
599
518
  let widget;
600
519
 
601
520
  (async () => {
602
- sdk = Product7.create({
521
+ sdk = new Product7({
603
522
  workspace: 'your-workspace',
604
523
  boardName: 'feature-requests',
605
- metadata: {
606
- user_id: 'user_123',
607
- email: 'user@example.com',
608
- name: 'Jane Doe',
609
- },
610
524
  });
611
525
 
612
526
  await sdk.init();
613
- widget = sdk.createWidget('button', { position: 'bottom-right' });
527
+ await sdk.identify({
528
+ user_id: 'user_123',
529
+ email: 'user@example.com',
530
+ name: 'Jane Doe',
531
+ });
532
+
533
+ widget = sdk.createFeedbackWidget({
534
+ position: 'bottom-right',
535
+ });
614
536
  widget.mount();
615
537
  })();
616
538
  </script>
617
539
  ```
618
540
 
619
- Use the component in a page:
620
-
621
- ```astro
622
- ---
623
- import FeedbackWidget from '../components/FeedbackWidget.astro';
624
541
  ---
625
542
 
626
- <html lang="en">
627
- <body>
628
- <FeedbackWidget client:load />
629
- </body>
630
- </html>
631
- ```
632
-
633
- ## Add More Widgets (Any Framework)
543
+ ## Adding More Widgets
634
544
 
635
- You can create survey, messenger, and changelog widgets from the same SDK instance:
545
+ Use the same SDK instance for feedback, survey, messenger, and changelog.
636
546
 
637
547
  ```ts
638
- const survey = sdk.createWidget('survey', {
548
+ const feedback = sdk.createFeedbackWidget({
549
+ headless: true,
550
+ boardName: 'feature-requests',
551
+ });
552
+ feedback.mount();
553
+
554
+ const survey = sdk.createSurveyWidget({
639
555
  surveyType: 'nps',
640
556
  position: 'center',
641
- theme: 'light',
642
- description:
643
- 'To what extent do you agree or disagree that our tools support the work you do?',
644
557
  ratingScale: 5,
645
- showTitle: false,
646
- showDescription: true,
647
- showFeedbackInput: false,
648
- showSubmitButton: false,
649
- autoSubmitOnSelect: true,
650
- lowLabel: 'Strongly Disagree',
651
- highLabel: 'Strongly Agree',
652
- onSubmit: (response) => {
653
- console.log('Survey submitted:', response);
654
- // Optional: send to analytics
655
- // analytics.track('survey_submitted', { type: response.type, score: response.score });
656
- },
657
- onDismiss: () => {
658
- console.log('Survey dismissed');
659
- },
558
+ showSubmitButton: true,
660
559
  });
661
- survey.show();
662
- // survey.hide();
663
- // survey.destroy();
560
+ survey.mount();
561
+ survey.open();
664
562
 
665
- const messenger = sdk.createWidget('messenger', {
563
+ const messenger = sdk.createMessengerWidget({
666
564
  position: 'bottom-right',
667
- theme: 'light',
668
565
  teamName: 'Support Team',
669
- welcomeMessage: 'How can we help?',
670
566
  enableHelp: true,
671
567
  enableChangelog: true,
672
- primaryColor: '#155EEF',
673
- onSendMessage: (conversationId, message) => {
674
- console.log('Message sent:', conversationId, message);
675
- },
676
- onArticleClick: (article) => {
677
- console.log('Article clicked:', article);
678
- },
679
- onChangelogClick: (item) => {
680
- console.log('Changelog clicked:', item);
681
- },
682
568
  });
683
569
  messenger.mount();
684
- // messenger.open();
685
- // messenger.navigateTo('help');
686
- // messenger.close();
687
570
 
688
- const changelog = sdk.createWidget('changelog', {
571
+ const changelog = sdk.createChangelogWidget({
689
572
  position: 'bottom-left',
690
- theme: 'light',
691
- showBackdrop: true,
692
573
  triggerText: "What's New",
693
574
  showBadge: true,
694
- title: "What's New",
695
- viewButtonText: 'View Update',
696
- onViewUpdate: (item) => {
697
- console.log('Viewed changelog item:', item);
698
- },
699
575
  });
700
576
  changelog.mount();
701
- // changelog.openSidebar();
702
- // changelog.closeSidebar();
703
577
  ```
704
578
 
579
+ ---
580
+
705
581
  ## Troubleshooting
706
582
 
707
583
  - `SDK must be initialized before creating widgets`
708
- Call `await sdk.init()` before any `createWidget(...)` call.
584
+ Call `await sdk.init()` before any widget factory method.
709
585
  - Widget not visible
710
- Make sure you call `widget.mount()` for mounted widget types.
586
+ Make sure you call `widget.mount()` for mountable widgets.
711
587
  - `window is not defined` or `document is not defined`
712
- Move SDK code to client-only lifecycle hooks/components.
588
+ Move SDK code to client-only lifecycle hooks or client-only components.
713
589
  - Duplicate widget instances
714
- Cache widget references and guard against double mount.
590
+ Cache widget references and avoid remounting in repeated renders.
591
+ - Messenger shows the launcher when you want only programmatic control
592
+ Use `headless: true` when creating the messenger widget.