@dismissible/react-client 0.3.2-canary.2.38782c4 → 0.3.2-canary.3.c1b8c41

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
@@ -1,10 +1,10 @@
1
1
  # @dismissible/react-client
2
2
 
3
- A React component library for creating dismissible UI elements with persistent state management.
3
+ A React component library for creating dismissible UI elements with persistent state management.
4
4
 
5
- Use this in combination with [dismissible.io](https://dismissible.io). Get your free account now!
5
+ **Free and open source** - use with the [Dismissible API Server](https://github.com/DismissibleIo/dismissible-api) that you can self-host with Docker or integrate into your NestJS application.
6
6
 
7
- 🌐 **[dismissible.io](https://dismissible.io)** | 💰 **[View Pricing](https://dismissible.io/pricing)** | 📖 **[Documentation](https://docs.dismissible.io)**
7
+ 🌐 **[dismissible.io](https://dismissible.io)** | 📖 **[Documentation](https://dismissible.io/docs)** | 🐙 **[API Server](https://github.com/DismissibleIo/dismissible-api)**
8
8
 
9
9
  [![npm version](https://badge.fury.io/js/@dismissible%2Freact-client.svg)](https://badge.fury.io/js/@dismissible%2Freact-client)
10
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -13,12 +13,14 @@ Use this in combination with [dismissible.io](https://dismissible.io). Get your
13
13
 
14
14
  - 🎯 **Easy to use** - Simple component API for dismissible content
15
15
  - 💾 **Persistent state** - Dismissal state is saved and restored across sessions
16
- - 🔐 **JWT Authentication** - Built-in support for JWT-based user authentication (Enterprise only)
16
+ - 🔄 **Restore support** - Restore previously dismissed items programmatically
17
+ - 🔐 **JWT Authentication** - Built-in support for secure JWT-based authentication
17
18
  - 🎨 **Customizable** - Custom loading, error, and dismiss button components
18
19
  - ♿ **Accessible** - Built with accessibility best practices
19
20
  - 🪝 **Hook-based** - Includes `useDismissibleItem` hook for custom implementations
20
21
  - 📦 **Lightweight** - Minimal bundle size with tree-shaking support
21
22
  - 🔧 **TypeScript** - Full TypeScript support with complete type definitions
23
+ - 🐳 **Self-hosted** - Works with your own Dismissible API server
22
24
 
23
25
  ## Installation
24
26
 
@@ -36,13 +38,69 @@ npm install react react-dom
36
38
 
37
39
  ## Quick Start
38
40
 
41
+ ### 1. Set up the Dismissible API Server
42
+
43
+ First, you need a Dismissible API server running. The easiest way is with Docker:
44
+
45
+ ```yaml
46
+ # docker-compose.yml
47
+ version: '3.8'
48
+ services:
49
+ api:
50
+ image: dismissibleio/dismissible-api:latest
51
+ ports:
52
+ - '3001:3001'
53
+ environment:
54
+ DISMISSIBLE_PORT: 3001
55
+ DISMISSIBLE_POSTGRES_STORAGE_CONNECTION_STRING: postgresql://postgres:postgres@db:5432/dismissible
56
+ depends_on:
57
+ - db
58
+
59
+ db:
60
+ image: postgres:15
61
+ environment:
62
+ POSTGRES_USER: postgres
63
+ POSTGRES_PASSWORD: postgres
64
+ POSTGRES_DB: dismissible
65
+ volumes:
66
+ - postgres_data:/var/lib/postgresql/data
67
+
68
+ volumes:
69
+ postgres_data:
70
+ ```
71
+
72
+ ```bash
73
+ docker-compose up -d
74
+ ```
75
+
76
+ See the [API Server documentation](https://github.com/DismissibleIo/dismissible-api) for more deployment options including NestJS integration.
77
+
78
+ ### 2. Configure the Provider
79
+
80
+ Wrap your app with `DismissibleProvider`. The `userId` prop is **required** to track dismissals per user:
81
+
39
82
  ```tsx
40
- import React from 'react';
41
- import { Dismissible } from '@dismissible/react-client';
83
+ import { DismissibleProvider } from '@dismissible/react-client';
42
84
 
43
85
  function App() {
86
+ const userId = getCurrentUserId(); // Get from your auth system
87
+
44
88
  return (
45
- <Dismissible id="welcome-banner-123-413-31-1">
89
+ <DismissibleProvider userId={userId} baseUrl="http://localhost:3001">
90
+ <YourApp />
91
+ </DismissibleProvider>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ### 3. Use Dismissible Components
97
+
98
+ ```tsx
99
+ import { Dismissible } from '@dismissible/react-client';
100
+
101
+ function WelcomeBanner() {
102
+ return (
103
+ <Dismissible id="welcome-banner">
46
104
  <div className="banner">
47
105
  <h2>Welcome to our app!</h2>
48
106
  <p>This banner can be dismissed and won't show again.</p>
@@ -56,16 +114,15 @@ function App() {
56
114
 
57
115
  ### `<DismissibleProvider>` Component
58
116
 
59
- Context provider for JWT authentication and configuration. Wrap your app or components that need JWT authentication.
60
-
61
- > **Note:** JWT authentication is only available for **Enterprise customers**. For standard usage, you can use the `<Dismissible>` component directly without a provider. [View pricing →](https://dismissible.io/pricing)
117
+ Context provider that configures authentication and API settings. **Required** - all `<Dismissible>` components must be wrapped in a provider.
62
118
 
63
119
  #### Props
64
120
 
65
121
  | Prop | Type | Required | Description |
66
122
  |------|------|----------|-------------|
67
- | `jwt` | `string \| (() => string) \| (() => Promise<string>)` | | JWT token for user-specific dismissals (**Enterprise only**) |
68
- | `baseUrl` | `string` | ❌ | Custom API base URL override |
123
+ | `userId` | `string` | | User ID for tracking dismissals per user |
124
+ | `jwt` | `string \| (() => string) \| (() => Promise<string>)` | ❌ | JWT token for secure authentication |
125
+ | `baseUrl` | `string` | ❌ | API base URL (defaults to your self-hosted server) |
69
126
  | `children` | `ReactNode` | ✅ | Components that will use the dismissible functionality |
70
127
 
71
128
  #### Example
@@ -73,37 +130,53 @@ Context provider for JWT authentication and configuration. Wrap your app or comp
73
130
  ```tsx
74
131
  import { DismissibleProvider } from '@dismissible/react-client';
75
132
 
76
- // With static JWT
133
+ // Basic setup with userId
77
134
  function App() {
78
135
  return (
79
- <DismissibleProvider jwt="eyJhbGciOiJIUzI1NiIs...">
136
+ <DismissibleProvider userId="user-123" baseUrl="http://localhost:3001">
80
137
  <YourApp />
81
138
  </DismissibleProvider>
82
139
  );
83
140
  }
84
141
 
85
- // With dynamic JWT function
86
- function AppWithDynamicAuth() {
142
+ // With static JWT
143
+ function AppWithJWT() {
87
144
  return (
88
- <DismissibleProvider jwt={() => getAccessToken()}>
145
+ <DismissibleProvider
146
+ userId="user-123"
147
+ jwt="eyJhbGciOiJIUzI1NiIs..."
148
+ baseUrl="https://api.yourapp.com"
149
+ >
89
150
  <YourApp />
90
151
  </DismissibleProvider>
91
152
  );
92
153
  }
93
154
 
94
- // With async JWT function
95
- function AppWithAsyncAuth() {
155
+ // With dynamic JWT function
156
+ function AppWithDynamicAuth() {
157
+ const { user, getAccessToken } = useAuth();
158
+
96
159
  return (
97
- <DismissibleProvider jwt={async () => await fetchAccessToken()}>
160
+ <DismissibleProvider
161
+ userId={user.id}
162
+ jwt={() => getAccessToken()}
163
+ baseUrl="https://api.yourapp.com"
164
+ >
98
165
  <YourApp />
99
166
  </DismissibleProvider>
100
167
  );
101
168
  }
102
169
 
103
- // Without JWT (anonymous/backwards compatible)
104
- function AppWithoutAuth() {
170
+ // With async JWT function
171
+ function AppWithAsyncAuth() {
172
+ const { user, refreshAndGetToken } = useAuth();
173
+
105
174
  return (
106
- <DismissibleProvider>
175
+ <DismissibleProvider
176
+ userId={user.id}
177
+ jwt={async () => await refreshAndGetToken()}
178
+ baseUrl="https://api.yourapp.com"
179
+ >
107
180
  <YourApp />
108
181
  </DismissibleProvider>
109
182
  );
@@ -114,6 +187,8 @@ function AppWithoutAuth() {
114
187
 
115
188
  The main component for creating dismissible content.
116
189
 
190
+ > **Note:** The `<Dismissible>` component renders `null` when an item is dismissed. For restore functionality, use the `useDismissibleItem` hook directly in custom implementations.
191
+
117
192
  #### Props
118
193
 
119
194
  | Prop | Type | Required | Description |
@@ -125,6 +200,9 @@ The main component for creating dismissible content.
125
200
  | `ErrorComponent` | `ComponentType<{id: string, error: Error}>` | ❌ | Custom error component |
126
201
  | `DismissButtonComponent` | `ComponentType<{id: string, onDismiss: () => Promise<void>, ariaLabel: string}>` | ❌ | Custom dismiss button |
127
202
  | `ignoreErrors` | `boolean` | ❌ | Ignore errors and display component anyway (default: false) |
203
+ | `enableCache` | `boolean` | ❌ | Enable localStorage caching (default: true) |
204
+ | `cachePrefix` | `string` | ❌ | Cache key prefix (default: 'dismissible') |
205
+ | `cacheExpiration` | `number` | ❌ | Cache expiration time in milliseconds |
128
206
 
129
207
  #### Example
130
208
 
@@ -132,7 +210,6 @@ The main component for creating dismissible content.
132
210
  <Dismissible
133
211
  id="promo-banner"
134
212
  onDismiss={() => console.log('Banner dismissed')}
135
- onRestore={() => console.log('Banner restored')}
136
213
  >
137
214
  <div className="promo">
138
215
  <h3>Special Offer!</h3>
@@ -150,6 +227,7 @@ For custom implementations and advanced use cases.
150
227
  | Parameter | Type | Required | Description |
151
228
  |-----------|------|----------|-------------|
152
229
  | `id` | `string` | ✅ | Unique identifier for the dismissible item |
230
+ | `options` | `object` | ❌ | Cache configuration options |
153
231
 
154
232
  #### Returns
155
233
 
@@ -157,8 +235,10 @@ For custom implementations and advanced use cases.
157
235
  |----------|------|-------------|
158
236
  | `dismissedOn` | `string \| null` | ISO date string when item was dismissed, or null |
159
237
  | `dismiss` | `() => Promise<void>` | Function to dismiss the item |
238
+ | `restore` | `() => Promise<void>` | Function to restore a dismissed item |
160
239
  | `isLoading` | `boolean` | Loading state indicator |
161
240
  | `error` | `Error \| null` | Error state, if any |
241
+ | `item` | `IDismissibleItem \| undefined` | The full dismissible item object |
162
242
 
163
243
  #### Example
164
244
 
@@ -166,11 +246,7 @@ For custom implementations and advanced use cases.
166
246
  import { useDismissibleItem } from '@dismissible/react-client';
167
247
 
168
248
  function CustomDismissible({ id, children }) {
169
- const { dismissedOn, dismiss, isLoading, error } = useDismissibleItem(id);
170
-
171
- if (dismissedOn) {
172
- return null; // Item is dismissed
173
- }
249
+ const { dismissedOn, dismiss, restore, isLoading, error } = useDismissibleItem(id);
174
250
 
175
251
  if (isLoading) {
176
252
  return <div>Loading...</div>;
@@ -180,6 +256,15 @@ function CustomDismissible({ id, children }) {
180
256
  return <div>Error: {error.message}</div>;
181
257
  }
182
258
 
259
+ if (dismissedOn) {
260
+ return (
261
+ <div>
262
+ <p>This item was dismissed.</p>
263
+ <button onClick={restore}>Restore</button>
264
+ </div>
265
+ );
266
+ }
267
+
183
268
  return (
184
269
  <div>
185
270
  {children}
@@ -193,21 +278,16 @@ function CustomDismissible({ id, children }) {
193
278
 
194
279
  ## Usage Examples
195
280
 
196
- ### JWT Authentication Setup (Enterprise Only)
197
-
198
- > **Enterprise Feature:** JWT authentication allows user-specific dismissible state management. This feature requires an Enterprise subscription. [Learn more about Enterprise features →](https://dismissible.io/pricing)
199
-
200
- For enterprise accounts that require user-specific dismissible state, wrap your app with the `DismissibleProvider`:
281
+ ### Basic Dismissible Banner
201
282
 
202
283
  ```tsx
203
284
  import { DismissibleProvider, Dismissible } from '@dismissible/react-client';
204
285
 
205
286
  function App() {
206
- // Example with static JWT token
207
- const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...";
287
+ const userId = getCurrentUserId();
208
288
 
209
289
  return (
210
- <DismissibleProvider jwt={jwt}>
290
+ <DismissibleProvider userId={userId} baseUrl="http://localhost:3001">
211
291
  <Dashboard />
212
292
  </DismissibleProvider>
213
293
  );
@@ -215,61 +295,48 @@ function App() {
215
295
 
216
296
  function Dashboard() {
217
297
  return (
218
- <div>
219
- {/* These dismissible items will be user-specific */}
220
- <Dismissible id="user-welcome-banner">
221
- <div className="alert alert-info">
222
- <h4>Welcome back!</h4>
223
- <p>You have 3 new notifications.</p>
224
- </div>
225
- </Dismissible>
226
- </div>
298
+ <Dismissible id="welcome-banner">
299
+ <div className="alert alert-info">
300
+ <h4>Welcome!</h4>
301
+ <p>Thanks for joining our platform. Here are some quick tips to get started.</p>
302
+ </div>
303
+ </Dismissible>
227
304
  );
228
305
  }
229
306
  ```
230
307
 
231
- ### Dynamic JWT with Authentication Provider
308
+ ### JWT Authentication Setup
309
+
310
+ For secure environments, configure JWT authentication:
232
311
 
233
312
  ```tsx
234
- import { DismissibleProvider } from '@dismissible/react-client';
235
- import { useAuth } from './auth'; // Your auth context
313
+ import { DismissibleProvider, Dismissible } from '@dismissible/react-client';
236
314
 
237
- // Synchronous JWT function
238
315
  function App() {
239
- const { getAccessToken } = useAuth();
316
+ const { user, getAccessToken } = useAuth();
240
317
 
241
318
  return (
242
- <DismissibleProvider jwt={() => getAccessToken()}>
243
- <YourApp />
244
- </DismissibleProvider>
245
- );
246
- }
247
-
248
- // Asynchronous JWT function
249
- function AppWithAsyncAuth() {
250
- const { refreshAndGetToken } = useAuth();
251
-
252
- return (
253
- <DismissibleProvider jwt={async () => await refreshAndGetToken()}>
254
- <YourApp />
319
+ <DismissibleProvider
320
+ userId={user.id}
321
+ jwt={() => getAccessToken()}
322
+ baseUrl="https://api.yourapp.com"
323
+ >
324
+ <Dashboard />
255
325
  </DismissibleProvider>
256
326
  );
257
327
  }
258
- ```
259
-
260
- ### Basic Dismissible Banner
261
-
262
- ```tsx
263
- import { Dismissible } from '@dismissible/react-client';
264
328
 
265
- function WelcomeBanner() {
329
+ function Dashboard() {
266
330
  return (
267
- <Dismissible id="welcome-banner-234-432-432-1">
268
- <div className="alert alert-info">
269
- <h4>Welcome!</h4>
270
- <p>Thanks for joining our platform. Here are some quick tips to get started.</p>
271
- </div>
272
- </Dismissible>
331
+ <div>
332
+ {/* Dismissible state is tracked per user */}
333
+ <Dismissible id="user-welcome-banner">
334
+ <div className="alert alert-info">
335
+ <h4>Welcome back!</h4>
336
+ <p>You have 3 new notifications.</p>
337
+ </div>
338
+ </Dismissible>
339
+ </div>
273
340
  );
274
341
  }
275
342
  ```
@@ -349,19 +416,19 @@ import { Dismissible } from '@dismissible/react-client';
349
416
  function Dashboard() {
350
417
  return (
351
418
  <div>
352
- <Dismissible id="feature-announcement-234-432-432-1">
419
+ <Dismissible id="feature-announcement">
353
420
  <div className="alert alert-success">
354
421
  🎉 New feature: Dark mode is now available!
355
422
  </div>
356
423
  </Dismissible>
357
424
 
358
- <Dismissible id="maintenance-notice-234-432-432-1">
425
+ <Dismissible id="maintenance-notice">
359
426
  <div className="alert alert-warning">
360
427
  ⚠️ Scheduled maintenance: Sunday 2AM-4AM EST
361
428
  </div>
362
429
  </Dismissible>
363
430
 
364
- <Dismissible id="survey-request-234-432-432-1">
431
+ <Dismissible id="survey-request">
365
432
  <div className="alert alert-info">
366
433
  📝 Help us improve! Take our 2-minute survey.
367
434
  </div>
@@ -371,35 +438,6 @@ function Dashboard() {
371
438
  }
372
439
  ```
373
440
 
374
- ### User-Specific vs Anonymous Dismissible Items
375
-
376
- The behavior changes based on whether JWT authentication is configured:
377
-
378
- > **Enterprise vs Standard:** JWT authentication for user-specific dismissals is an Enterprise feature. Standard accounts use anonymous (account-level) dismissals. [Compare plans →](https://dismissible.io/pricing)
379
-
380
- ```tsx
381
- import { DismissibleProvider, Dismissible } from '@dismissible/react-client';
382
-
383
- // With JWT - dismissible state is user-specific
384
- function AuthenticatedApp() {
385
- return (
386
- <DismissibleProvider jwt={() => getAccessToken()}>
387
- <div>
388
- {/* Each user will see this banner independently */}
389
- <Dismissible id="feature-announcement">
390
- <div>New feature available!</div>
391
- </Dismissible>
392
-
393
- {/* User A dismissing this won't affect User B */}
394
- <Dismissible id="survey-request">
395
- <div>Please take our survey!</div>
396
- </Dismissible>
397
- </div>
398
- </DismissibleProvider>
399
- );
400
- }
401
- ```
402
-
403
441
  ### Error Handling with ignoreErrors
404
442
 
405
443
  ```tsx
@@ -421,57 +459,68 @@ function RobustBanner() {
421
459
  }
422
460
  ```
423
461
 
424
- ### Async JWT Authentication Examples
462
+ ### Integration with Auth Providers
425
463
 
426
464
  ```tsx
427
465
  import { DismissibleProvider } from '@dismissible/react-client';
428
466
 
429
- // With token refresh logic
430
- function AppWithTokenRefresh() {
467
+ // With Firebase Auth
468
+ function AppWithFirebase() {
469
+ const user = firebase.auth().currentUser;
470
+
431
471
  return (
432
472
  <DismissibleProvider
473
+ userId={user.uid}
433
474
  jwt={async () => {
434
- try {
435
- const token = await fetch('/api/auth/refresh', {
436
- method: 'POST',
437
- credentials: 'include'
438
- });
439
- const { accessToken } = await token.json();
440
- return accessToken;
441
- } catch (error) {
442
- console.error('Failed to refresh token:', error);
443
- throw error;
475
+ if (user) {
476
+ return await user.getIdToken();
444
477
  }
478
+ throw new Error('User not authenticated');
445
479
  }}
480
+ baseUrl="https://api.yourapp.com"
446
481
  >
447
482
  <YourApp />
448
483
  </DismissibleProvider>
449
484
  );
450
485
  }
451
486
 
452
- // With Firebase Auth
453
- function AppWithFirebase() {
487
+ // With Auth0
488
+ function AppWithAuth0() {
489
+ const { user, getAccessTokenSilently } = useAuth0();
490
+
454
491
  return (
455
492
  <DismissibleProvider
456
- jwt={async () => {
457
- const user = firebase.auth().currentUser;
458
- if (user) {
459
- return await user.getIdToken();
460
- }
461
- throw new Error('User not authenticated');
462
- }}
493
+ userId={user.sub}
494
+ jwt={async () => await getAccessTokenSilently()}
495
+ baseUrl="https://api.yourapp.com"
463
496
  >
464
497
  <YourApp />
465
498
  </DismissibleProvider>
466
499
  );
467
500
  }
468
501
 
469
- // With Auth0
470
- function AppWithAuth0() {
471
- const { getAccessTokenSilently } = useAuth0();
502
+ // With token refresh logic
503
+ function AppWithTokenRefresh() {
504
+ const { user } = useAuth();
472
505
 
473
506
  return (
474
- <DismissibleProvider jwt={async () => await getAccessTokenSilently()}>
507
+ <DismissibleProvider
508
+ userId={user.id}
509
+ jwt={async () => {
510
+ try {
511
+ const response = await fetch('/api/auth/refresh', {
512
+ method: 'POST',
513
+ credentials: 'include'
514
+ });
515
+ const { accessToken } = await response.json();
516
+ return accessToken;
517
+ } catch (error) {
518
+ console.error('Failed to refresh token:', error);
519
+ throw error;
520
+ }
521
+ }}
522
+ baseUrl="https://api.yourapp.com"
523
+ >
475
524
  <YourApp />
476
525
  </DismissibleProvider>
477
526
  );
@@ -481,7 +530,7 @@ function AppWithAuth0() {
481
530
  ### Using the Hook for Complex Logic
482
531
 
483
532
  ```tsx
484
- import { useDismissibleItem, DismissibleProvider } from '@dismissible/react-client';
533
+ import { useDismissibleItem } from '@dismissible/react-client';
485
534
  import { useState, useEffect } from 'react';
486
535
 
487
536
  function SmartNotification({ id, message, type = 'info' }) {
@@ -517,17 +566,82 @@ function SmartNotification({ id, message, type = 'info' }) {
517
566
  </div>
518
567
  );
519
568
  }
569
+ ```
570
+
571
+ ### Restoring Dismissed Items
572
+
573
+ Use the `restore` function to bring back previously dismissed content:
574
+
575
+ ```tsx
576
+ import { useDismissibleItem } from '@dismissible/react-client';
577
+
578
+ function RestorableBanner({ id }) {
579
+ const { dismissedOn, dismiss, restore, isLoading } = useDismissibleItem(id);
580
+
581
+ if (dismissedOn) {
582
+ return (
583
+ <div className="dismissed-placeholder">
584
+ <p>Banner was dismissed on {new Date(dismissedOn).toLocaleDateString()}</p>
585
+ <button onClick={restore} disabled={isLoading}>
586
+ {isLoading ? 'Restoring...' : 'Show Banner Again'}
587
+ </button>
588
+ </div>
589
+ );
590
+ }
520
591
 
521
- // Usage with async authentication
522
- function App() {
523
592
  return (
524
- <DismissibleProvider jwt={async () => await getUserToken()}>
525
- <SmartNotification
526
- id="user-specific-notification"
527
- message="Welcome back!"
528
- type="info"
529
- />
530
- </DismissibleProvider>
593
+ <div className="banner">
594
+ <h3>Welcome!</h3>
595
+ <p>This is a restorable banner.</p>
596
+ <button onClick={dismiss} disabled={isLoading}>
597
+ Dismiss
598
+ </button>
599
+ </div>
600
+ );
601
+ }
602
+ ```
603
+
604
+ ### Admin Panel with Restore Capability
605
+
606
+ ```tsx
607
+ import { useDismissibleItem } from '@dismissible/react-client';
608
+
609
+ function AdminNotificationManager({ notificationId }) {
610
+ const { dismissedOn, dismiss, restore, item, isLoading, error } =
611
+ useDismissibleItem(notificationId);
612
+
613
+ if (error) {
614
+ return <div className="error">Error: {error.message}</div>;
615
+ }
616
+
617
+ return (
618
+ <div className="admin-panel">
619
+ <h4>Notification: {notificationId}</h4>
620
+ <p>Status: {dismissedOn ? 'Dismissed' : 'Active'}</p>
621
+ {dismissedOn && (
622
+ <p>Dismissed at: {new Date(dismissedOn).toLocaleString()}</p>
623
+ )}
624
+
625
+ <div className="actions">
626
+ {dismissedOn ? (
627
+ <button
628
+ onClick={restore}
629
+ disabled={isLoading}
630
+ className="btn-restore"
631
+ >
632
+ Restore
633
+ </button>
634
+ ) : (
635
+ <button
636
+ onClick={dismiss}
637
+ disabled={isLoading}
638
+ className="btn-dismiss"
639
+ >
640
+ Dismiss
641
+ </button>
642
+ )}
643
+ </div>
644
+ </div>
531
645
  );
532
646
  }
533
647
  ```
@@ -564,28 +678,60 @@ import type {
564
678
  DismissibleProps,
565
679
  DismissibleProviderProps,
566
680
  JwtToken,
567
- IDismissibleItem
568
681
  } from '@dismissible/react-client';
569
682
 
570
- // Dismissible component with custom props
571
- const MyComponent: React.FC<DismissibleProps> = (props) => {
572
- // Your component implementation
573
- };
574
-
575
- // Provider with JWT authentication
576
- const AuthProvider: React.FC<DismissibleProviderProps> = ({ children, jwt }) => {
683
+ // Custom provider wrapper
684
+ const AuthenticatedDismissibleProvider: React.FC<{
685
+ children: React.ReactNode;
686
+ }> = ({ children }) => {
687
+ const { user, getToken } = useAuth();
688
+
577
689
  return (
578
- <DismissibleProvider jwt={jwt}>
690
+ <DismissibleProvider
691
+ userId={user.id}
692
+ jwt={getToken}
693
+ baseUrl={process.env.DISMISSIBLE_API_URL}
694
+ >
579
695
  {children}
580
696
  </DismissibleProvider>
581
697
  );
582
698
  };
583
699
  ```
584
700
 
701
+ ## Self-Hosting
702
+
703
+ Dismissible is designed to be self-hosted. You have full control over your data.
704
+
705
+ ### Option 1: Docker (Recommended)
706
+
707
+ The fastest way to get started:
708
+
709
+ ```bash
710
+ docker run -p 3001:3001 dismissibleio/dismissible-api:latest
711
+ ```
712
+
713
+ See the [Docker documentation](https://dismissible.io/docs/docker) for production configuration.
714
+
715
+ ### Option 2: NestJS Module
716
+
717
+ Integrate directly into your existing NestJS application:
718
+
719
+ ```bash
720
+ npm install @dismissible/nestjs-api
721
+ ```
722
+
723
+ See the [NestJS documentation](https://dismissible.io/docs/nestjs) for setup instructions.
724
+
585
725
  ## Support
586
726
 
587
- - 📖 [Documentation](https://docs.dismissible.io)
727
+ - 📖 [Documentation](https://dismissible.io/docs)
728
+ - 🐙 [GitHub - React Client](https://github.com/DismissibleIo/dismissible-react-client)
729
+ - 🐙 [GitHub - API Server](https://github.com/DismissibleIo/dismissible-api)
730
+
731
+ ## License
732
+
733
+ MIT © [Dismissible](https://dismissible.io)
588
734
 
589
735
  ## Changelog
590
736
 
591
- See [CHANGELOG.md](./CHANGELOG.md) for a detailed list of changes.
737
+ See [CHANGELOG.md](./CHANGELOG.md) for a detailed list of changes.