@developer.notchatbot/webchat 1.5.3 → 1.7.1

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
@@ -44,6 +44,8 @@ interface WebChatConfig {
44
44
  active?: boolean; // Enable or disable the button (default: true)
45
45
  color?: string; // Customize the internal WhatsApp button color
46
46
  };
47
+ apiBaseUrl?: string; // Backend override (see "Targeting a non-production backend")
48
+ realtimeUrl?: string; // Convex deployment override (same section)
47
49
  }
48
50
 
49
51
  interface WebChatPositionConfig {
@@ -85,6 +87,33 @@ WebChat.initialize({
85
87
  });
86
88
  ```
87
89
 
90
+ ## 🎯 Targeting a non-production backend
91
+
92
+ By default the widget picks its backend from the hostname of the page it runs
93
+ on: `localhost` → `http://localhost:3000`, anything else → production
94
+ (`https://next.notchatbot.com`). That default is right for a merchant's
95
+ storefront, but wrong wherever the page itself is a non-production deployment —
96
+ a Vercel preview, `dev.notchatbot.com`, or a local dev server on a port other
97
+ than 3000. Without an override, a message typed on a preview lands in the
98
+ **production** inbox.
99
+
100
+ `apiBaseUrl` and `realtimeUrl` override that detection:
101
+
102
+ ```js
103
+ WebChat.initialize({
104
+ apiKey: "your-chatbot-uid",
105
+ apiBaseUrl: window.location.origin, // HTTP: /api/webchat, /upload, /rate
106
+ realtimeUrl: "https://<deployment>.convex.cloud" // Convex realtime subscriptions
107
+ });
108
+ ```
109
+
110
+ - Both must be `https://` (or `http://localhost` / `http://127.0.0.1`); anything
111
+ else is ignored with a console warning and the defaults are kept.
112
+ - **Set them as a pair.** `apiBaseUrl` alone writes to one deployment while the
113
+ widget keeps listening on the default Convex, so agent replies never arrive.
114
+ - Omitting them keeps the previous behaviour exactly — existing embeds are
115
+ unaffected, and each `initialize()` clears any override from a prior call.
116
+
88
117
  # 💬 WhatsApp Button
89
118
 
90
119
  If you simply want a floating WhatsApp action button with no WebChat, you can use `WhatsAppButtonAPI`, which isolates itself in the Shadow DOM to avoid CSS conflicts.
@@ -5,6 +5,15 @@ export interface EndpointConfig {
5
5
  rateEndpoint: string;
6
6
  realtimeUrl: string;
7
7
  }
8
+ /**
9
+ * Replaces the endpoint overrides. Called on every `initialize()`, so a config
10
+ * without these keys clears any previous override. Invalid URLs are ignored
11
+ * with a warning and the environment defaults are kept.
12
+ */
13
+ export declare function setEndpointOverrides(overrides: {
14
+ apiBaseUrl?: string;
15
+ realtimeUrl?: string;
16
+ }): void;
8
17
  /**
9
18
  * Detecta si estamos en ambiente de desarrollo
10
19
  */
package/dist/types.d.ts CHANGED
@@ -25,6 +25,22 @@ export interface WebChatConfig {
25
25
  whatsapp?: Omit<WhatsAppConfig, 'position' | 'marginBottom' | 'marginSide' | 'mobile' | 'desktop'> & {
26
26
  active?: boolean;
27
27
  };
28
+ /**
29
+ * Optional backend base URL override (e.g. a Vercel preview deployment or
30
+ * dev.notchatbot.com). `/api/webchat`, `/api/webchat/upload` and
31
+ * `/api/webchat/conversation/rate` are derived from it. Must be https://
32
+ * (or http://localhost). Omitted or invalid → the environment defaults.
33
+ */
34
+ apiBaseUrl?: string;
35
+ /**
36
+ * Optional Convex deployment URL override for the realtime subscriptions
37
+ * (agent replies, history, activation). Must be https:// (or
38
+ * http://localhost). Omitted or invalid → the environment default.
39
+ *
40
+ * Pair it with `apiBaseUrl`: pointing HTTP at one deployment and realtime at
41
+ * another means the widget writes to one database and listens on another.
42
+ */
43
+ realtimeUrl?: string;
28
44
  /**
29
45
  * Optional GTM/GA event name overrides for dataLayer pushes.
30
46
  * If omitted, defaults are used.
@@ -142,6 +158,15 @@ export interface ChatWindowProps {
142
158
  isRated?: boolean;
143
159
  onRatingChange?: (isRated: boolean) => void;
144
160
  conversationId?: string;
161
+ /** Configurable auto-survey threshold from the backend; undefined = legacy default (2). */
162
+ surveyAfterUserMessages?: number;
163
+ /** Timestamp of the last manual survey request from Livechat; undefined = never requested. */
164
+ webchatSurveyRequestedAt?: number;
165
+ }
166
+ /** Survey-related conversation state forwarded from the Convex realtime subscription. */
167
+ export interface ConversationSurveyState {
168
+ surveyAfterUserMessages?: number;
169
+ webchatSurveyRequestedAt?: number;
145
170
  }
146
171
  export interface ChatHeaderProps {
147
172
  title: string;
@@ -195,8 +220,16 @@ export interface WebChatInstance {
195
220
  setIsOpen?: (open: boolean) => void;
196
221
  updateConfig?: (newConfig: Partial<WebChatConfig>) => void;
197
222
  setPosition?: (pos: Partial<WebChatConfig>) => void;
223
+ getIsOpen?: () => boolean;
198
224
  hide?: () => void;
199
225
  show?: () => void;
226
+ /**
227
+ * Starts a new conversation in place: new conversation id, empty transcript,
228
+ * fresh visitor session, realtime subscriptions reopened. Unlike calling
229
+ * `initialize()` again it does not destroy and rebuild the widget, so the
230
+ * panel does not blink out while the new thread is prepared.
231
+ */
232
+ restartConversation?: () => void;
200
233
  injectCSS?: (css: string) => void;
201
234
  removeCustomCSS?: () => void;
202
235
  }
@@ -1,5 +1,5 @@
1
1
  import { ConvexReactClient } from 'convex/react';
2
- import { Message } from '../types';
2
+ import { ConversationSurveyState, Message } from '../types';
3
3
  export interface ConvexConnectionData {
4
4
  client: ConvexReactClient;
5
5
  unsubscribe: (() => void) | null;
@@ -11,9 +11,10 @@ export interface ConvexConnectionData {
11
11
  * @param conversationId Conversation ID
12
12
  * @param onMessage Callback for new messages
13
13
  * @param onChatbotToggle Callback for chatbot activation changes
14
+ * @param onSurveyStateChange Callback for survey state changes (threshold + manual trigger)
14
15
  * @returns Promise that resolves to Convex connection data
15
16
  */
16
- export declare const initializeConvexConnection: (realtimeEndpoint: string, _chatbotUid: string, conversationId: string, onMessage: (message: Message) => void, onChatbotToggle?: (chatbotActivated: boolean) => void) => Promise<ConvexConnectionData>;
17
+ export declare const initializeConvexConnection: (realtimeEndpoint: string, _chatbotUid: string, conversationId: string, onMessage: (message: Message) => void, onChatbotToggle?: (chatbotActivated: boolean) => void, onSurveyStateChange?: (surveyState: ConversationSurveyState) => void) => Promise<ConvexConnectionData>;
17
18
  /**
18
19
  * Fetches initial conversation data with messages from Convex
19
20
  * @param realtimeEndpoint Realtime service deployment URL
@@ -0,0 +1,30 @@
1
+ import { Message } from '../types';
2
+ /** Legacy hardcoded threshold, used when the backend does not send one. */
3
+ export declare const DEFAULT_SURVEY_AFTER_USER_MESSAGES = 2;
4
+ /**
5
+ * Resolves the configured auto-survey threshold.
6
+ * Falls back to the default when the backend does not expose the field yet
7
+ * or sends something unusable (non-number, NaN, zero/negative).
8
+ */
9
+ export declare const resolveSurveyThreshold: (value: unknown) => number;
10
+ /** Counts only messages sent by the end user (bot/employee ones don't count). */
11
+ export declare const countUserMessages: (messages: Message[]) => number;
12
+ /**
13
+ * Whether the survey should auto-open because the user reached the
14
+ * configured message threshold. Never true for already rated conversations.
15
+ */
16
+ export declare const shouldAutoShowSurvey: (messages: Message[], surveyAfterUserMessages: number | undefined, isRated: boolean) => boolean;
17
+ /**
18
+ * Whether a manual survey request (`webchatSurveyRequestedAt`, a timestamp
19
+ * written by an agent from Livechat) is a new, still unhandled trigger.
20
+ * Comparing against the last handled value — not just presence — keeps
21
+ * re-renders and Convex reconnections from re-opening the survey for the
22
+ * same request, while a fresh timestamp triggers it again.
23
+ *
24
+ * Deliberately NOT gated on `isRated`: a manual request fired after a rating
25
+ * re-opens the survey so the agent can re-validate the experience (the new
26
+ * rating overwrites the previous one server-side). Stale requests can't leak
27
+ * across reloads because the backend clears `webchatSurveyRequestedAt` when a
28
+ * rating is submitted — a present timestamp is always a pending ask.
29
+ */
30
+ export declare const isNewSurveyRequest: (requestedAt: number | undefined, lastHandledRequestedAt: number | undefined) => boolean;