wealth-alpha-chat-widget 1.0.1 → 1.0.3
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/dist/index.cjs +294 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1478 -152
- package/dist/index.css.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.mjs +295 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/docs/BACKEND_CHAT_WIDGET.md +0 -357
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useState, useRef, useEffect, useCallback, useReducer, useMemo } from 'react';
|
|
3
|
-
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import DOMPurify from 'isomorphic-dompurify';
|
|
5
5
|
|
|
6
6
|
// src/components/WealthChat.tsx
|
|
@@ -278,6 +278,50 @@ async function sendMessage(apiBase, message, sessionId, context, signal) {
|
|
|
278
278
|
signal
|
|
279
279
|
});
|
|
280
280
|
}
|
|
281
|
+
async function uploadPortfolioCsv(apiBase, file, sessionId, signal) {
|
|
282
|
+
const url = joinUrl(apiBase, "/upload-portfolio-csv");
|
|
283
|
+
const requestId = generateRequestId();
|
|
284
|
+
const session = readSession();
|
|
285
|
+
const form = new FormData();
|
|
286
|
+
form.append("sessionId", sessionId);
|
|
287
|
+
form.append("file", file, file.name);
|
|
288
|
+
const controller = new AbortController();
|
|
289
|
+
const timeoutId = setTimeout(() => controller.abort(), 6e4);
|
|
290
|
+
if (signal) {
|
|
291
|
+
if (signal.aborted) {
|
|
292
|
+
clearTimeout(timeoutId);
|
|
293
|
+
throw new DOMException("Aborted", "AbortError");
|
|
294
|
+
}
|
|
295
|
+
signal.addEventListener("abort", () => controller.abort(), { once: true });
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
const headers = { "X-Request-Id": requestId };
|
|
299
|
+
if (session?.token) headers["Authorization"] = `Bearer ${session.token}`;
|
|
300
|
+
const res = await fetch(url, {
|
|
301
|
+
method: "POST",
|
|
302
|
+
headers,
|
|
303
|
+
body: form,
|
|
304
|
+
signal: controller.signal,
|
|
305
|
+
credentials: "same-origin"
|
|
306
|
+
});
|
|
307
|
+
if (res.status === 401 || res.status === 403) {
|
|
308
|
+
clearSession();
|
|
309
|
+
throw new AuthExpiredError(requestId);
|
|
310
|
+
}
|
|
311
|
+
if (!res.ok) {
|
|
312
|
+
let detail = res.statusText;
|
|
313
|
+
try {
|
|
314
|
+
const errBody = await res.json();
|
|
315
|
+
detail = errBody.message ?? errBody.detail ?? detail;
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
throw new ApiError(detail || `HTTP ${res.status}`, res.status, requestId);
|
|
319
|
+
}
|
|
320
|
+
return await res.json();
|
|
321
|
+
} finally {
|
|
322
|
+
clearTimeout(timeoutId);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
281
325
|
async function logout(apiBase, signal) {
|
|
282
326
|
try {
|
|
283
327
|
await request(apiBase, "/auth/logout", {
|
|
@@ -424,6 +468,37 @@ function useChat(opts) {
|
|
|
424
468
|
const deactivatePriorChips = useCallback((keepId) => {
|
|
425
469
|
dispatch({ type: "DEACTIVATE_CHIPS", payload: { exceptId: keepId } });
|
|
426
470
|
}, []);
|
|
471
|
+
const uploadCsv = useCallback(
|
|
472
|
+
async (file) => {
|
|
473
|
+
if (!sessionId) return;
|
|
474
|
+
abortRef.current?.abort();
|
|
475
|
+
const controller = new AbortController();
|
|
476
|
+
abortRef.current = controller;
|
|
477
|
+
deactivatePriorChips();
|
|
478
|
+
appendUserMessage(`Uploaded ${file.name}`);
|
|
479
|
+
dispatch({ type: "SET_TYPING", payload: true });
|
|
480
|
+
dispatch({ type: "SET_STATUS", payload: "sending" });
|
|
481
|
+
dispatch({ type: "SET_ERROR", payload: null });
|
|
482
|
+
try {
|
|
483
|
+
const resp = await uploadPortfolioCsv(apiBase, file, sessionId, controller.signal);
|
|
484
|
+
appendBotResponse(resp);
|
|
485
|
+
dispatch({ type: "SET_STATUS", payload: "idle" });
|
|
486
|
+
} catch (err) {
|
|
487
|
+
const error = err;
|
|
488
|
+
if (error.name === "AbortError") return;
|
|
489
|
+
if (error instanceof AuthExpiredError) {
|
|
490
|
+
onAuthExpiredRef.current?.();
|
|
491
|
+
} else {
|
|
492
|
+
onErrorRef.current?.(error);
|
|
493
|
+
dispatch({ type: "SET_ERROR", payload: error.message });
|
|
494
|
+
}
|
|
495
|
+
dispatch({ type: "SET_STATUS", payload: "error" });
|
|
496
|
+
} finally {
|
|
497
|
+
dispatch({ type: "SET_TYPING", payload: false });
|
|
498
|
+
}
|
|
499
|
+
},
|
|
500
|
+
[apiBase, sessionId, appendUserMessage, appendBotResponse, deactivatePriorChips]
|
|
501
|
+
);
|
|
427
502
|
const sendText = useCallback(
|
|
428
503
|
async (text) => {
|
|
429
504
|
const trimmed = text.trim();
|
|
@@ -470,6 +545,7 @@ function useChat(opts) {
|
|
|
470
545
|
return {
|
|
471
546
|
state,
|
|
472
547
|
sendText,
|
|
548
|
+
uploadCsv,
|
|
473
549
|
appendBotResponse,
|
|
474
550
|
appendUserMessage,
|
|
475
551
|
deactivatePriorChips,
|
|
@@ -605,6 +681,8 @@ var chat_default = {
|
|
|
605
681
|
positionRight: "chat_positionRight",
|
|
606
682
|
positionLeft: "chat_positionLeft",
|
|
607
683
|
widget: "chat_widget",
|
|
684
|
+
popupBubbleLeft: "chat_popupBubbleLeft",
|
|
685
|
+
popupBubbleRight: "chat_popupBubbleRight",
|
|
608
686
|
header: "chat_header",
|
|
609
687
|
headerTitle: "chat_headerTitle",
|
|
610
688
|
headerMeta: "chat_headerMeta",
|
|
@@ -615,15 +693,19 @@ var chat_default = {
|
|
|
615
693
|
markdown: "chat_markdown",
|
|
616
694
|
bubbleBot: "chat_bubbleBot",
|
|
617
695
|
bubbleUser: "chat_bubbleUser",
|
|
696
|
+
bubbleMeta: "chat_bubbleMeta",
|
|
618
697
|
chipRow: "chat_chipRow",
|
|
619
698
|
chip: "chat_chip",
|
|
620
699
|
chipActive: "chat_chipActive",
|
|
621
700
|
chipDisabled: "chat_chipDisabled",
|
|
622
701
|
typing: "chat_typing",
|
|
623
702
|
typingDot: "chat_typingDot",
|
|
703
|
+
wacBlink: "chat_wacBlink",
|
|
624
704
|
input: "chat_input",
|
|
625
705
|
inputBox: "chat_inputBox",
|
|
626
706
|
sendButton: "chat_sendButton",
|
|
707
|
+
hiddenFileInput: "chat_hiddenFileInput",
|
|
708
|
+
csvButton: "chat_csvButton",
|
|
627
709
|
authGate: "chat_authGate",
|
|
628
710
|
authGateIcon: "chat_authGateIcon",
|
|
629
711
|
authGateTitle: "chat_authGateTitle",
|
|
@@ -631,7 +713,23 @@ var chat_default = {
|
|
|
631
713
|
authGateButton: "chat_authGateButton",
|
|
632
714
|
authGateDisclaimer: "chat_authGateDisclaimer",
|
|
633
715
|
authGateDisclaimerTitle: "chat_authGateDisclaimerTitle",
|
|
634
|
-
errorBanner: "chat_errorBanner"
|
|
716
|
+
errorBanner: "chat_errorBanner",
|
|
717
|
+
popupBubble: "chat_popupBubble",
|
|
718
|
+
wacPopIn: "chat_wacPopIn",
|
|
719
|
+
popupDismiss: "chat_popupDismiss",
|
|
720
|
+
menuGrid: "chat_menuGrid",
|
|
721
|
+
menuItem: "chat_menuItem",
|
|
722
|
+
menuItemIcon: "chat_menuItemIcon",
|
|
723
|
+
menuItemTitle: "chat_menuItemTitle",
|
|
724
|
+
menuItemSub: "chat_menuItemSub",
|
|
725
|
+
menuIconGreen: "chat_menuIconGreen",
|
|
726
|
+
menuIconBlue: "chat_menuIconBlue",
|
|
727
|
+
menuIconLeaf: "chat_menuIconLeaf",
|
|
728
|
+
menuIconRed: "chat_menuIconRed",
|
|
729
|
+
menuIconOrange: "chat_menuIconOrange",
|
|
730
|
+
menuIconGold: "chat_menuIconGold",
|
|
731
|
+
menuIconTeal: "chat_menuIconTeal",
|
|
732
|
+
menuIconPurple: "chat_menuIconPurple"
|
|
635
733
|
};
|
|
636
734
|
function AuthGate({ brandName, loginUrl, onLoginClick }) {
|
|
637
735
|
const handleClick = () => {
|
|
@@ -703,9 +801,10 @@ var ALLOWED_TAGS = [
|
|
|
703
801
|
"ol",
|
|
704
802
|
"li",
|
|
705
803
|
"blockquote",
|
|
706
|
-
"span"
|
|
804
|
+
"span",
|
|
805
|
+
"div"
|
|
707
806
|
];
|
|
708
|
-
var ALLOWED_ATTR = ["href", "target", "rel", "class"];
|
|
807
|
+
var ALLOWED_ATTR = ["href", "target", "rel", "class", "style", "data-wa-gauge-pct"];
|
|
709
808
|
var SANITIZE_CFG = {
|
|
710
809
|
ALLOWED_TAGS,
|
|
711
810
|
ALLOWED_ATTR,
|
|
@@ -719,25 +818,87 @@ function inlineMarkdownToHtml(text) {
|
|
|
719
818
|
function renderMarkdown(text) {
|
|
720
819
|
if (!text) return "";
|
|
721
820
|
const inlined = inlineMarkdownToHtml(text);
|
|
821
|
+
if (/<div[\s>]/i.test(inlined)) {
|
|
822
|
+
return DOMPurify.sanitize(inlined, SANITIZE_CFG);
|
|
823
|
+
}
|
|
722
824
|
const paragraphs = inlined.split(/\n{2,}/).map((para) => para.replace(/\n/g, "<br>")).filter((p) => p.length > 0);
|
|
723
825
|
const html = paragraphs.length > 1 ? paragraphs.map((p) => `<p>${p}</p>`).join("") : paragraphs[0] ?? "";
|
|
724
826
|
return DOMPurify.sanitize(html, SANITIZE_CFG);
|
|
725
827
|
}
|
|
828
|
+
var ROOT_MENU_CHIP_IDS = /* @__PURE__ */ new Set([
|
|
829
|
+
"stock_analysis",
|
|
830
|
+
"stock_discovery",
|
|
831
|
+
"new_listings",
|
|
832
|
+
"portfolio_risk",
|
|
833
|
+
"market_forecast",
|
|
834
|
+
"crypto",
|
|
835
|
+
"tradable_picks",
|
|
836
|
+
"peer_compare"
|
|
837
|
+
]);
|
|
838
|
+
var MENU_SUBTITLES = {
|
|
839
|
+
stock_analysis: "Fundamental & technical",
|
|
840
|
+
stock_discovery: "Find promising stocks",
|
|
841
|
+
new_listings: "Track IPOs & new stocks",
|
|
842
|
+
portfolio_risk: "Analyze your portfolio",
|
|
843
|
+
market_forecast: "Outlook & key events",
|
|
844
|
+
crypto: "BTC, ETH & trends",
|
|
845
|
+
tradable_picks: "Short-term setups",
|
|
846
|
+
peer_compare: "Compare peer stocks"
|
|
847
|
+
};
|
|
848
|
+
var MENU_ICON_KEY = {
|
|
849
|
+
stock_analysis: "menuIconGreen",
|
|
850
|
+
stock_discovery: "menuIconBlue",
|
|
851
|
+
new_listings: "menuIconLeaf",
|
|
852
|
+
portfolio_risk: "menuIconRed",
|
|
853
|
+
market_forecast: "menuIconOrange",
|
|
854
|
+
crypto: "menuIconGold",
|
|
855
|
+
tradable_picks: "menuIconTeal",
|
|
856
|
+
peer_compare: "menuIconPurple"
|
|
857
|
+
};
|
|
858
|
+
function isRootMenuChips(chips) {
|
|
859
|
+
return chips.some((c) => ROOT_MENU_CHIP_IDS.has(c.id));
|
|
860
|
+
}
|
|
726
861
|
function MessageBubble({ message, onChipClick }) {
|
|
727
862
|
const isBot = message.role === "bot";
|
|
728
863
|
const bubbleClass = isBot ? `${chat_default.bubble} ${chat_default.bubbleBot}` : `${chat_default.bubble} ${chat_default.bubbleUser}`;
|
|
864
|
+
const handleMarkdownClick = (e) => {
|
|
865
|
+
const anchor = e.target.closest("a");
|
|
866
|
+
if (!anchor?.href) return;
|
|
867
|
+
e.preventDefault();
|
|
868
|
+
window.location.assign(anchor.href);
|
|
869
|
+
};
|
|
870
|
+
const chips = message.chips ?? [];
|
|
871
|
+
const showMenuGrid = isBot && message.chipsActive && isRootMenuChips(chips);
|
|
729
872
|
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column" }, children: [
|
|
730
873
|
/* @__PURE__ */ jsx("div", { className: bubbleClass, children: isBot ? /* @__PURE__ */ jsx(
|
|
731
874
|
"div",
|
|
732
875
|
{
|
|
733
876
|
className: chat_default.markdown,
|
|
734
|
-
dangerouslySetInnerHTML: { __html: renderMarkdown(message.content) }
|
|
877
|
+
dangerouslySetInnerHTML: { __html: renderMarkdown(message.content) },
|
|
878
|
+
onClick: handleMarkdownClick
|
|
735
879
|
}
|
|
736
880
|
) : message.content }),
|
|
737
|
-
|
|
881
|
+
showMenuGrid ? /* @__PURE__ */ jsx("div", { className: chat_default.menuGrid, children: chips.map((chip) => {
|
|
882
|
+
const iconKey = MENU_ICON_KEY[chip.id];
|
|
883
|
+
const iconClass = iconKey && chat_default[iconKey] || "";
|
|
884
|
+
return /* @__PURE__ */ jsxs(
|
|
885
|
+
"button",
|
|
886
|
+
{
|
|
887
|
+
type: "button",
|
|
888
|
+
className: chat_default.menuItem,
|
|
889
|
+
onClick: () => onChipClick(chip),
|
|
890
|
+
children: [
|
|
891
|
+
/* @__PURE__ */ jsx("div", { className: `${chat_default.menuItemIcon} ${iconClass}`, children: chip.icon }),
|
|
892
|
+
/* @__PURE__ */ jsx("div", { className: chat_default.menuItemTitle, children: chip.label }),
|
|
893
|
+
/* @__PURE__ */ jsx("div", { className: chat_default.menuItemSub, children: MENU_SUBTITLES[chip.id] ?? "" })
|
|
894
|
+
]
|
|
895
|
+
},
|
|
896
|
+
chip.id
|
|
897
|
+
);
|
|
898
|
+
}) }) : isBot && chips.length > 0 ? /* @__PURE__ */ jsx(
|
|
738
899
|
ChipRow,
|
|
739
900
|
{
|
|
740
|
-
chips
|
|
901
|
+
chips,
|
|
741
902
|
disabled: !message.chipsActive,
|
|
742
903
|
onClick: onChipClick
|
|
743
904
|
}
|
|
@@ -841,16 +1002,24 @@ function ChatHeader({
|
|
|
841
1002
|
}
|
|
842
1003
|
function ChatInput({ disabled, placeholder, onSend }) {
|
|
843
1004
|
const [value, setValue] = useState("");
|
|
1005
|
+
const inputRef = useRef(null);
|
|
1006
|
+
useEffect(() => {
|
|
1007
|
+
if (!disabled) {
|
|
1008
|
+
inputRef.current?.focus();
|
|
1009
|
+
}
|
|
1010
|
+
}, [disabled]);
|
|
844
1011
|
const submit = () => {
|
|
845
1012
|
const trimmed = value.trim();
|
|
846
1013
|
if (!trimmed || disabled) return;
|
|
847
1014
|
onSend(trimmed);
|
|
848
1015
|
setValue("");
|
|
1016
|
+
inputRef.current?.focus();
|
|
849
1017
|
};
|
|
850
1018
|
return /* @__PURE__ */ jsxs("div", { className: chat_default.input, children: [
|
|
851
1019
|
/* @__PURE__ */ jsx(
|
|
852
1020
|
"input",
|
|
853
1021
|
{
|
|
1022
|
+
ref: inputRef,
|
|
854
1023
|
type: "text",
|
|
855
1024
|
className: chat_default.inputBox,
|
|
856
1025
|
value,
|
|
@@ -878,31 +1047,62 @@ function ChatInput({ disabled, placeholder, onSend }) {
|
|
|
878
1047
|
)
|
|
879
1048
|
] });
|
|
880
1049
|
}
|
|
881
|
-
function FloatingButton({ position, onClick, brandColor }) {
|
|
1050
|
+
function FloatingButton({ position, onClick, brandColor, showPopup, popupMessage, onPopupDismiss }) {
|
|
882
1051
|
const posClass = position === "bottom-left" ? chat_default.positionLeft : chat_default.positionRight;
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
1052
|
+
const popupPosClass = position === "bottom-left" ? chat_default.popupBubbleLeft : chat_default.popupBubbleRight;
|
|
1053
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1054
|
+
showPopup && popupMessage ? /* @__PURE__ */ jsxs(
|
|
1055
|
+
"div",
|
|
1056
|
+
{
|
|
1057
|
+
className: `${chat_default.popupBubble} ${popupPosClass}`,
|
|
1058
|
+
role: "status",
|
|
1059
|
+
"aria-live": "polite",
|
|
1060
|
+
children: [
|
|
1061
|
+
popupMessage,
|
|
1062
|
+
/* @__PURE__ */ jsx(
|
|
1063
|
+
"button",
|
|
1064
|
+
{
|
|
1065
|
+
type: "button",
|
|
1066
|
+
className: chat_default.popupDismiss,
|
|
1067
|
+
onClick: onPopupDismiss,
|
|
1068
|
+
"aria-label": "Dismiss greeting",
|
|
1069
|
+
children: "\u2715"
|
|
1070
|
+
}
|
|
1071
|
+
)
|
|
1072
|
+
]
|
|
1073
|
+
}
|
|
1074
|
+
) : null,
|
|
1075
|
+
/* @__PURE__ */ jsx(
|
|
1076
|
+
"button",
|
|
1077
|
+
{
|
|
1078
|
+
type: "button",
|
|
1079
|
+
className: `${chat_default.floatingButton} ${posClass}`,
|
|
1080
|
+
onClick,
|
|
1081
|
+
"aria-label": "Open chat",
|
|
1082
|
+
style: brandColor ? { background: brandColor } : void 0,
|
|
1083
|
+
children: "\u{1F4AC}"
|
|
1084
|
+
}
|
|
1085
|
+
)
|
|
1086
|
+
] });
|
|
894
1087
|
}
|
|
895
1088
|
var DEFAULT_BRAND_NAME = "Wealth Alpha AI";
|
|
896
1089
|
var DEFAULT_BRAND_COLOR = "#1a2d5a";
|
|
897
1090
|
var DEFAULT_AUTH_CHECK = "/me";
|
|
898
|
-
var
|
|
899
|
-
var CTA_LINE = "Select a quick command below
|
|
1091
|
+
var DEFAULT_GREETING = "Hi! I'm your Wealth Alpha AI assistant. How can I help you?";
|
|
1092
|
+
var CTA_LINE = "Select a quick command below";
|
|
1093
|
+
var PORTFOLIO_CSV_CHIP_ID = "__portfolio_csv_upload";
|
|
1094
|
+
var UPGRADE_PREMIUM_CHIP_ID = "__upgrade_premium__";
|
|
1095
|
+
function resolvePricingUrl(upgradeUrl) {
|
|
1096
|
+
if (upgradeUrl) return upgradeUrl;
|
|
1097
|
+
if (typeof window !== "undefined") {
|
|
1098
|
+
return `${window.location.origin}/pricing`;
|
|
1099
|
+
}
|
|
1100
|
+
return "";
|
|
1101
|
+
}
|
|
900
1102
|
function buildWelcome(name, plan) {
|
|
901
1103
|
const greeting = name ? `Welcome back, ${name}! You have ${plan ? plan : "Premium"} access.` : `Welcome! You have ${plan ? plan : "Premium"} access.`;
|
|
902
1104
|
return `${greeting}
|
|
903
1105
|
|
|
904
|
-
${DISCLAIMER_BLOCK}
|
|
905
|
-
|
|
906
1106
|
${CTA_LINE}`;
|
|
907
1107
|
}
|
|
908
1108
|
function WealthChat(props) {
|
|
@@ -917,6 +1117,7 @@ function WealthChat(props) {
|
|
|
917
1117
|
position = "bottom-right",
|
|
918
1118
|
defaultOpen = false,
|
|
919
1119
|
showCountdown = true,
|
|
1120
|
+
greetingMessage = DEFAULT_GREETING,
|
|
920
1121
|
onLogin,
|
|
921
1122
|
onLogout,
|
|
922
1123
|
onSessionExpire,
|
|
@@ -924,9 +1125,19 @@ function WealthChat(props) {
|
|
|
924
1125
|
} = props;
|
|
925
1126
|
const [mounted, setMounted] = useState(false);
|
|
926
1127
|
const [open, setOpen] = useState(defaultOpen);
|
|
1128
|
+
const [showPopup, setShowPopup] = useState(false);
|
|
1129
|
+
const popupShownRef = useRef(defaultOpen);
|
|
927
1130
|
useEffect(() => {
|
|
928
1131
|
setMounted(true);
|
|
929
1132
|
}, []);
|
|
1133
|
+
useEffect(() => {
|
|
1134
|
+
if (!mounted || open || popupShownRef.current) return;
|
|
1135
|
+
const timer = setTimeout(() => {
|
|
1136
|
+
setShowPopup(true);
|
|
1137
|
+
popupShownRef.current = true;
|
|
1138
|
+
}, 1e3);
|
|
1139
|
+
return () => clearTimeout(timer);
|
|
1140
|
+
}, [mounted, open]);
|
|
930
1141
|
const {
|
|
931
1142
|
session,
|
|
932
1143
|
remainingMs,
|
|
@@ -954,9 +1165,11 @@ function WealthChat(props) {
|
|
|
954
1165
|
},
|
|
955
1166
|
[session, setHistory]
|
|
956
1167
|
);
|
|
1168
|
+
const csvFileRef = useRef(null);
|
|
957
1169
|
const {
|
|
958
1170
|
state: chatState,
|
|
959
1171
|
sendText,
|
|
1172
|
+
uploadCsv,
|
|
960
1173
|
appendBotResponse,
|
|
961
1174
|
appendUserMessage,
|
|
962
1175
|
deactivatePriorChips,
|
|
@@ -996,16 +1209,46 @@ function WealthChat(props) {
|
|
|
996
1209
|
async (chip) => {
|
|
997
1210
|
touch();
|
|
998
1211
|
deactivatePriorChips();
|
|
1212
|
+
if (chip.id === PORTFOLIO_CSV_CHIP_ID) {
|
|
1213
|
+
appendUserMessage(chip.label);
|
|
1214
|
+
csvFileRef.current?.click();
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
1217
|
+
if (chip.id === UPGRADE_PREMIUM_CHIP_ID) {
|
|
1218
|
+
appendUserMessage(chip.label);
|
|
1219
|
+
const pricingUrl = resolvePricingUrl(user?.upgradeUrl);
|
|
1220
|
+
if (pricingUrl && typeof window !== "undefined") {
|
|
1221
|
+
window.location.assign(pricingUrl);
|
|
1222
|
+
}
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
999
1225
|
appendUserMessage(chip.label);
|
|
1000
1226
|
setTyping(true);
|
|
1001
1227
|
try {
|
|
1002
1228
|
const resp = await callChip(chip);
|
|
1003
|
-
if (resp)
|
|
1229
|
+
if (!resp) return;
|
|
1230
|
+
const redirectUrl = resp.metadata?.redirectUrl;
|
|
1231
|
+
if (typeof redirectUrl === "string" && redirectUrl && typeof window !== "undefined") {
|
|
1232
|
+
window.location.assign(redirectUrl);
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
appendBotResponse(resp);
|
|
1004
1236
|
} finally {
|
|
1005
1237
|
setTyping(false);
|
|
1006
1238
|
}
|
|
1007
1239
|
},
|
|
1008
|
-
[touch, deactivatePriorChips, appendUserMessage, callChip, setTyping, appendBotResponse]
|
|
1240
|
+
[touch, deactivatePriorChips, appendUserMessage, callChip, setTyping, appendBotResponse, user?.upgradeUrl]
|
|
1241
|
+
);
|
|
1242
|
+
const handleCsvFileSelected = useCallback(
|
|
1243
|
+
(e) => {
|
|
1244
|
+
const file = e.target.files?.[0];
|
|
1245
|
+
e.target.value = "";
|
|
1246
|
+
if (file) {
|
|
1247
|
+
touch();
|
|
1248
|
+
void uploadCsv(file);
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
[touch, uploadCsv]
|
|
1009
1252
|
);
|
|
1010
1253
|
const handleSend = useCallback(
|
|
1011
1254
|
(text) => {
|
|
@@ -1014,6 +1257,10 @@ function WealthChat(props) {
|
|
|
1014
1257
|
},
|
|
1015
1258
|
[touch, sendText]
|
|
1016
1259
|
);
|
|
1260
|
+
const handleFloatingButtonClick = useCallback(() => {
|
|
1261
|
+
setOpen(true);
|
|
1262
|
+
setShowPopup(false);
|
|
1263
|
+
}, []);
|
|
1017
1264
|
const handleClose = useCallback(() => setOpen(false), []);
|
|
1018
1265
|
const handleClear = useCallback(() => {
|
|
1019
1266
|
clearChat();
|
|
@@ -1030,7 +1277,17 @@ function WealthChat(props) {
|
|
|
1030
1277
|
["--wac-brand"]: brandColor
|
|
1031
1278
|
};
|
|
1032
1279
|
return /* @__PURE__ */ jsxs("div", { className: chat_default.root, style: rootStyle, children: [
|
|
1033
|
-
!open ? /* @__PURE__ */ jsx(
|
|
1280
|
+
!open ? /* @__PURE__ */ jsx(
|
|
1281
|
+
FloatingButton,
|
|
1282
|
+
{
|
|
1283
|
+
position,
|
|
1284
|
+
onClick: handleFloatingButtonClick,
|
|
1285
|
+
brandColor,
|
|
1286
|
+
showPopup,
|
|
1287
|
+
popupMessage: greetingMessage,
|
|
1288
|
+
onPopupDismiss: () => setShowPopup(false)
|
|
1289
|
+
}
|
|
1290
|
+
) : null,
|
|
1034
1291
|
open ? /* @__PURE__ */ jsxs(
|
|
1035
1292
|
"div",
|
|
1036
1293
|
{
|
|
@@ -1060,6 +1317,18 @@ function WealthChat(props) {
|
|
|
1060
1317
|
onLoginClick: handleLoginClick
|
|
1061
1318
|
}
|
|
1062
1319
|
),
|
|
1320
|
+
/* @__PURE__ */ jsx(
|
|
1321
|
+
"input",
|
|
1322
|
+
{
|
|
1323
|
+
ref: csvFileRef,
|
|
1324
|
+
type: "file",
|
|
1325
|
+
accept: ".csv,text/csv",
|
|
1326
|
+
className: chat_default.hiddenFileInput,
|
|
1327
|
+
onChange: handleCsvFileSelected,
|
|
1328
|
+
"aria-hidden": true,
|
|
1329
|
+
tabIndex: -1
|
|
1330
|
+
}
|
|
1331
|
+
),
|
|
1063
1332
|
isLoggedIn ? /* @__PURE__ */ jsx(
|
|
1064
1333
|
ChatInput,
|
|
1065
1334
|
{
|