@data-netmonk/mona-chat-widget 2.3.8 → 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 +760 -760
- package/dist/index.cjs +56 -56
- package/dist/index.js +9401 -9401
- package/package.json +84 -84
package/README.md
CHANGED
|
@@ -1,760 +1,760 @@
|
|
|
1
|
-
# Mona Chat Widget
|
|
2
|
-
|
|
3
|
-
Chat widget package developed by Netmonk data & solution team to be imported in Netmonk products
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
### Recent Updates & Breaking Changes
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
**Latest Version Changes:**
|
|
12
|
-
|
|
13
|
-
⚠️ **Breaking Changes:**
|
|
14
|
-
1. **Removed `type` and `agentType` props** - These parameters are no longer used and have been removed from all components
|
|
15
|
-
2. **Renamed `botServerUrl` to `webhookUrl`** - For better clarity and consistency
|
|
16
|
-
3. **`webhookUrl` is now required** - Must be provided as a prop
|
|
17
|
-
4. **`authUrl` and `username` are now direct props** - No longer part of `data` prop for better clarity and type safety
|
|
18
|
-
5. **`userId` is now optional** - Widget automatically generates visitor ID for guest users via browser fingerprinting when `userId` is not provided
|
|
19
|
-
|
|
20
|
-
✨ **New Features:**
|
|
21
|
-
1. **Guest user support** - Users can chat without logging in
|
|
22
|
-
- Automatic visitor ID generation using browser fingerprinting (FingerprintJS + SHA256)
|
|
23
|
-
- `auth: false` flag automatically added to API requests for guest users
|
|
24
|
-
- Persistent sessions across page reloads for anonymous visitors
|
|
25
|
-
2. **Authentication support** - Automatic token management with refresh on 401 errors
|
|
26
|
-
- Pass `authUrl` as a direct prop
|
|
27
|
-
- Widget handles token lifecycle automatically
|
|
28
|
-
3. **Enhanced user authentication handling** - Smart detection of authenticated vs guest users
|
|
29
|
-
- Compares `userId` with visitor ID to determine authentication status
|
|
30
|
-
- Automatic `auth` flag management in all API requests
|
|
31
|
-
4. **Enhanced data prop** - Support for custom variables passed to backend via `data` prop
|
|
32
|
-
5. **Improved error handling** - Better fallback mechanisms and error messages
|
|
33
|
-
6. **Direct `username` prop** - Pass username as a top-level prop instead of in data string
|
|
34
|
-
|
|
35
|
-
📝 **Migration Guide:**
|
|
36
|
-
```jsx
|
|
37
|
-
// Old usage (deprecated)
|
|
38
|
-
<ChatWidget
|
|
39
|
-
userId="user123"
|
|
40
|
-
sourceId="source456"
|
|
41
|
-
type="prime"
|
|
42
|
-
botServerUrl="https://api.example.com"
|
|
43
|
-
/>
|
|
44
|
-
|
|
45
|
-
// Previous version (with data prop)
|
|
46
|
-
<ChatWidget
|
|
47
|
-
userId="user123"
|
|
48
|
-
sourceId="source456"
|
|
49
|
-
webhookUrl="https://api.example.com/webhook"
|
|
50
|
-
data="authUrl=https://api.example.com/login/chatwidget~username=John"
|
|
51
|
-
/>
|
|
52
|
-
|
|
53
|
-
// New usage - Authenticated user (current - recommended)
|
|
54
|
-
<ChatWidget
|
|
55
|
-
userId="user123"
|
|
56
|
-
sourceId="source456"
|
|
57
|
-
webhookUrl="https://api.example.com/webhook"
|
|
58
|
-
authUrl="https://api.example.com/login/chatwidget"
|
|
59
|
-
username="John"
|
|
60
|
-
data="email=john@example.com~phone=+1234567890"
|
|
61
|
-
/>
|
|
62
|
-
|
|
63
|
-
// New usage - Guest user (without login)
|
|
64
|
-
<ChatWidget
|
|
65
|
-
sourceId="source456"
|
|
66
|
-
webhookUrl="https://api.example.com/webhook"
|
|
67
|
-
username="Guest"
|
|
68
|
-
/>
|
|
69
|
-
// Widget automatically generates visitor ID and adds auth: false flag
|
|
70
|
-
|
|
71
|
-
// New usage - Conditional (handles both logged-in and guest users)
|
|
72
|
-
<ChatWidget
|
|
73
|
-
userId={currentUser?.id} // undefined for guests
|
|
74
|
-
sourceId="source456"
|
|
75
|
-
webhookUrl="https://api.example.com/webhook"
|
|
76
|
-
username={currentUser?.name || "Guest"}
|
|
77
|
-
/>
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
---
|
|
81
|
-
|
|
82
|
-
## 🚅 Quick start
|
|
83
|
-
|
|
84
|
-
### Prerequisites
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
1. Install dependencies
|
|
89
|
-
```
|
|
90
|
-
npm install --legacy-peer-deps
|
|
91
|
-
```
|
|
92
|
-
2. Copy .env.example
|
|
93
|
-
```
|
|
94
|
-
cp .env.example .env
|
|
95
|
-
```
|
|
96
|
-
3. Populate .env
|
|
97
|
-
4. Enable mock mode (optional)
|
|
98
|
-
|
|
99
|
-
To test the chat widget without a backend server, set `VITE_USE_MOCK_RESPONSES=true` in your `.env` file. The widget will respond to messages like:
|
|
100
|
-
- "start", "hello", "hi", "halo" - Greeting messages
|
|
101
|
-
- "help", "bantuan" - Help information
|
|
102
|
-
- "terima kasih", "thank you" - Acknowledgments
|
|
103
|
-
- "bye", "goodbye" - Farewell messages
|
|
104
|
-
- And more! Check `src/components/ChatWidget/utils/helpers.js` for full list
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
### Mock Mode (Demo without Backend)
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
The chat widget includes a built-in mock mode for testing and demonstrations without requiring a backend server.
|
|
113
|
-
|
|
114
|
-
**To enable mock mode:**
|
|
115
|
-
1. Set `VITE_USE_MOCK_RESPONSES=true` in your `.env` file
|
|
116
|
-
2. Run the app normally with `npm run dev`
|
|
117
|
-
|
|
118
|
-
**Mock responses include:**
|
|
119
|
-
- Greetings (hello, hi, halo, start) - with interactive buttons
|
|
120
|
-
- Help commands
|
|
121
|
-
- Device list with clickable buttons (type "devices" or "show devices")
|
|
122
|
-
- Thank you acknowledgments
|
|
123
|
-
- Farewells
|
|
124
|
-
- Time-based greetings (good morning, etc.)
|
|
125
|
-
- And automatically falls back to mock mode if backend fails
|
|
126
|
-
|
|
127
|
-
**Interactive Buttons Demo:**
|
|
128
|
-
Type these messages to see button responses:
|
|
129
|
-
- "start" - Welcome message with action buttons
|
|
130
|
-
- "show devices" or "devices" - List of devices as clickable buttons
|
|
131
|
-
- Click any button to trigger the next action
|
|
132
|
-
|
|
133
|
-
**To add custom mock responses:**
|
|
134
|
-
Edit the `mockBotResponses` object in `src/components/ChatWidget/utils/mockBotResponses.js`
|
|
135
|
-
|
|
136
|
-
For text-only responses:
|
|
137
|
-
```javascript
|
|
138
|
-
"trigger": "Response text"
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
For responses with buttons:
|
|
142
|
-
```javascript
|
|
143
|
-
"trigger": {
|
|
144
|
-
text: "Your message here",
|
|
145
|
-
buttons: [
|
|
146
|
-
{ title: "Button 1", payload: "action_1" },
|
|
147
|
-
{ title: "Button 2", payload: "action_2" },
|
|
148
|
-
{ title: "Link Button", url: "https://example.com" }
|
|
149
|
-
]
|
|
150
|
-
}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
### Storybook
|
|
156
|
-
|
|
157
|
-
---
|
|
158
|
-
|
|
159
|
-
1. **How to run Storybook locally** (access at http://localhost:5177)
|
|
160
|
-
|
|
161
|
-
```
|
|
162
|
-
npm run storybook
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
2. **How to build Storybook**
|
|
166
|
-
|
|
167
|
-
```
|
|
168
|
-
npm run build-storybook
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
3. **How to serve Storybook**
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
npm run serve-storybook
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
---
|
|
178
|
-
|
|
179
|
-
### Library (how to update and publish)
|
|
180
|
-
|
|
181
|
-
---
|
|
182
|
-
|
|
183
|
-
1. **Commit changes**
|
|
184
|
-
```
|
|
185
|
-
git add .
|
|
186
|
-
git commit -m "Your commit message"
|
|
187
|
-
```
|
|
188
|
-
2. **Update version**
|
|
189
|
-
|
|
190
|
-
```bash
|
|
191
|
-
npm version patch # for bug fixes (1.0.0 -> 1.0.1)
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
```bash
|
|
195
|
-
npm version minor # for new features (1.0.0 -> 1.1.0)
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
```bash
|
|
199
|
-
npm version major # for breaking changes (1.0.0 -> 2.0.0)
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
3. **Build as a library** (build file at `/dist` directory)
|
|
203
|
-
|
|
204
|
-
```
|
|
205
|
-
npm run build
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
4. **Copy declaration file to `/dist`**
|
|
209
|
-
|
|
210
|
-
```
|
|
211
|
-
cp ./src/declarations/index.d.ts ./dist/index.d.ts
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
5. **Publish**
|
|
215
|
-
|
|
216
|
-
```
|
|
217
|
-
npm publish
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
---
|
|
221
|
-
|
|
222
|
-
### Library (how to import on your project)
|
|
223
|
-
|
|
224
|
-
---
|
|
225
|
-
|
|
226
|
-
1. **Install package**
|
|
227
|
-
```bash
|
|
228
|
-
npm install @data-netmonk/mona-chat-widget
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
2. **Import styles on your `App.jsx` or `index.jsx`**
|
|
232
|
-
|
|
233
|
-
```jsx
|
|
234
|
-
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
3. **Import & use component**
|
|
238
|
-
|
|
239
|
-
```jsx
|
|
240
|
-
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
241
|
-
|
|
242
|
-
function App() {
|
|
243
|
-
return (
|
|
244
|
-
<ChatWidget
|
|
245
|
-
userId="user123"
|
|
246
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
247
|
-
webhookUrl="https://your-backend-url.com"
|
|
248
|
-
/>
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
### Component Props
|
|
256
|
-
|
|
257
|
-
---
|
|
258
|
-
|
|
259
|
-
#### Required Props
|
|
260
|
-
|
|
261
|
-
| Prop | Type | Description |
|
|
262
|
-
|------|------|-------------|
|
|
263
|
-
| `sourceId` | `string` | **Required.** Source/channel identifier for the chat |
|
|
264
|
-
| `webhookUrl` | `string` | **Required.** Backend webhook URL |
|
|
265
|
-
|
|
266
|
-
#### Optional Props
|
|
267
|
-
|
|
268
|
-
| Prop | Type | Default | Description |
|
|
269
|
-
|------|------|---------|-------------|
|
|
270
|
-
| `userId` | `string` | Generated visitor ID | Unique identifier for the user. **Optional** - if not provided, a unique visitor ID is automatically generated using browser fingerprinting for guest users |
|
|
271
|
-
| `authUrl` | `string` | - | Authentication endpoint URL for token-based authentication |
|
|
272
|
-
| `username` | `string` | - | Username for the session (sent to backend in variables) |
|
|
273
|
-
| `data` | `string` | - | Additional custom variables in format `key1=value1~key2=value2` |
|
|
274
|
-
| `width` | `string` | `"25vw"` | Widget width (CSS value) |
|
|
275
|
-
| `height` | `string` | `"90vh"` | Widget height (CSS value) |
|
|
276
|
-
| `right` | `string` | `"1.25rem"` | Distance from right edge |
|
|
277
|
-
| `bottom` | `string` | `"1.25rem"` | Distance from bottom edge |
|
|
278
|
-
| `zIndex` | `number` | `2000` | CSS z-index for widget positioning |
|
|
279
|
-
| `position` | `string` | `"fixed"` | CSS position (`"fixed"` or `"relative"`) |
|
|
280
|
-
| `onToggle` | `function` | - | Callback when widget opens/closes: `(isOpen: boolean) => void` |
|
|
281
|
-
|
|
282
|
-
---
|
|
283
|
-
|
|
284
|
-
### Guest User Support
|
|
285
|
-
|
|
286
|
-
---
|
|
287
|
-
|
|
288
|
-
The widget now supports **guest users** (users who haven't logged in or before logging in). When `userId` is not provided, the widget automatically generates a unique visitor ID using browser fingerprinting.
|
|
289
|
-
|
|
290
|
-
**How it works:**
|
|
291
|
-
|
|
292
|
-
1. **Browser Fingerprinting**: Uses FingerprintJS to generate a unique identifier based on:
|
|
293
|
-
- Browser characteristics (user agent, screen resolution, timezone, etc.)
|
|
294
|
-
- Incognito/private browsing detection
|
|
295
|
-
- Combined into a SHA256 hash for consistency
|
|
296
|
-
|
|
297
|
-
2. **Automatic Guest Detection**: The widget automatically detects guest users and:
|
|
298
|
-
- Generates a visitor ID if `userId` is not provided
|
|
299
|
-
- Adds `auth: false` flag to all API requests for guest users
|
|
300
|
-
- Uses the visitor ID as the effective user ID throughout the session
|
|
301
|
-
|
|
302
|
-
3. **Persistent Sessions**: The visitor ID remains consistent across page reloads (unless browser fingerprint changes or user clears data)
|
|
303
|
-
|
|
304
|
-
**When to use guest mode:**
|
|
305
|
-
- Public-facing websites where users can chat without logging in
|
|
306
|
-
- Support widgets for anonymous visitors
|
|
307
|
-
- Pre-login customer service interactions
|
|
308
|
-
- Any scenario where user authentication is optional
|
|
309
|
-
|
|
310
|
-
**When to provide userId:**
|
|
311
|
-
- Users who are logged into your application
|
|
312
|
-
- When you need to track chat history across devices
|
|
313
|
-
- When authentication is required for personalized responses
|
|
314
|
-
- Enterprise or internal applications
|
|
315
|
-
|
|
316
|
-
---
|
|
317
|
-
|
|
318
|
-
### Usage Examples
|
|
319
|
-
|
|
320
|
-
---
|
|
321
|
-
|
|
322
|
-
#### Basic Usage (Authenticated User)
|
|
323
|
-
|
|
324
|
-
```jsx
|
|
325
|
-
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
326
|
-
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
327
|
-
|
|
328
|
-
function App() {
|
|
329
|
-
return (
|
|
330
|
-
<ChatWidget
|
|
331
|
-
userId="user123"
|
|
332
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
333
|
-
webhookUrl="https://api.example.com/webhook"
|
|
334
|
-
/>
|
|
335
|
-
);
|
|
336
|
-
}
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
#### Guest User (Without Login)
|
|
340
|
-
|
|
341
|
-
For anonymous visitors or users who haven't logged in:
|
|
342
|
-
|
|
343
|
-
```jsx
|
|
344
|
-
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
345
|
-
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
346
|
-
|
|
347
|
-
function App() {
|
|
348
|
-
return (
|
|
349
|
-
<ChatWidget
|
|
350
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
351
|
-
webhookUrl="https://api.example.com/webhook"
|
|
352
|
-
/>
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
**What happens:**
|
|
358
|
-
- Widget automatically generates a visitor ID using browser fingerprinting
|
|
359
|
-
- All API requests include `auth: false` in variables to indicate guest user
|
|
360
|
-
- No login required - users can start chatting immediately
|
|
361
|
-
|
|
362
|
-
#### Conditional userId (Logged in or Guest)
|
|
363
|
-
|
|
364
|
-
Handle both logged-in users and guests dynamically:
|
|
365
|
-
|
|
366
|
-
```jsx
|
|
367
|
-
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
368
|
-
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
369
|
-
|
|
370
|
-
function App() {
|
|
371
|
-
const currentUser = getCurrentUser(); // Your auth function
|
|
372
|
-
|
|
373
|
-
return (
|
|
374
|
-
<ChatWidget
|
|
375
|
-
userId={currentUser?.id} // Provide userId if logged in, undefined if not
|
|
376
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
377
|
-
webhookUrl="https://api.example.com/webhook"
|
|
378
|
-
username={currentUser?.name}
|
|
379
|
-
/>
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
**Behavior:**
|
|
385
|
-
- If `currentUser.id` exists → Uses provided userId (authenticated user)
|
|
386
|
-
- If `currentUser.id` is `null`/`undefined` → Generates visitor ID (guest user)
|
|
387
|
-
- Backend receives `auth: false` flag only for guest users
|
|
388
|
-
|
|
389
|
-
#### With Custom Variables
|
|
390
|
-
|
|
391
|
-
Pass custom user data like email, phone number, etc. to the backend:
|
|
392
|
-
|
|
393
|
-
```jsx
|
|
394
|
-
<ChatWidget
|
|
395
|
-
userId="user123"
|
|
396
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
397
|
-
webhookUrl="https://api.example.com/webhook"
|
|
398
|
-
username="John Doe"
|
|
399
|
-
data="telephone_number=+628123456789~email=john@example.com"
|
|
400
|
-
/>
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
**Variables sent to backend:**
|
|
404
|
-
```json
|
|
405
|
-
{
|
|
406
|
-
"chat_id": "...",
|
|
407
|
-
"session_id": "...",
|
|
408
|
-
"user_id": "user123",
|
|
409
|
-
"message": "Hello",
|
|
410
|
-
"type": "text",
|
|
411
|
-
"variables": {
|
|
412
|
-
"username": "John Doe",
|
|
413
|
-
"telephone_number": "+628123456789",
|
|
414
|
-
"email": "john@example.com"
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
```
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
#### With Authentication (NEW!)
|
|
421
|
-
|
|
422
|
-
The widget supports automatic authentication with token refresh on expiry:
|
|
423
|
-
|
|
424
|
-
```jsx
|
|
425
|
-
<ChatWidget
|
|
426
|
-
userId="user123"
|
|
427
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
428
|
-
webhookUrl="https://api.example.com/webhook"
|
|
429
|
-
authUrl="https://api.example.com/login/chatwidget"
|
|
430
|
-
username="John Doe"
|
|
431
|
-
/>
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
**How authentication works:**
|
|
435
|
-
|
|
436
|
-
1. **Initial Authentication**: When the widget loads (on Launcher mount), if `authUrl` is provided as a prop, it automatically calls the auth API:
|
|
437
|
-
```
|
|
438
|
-
POST {authUrl}
|
|
439
|
-
Body: { "user_id": "user123" }
|
|
440
|
-
Response: { "token": "eyJ..." }
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
2. **Session Initialization**: After getting the token, the widget calls the init endpoint to establish the session:
|
|
444
|
-
```
|
|
445
|
-
POST {webhookUrl}/{sourceId}/init
|
|
446
|
-
Body: {
|
|
447
|
-
"session_id": "...",
|
|
448
|
-
"user_id": "user123",
|
|
449
|
-
"token": "eyJ...",
|
|
450
|
-
"username": "John Doe"
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
3. **Automatic Token Refresh**: If the webhook returns **401 Unauthorized** (token expired/revoked), the widget automatically:
|
|
455
|
-
- Calls the `authUrl` again to get a fresh token
|
|
456
|
-
- Re-initializes the session with the new token
|
|
457
|
-
- Updates the `authToken` in variables
|
|
458
|
-
- Retries the failed request with the new token
|
|
459
|
-
- This happens transparently without user interaction
|
|
460
|
-
|
|
461
|
-
**Combined example with auth and custom variables:**
|
|
462
|
-
```jsx
|
|
463
|
-
<ChatWidget
|
|
464
|
-
userId="user123"
|
|
465
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
466
|
-
webhookUrl="https://api.example.com/webhook"
|
|
467
|
-
authUrl="https://api.example.com/login/chatwidget"
|
|
468
|
-
username="John"
|
|
469
|
-
data="email=john@example.com~phone=+628123456789"
|
|
470
|
-
/>
|
|
471
|
-
```
|
|
472
|
-
|
|
473
|
-
**Auth Flag (`auth` variable):**
|
|
474
|
-
The widget automatically adds an `auth: false` flag to the `variables` object when the user is a guest (not authenticated):
|
|
475
|
-
- When `userId === visitorId` (browser fingerprint), the widget adds `auth: false` to variables
|
|
476
|
-
- When `userId !== visitorId` (authenticated user), no `auth` flag is added to variables
|
|
477
|
-
|
|
478
|
-
**Guest user example** (userId equals browser fingerprint):
|
|
479
|
-
```json
|
|
480
|
-
{
|
|
481
|
-
"chat_id": "...",
|
|
482
|
-
"session_id": "...",
|
|
483
|
-
"user_id": "visitor_abc123",
|
|
484
|
-
"message": "Hello",
|
|
485
|
-
"type": "text",
|
|
486
|
-
"variables": {
|
|
487
|
-
"username": "Guest",
|
|
488
|
-
"auth": false
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
**Authenticated user example** (userId is different from browser fingerprint):
|
|
494
|
-
```json
|
|
495
|
-
{
|
|
496
|
-
"chat_id": "...",
|
|
497
|
-
"session_id": "...",
|
|
498
|
-
"user_id": "user123",
|
|
499
|
-
"message": "Hello",
|
|
500
|
-
"type": "text",
|
|
501
|
-
"variables": {
|
|
502
|
-
"username": "John Doe",
|
|
503
|
-
"email": "john@example.com"
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
This `auth: false` flag is automatically added in:
|
|
509
|
-
- Session initialization payload (when guest)
|
|
510
|
-
- All message requests (when guest)
|
|
511
|
-
- Button postback requests (when guest)
|
|
512
|
-
|
|
513
|
-
**Note:** The `data` prop is for additional custom variables only. Required parameters (`userId`, `sourceId`, `webhookUrl`) and common optional parameters (`authUrl`, `username`) should be passed as direct props for better type safety and clarity.
|
|
514
|
-
|
|
515
|
-
#### Custom Styling
|
|
516
|
-
|
|
517
|
-
```jsx
|
|
518
|
-
<ChatWidget
|
|
519
|
-
userId="user123" // Optional
|
|
520
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
521
|
-
webhookUrl="https://api.example.com/webhook"
|
|
522
|
-
width="400px"
|
|
523
|
-
height="600px"
|
|
524
|
-
right="20px"
|
|
525
|
-
bottom="20px"
|
|
526
|
-
zIndex={9999}
|
|
527
|
-
/>
|
|
528
|
-
```
|
|
529
|
-
|
|
530
|
-
#### Embedded in Layout (Relative Positioning)
|
|
531
|
-
|
|
532
|
-
```jsx
|
|
533
|
-
<div style={{ display: 'grid', gridTemplateColumns: '1fr 400px' }}>
|
|
534
|
-
<div>Main content</div>
|
|
535
|
-
<ChatWidget
|
|
536
|
-
userId="user123" // Optional
|
|
537
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
538
|
-
webhookUrl="https://api.example.com/webhook"
|
|
539
|
-
position="relative"
|
|
540
|
-
width="100%"
|
|
541
|
-
height="100vh"
|
|
542
|
-
/>
|
|
543
|
-
</div>
|
|
544
|
-
```
|
|
545
|
-
|
|
546
|
-
#### With Toggle Callback
|
|
547
|
-
|
|
548
|
-
```jsx
|
|
549
|
-
function App() {
|
|
550
|
-
const handleToggle = (isOpen) => {
|
|
551
|
-
console.log('Chat widget is', isOpen ? 'open' : 'closed');
|
|
552
|
-
// Track analytics, update UI, etc.
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
return (
|
|
556
|
-
<ChatWidget
|
|
557
|
-
userId="user123" // Optional
|
|
558
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
559
|
-
webhookUrl="https://api.example.com/webhook"
|
|
560
|
-
onToggle={handleToggle}
|
|
561
|
-
/>
|
|
562
|
-
);
|
|
563
|
-
}
|
|
564
|
-
```
|
|
565
|
-
|
|
566
|
-
#### Full-Screen Widget
|
|
567
|
-
|
|
568
|
-
```jsx
|
|
569
|
-
<ChatWidget
|
|
570
|
-
userId="user123" // Optional - supports guest users
|
|
571
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
572
|
-
webhookUrl="https://api.example.com/webhook"
|
|
573
|
-
width="100vw"
|
|
574
|
-
height="100vh"
|
|
575
|
-
right="0"
|
|
576
|
-
bottom="0"
|
|
577
|
-
/>
|
|
578
|
-
```
|
|
579
|
-
|
|
580
|
-
---
|
|
581
|
-
|
|
582
|
-
### Data Prop Format & Helper Function
|
|
583
|
-
|
|
584
|
-
---
|
|
585
|
-
|
|
586
|
-
The `data` prop allows you to pass additional custom variables via a single string. This is especially useful when you need to dynamically pass user-specific data or pass it through URL parameters.
|
|
587
|
-
|
|
588
|
-
**Format:** `key=value` pairs separated by `~`
|
|
589
|
-
|
|
590
|
-
**Example data string:**
|
|
591
|
-
```
|
|
592
|
-
email=john@example.com~phone=+1234567890~department=Engineering
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
**Note:** `authUrl` and `username` are now **direct props** and should not be included in the `data` string.
|
|
596
|
-
|
|
597
|
-
#### Building Data String Programmatically
|
|
598
|
-
|
|
599
|
-
Instead of manually constructing the data string, you can create a helper function to build it dynamically. This approach is recommended for production applications as it:
|
|
600
|
-
- Prevents syntax errors in the data string format
|
|
601
|
-
- Makes the code more maintainable and readable
|
|
602
|
-
- Allows for conditional inclusion of parameters
|
|
603
|
-
- Handles URL encoding automatically if needed
|
|
604
|
-
|
|
605
|
-
**Create a helper function** (`src/helpers/chatWidget.js` or similar):
|
|
606
|
-
|
|
607
|
-
```jsx
|
|
608
|
-
/**
|
|
609
|
-
* Builds the data string for ChatWidget component
|
|
610
|
-
* @param {Object} params - Object containing all parameters
|
|
611
|
-
* @param {string} params.email - Optional: User's email address
|
|
612
|
-
* @param {string} params.phone - Optional: User's phone number
|
|
613
|
-
* @param {Object} params.customFields - Optional: Any additional custom fields as key-value pairs
|
|
614
|
-
* @returns {string} Formatted data string for ChatWidget (always returns a string)
|
|
615
|
-
*
|
|
616
|
-
* @example
|
|
617
|
-
* // Returns: "email=john@example.com~phone=+1234567890~department=Engineering"
|
|
618
|
-
* buildChatWidgetData({
|
|
619
|
-
* email: "john@example.com",
|
|
620
|
-
* phone: "+1234567890",
|
|
621
|
-
* customFields: { department: "Engineering" }
|
|
622
|
-
* });
|
|
623
|
-
*/
|
|
624
|
-
export const buildChatWidgetData = ({
|
|
625
|
-
email,
|
|
626
|
-
phone,
|
|
627
|
-
customFields = {}
|
|
628
|
-
}) => {
|
|
629
|
-
const parts = [];
|
|
630
|
-
|
|
631
|
-
// Add standard fields
|
|
632
|
-
if (email) {
|
|
633
|
-
parts.push(`email=${encodeURIComponent(email)}`);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
if (phone) {
|
|
637
|
-
parts.push(`phone=${encodeURIComponent(phone)}`);
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
// Add any custom fields dynamically
|
|
641
|
-
// Note: customFields is just a convenience parameter for the helper function
|
|
642
|
-
// Each field will be flattened into the string format: key=value~key2=value2
|
|
643
|
-
Object.entries(customFields).forEach(([key, value]) => {
|
|
644
|
-
if (value) {
|
|
645
|
-
parts.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
646
|
-
}
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
// Returns a string like: "email=john@example.com~phone=+1234567890~department=Engineering"
|
|
650
|
-
return parts.join("~");
|
|
651
|
-
};
|
|
652
|
-
```
|
|
653
|
-
|
|
654
|
-
**Important:** The `customFields` parameter is **NOT** passed as an object to the widget. It's just a convenient way to pass multiple additional fields to the helper function. The function flattens everything into a single string format.
|
|
655
|
-
|
|
656
|
-
**Usage in your application:**
|
|
657
|
-
|
|
658
|
-
```jsx
|
|
659
|
-
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
660
|
-
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
661
|
-
import { buildChatWidgetData } from "./helpers/chatWidget";
|
|
662
|
-
|
|
663
|
-
function App() {
|
|
664
|
-
// Get user data from your auth system, state, or props
|
|
665
|
-
const user = {
|
|
666
|
-
id: "user123",
|
|
667
|
-
name: "John Doe",
|
|
668
|
-
email: "john@example.com",
|
|
669
|
-
phone: "+628123456789"
|
|
670
|
-
};
|
|
671
|
-
|
|
672
|
-
// Build the data string with custom variables only
|
|
673
|
-
const customData = buildChatWidgetData({
|
|
674
|
-
email: user.email,
|
|
675
|
-
phone: user.phone,
|
|
676
|
-
customFields: {
|
|
677
|
-
department: "Engineering",
|
|
678
|
-
role: "Developer",
|
|
679
|
-
company: "Acme Corp"
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
// customData is now a STRING like:
|
|
684
|
-
// "email=john@example.com~phone=%2B628123456789~department=Engineering~role=Developer~company=Acme%20Corp"
|
|
685
|
-
|
|
686
|
-
return (
|
|
687
|
-
<ChatWidget
|
|
688
|
-
userId={user.id}
|
|
689
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
690
|
-
webhookUrl="https://api.example.com/webhook"
|
|
691
|
-
authUrl="https://api.example.com/login/chatwidget" // Direct prop
|
|
692
|
-
username={user.name} // Direct prop
|
|
693
|
-
data={customData} // Only additional variables
|
|
694
|
-
/>
|
|
695
|
-
);
|
|
696
|
-
}
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
**With conditional authentication:**
|
|
700
|
-
|
|
701
|
-
```jsx
|
|
702
|
-
function App() {
|
|
703
|
-
const authUrl = import.meta.env.VITE_CHAT_AUTH_ENABLED === "true"
|
|
704
|
-
? import.meta.env.VITE_CHAT_AUTH_URL
|
|
705
|
-
: null;
|
|
706
|
-
|
|
707
|
-
const customData = buildChatWidgetData({
|
|
708
|
-
email: getCurrentUser().email,
|
|
709
|
-
customFields: {
|
|
710
|
-
department: "Sales"
|
|
711
|
-
}
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
return (
|
|
715
|
-
<ChatWidget
|
|
716
|
-
userId={getCurrentUser().id}
|
|
717
|
-
sourceId="691e1b5952068ff7aaeccffc9"
|
|
718
|
-
webhookUrl={import.meta.env.VITE_WEBHOOK_URL}
|
|
719
|
-
authUrl={authUrl} // Direct prop (conditionally set)
|
|
720
|
-
username={getCurrentUser().name} // Direct prop
|
|
721
|
-
data={customData} // Only additional variables
|
|
722
|
-
/>
|
|
723
|
-
);
|
|
724
|
-
}
|
|
725
|
-
```
|
|
726
|
-
|
|
727
|
-
**The helper function handles:**
|
|
728
|
-
- ✅ Proper formatting with `~` separators
|
|
729
|
-
- ✅ URL encoding for special characters
|
|
730
|
-
- ✅ Conditional parameter inclusion (only adds if value exists)
|
|
731
|
-
- ✅ Support for dynamic custom fields
|
|
732
|
-
- ✅ Type safety and documentation via JSDoc comments
|
|
733
|
-
|
|
734
|
-
### Standalone app (for demonstration)
|
|
735
|
-
|
|
736
|
-
1. **How to run locally** (access at `http://localhost:${PORT}/${APP_PREFIX}`)
|
|
737
|
-
|
|
738
|
-
```
|
|
739
|
-
npm run dev
|
|
740
|
-
```
|
|
741
|
-
|
|
742
|
-
2. **How to build as a standalone app** (build file at `/dist-app` directory)
|
|
743
|
-
|
|
744
|
-
```
|
|
745
|
-
npm run build-app
|
|
746
|
-
```
|
|
747
|
-
|
|
748
|
-
3. **How to serve standalone app** (access at `http://localhost:${PORT}/${APP_PREFIX}`)
|
|
749
|
-
|
|
750
|
-
```
|
|
751
|
-
npm run serve
|
|
752
|
-
```
|
|
753
|
-
|
|
754
|
-
4. **How to run on docker** (access at `http://localhost:${PORT}/${APP_PREFIX}`)
|
|
755
|
-
|
|
756
|
-
```
|
|
757
|
-
docker-compose up --build
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
---
|
|
1
|
+
# Mona Chat Widget
|
|
2
|
+
|
|
3
|
+
Chat widget package developed by Netmonk data & solution team to be imported in Netmonk products
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
### Recent Updates & Breaking Changes
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**Latest Version Changes:**
|
|
12
|
+
|
|
13
|
+
⚠️ **Breaking Changes:**
|
|
14
|
+
1. **Removed `type` and `agentType` props** - These parameters are no longer used and have been removed from all components
|
|
15
|
+
2. **Renamed `botServerUrl` to `webhookUrl`** - For better clarity and consistency
|
|
16
|
+
3. **`webhookUrl` is now required** - Must be provided as a prop
|
|
17
|
+
4. **`authUrl` and `username` are now direct props** - No longer part of `data` prop for better clarity and type safety
|
|
18
|
+
5. **`userId` is now optional** - Widget automatically generates visitor ID for guest users via browser fingerprinting when `userId` is not provided
|
|
19
|
+
|
|
20
|
+
✨ **New Features:**
|
|
21
|
+
1. **Guest user support** - Users can chat without logging in
|
|
22
|
+
- Automatic visitor ID generation using browser fingerprinting (FingerprintJS + SHA256)
|
|
23
|
+
- `auth: false` flag automatically added to API requests for guest users
|
|
24
|
+
- Persistent sessions across page reloads for anonymous visitors
|
|
25
|
+
2. **Authentication support** - Automatic token management with refresh on 401 errors
|
|
26
|
+
- Pass `authUrl` as a direct prop
|
|
27
|
+
- Widget handles token lifecycle automatically
|
|
28
|
+
3. **Enhanced user authentication handling** - Smart detection of authenticated vs guest users
|
|
29
|
+
- Compares `userId` with visitor ID to determine authentication status
|
|
30
|
+
- Automatic `auth` flag management in all API requests
|
|
31
|
+
4. **Enhanced data prop** - Support for custom variables passed to backend via `data` prop
|
|
32
|
+
5. **Improved error handling** - Better fallback mechanisms and error messages
|
|
33
|
+
6. **Direct `username` prop** - Pass username as a top-level prop instead of in data string
|
|
34
|
+
|
|
35
|
+
📝 **Migration Guide:**
|
|
36
|
+
```jsx
|
|
37
|
+
// Old usage (deprecated)
|
|
38
|
+
<ChatWidget
|
|
39
|
+
userId="user123"
|
|
40
|
+
sourceId="source456"
|
|
41
|
+
type="prime"
|
|
42
|
+
botServerUrl="https://api.example.com"
|
|
43
|
+
/>
|
|
44
|
+
|
|
45
|
+
// Previous version (with data prop)
|
|
46
|
+
<ChatWidget
|
|
47
|
+
userId="user123"
|
|
48
|
+
sourceId="source456"
|
|
49
|
+
webhookUrl="https://api.example.com/webhook"
|
|
50
|
+
data="authUrl=https://api.example.com/login/chatwidget~username=John"
|
|
51
|
+
/>
|
|
52
|
+
|
|
53
|
+
// New usage - Authenticated user (current - recommended)
|
|
54
|
+
<ChatWidget
|
|
55
|
+
userId="user123"
|
|
56
|
+
sourceId="source456"
|
|
57
|
+
webhookUrl="https://api.example.com/webhook"
|
|
58
|
+
authUrl="https://api.example.com/login/chatwidget"
|
|
59
|
+
username="John"
|
|
60
|
+
data="email=john@example.com~phone=+1234567890"
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
// New usage - Guest user (without login)
|
|
64
|
+
<ChatWidget
|
|
65
|
+
sourceId="source456"
|
|
66
|
+
webhookUrl="https://api.example.com/webhook"
|
|
67
|
+
username="Guest"
|
|
68
|
+
/>
|
|
69
|
+
// Widget automatically generates visitor ID and adds auth: false flag
|
|
70
|
+
|
|
71
|
+
// New usage - Conditional (handles both logged-in and guest users)
|
|
72
|
+
<ChatWidget
|
|
73
|
+
userId={currentUser?.id} // undefined for guests
|
|
74
|
+
sourceId="source456"
|
|
75
|
+
webhookUrl="https://api.example.com/webhook"
|
|
76
|
+
username={currentUser?.name || "Guest"}
|
|
77
|
+
/>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 🚅 Quick start
|
|
83
|
+
|
|
84
|
+
### Prerequisites
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
1. Install dependencies
|
|
89
|
+
```
|
|
90
|
+
npm install --legacy-peer-deps
|
|
91
|
+
```
|
|
92
|
+
2. Copy .env.example
|
|
93
|
+
```
|
|
94
|
+
cp .env.example .env
|
|
95
|
+
```
|
|
96
|
+
3. Populate .env
|
|
97
|
+
4. Enable mock mode (optional)
|
|
98
|
+
|
|
99
|
+
To test the chat widget without a backend server, set `VITE_USE_MOCK_RESPONSES=true` in your `.env` file. The widget will respond to messages like:
|
|
100
|
+
- "start", "hello", "hi", "halo" - Greeting messages
|
|
101
|
+
- "help", "bantuan" - Help information
|
|
102
|
+
- "terima kasih", "thank you" - Acknowledgments
|
|
103
|
+
- "bye", "goodbye" - Farewell messages
|
|
104
|
+
- And more! Check `src/components/ChatWidget/utils/helpers.js` for full list
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### Mock Mode (Demo without Backend)
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
The chat widget includes a built-in mock mode for testing and demonstrations without requiring a backend server.
|
|
113
|
+
|
|
114
|
+
**To enable mock mode:**
|
|
115
|
+
1. Set `VITE_USE_MOCK_RESPONSES=true` in your `.env` file
|
|
116
|
+
2. Run the app normally with `npm run dev`
|
|
117
|
+
|
|
118
|
+
**Mock responses include:**
|
|
119
|
+
- Greetings (hello, hi, halo, start) - with interactive buttons
|
|
120
|
+
- Help commands
|
|
121
|
+
- Device list with clickable buttons (type "devices" or "show devices")
|
|
122
|
+
- Thank you acknowledgments
|
|
123
|
+
- Farewells
|
|
124
|
+
- Time-based greetings (good morning, etc.)
|
|
125
|
+
- And automatically falls back to mock mode if backend fails
|
|
126
|
+
|
|
127
|
+
**Interactive Buttons Demo:**
|
|
128
|
+
Type these messages to see button responses:
|
|
129
|
+
- "start" - Welcome message with action buttons
|
|
130
|
+
- "show devices" or "devices" - List of devices as clickable buttons
|
|
131
|
+
- Click any button to trigger the next action
|
|
132
|
+
|
|
133
|
+
**To add custom mock responses:**
|
|
134
|
+
Edit the `mockBotResponses` object in `src/components/ChatWidget/utils/mockBotResponses.js`
|
|
135
|
+
|
|
136
|
+
For text-only responses:
|
|
137
|
+
```javascript
|
|
138
|
+
"trigger": "Response text"
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
For responses with buttons:
|
|
142
|
+
```javascript
|
|
143
|
+
"trigger": {
|
|
144
|
+
text: "Your message here",
|
|
145
|
+
buttons: [
|
|
146
|
+
{ title: "Button 1", payload: "action_1" },
|
|
147
|
+
{ title: "Button 2", payload: "action_2" },
|
|
148
|
+
{ title: "Link Button", url: "https://example.com" }
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### Storybook
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
1. **How to run Storybook locally** (access at http://localhost:5177)
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
npm run storybook
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
2. **How to build Storybook**
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
npm run build-storybook
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
3. **How to serve Storybook**
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
npm run serve-storybook
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### Library (how to update and publish)
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
1. **Commit changes**
|
|
184
|
+
```
|
|
185
|
+
git add .
|
|
186
|
+
git commit -m "Your commit message"
|
|
187
|
+
```
|
|
188
|
+
2. **Update version**
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm version patch # for bug fixes (1.0.0 -> 1.0.1)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
npm version minor # for new features (1.0.0 -> 1.1.0)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
npm version major # for breaking changes (1.0.0 -> 2.0.0)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
3. **Build as a library** (build file at `/dist` directory)
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
npm run build
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
4. **Copy declaration file to `/dist`**
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
cp ./src/declarations/index.d.ts ./dist/index.d.ts
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
5. **Publish**
|
|
215
|
+
|
|
216
|
+
```
|
|
217
|
+
npm publish
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
### Library (how to import on your project)
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
1. **Install package**
|
|
227
|
+
```bash
|
|
228
|
+
npm install @data-netmonk/mona-chat-widget
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
2. **Import styles on your `App.jsx` or `index.jsx`**
|
|
232
|
+
|
|
233
|
+
```jsx
|
|
234
|
+
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
3. **Import & use component**
|
|
238
|
+
|
|
239
|
+
```jsx
|
|
240
|
+
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
241
|
+
|
|
242
|
+
function App() {
|
|
243
|
+
return (
|
|
244
|
+
<ChatWidget
|
|
245
|
+
userId="user123"
|
|
246
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
247
|
+
webhookUrl="https://your-backend-url.com"
|
|
248
|
+
/>
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### Component Props
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
#### Required Props
|
|
260
|
+
|
|
261
|
+
| Prop | Type | Description |
|
|
262
|
+
|------|------|-------------|
|
|
263
|
+
| `sourceId` | `string` | **Required.** Source/channel identifier for the chat |
|
|
264
|
+
| `webhookUrl` | `string` | **Required.** Backend webhook URL |
|
|
265
|
+
|
|
266
|
+
#### Optional Props
|
|
267
|
+
|
|
268
|
+
| Prop | Type | Default | Description |
|
|
269
|
+
|------|------|---------|-------------|
|
|
270
|
+
| `userId` | `string` | Generated visitor ID | Unique identifier for the user. **Optional** - if not provided, a unique visitor ID is automatically generated using browser fingerprinting for guest users |
|
|
271
|
+
| `authUrl` | `string` | - | Authentication endpoint URL for token-based authentication |
|
|
272
|
+
| `username` | `string` | - | Username for the session (sent to backend in variables) |
|
|
273
|
+
| `data` | `string` | - | Additional custom variables in format `key1=value1~key2=value2` |
|
|
274
|
+
| `width` | `string` | `"25vw"` | Widget width (CSS value) |
|
|
275
|
+
| `height` | `string` | `"90vh"` | Widget height (CSS value) |
|
|
276
|
+
| `right` | `string` | `"1.25rem"` | Distance from right edge |
|
|
277
|
+
| `bottom` | `string` | `"1.25rem"` | Distance from bottom edge |
|
|
278
|
+
| `zIndex` | `number` | `2000` | CSS z-index for widget positioning |
|
|
279
|
+
| `position` | `string` | `"fixed"` | CSS position (`"fixed"` or `"relative"`) |
|
|
280
|
+
| `onToggle` | `function` | - | Callback when widget opens/closes: `(isOpen: boolean) => void` |
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
### Guest User Support
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
The widget now supports **guest users** (users who haven't logged in or before logging in). When `userId` is not provided, the widget automatically generates a unique visitor ID using browser fingerprinting.
|
|
289
|
+
|
|
290
|
+
**How it works:**
|
|
291
|
+
|
|
292
|
+
1. **Browser Fingerprinting**: Uses FingerprintJS to generate a unique identifier based on:
|
|
293
|
+
- Browser characteristics (user agent, screen resolution, timezone, etc.)
|
|
294
|
+
- Incognito/private browsing detection
|
|
295
|
+
- Combined into a SHA256 hash for consistency
|
|
296
|
+
|
|
297
|
+
2. **Automatic Guest Detection**: The widget automatically detects guest users and:
|
|
298
|
+
- Generates a visitor ID if `userId` is not provided
|
|
299
|
+
- Adds `auth: false` flag to all API requests for guest users
|
|
300
|
+
- Uses the visitor ID as the effective user ID throughout the session
|
|
301
|
+
|
|
302
|
+
3. **Persistent Sessions**: The visitor ID remains consistent across page reloads (unless browser fingerprint changes or user clears data)
|
|
303
|
+
|
|
304
|
+
**When to use guest mode:**
|
|
305
|
+
- Public-facing websites where users can chat without logging in
|
|
306
|
+
- Support widgets for anonymous visitors
|
|
307
|
+
- Pre-login customer service interactions
|
|
308
|
+
- Any scenario where user authentication is optional
|
|
309
|
+
|
|
310
|
+
**When to provide userId:**
|
|
311
|
+
- Users who are logged into your application
|
|
312
|
+
- When you need to track chat history across devices
|
|
313
|
+
- When authentication is required for personalized responses
|
|
314
|
+
- Enterprise or internal applications
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### Usage Examples
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
#### Basic Usage (Authenticated User)
|
|
323
|
+
|
|
324
|
+
```jsx
|
|
325
|
+
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
326
|
+
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
327
|
+
|
|
328
|
+
function App() {
|
|
329
|
+
return (
|
|
330
|
+
<ChatWidget
|
|
331
|
+
userId="user123"
|
|
332
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
333
|
+
webhookUrl="https://api.example.com/webhook"
|
|
334
|
+
/>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### Guest User (Without Login)
|
|
340
|
+
|
|
341
|
+
For anonymous visitors or users who haven't logged in:
|
|
342
|
+
|
|
343
|
+
```jsx
|
|
344
|
+
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
345
|
+
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
346
|
+
|
|
347
|
+
function App() {
|
|
348
|
+
return (
|
|
349
|
+
<ChatWidget
|
|
350
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
351
|
+
webhookUrl="https://api.example.com/webhook"
|
|
352
|
+
/>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**What happens:**
|
|
358
|
+
- Widget automatically generates a visitor ID using browser fingerprinting
|
|
359
|
+
- All API requests include `auth: false` in variables to indicate guest user
|
|
360
|
+
- No login required - users can start chatting immediately
|
|
361
|
+
|
|
362
|
+
#### Conditional userId (Logged in or Guest)
|
|
363
|
+
|
|
364
|
+
Handle both logged-in users and guests dynamically:
|
|
365
|
+
|
|
366
|
+
```jsx
|
|
367
|
+
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
368
|
+
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
369
|
+
|
|
370
|
+
function App() {
|
|
371
|
+
const currentUser = getCurrentUser(); // Your auth function
|
|
372
|
+
|
|
373
|
+
return (
|
|
374
|
+
<ChatWidget
|
|
375
|
+
userId={currentUser?.id} // Provide userId if logged in, undefined if not
|
|
376
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
377
|
+
webhookUrl="https://api.example.com/webhook"
|
|
378
|
+
username={currentUser?.name}
|
|
379
|
+
/>
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
**Behavior:**
|
|
385
|
+
- If `currentUser.id` exists → Uses provided userId (authenticated user)
|
|
386
|
+
- If `currentUser.id` is `null`/`undefined` → Generates visitor ID (guest user)
|
|
387
|
+
- Backend receives `auth: false` flag only for guest users
|
|
388
|
+
|
|
389
|
+
#### With Custom Variables
|
|
390
|
+
|
|
391
|
+
Pass custom user data like email, phone number, etc. to the backend:
|
|
392
|
+
|
|
393
|
+
```jsx
|
|
394
|
+
<ChatWidget
|
|
395
|
+
userId="user123"
|
|
396
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
397
|
+
webhookUrl="https://api.example.com/webhook"
|
|
398
|
+
username="John Doe"
|
|
399
|
+
data="telephone_number=+628123456789~email=john@example.com"
|
|
400
|
+
/>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Variables sent to backend:**
|
|
404
|
+
```json
|
|
405
|
+
{
|
|
406
|
+
"chat_id": "...",
|
|
407
|
+
"session_id": "...",
|
|
408
|
+
"user_id": "user123",
|
|
409
|
+
"message": "Hello",
|
|
410
|
+
"type": "text",
|
|
411
|
+
"variables": {
|
|
412
|
+
"username": "John Doe",
|
|
413
|
+
"telephone_number": "+628123456789",
|
|
414
|
+
"email": "john@example.com"
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
#### With Authentication (NEW!)
|
|
421
|
+
|
|
422
|
+
The widget supports automatic authentication with token refresh on expiry:
|
|
423
|
+
|
|
424
|
+
```jsx
|
|
425
|
+
<ChatWidget
|
|
426
|
+
userId="user123"
|
|
427
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
428
|
+
webhookUrl="https://api.example.com/webhook"
|
|
429
|
+
authUrl="https://api.example.com/login/chatwidget"
|
|
430
|
+
username="John Doe"
|
|
431
|
+
/>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**How authentication works:**
|
|
435
|
+
|
|
436
|
+
1. **Initial Authentication**: When the widget loads (on Launcher mount), if `authUrl` is provided as a prop, it automatically calls the auth API:
|
|
437
|
+
```
|
|
438
|
+
POST {authUrl}
|
|
439
|
+
Body: { "user_id": "user123" }
|
|
440
|
+
Response: { "token": "eyJ..." }
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
2. **Session Initialization**: After getting the token, the widget calls the init endpoint to establish the session:
|
|
444
|
+
```
|
|
445
|
+
POST {webhookUrl}/{sourceId}/init
|
|
446
|
+
Body: {
|
|
447
|
+
"session_id": "...",
|
|
448
|
+
"user_id": "user123",
|
|
449
|
+
"token": "eyJ...",
|
|
450
|
+
"username": "John Doe"
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
3. **Automatic Token Refresh**: If the webhook returns **401 Unauthorized** (token expired/revoked), the widget automatically:
|
|
455
|
+
- Calls the `authUrl` again to get a fresh token
|
|
456
|
+
- Re-initializes the session with the new token
|
|
457
|
+
- Updates the `authToken` in variables
|
|
458
|
+
- Retries the failed request with the new token
|
|
459
|
+
- This happens transparently without user interaction
|
|
460
|
+
|
|
461
|
+
**Combined example with auth and custom variables:**
|
|
462
|
+
```jsx
|
|
463
|
+
<ChatWidget
|
|
464
|
+
userId="user123"
|
|
465
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
466
|
+
webhookUrl="https://api.example.com/webhook"
|
|
467
|
+
authUrl="https://api.example.com/login/chatwidget"
|
|
468
|
+
username="John"
|
|
469
|
+
data="email=john@example.com~phone=+628123456789"
|
|
470
|
+
/>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Auth Flag (`auth` variable):**
|
|
474
|
+
The widget automatically adds an `auth: false` flag to the `variables` object when the user is a guest (not authenticated):
|
|
475
|
+
- When `userId === visitorId` (browser fingerprint), the widget adds `auth: false` to variables
|
|
476
|
+
- When `userId !== visitorId` (authenticated user), no `auth` flag is added to variables
|
|
477
|
+
|
|
478
|
+
**Guest user example** (userId equals browser fingerprint):
|
|
479
|
+
```json
|
|
480
|
+
{
|
|
481
|
+
"chat_id": "...",
|
|
482
|
+
"session_id": "...",
|
|
483
|
+
"user_id": "visitor_abc123",
|
|
484
|
+
"message": "Hello",
|
|
485
|
+
"type": "text",
|
|
486
|
+
"variables": {
|
|
487
|
+
"username": "Guest",
|
|
488
|
+
"auth": false
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
**Authenticated user example** (userId is different from browser fingerprint):
|
|
494
|
+
```json
|
|
495
|
+
{
|
|
496
|
+
"chat_id": "...",
|
|
497
|
+
"session_id": "...",
|
|
498
|
+
"user_id": "user123",
|
|
499
|
+
"message": "Hello",
|
|
500
|
+
"type": "text",
|
|
501
|
+
"variables": {
|
|
502
|
+
"username": "John Doe",
|
|
503
|
+
"email": "john@example.com"
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
This `auth: false` flag is automatically added in:
|
|
509
|
+
- Session initialization payload (when guest)
|
|
510
|
+
- All message requests (when guest)
|
|
511
|
+
- Button postback requests (when guest)
|
|
512
|
+
|
|
513
|
+
**Note:** The `data` prop is for additional custom variables only. Required parameters (`userId`, `sourceId`, `webhookUrl`) and common optional parameters (`authUrl`, `username`) should be passed as direct props for better type safety and clarity.
|
|
514
|
+
|
|
515
|
+
#### Custom Styling
|
|
516
|
+
|
|
517
|
+
```jsx
|
|
518
|
+
<ChatWidget
|
|
519
|
+
userId="user123" // Optional
|
|
520
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
521
|
+
webhookUrl="https://api.example.com/webhook"
|
|
522
|
+
width="400px"
|
|
523
|
+
height="600px"
|
|
524
|
+
right="20px"
|
|
525
|
+
bottom="20px"
|
|
526
|
+
zIndex={9999}
|
|
527
|
+
/>
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
#### Embedded in Layout (Relative Positioning)
|
|
531
|
+
|
|
532
|
+
```jsx
|
|
533
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 400px' }}>
|
|
534
|
+
<div>Main content</div>
|
|
535
|
+
<ChatWidget
|
|
536
|
+
userId="user123" // Optional
|
|
537
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
538
|
+
webhookUrl="https://api.example.com/webhook"
|
|
539
|
+
position="relative"
|
|
540
|
+
width="100%"
|
|
541
|
+
height="100vh"
|
|
542
|
+
/>
|
|
543
|
+
</div>
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
#### With Toggle Callback
|
|
547
|
+
|
|
548
|
+
```jsx
|
|
549
|
+
function App() {
|
|
550
|
+
const handleToggle = (isOpen) => {
|
|
551
|
+
console.log('Chat widget is', isOpen ? 'open' : 'closed');
|
|
552
|
+
// Track analytics, update UI, etc.
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
return (
|
|
556
|
+
<ChatWidget
|
|
557
|
+
userId="user123" // Optional
|
|
558
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
559
|
+
webhookUrl="https://api.example.com/webhook"
|
|
560
|
+
onToggle={handleToggle}
|
|
561
|
+
/>
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
#### Full-Screen Widget
|
|
567
|
+
|
|
568
|
+
```jsx
|
|
569
|
+
<ChatWidget
|
|
570
|
+
userId="user123" // Optional - supports guest users
|
|
571
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
572
|
+
webhookUrl="https://api.example.com/webhook"
|
|
573
|
+
width="100vw"
|
|
574
|
+
height="100vh"
|
|
575
|
+
right="0"
|
|
576
|
+
bottom="0"
|
|
577
|
+
/>
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
### Data Prop Format & Helper Function
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
The `data` prop allows you to pass additional custom variables via a single string. This is especially useful when you need to dynamically pass user-specific data or pass it through URL parameters.
|
|
587
|
+
|
|
588
|
+
**Format:** `key=value` pairs separated by `~`
|
|
589
|
+
|
|
590
|
+
**Example data string:**
|
|
591
|
+
```
|
|
592
|
+
email=john@example.com~phone=+1234567890~department=Engineering
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
**Note:** `authUrl` and `username` are now **direct props** and should not be included in the `data` string.
|
|
596
|
+
|
|
597
|
+
#### Building Data String Programmatically
|
|
598
|
+
|
|
599
|
+
Instead of manually constructing the data string, you can create a helper function to build it dynamically. This approach is recommended for production applications as it:
|
|
600
|
+
- Prevents syntax errors in the data string format
|
|
601
|
+
- Makes the code more maintainable and readable
|
|
602
|
+
- Allows for conditional inclusion of parameters
|
|
603
|
+
- Handles URL encoding automatically if needed
|
|
604
|
+
|
|
605
|
+
**Create a helper function** (`src/helpers/chatWidget.js` or similar):
|
|
606
|
+
|
|
607
|
+
```jsx
|
|
608
|
+
/**
|
|
609
|
+
* Builds the data string for ChatWidget component
|
|
610
|
+
* @param {Object} params - Object containing all parameters
|
|
611
|
+
* @param {string} params.email - Optional: User's email address
|
|
612
|
+
* @param {string} params.phone - Optional: User's phone number
|
|
613
|
+
* @param {Object} params.customFields - Optional: Any additional custom fields as key-value pairs
|
|
614
|
+
* @returns {string} Formatted data string for ChatWidget (always returns a string)
|
|
615
|
+
*
|
|
616
|
+
* @example
|
|
617
|
+
* // Returns: "email=john@example.com~phone=+1234567890~department=Engineering"
|
|
618
|
+
* buildChatWidgetData({
|
|
619
|
+
* email: "john@example.com",
|
|
620
|
+
* phone: "+1234567890",
|
|
621
|
+
* customFields: { department: "Engineering" }
|
|
622
|
+
* });
|
|
623
|
+
*/
|
|
624
|
+
export const buildChatWidgetData = ({
|
|
625
|
+
email,
|
|
626
|
+
phone,
|
|
627
|
+
customFields = {}
|
|
628
|
+
}) => {
|
|
629
|
+
const parts = [];
|
|
630
|
+
|
|
631
|
+
// Add standard fields
|
|
632
|
+
if (email) {
|
|
633
|
+
parts.push(`email=${encodeURIComponent(email)}`);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (phone) {
|
|
637
|
+
parts.push(`phone=${encodeURIComponent(phone)}`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// Add any custom fields dynamically
|
|
641
|
+
// Note: customFields is just a convenience parameter for the helper function
|
|
642
|
+
// Each field will be flattened into the string format: key=value~key2=value2
|
|
643
|
+
Object.entries(customFields).forEach(([key, value]) => {
|
|
644
|
+
if (value) {
|
|
645
|
+
parts.push(`${key}=${encodeURIComponent(String(value))}`);
|
|
646
|
+
}
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// Returns a string like: "email=john@example.com~phone=+1234567890~department=Engineering"
|
|
650
|
+
return parts.join("~");
|
|
651
|
+
};
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
**Important:** The `customFields` parameter is **NOT** passed as an object to the widget. It's just a convenient way to pass multiple additional fields to the helper function. The function flattens everything into a single string format.
|
|
655
|
+
|
|
656
|
+
**Usage in your application:**
|
|
657
|
+
|
|
658
|
+
```jsx
|
|
659
|
+
import { ChatWidget } from "@data-netmonk/mona-chat-widget";
|
|
660
|
+
import "@data-netmonk/mona-chat-widget/dist/style.css";
|
|
661
|
+
import { buildChatWidgetData } from "./helpers/chatWidget";
|
|
662
|
+
|
|
663
|
+
function App() {
|
|
664
|
+
// Get user data from your auth system, state, or props
|
|
665
|
+
const user = {
|
|
666
|
+
id: "user123",
|
|
667
|
+
name: "John Doe",
|
|
668
|
+
email: "john@example.com",
|
|
669
|
+
phone: "+628123456789"
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// Build the data string with custom variables only
|
|
673
|
+
const customData = buildChatWidgetData({
|
|
674
|
+
email: user.email,
|
|
675
|
+
phone: user.phone,
|
|
676
|
+
customFields: {
|
|
677
|
+
department: "Engineering",
|
|
678
|
+
role: "Developer",
|
|
679
|
+
company: "Acme Corp"
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
// customData is now a STRING like:
|
|
684
|
+
// "email=john@example.com~phone=%2B628123456789~department=Engineering~role=Developer~company=Acme%20Corp"
|
|
685
|
+
|
|
686
|
+
return (
|
|
687
|
+
<ChatWidget
|
|
688
|
+
userId={user.id}
|
|
689
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
690
|
+
webhookUrl="https://api.example.com/webhook"
|
|
691
|
+
authUrl="https://api.example.com/login/chatwidget" // Direct prop
|
|
692
|
+
username={user.name} // Direct prop
|
|
693
|
+
data={customData} // Only additional variables
|
|
694
|
+
/>
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
**With conditional authentication:**
|
|
700
|
+
|
|
701
|
+
```jsx
|
|
702
|
+
function App() {
|
|
703
|
+
const authUrl = import.meta.env.VITE_CHAT_AUTH_ENABLED === "true"
|
|
704
|
+
? import.meta.env.VITE_CHAT_AUTH_URL
|
|
705
|
+
: null;
|
|
706
|
+
|
|
707
|
+
const customData = buildChatWidgetData({
|
|
708
|
+
email: getCurrentUser().email,
|
|
709
|
+
customFields: {
|
|
710
|
+
department: "Sales"
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
return (
|
|
715
|
+
<ChatWidget
|
|
716
|
+
userId={getCurrentUser().id}
|
|
717
|
+
sourceId="691e1b5952068ff7aaeccffc9"
|
|
718
|
+
webhookUrl={import.meta.env.VITE_WEBHOOK_URL}
|
|
719
|
+
authUrl={authUrl} // Direct prop (conditionally set)
|
|
720
|
+
username={getCurrentUser().name} // Direct prop
|
|
721
|
+
data={customData} // Only additional variables
|
|
722
|
+
/>
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
**The helper function handles:**
|
|
728
|
+
- ✅ Proper formatting with `~` separators
|
|
729
|
+
- ✅ URL encoding for special characters
|
|
730
|
+
- ✅ Conditional parameter inclusion (only adds if value exists)
|
|
731
|
+
- ✅ Support for dynamic custom fields
|
|
732
|
+
- ✅ Type safety and documentation via JSDoc comments
|
|
733
|
+
|
|
734
|
+
### Standalone app (for demonstration)
|
|
735
|
+
|
|
736
|
+
1. **How to run locally** (access at `http://localhost:${PORT}/${APP_PREFIX}`)
|
|
737
|
+
|
|
738
|
+
```
|
|
739
|
+
npm run dev
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
2. **How to build as a standalone app** (build file at `/dist-app` directory)
|
|
743
|
+
|
|
744
|
+
```
|
|
745
|
+
npm run build-app
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
3. **How to serve standalone app** (access at `http://localhost:${PORT}/${APP_PREFIX}`)
|
|
749
|
+
|
|
750
|
+
```
|
|
751
|
+
npm run serve
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
4. **How to run on docker** (access at `http://localhost:${PORT}/${APP_PREFIX}`)
|
|
755
|
+
|
|
756
|
+
```
|
|
757
|
+
docker-compose up --build
|
|
758
|
+
```
|
|
759
|
+
|
|
760
|
+
---
|