@m13v/seo-components 0.40.0 → 0.41.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/package.json
CHANGED
|
@@ -24,6 +24,11 @@
|
|
|
24
24
|
* greeting initial system bubble copy
|
|
25
25
|
* replyEta "usually within 2 hours" line under greeting
|
|
26
26
|
* accent tailwind color name for the bubble (default: "teal")
|
|
27
|
+
* userEmail pre-fill from consumer auth (Firebase/Clerk/NextAuth) so
|
|
28
|
+
* authed users don't get re-asked for their email. Falls back
|
|
29
|
+
* to the built-in prompt for anonymous users (undefined / "").
|
|
30
|
+
* userName pre-fill display name; used as sender_name on outgoing
|
|
31
|
+
* messages and forwarded to backend for personalized replies.
|
|
27
32
|
*/
|
|
28
33
|
|
|
29
34
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
@@ -56,6 +61,23 @@ export interface FounderChatPanelProps {
|
|
|
56
61
|
replyEta?: string;
|
|
57
62
|
/** Whether to require email before allowing the first message (default: true). */
|
|
58
63
|
requireEmail?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Optional: pre-fill the visitor's email from the consumer app's auth system,
|
|
66
|
+
* so already-authenticated users (Firebase, Clerk, NextAuth, etc.) don't get
|
|
67
|
+
* asked again. When set to a non-empty value, the email gate is skipped on
|
|
68
|
+
* mount and the value is also persisted to localStorage so subsequent
|
|
69
|
+
* anonymous sessions on the same device reuse it.
|
|
70
|
+
*
|
|
71
|
+
* Pass undefined / empty string for anonymous users, the panel will fall back
|
|
72
|
+
* to its built-in email prompt.
|
|
73
|
+
*/
|
|
74
|
+
userEmail?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Optional: pre-fill the visitor's display name. Used as the optimistic
|
|
77
|
+
* "sender_name" on outgoing messages and forwarded to the backend so founder
|
|
78
|
+
* replies can address the user by name.
|
|
79
|
+
*/
|
|
80
|
+
userName?: string;
|
|
59
81
|
}
|
|
60
82
|
|
|
61
83
|
function capture(event: string, props?: Record<string, unknown>) {
|
|
@@ -116,6 +138,8 @@ export function FounderChatPanel({
|
|
|
116
138
|
greeting,
|
|
117
139
|
replyEta = "usually replies within a couple hours",
|
|
118
140
|
requireEmail = true,
|
|
141
|
+
userEmail,
|
|
142
|
+
userName,
|
|
119
143
|
}: FounderChatPanelProps) {
|
|
120
144
|
const [open, setOpen] = useState(false);
|
|
121
145
|
const [messages, setMessages] = useState<ApiMessage[]>([]);
|
|
@@ -141,6 +165,19 @@ export function FounderChatPanel({
|
|
|
141
165
|
}
|
|
142
166
|
}, [project]);
|
|
143
167
|
|
|
168
|
+
// Prefer the consumer-supplied authed email over the localStorage one, and
|
|
169
|
+
// re-sync whenever the prop changes (e.g. the user signs in via Google
|
|
170
|
+
// popup after the panel was already mounted on the page).
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
if (!userEmail) return;
|
|
173
|
+
const trimmed = userEmail.trim();
|
|
174
|
+
if (!trimmed) return;
|
|
175
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) return;
|
|
176
|
+
setEmail(trimmed);
|
|
177
|
+
setEmailSubmitted(true);
|
|
178
|
+
setStoredEmail(trimmed);
|
|
179
|
+
}, [userEmail]);
|
|
180
|
+
|
|
144
181
|
// Auto-scroll to bottom on new messages.
|
|
145
182
|
useEffect(() => {
|
|
146
183
|
if (!scrollRef.current) return;
|
|
@@ -188,7 +225,7 @@ export function FounderChatPanel({
|
|
|
188
225
|
const optimistic: ApiMessage = {
|
|
189
226
|
id: -Date.now(),
|
|
190
227
|
sender: "visitor",
|
|
191
|
-
sender_name: email || "you",
|
|
228
|
+
sender_name: userName || email || "you",
|
|
192
229
|
text,
|
|
193
230
|
createdAt: new Date().toISOString(),
|
|
194
231
|
};
|
|
@@ -202,6 +239,10 @@ export function FounderChatPanel({
|
|
|
202
239
|
threadId: threadId || undefined,
|
|
203
240
|
text,
|
|
204
241
|
email: email || undefined,
|
|
242
|
+
// Forwarded to the backend so the visitor row gets a real display
|
|
243
|
+
// name (web_chat_threads.visitor_name); the route handler reads this
|
|
244
|
+
// as `name` per /api/web-chat/send/route.ts.
|
|
245
|
+
name: userName || undefined,
|
|
205
246
|
pageUrl: typeof window !== "undefined" ? window.location.href : undefined,
|
|
206
247
|
referrer: typeof document !== "undefined" ? document.referrer || undefined : undefined,
|
|
207
248
|
};
|
|
@@ -229,7 +270,7 @@ export function FounderChatPanel({
|
|
|
229
270
|
} finally {
|
|
230
271
|
setSending(false);
|
|
231
272
|
}
|
|
232
|
-
}, [apiOrigin, draft, email, emailSubmitted, fetchThread, project, requireEmail, sending, threadId]);
|
|
273
|
+
}, [apiOrigin, draft, email, emailSubmitted, fetchThread, project, requireEmail, sending, threadId, userName]);
|
|
233
274
|
|
|
234
275
|
const onSubmitEmail = useCallback(() => {
|
|
235
276
|
const trimmed = email.trim();
|