@huskel/sdk 0.4.6 → 0.4.8
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.css +531 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +18 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +464 -123
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +464 -125
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -52,8 +52,10 @@ __export(index_exports, {
|
|
|
52
52
|
useCart: () => useCart,
|
|
53
53
|
useChat: () => useChat,
|
|
54
54
|
useHuskel: () => useHuskel,
|
|
55
|
+
useHuskelContext: () => useHuskelContext,
|
|
55
56
|
useIngest: () => useIngest,
|
|
56
57
|
usePageIngest: () => usePageIngest,
|
|
58
|
+
usePaymentPolling: () => usePaymentPolling,
|
|
57
59
|
useSearch: () => useSearch
|
|
58
60
|
});
|
|
59
61
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -95,6 +97,12 @@ var HuskelAPI = class {
|
|
|
95
97
|
if (sessionId) {
|
|
96
98
|
headers["X-Huskel-Session-Id"] = sessionId;
|
|
97
99
|
}
|
|
100
|
+
if (typeof window !== "undefined") {
|
|
101
|
+
const phone = localStorage.getItem("huskel_user_phone");
|
|
102
|
+
if (phone) {
|
|
103
|
+
headers["X-Huskel-Shopper-Phone"] = phone;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
98
106
|
const res = await fetch(url, {
|
|
99
107
|
method: "POST",
|
|
100
108
|
headers,
|
|
@@ -173,6 +181,12 @@ var HuskelAPI = class {
|
|
|
173
181
|
if (shopperId) headers["X-Huskel-Shopper-Id"] = shopperId;
|
|
174
182
|
const sessionId = (_b = this.getSessionId) == null ? void 0 : _b.call(this);
|
|
175
183
|
if (sessionId) headers["X-Huskel-Session-Id"] = sessionId;
|
|
184
|
+
if (typeof window !== "undefined") {
|
|
185
|
+
const phone = localStorage.getItem("huskel_user_phone");
|
|
186
|
+
if (phone) {
|
|
187
|
+
headers["X-Huskel-Shopper-Phone"] = phone;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
176
190
|
return headers;
|
|
177
191
|
}
|
|
178
192
|
async getCart() {
|
|
@@ -207,6 +221,32 @@ var HuskelAPI = class {
|
|
|
207
221
|
if (!res.ok) throw new Error("Failed to fetch checkout config");
|
|
208
222
|
return res.json();
|
|
209
223
|
}
|
|
224
|
+
async initiatePayment(phoneNumber, email, firstName, lastName) {
|
|
225
|
+
const res = await fetch(`${this.apiUrl}/payment/initiate`, {
|
|
226
|
+
method: "POST",
|
|
227
|
+
headers: this.buildHeaders(),
|
|
228
|
+
body: JSON.stringify({
|
|
229
|
+
siteId: this.siteId,
|
|
230
|
+
phoneNumber,
|
|
231
|
+
email,
|
|
232
|
+
firstName,
|
|
233
|
+
lastName
|
|
234
|
+
})
|
|
235
|
+
});
|
|
236
|
+
if (!res.ok) {
|
|
237
|
+
const errText = await res.text();
|
|
238
|
+
throw new Error("Failed to initiate payment: " + errText);
|
|
239
|
+
}
|
|
240
|
+
return res.json();
|
|
241
|
+
}
|
|
242
|
+
async getPaymentStatus(ref) {
|
|
243
|
+
const res = await fetch(`${this.apiUrl}/payment/status?ref=${ref}`, {
|
|
244
|
+
method: "GET",
|
|
245
|
+
headers: this.buildHeaders()
|
|
246
|
+
});
|
|
247
|
+
if (!res.ok) throw new Error("Failed to get payment status");
|
|
248
|
+
return res.json();
|
|
249
|
+
}
|
|
210
250
|
};
|
|
211
251
|
|
|
212
252
|
// src/client.ts
|
|
@@ -627,9 +667,10 @@ function useChat() {
|
|
|
627
667
|
const [sources, setSources] = (0, import_react6.useState)([]);
|
|
628
668
|
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
629
669
|
const [error, setError] = (0, import_react6.useState)(null);
|
|
670
|
+
const [lastAction, setLastAction] = (0, import_react6.useState)(null);
|
|
630
671
|
const abortRef = (0, import_react6.useRef)(null);
|
|
631
672
|
const send = (0, import_react6.useCallback)(async (query, displayQuery) => {
|
|
632
|
-
var _a, _b, _c, _d, _e;
|
|
673
|
+
var _a, _b, _c, _d, _e, _f;
|
|
633
674
|
if (!query.trim() || loading) return;
|
|
634
675
|
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
635
676
|
abortRef.current = new AbortController();
|
|
@@ -660,6 +701,7 @@ function useChat() {
|
|
|
660
701
|
}
|
|
661
702
|
if (signal.aborted) return;
|
|
662
703
|
setSources((_b = res.sources) != null ? _b : []);
|
|
704
|
+
if (res.action) setLastAction(res.action);
|
|
663
705
|
if (((_c = res.action) == null ? void 0 : _c.type) === "add_to_cart" || res.checkout) {
|
|
664
706
|
if (typeof window !== "undefined") {
|
|
665
707
|
window.dispatchEvent(new CustomEvent("huskel:cart_updated", { detail: res.checkout }));
|
|
@@ -670,12 +712,17 @@ function useChat() {
|
|
|
670
712
|
window.dispatchEvent(new CustomEvent("huskel:trigger_checkout", { detail: res.checkout }));
|
|
671
713
|
}
|
|
672
714
|
}
|
|
715
|
+
if (((_e = res.action) == null ? void 0 : _e.type) === "awaiting_payment") {
|
|
716
|
+
if (typeof window !== "undefined") {
|
|
717
|
+
window.dispatchEvent(new CustomEvent("huskel:awaiting_payment", { detail: res.action }));
|
|
718
|
+
}
|
|
719
|
+
}
|
|
673
720
|
if (res.checkout && client.onCheckout) {
|
|
674
721
|
client.onCheckout(res.checkout);
|
|
675
722
|
}
|
|
676
723
|
} catch (e) {
|
|
677
724
|
if (signal.aborted) return;
|
|
678
|
-
let msg = (
|
|
725
|
+
let msg = (_f = e == null ? void 0 : e.message) != null ? _f : "Chat request failed";
|
|
679
726
|
try {
|
|
680
727
|
const parsed = JSON.parse(msg);
|
|
681
728
|
if (parsed && parsed.error) {
|
|
@@ -698,8 +745,9 @@ function useChat() {
|
|
|
698
745
|
setSources([]);
|
|
699
746
|
setError(null);
|
|
700
747
|
setLoading(false);
|
|
748
|
+
setLastAction(null);
|
|
701
749
|
}, []);
|
|
702
|
-
return { messages, sources, loading, error, send, reset };
|
|
750
|
+
return { messages, sources, loading, error, lastAction, send, reset };
|
|
703
751
|
}
|
|
704
752
|
|
|
705
753
|
// src/hooks/useCart.ts
|
|
@@ -738,8 +786,71 @@ function useCart() {
|
|
|
738
786
|
return { cart, loading, fetchCart };
|
|
739
787
|
}
|
|
740
788
|
|
|
741
|
-
// src/
|
|
789
|
+
// src/hooks/usePaymentPolling.ts
|
|
742
790
|
var import_react8 = require("react");
|
|
791
|
+
function usePaymentPolling({
|
|
792
|
+
client,
|
|
793
|
+
merchantReference,
|
|
794
|
+
onSuccess,
|
|
795
|
+
onFailure,
|
|
796
|
+
intervalMs = 3e3,
|
|
797
|
+
timeoutMs = 3e5
|
|
798
|
+
// 5 minutes default
|
|
799
|
+
}) {
|
|
800
|
+
const [status, setStatus] = (0, import_react8.useState)("IDLE");
|
|
801
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
802
|
+
const onSuccessRef = (0, import_react8.useRef)(onSuccess);
|
|
803
|
+
const onFailureRef = (0, import_react8.useRef)(onFailure);
|
|
804
|
+
(0, import_react8.useEffect)(() => {
|
|
805
|
+
onSuccessRef.current = onSuccess;
|
|
806
|
+
onFailureRef.current = onFailure;
|
|
807
|
+
}, [onSuccess, onFailure]);
|
|
808
|
+
(0, import_react8.useEffect)(() => {
|
|
809
|
+
if (!merchantReference) {
|
|
810
|
+
setStatus("IDLE");
|
|
811
|
+
setError(null);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
setStatus("PENDING");
|
|
815
|
+
setError(null);
|
|
816
|
+
const startTime = Date.now();
|
|
817
|
+
let timerId = null;
|
|
818
|
+
async function checkStatus() {
|
|
819
|
+
try {
|
|
820
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
821
|
+
setStatus("FAILED");
|
|
822
|
+
setError("Payment session timed out");
|
|
823
|
+
if (onFailureRef.current) onFailureRef.current("Payment session timed out");
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
const res = await client.getPaymentStatus(merchantReference);
|
|
827
|
+
if (res.status === "COMPLETED") {
|
|
828
|
+
setStatus("COMPLETED");
|
|
829
|
+
if (onSuccessRef.current) onSuccessRef.current();
|
|
830
|
+
} else if (res.status === "FAILED") {
|
|
831
|
+
setStatus("FAILED");
|
|
832
|
+
setError("Payment failed");
|
|
833
|
+
if (onFailureRef.current) onFailureRef.current("Payment failed");
|
|
834
|
+
} else {
|
|
835
|
+
timerId = setTimeout(checkStatus, intervalMs);
|
|
836
|
+
}
|
|
837
|
+
} catch (err) {
|
|
838
|
+
console.error("[Huskel Polling Error]", err);
|
|
839
|
+
timerId = setTimeout(checkStatus, intervalMs);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
timerId = setTimeout(checkStatus, intervalMs);
|
|
843
|
+
return () => {
|
|
844
|
+
if (timerId) {
|
|
845
|
+
clearTimeout(timerId);
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
}, [client, merchantReference, intervalMs, timeoutMs]);
|
|
849
|
+
return { status, error };
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
// src/components/SearchBar.tsx
|
|
853
|
+
var import_react9 = require("react");
|
|
743
854
|
|
|
744
855
|
// src/utils/cn.ts
|
|
745
856
|
var import_clsx = require("clsx");
|
|
@@ -766,15 +877,15 @@ function SearchBar({
|
|
|
766
877
|
theme,
|
|
767
878
|
classNames = {}
|
|
768
879
|
}) {
|
|
769
|
-
const [query, setQuery] = (0,
|
|
770
|
-
const [open, setOpen] = (0,
|
|
771
|
-
const [isDebouncing, setIsDebouncing] = (0,
|
|
880
|
+
const [query, setQuery] = (0, import_react9.useState)("");
|
|
881
|
+
const [open, setOpen] = (0, import_react9.useState)(false);
|
|
882
|
+
const [isDebouncing, setIsDebouncing] = (0, import_react9.useState)(false);
|
|
772
883
|
const { results, loading, search, clear } = useSearch();
|
|
773
884
|
const client = useHuskelContext();
|
|
774
|
-
const timer = (0,
|
|
775
|
-
const wrap = (0,
|
|
776
|
-
const ignoreNextQueryChange = (0,
|
|
777
|
-
(0,
|
|
885
|
+
const timer = (0, import_react9.useRef)();
|
|
886
|
+
const wrap = (0, import_react9.useRef)(null);
|
|
887
|
+
const ignoreNextQueryChange = (0, import_react9.useRef)(false);
|
|
888
|
+
(0, import_react9.useEffect)(() => {
|
|
778
889
|
if (ignoreNextQueryChange.current) {
|
|
779
890
|
ignoreNextQueryChange.current = false;
|
|
780
891
|
return;
|
|
@@ -794,7 +905,7 @@ function SearchBar({
|
|
|
794
905
|
}, debounceMs);
|
|
795
906
|
return () => clearTimeout(timer.current);
|
|
796
907
|
}, [query]);
|
|
797
|
-
(0,
|
|
908
|
+
(0, import_react9.useEffect)(() => {
|
|
798
909
|
const h = (e) => {
|
|
799
910
|
if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
|
|
800
911
|
};
|
|
@@ -898,7 +1009,7 @@ function SearchBar({
|
|
|
898
1009
|
}
|
|
899
1010
|
|
|
900
1011
|
// src/components/Sparkle.tsx
|
|
901
|
-
var
|
|
1012
|
+
var import_react10 = require("react");
|
|
902
1013
|
var import_react_dom = require("react-dom");
|
|
903
1014
|
|
|
904
1015
|
// src/utils/markdown.tsx
|
|
@@ -1030,14 +1141,14 @@ function SparkleModal({
|
|
|
1030
1141
|
}) {
|
|
1031
1142
|
var _a, _b, _c;
|
|
1032
1143
|
const client = useHuskelContext();
|
|
1033
|
-
const [fetchedProduct, setFetchedProduct] = (0,
|
|
1144
|
+
const [fetchedProduct, setFetchedProduct] = (0, import_react10.useState)(null);
|
|
1034
1145
|
const displayProduct = initialProduct || fetchedProduct;
|
|
1035
1146
|
const { results, loading: searchLoading, search } = useSearch();
|
|
1036
1147
|
const { messages, sources, loading: chatLoading, error: chatError, send } = useChat();
|
|
1037
|
-
const [chatInput, setChatInput] = (0,
|
|
1038
|
-
const chatBottomRef = (0,
|
|
1039
|
-
const chatTextareaRef = (0,
|
|
1040
|
-
(0,
|
|
1148
|
+
const [chatInput, setChatInput] = (0, import_react10.useState)("");
|
|
1149
|
+
const chatBottomRef = (0, import_react10.useRef)(null);
|
|
1150
|
+
const chatTextareaRef = (0, import_react10.useRef)(null);
|
|
1151
|
+
(0, import_react10.useEffect)(() => {
|
|
1041
1152
|
if (!initialProduct && !fetchedProduct) {
|
|
1042
1153
|
client.api.searchVector(productName, 1).then((res) => {
|
|
1043
1154
|
if (res.results && res.results.length > 0) {
|
|
@@ -1047,17 +1158,17 @@ function SparkleModal({
|
|
|
1047
1158
|
}
|
|
1048
1159
|
search(productName, limit);
|
|
1049
1160
|
}, [productName, initialProduct, fetchedProduct, client, limit, search]);
|
|
1050
|
-
(0,
|
|
1161
|
+
(0, import_react10.useEffect)(() => {
|
|
1051
1162
|
if (results.length > 0) onResult == null ? void 0 : onResult(results);
|
|
1052
1163
|
}, [results, onResult]);
|
|
1053
|
-
(0,
|
|
1164
|
+
(0, import_react10.useEffect)(() => {
|
|
1054
1165
|
const h = (e) => {
|
|
1055
1166
|
if (e.key === "Escape") onClose();
|
|
1056
1167
|
};
|
|
1057
1168
|
document.addEventListener("keydown", h);
|
|
1058
1169
|
return () => document.removeEventListener("keydown", h);
|
|
1059
1170
|
}, [onClose]);
|
|
1060
|
-
(0,
|
|
1171
|
+
(0, import_react10.useEffect)(() => {
|
|
1061
1172
|
var _a2;
|
|
1062
1173
|
(_a2 = chatBottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1063
1174
|
}, [messages, chatLoading]);
|
|
@@ -1289,9 +1400,9 @@ function Sparkle({
|
|
|
1289
1400
|
classNames = {},
|
|
1290
1401
|
product
|
|
1291
1402
|
}) {
|
|
1292
|
-
const [open, setOpen] = (0,
|
|
1293
|
-
const [mounted, setMounted] = (0,
|
|
1294
|
-
(0,
|
|
1403
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
1404
|
+
const [mounted, setMounted] = (0, import_react10.useState)(false);
|
|
1405
|
+
(0, import_react10.useEffect)(() => {
|
|
1295
1406
|
setMounted(true);
|
|
1296
1407
|
}, []);
|
|
1297
1408
|
const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
@@ -1329,7 +1440,7 @@ function Sparkle({
|
|
|
1329
1440
|
}
|
|
1330
1441
|
|
|
1331
1442
|
// src/components/ChatWidget.tsx
|
|
1332
|
-
var
|
|
1443
|
+
var import_react11 = require("react");
|
|
1333
1444
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1334
1445
|
var SparkleIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }) });
|
|
1335
1446
|
var ArrowUpIcon2 = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
@@ -1362,10 +1473,10 @@ function ChatWidget({
|
|
|
1362
1473
|
onSelectSource
|
|
1363
1474
|
}) {
|
|
1364
1475
|
const { messages, sources, loading, error, send, reset } = useChat();
|
|
1365
|
-
const [input, setInput] = (0,
|
|
1366
|
-
const bottomRef = (0,
|
|
1367
|
-
const textareaRef = (0,
|
|
1368
|
-
(0,
|
|
1476
|
+
const [input, setInput] = (0, import_react11.useState)("");
|
|
1477
|
+
const bottomRef = (0, import_react11.useRef)(null);
|
|
1478
|
+
const textareaRef = (0, import_react11.useRef)(null);
|
|
1479
|
+
(0, import_react11.useEffect)(() => {
|
|
1369
1480
|
var _a;
|
|
1370
1481
|
(_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
|
|
1371
1482
|
}, [messages, loading]);
|
|
@@ -1462,7 +1573,7 @@ function ChatWidget({
|
|
|
1462
1573
|
}
|
|
1463
1574
|
|
|
1464
1575
|
// src/components/AIChatButton.tsx
|
|
1465
|
-
var
|
|
1576
|
+
var import_react12 = require("react");
|
|
1466
1577
|
var import_react_dom2 = require("react-dom");
|
|
1467
1578
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1468
1579
|
var SparkleIcon3 = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("path", { d: "m12 3-1.912 5.813a2 2 0 0 1-1.275 1.275L3 12l5.813 1.912a2 2 0 0 1 1.275 1.275L12 21l1.912-5.813a2 2 0 0 1 1.275-1.275L21 12l-5.813-1.912a2 2 0 0 1-1.275-1.275L12 3Z" }) });
|
|
@@ -1482,15 +1593,15 @@ var DEFAULT_CHIPS = [
|
|
|
1482
1593
|
"Best laptop for students"
|
|
1483
1594
|
];
|
|
1484
1595
|
function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
1485
|
-
const railRef = (0,
|
|
1486
|
-
const [showNext, setShowNext] = (0,
|
|
1487
|
-
const measure = (0,
|
|
1596
|
+
const railRef = (0, import_react12.useRef)(null);
|
|
1597
|
+
const [showNext, setShowNext] = (0, import_react12.useState)(false);
|
|
1598
|
+
const measure = (0, import_react12.useCallback)(() => {
|
|
1488
1599
|
const el = railRef.current;
|
|
1489
1600
|
if (!el) return;
|
|
1490
1601
|
const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;
|
|
1491
1602
|
setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);
|
|
1492
1603
|
}, []);
|
|
1493
|
-
(0,
|
|
1604
|
+
(0, import_react12.useEffect)(() => {
|
|
1494
1605
|
measure();
|
|
1495
1606
|
const el = railRef.current;
|
|
1496
1607
|
if (!el) return;
|
|
@@ -1555,16 +1666,61 @@ function ChatModal({
|
|
|
1555
1666
|
classNames = {}
|
|
1556
1667
|
}) {
|
|
1557
1668
|
var _a, _b;
|
|
1558
|
-
const
|
|
1559
|
-
const
|
|
1560
|
-
const [
|
|
1561
|
-
const
|
|
1562
|
-
const
|
|
1563
|
-
(0,
|
|
1669
|
+
const client = useHuskelContext();
|
|
1670
|
+
const { messages, sources, loading, error, lastAction, send, reset } = useChat();
|
|
1671
|
+
const [input, setInput] = (0, import_react12.useState)("");
|
|
1672
|
+
const [selectedProduct, setSelectedProduct] = (0, import_react12.useState)(null);
|
|
1673
|
+
const bottomRef = (0, import_react12.useRef)(null);
|
|
1674
|
+
const textareaRef = (0, import_react12.useRef)(null);
|
|
1675
|
+
const [phoneInput, setPhoneInput] = (0, import_react12.useState)(() => {
|
|
1676
|
+
if (typeof window !== "undefined") {
|
|
1677
|
+
return localStorage.getItem("huskel_user_phone") || "";
|
|
1678
|
+
}
|
|
1679
|
+
return "";
|
|
1680
|
+
});
|
|
1681
|
+
const [merchantRef, setMerchantRef] = (0, import_react12.useState)(null);
|
|
1682
|
+
const [paymentPhase, setPaymentPhase] = (0, import_react12.useState)("idle");
|
|
1683
|
+
const { status: pollStatus } = usePaymentPolling({
|
|
1684
|
+
client: client.api,
|
|
1685
|
+
merchantReference: merchantRef,
|
|
1686
|
+
onSuccess: () => {
|
|
1687
|
+
setPaymentPhase("done");
|
|
1688
|
+
setMerchantRef(null);
|
|
1689
|
+
},
|
|
1690
|
+
onFailure: () => {
|
|
1691
|
+
setPaymentPhase("failed");
|
|
1692
|
+
setMerchantRef(null);
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
(0, import_react12.useEffect)(() => {
|
|
1696
|
+
var _a2;
|
|
1697
|
+
if (!lastAction) return;
|
|
1698
|
+
if (lastAction.type === "request_phone") {
|
|
1699
|
+
setPaymentPhase("prompt_phone");
|
|
1700
|
+
} else if (lastAction.type === "awaiting_payment") {
|
|
1701
|
+
setMerchantRef((_a2 = lastAction.merchantReference) != null ? _a2 : null);
|
|
1702
|
+
setPaymentPhase("awaiting");
|
|
1703
|
+
}
|
|
1704
|
+
}, [lastAction]);
|
|
1705
|
+
const handlePhoneSubmit = async () => {
|
|
1706
|
+
if (!phoneInput.trim()) return;
|
|
1707
|
+
try {
|
|
1708
|
+
if (typeof window !== "undefined") {
|
|
1709
|
+
localStorage.setItem("huskel_user_phone", phoneInput.trim());
|
|
1710
|
+
}
|
|
1711
|
+
const res = await client.api.initiatePayment(phoneInput.trim());
|
|
1712
|
+
setMerchantRef(res.merchantReference);
|
|
1713
|
+
setPaymentPhase("awaiting");
|
|
1714
|
+
} catch (e) {
|
|
1715
|
+
console.error("[Huskel] initiatePayment error", e);
|
|
1716
|
+
setPaymentPhase("failed");
|
|
1717
|
+
}
|
|
1718
|
+
};
|
|
1719
|
+
(0, import_react12.useEffect)(() => {
|
|
1564
1720
|
var _a2;
|
|
1565
1721
|
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1566
1722
|
}, [messages, loading, selectedProduct]);
|
|
1567
|
-
(0,
|
|
1723
|
+
(0, import_react12.useEffect)(() => {
|
|
1568
1724
|
const h = (e) => {
|
|
1569
1725
|
if (e.key === "Escape") onClose();
|
|
1570
1726
|
};
|
|
@@ -1642,7 +1798,7 @@ function ChatModal({
|
|
|
1642
1798
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(SparkleIcon3, {}) }),
|
|
1643
1799
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-ai-body", children: [
|
|
1644
1800
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
|
|
1645
|
-
isLast && sources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1801
|
+
isLast && sources.length > 0 && (lastAction == null ? void 0 : lastAction.type) !== "request_phone" && (lastAction == null ? void 0 : lastAction.type) !== "awaiting_payment" && (lastAction == null ? void 0 : lastAction.type) !== "checkout" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1646
1802
|
SourcesCarousel,
|
|
1647
1803
|
{
|
|
1648
1804
|
sources,
|
|
@@ -1680,6 +1836,39 @@ function ChatModal({
|
|
|
1680
1836
|
] })
|
|
1681
1837
|
] }),
|
|
1682
1838
|
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-error", children: error }),
|
|
1839
|
+
paymentPhase === "prompt_phone" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1840
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", children: "\u{1F4F1}" }),
|
|
1841
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", children: "Enter your M-Pesa number to pay" }),
|
|
1842
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1843
|
+
"input",
|
|
1844
|
+
{
|
|
1845
|
+
type: "tel",
|
|
1846
|
+
className: "hsk-cb-phone-input",
|
|
1847
|
+
placeholder: "e.g. 0712 345 678",
|
|
1848
|
+
value: phoneInput,
|
|
1849
|
+
onChange: (e) => setPhoneInput(e.target.value),
|
|
1850
|
+
onKeyDown: (e) => e.key === "Enter" && handlePhoneSubmit()
|
|
1851
|
+
}
|
|
1852
|
+
),
|
|
1853
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-pay-submit", onClick: handlePhoneSubmit, children: "Send STK Push \u2192" })
|
|
1854
|
+
] }),
|
|
1855
|
+
paymentPhase === "awaiting" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1856
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", style: { fontSize: "2rem" }, children: "\u23F3" }),
|
|
1857
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", style: { fontWeight: 600 }, children: "Check your phone" }),
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { style: { fontSize: "0.8rem", opacity: 0.6 }, children: "An M-Pesa STK push has been sent. Enter your PIN to complete payment." })
|
|
1859
|
+
] }),
|
|
1860
|
+
paymentPhase === "done" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", style: { color: "#22c55e", fontSize: "2rem" }, children: "\u2705" }),
|
|
1862
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", children: "Payment complete! Thank you." })
|
|
1863
|
+
] }),
|
|
1864
|
+
paymentPhase === "failed" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", style: { color: "#ef4444", fontSize: "2rem" }, children: "\u274C" }),
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", children: "Payment failed or timed out." }),
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-pay-submit", onClick: () => {
|
|
1868
|
+
setPaymentPhase("idle");
|
|
1869
|
+
setMerchantRef(null);
|
|
1870
|
+
}, children: "Try again" })
|
|
1871
|
+
] }),
|
|
1683
1872
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: bottomRef, style: { height: 1 } })
|
|
1684
1873
|
] }),
|
|
1685
1874
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
|
|
@@ -1728,9 +1917,9 @@ function AIChatButton({
|
|
|
1728
1917
|
theme,
|
|
1729
1918
|
classNames = {}
|
|
1730
1919
|
}) {
|
|
1731
|
-
const [open, setOpen] = (0,
|
|
1732
|
-
const [mounted, setMounted] = (0,
|
|
1733
|
-
(0,
|
|
1920
|
+
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
1921
|
+
const [mounted, setMounted] = (0, import_react12.useState)(false);
|
|
1922
|
+
(0, import_react12.useEffect)(() => {
|
|
1734
1923
|
setMounted(true);
|
|
1735
1924
|
}, []);
|
|
1736
1925
|
const customStyles = __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor }), (theme == null ? void 0 : theme.backgroundColor) && { "--hsk-bg": theme.backgroundColor }), (theme == null ? void 0 : theme.textColor) && { "--hsk-text": theme.textColor }), (theme == null ? void 0 : theme.fontFamily) && { "--hsk-font": theme.fontFamily }), (theme == null ? void 0 : theme.borderRadius) && { "--hsk-border-radius": theme.borderRadius });
|
|
@@ -1778,11 +1967,11 @@ function CartBadge({ className }) {
|
|
|
1778
1967
|
}
|
|
1779
1968
|
|
|
1780
1969
|
// src/components/CartDrawer.tsx
|
|
1781
|
-
var
|
|
1970
|
+
var import_react14 = require("react");
|
|
1782
1971
|
var import_react_dom4 = require("react-dom");
|
|
1783
1972
|
|
|
1784
1973
|
// src/components/CheckoutModal.tsx
|
|
1785
|
-
var
|
|
1974
|
+
var import_react13 = require("react");
|
|
1786
1975
|
var import_react_dom3 = require("react-dom");
|
|
1787
1976
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1788
1977
|
function CheckoutModal({
|
|
@@ -1791,98 +1980,242 @@ function CheckoutModal({
|
|
|
1791
1980
|
customStyles,
|
|
1792
1981
|
hskThemeAttr
|
|
1793
1982
|
}) {
|
|
1794
|
-
var _a, _b, _c, _d;
|
|
1795
1983
|
const { cart, loading: cartLoading } = useCart();
|
|
1796
1984
|
const client = useHuskelContext();
|
|
1797
|
-
const [config, setConfig] = (0,
|
|
1798
|
-
const [
|
|
1799
|
-
const [
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1985
|
+
const [config, setConfig] = (0, import_react13.useState)(null);
|
|
1986
|
+
const [loadingConfig, setLoadingConfig] = (0, import_react13.useState)(true);
|
|
1987
|
+
const [phone, setPhone] = (0, import_react13.useState)(() => {
|
|
1988
|
+
if (typeof window !== "undefined") {
|
|
1989
|
+
return localStorage.getItem("huskel_user_phone") || "";
|
|
1990
|
+
}
|
|
1991
|
+
return "";
|
|
1992
|
+
});
|
|
1993
|
+
const [email, setEmail] = (0, import_react13.useState)(() => {
|
|
1994
|
+
if (typeof window !== "undefined") {
|
|
1995
|
+
return localStorage.getItem("huskel_user_email") || "";
|
|
1996
|
+
}
|
|
1997
|
+
return "";
|
|
1998
|
+
});
|
|
1999
|
+
const [firstName, setFirstName] = (0, import_react13.useState)(() => {
|
|
2000
|
+
if (typeof window !== "undefined") {
|
|
2001
|
+
return localStorage.getItem("huskel_user_firstname") || "";
|
|
2002
|
+
}
|
|
2003
|
+
return "";
|
|
2004
|
+
});
|
|
2005
|
+
const [lastName, setLastName] = (0, import_react13.useState)(() => {
|
|
2006
|
+
if (typeof window !== "undefined") {
|
|
2007
|
+
return localStorage.getItem("huskel_user_lastname") || "";
|
|
2008
|
+
}
|
|
2009
|
+
return "";
|
|
2010
|
+
});
|
|
2011
|
+
const [phase, setPhase] = (0, import_react13.useState)("idle");
|
|
2012
|
+
const [merchantRef, setMerchantRef] = (0, import_react13.useState)(null);
|
|
2013
|
+
const [payError, setPayError] = (0, import_react13.useState)(null);
|
|
2014
|
+
const {} = usePaymentPolling({
|
|
2015
|
+
client: client.api,
|
|
2016
|
+
merchantReference: merchantRef,
|
|
2017
|
+
onSuccess: () => {
|
|
2018
|
+
setPhase("done");
|
|
2019
|
+
setMerchantRef(null);
|
|
2020
|
+
},
|
|
2021
|
+
onFailure: () => {
|
|
2022
|
+
setPhase("failed");
|
|
2023
|
+
setPayError("Payment failed or timed out. Please try again.");
|
|
2024
|
+
setMerchantRef(null);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
(0, import_react13.useEffect)(() => {
|
|
2028
|
+
client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch(() => {
|
|
2029
|
+
}).finally(() => setLoadingConfig(false));
|
|
1803
2030
|
}, [client]);
|
|
1804
|
-
const
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
2031
|
+
const hasPaymentMethods = config && Object.values(config).some((m) => m.enabled);
|
|
2032
|
+
const handlePay = async (e) => {
|
|
2033
|
+
e.preventDefault();
|
|
2034
|
+
if (!phone.trim()) {
|
|
2035
|
+
setPayError("Phone number is required.");
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
setPayError(null);
|
|
2039
|
+
setPhase("awaiting");
|
|
2040
|
+
try {
|
|
2041
|
+
if (typeof window !== "undefined") {
|
|
2042
|
+
localStorage.setItem("huskel_user_phone", phone.trim());
|
|
2043
|
+
localStorage.setItem("huskel_user_email", email.trim());
|
|
2044
|
+
localStorage.setItem("huskel_user_firstname", firstName.trim());
|
|
2045
|
+
localStorage.setItem("huskel_user_lastname", lastName.trim());
|
|
2046
|
+
}
|
|
2047
|
+
const res = await client.api.initiatePayment(phone.trim(), email, firstName, lastName);
|
|
2048
|
+
if (res == null ? void 0 : res.merchantReference) {
|
|
2049
|
+
setMerchantRef(res.merchantReference);
|
|
2050
|
+
} else {
|
|
2051
|
+
throw new Error("No merchant reference returned.");
|
|
1819
2052
|
}
|
|
1820
|
-
}
|
|
2053
|
+
} catch (err) {
|
|
2054
|
+
setPhase("failed");
|
|
2055
|
+
setPayError(err.message || "Could not connect to payment processor.");
|
|
2056
|
+
}
|
|
1821
2057
|
};
|
|
1822
|
-
const
|
|
2058
|
+
const currency = (cart == null ? void 0 : cart.currency) || "KES";
|
|
2059
|
+
const total = (cart == null ? void 0 : cart.total) || 0;
|
|
2060
|
+
const backdropStyle = __spreadProps(__spreadValues({}, customStyles), { fontSize: "15px", fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif', zIndex: 999999 });
|
|
1823
2061
|
return (0, import_react_dom3.createPortal)(
|
|
1824
2062
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1825
2063
|
"div",
|
|
1826
2064
|
{
|
|
1827
|
-
className: "hsk-
|
|
1828
|
-
style:
|
|
2065
|
+
className: "hsk-checkout-backdrop-full",
|
|
2066
|
+
style: backdropStyle,
|
|
1829
2067
|
"data-hsk-theme": hskThemeAttr,
|
|
1830
|
-
onClick: onClose,
|
|
1831
2068
|
children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1832
2069
|
"div",
|
|
1833
2070
|
{
|
|
1834
|
-
className: "hsk-checkout-modal",
|
|
2071
|
+
className: "hsk-checkout-modal-full",
|
|
1835
2072
|
style: customStyles,
|
|
1836
2073
|
"data-hsk-theme": hskThemeAttr,
|
|
1837
|
-
onClick: (e) => e.stopPropagation(),
|
|
1838
2074
|
children: [
|
|
1839
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-
|
|
1840
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "22 4 12 14.01 9 11.01" })
|
|
2075
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-panel-left", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-left-content", children: [
|
|
2076
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { onClick: onClose, className: "hsk-checkout-back-btn", disabled: phase !== "idle", children: [
|
|
2077
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
2078
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("line", { x1: "19", y1: "12", x2: "5", y2: "12" }),
|
|
2079
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("polyline", { points: "12 19 5 12 12 5" })
|
|
2080
|
+
] }),
|
|
2081
|
+
"Back to store"
|
|
1847
2082
|
] }),
|
|
1848
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("
|
|
1849
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
|
|
1856
|
-
item.quantity,
|
|
1857
|
-
"x ",
|
|
1858
|
-
item.name
|
|
1859
|
-
] }),
|
|
1860
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
|
|
1861
|
-
item.currency,
|
|
1862
|
-
" ",
|
|
1863
|
-
(item.price_numeric * item.quantity).toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1864
|
-
] })
|
|
1865
|
-
] }, item.id)) }),
|
|
1866
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-total", children: [
|
|
1867
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Total" }),
|
|
1868
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
|
|
1869
|
-
cart.currency,
|
|
1870
|
-
" ",
|
|
1871
|
-
cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1872
|
-
] })
|
|
1873
|
-
] })
|
|
2083
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-store-info", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { children: "Secure Checkout" }) }),
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-amount-due", children: [
|
|
2085
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-checkout-label-muted", children: "Pay total" }),
|
|
2086
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-grand-total", children: [
|
|
2087
|
+
currency,
|
|
2088
|
+
" ",
|
|
2089
|
+
total.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1874
2090
|
] })
|
|
1875
2091
|
] }),
|
|
1876
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.
|
|
1877
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
2092
|
+
cartLoading || !cart ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-cart-loading", children: "Loading order..." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-items-list-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "hsk-checkout-items-list", children: cart.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { className: "hsk-checkout-item-row", children: [
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-item-img-container", children: [
|
|
2094
|
+
item.image ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("img", { src: item.image, alt: item.name, className: "hsk-checkout-item-img" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-item-img-placeholder", children: "\u{1F6D2}" }),
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-checkout-item-qty-badge", children: item.quantity })
|
|
2096
|
+
] }),
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-item-details", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-checkout-item-name", children: item.name }) }),
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "hsk-checkout-item-price", children: [
|
|
2099
|
+
item.currency,
|
|
2100
|
+
" ",
|
|
2101
|
+
(item.price_numeric * item.quantity).toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1883
2102
|
] })
|
|
2103
|
+
] }, item.id)) }) })
|
|
2104
|
+
] }) }),
|
|
2105
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-panel-right", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-right-content", children: phase === "done" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-status-card success", children: [
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-status-icon-wrap success", children: "\u2705" }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Payment Successful!" }),
|
|
2108
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "Your transaction has been confirmed successfully. Thank you for your order!" }),
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: onClose, className: "hsk-pay-btn hsk-btn-primary", style: { marginTop: "1.5rem" }, children: "Continue Shopping" })
|
|
2110
|
+
] }) : phase === "awaiting" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-status-card awaiting", children: [
|
|
2111
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-status-spinner-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-status-spinner" }) }),
|
|
2112
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Confirm payment on your phone" }),
|
|
2113
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("p", { children: [
|
|
2114
|
+
"We've sent an M-Pesa STK push prompt to ",
|
|
2115
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("strong", { children: [
|
|
2116
|
+
"254",
|
|
2117
|
+
phone
|
|
2118
|
+
] }),
|
|
2119
|
+
"."
|
|
2120
|
+
] }),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-stk-instructions", children: [
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "1. Check your phone lockscreen for the M-Pesa prompt." }),
|
|
2123
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "2. Enter your M-Pesa PIN and press OK." }),
|
|
2124
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "3. Wait here; this page will automatically redirect once confirmed." })
|
|
2125
|
+
] }),
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2127
|
+
"button",
|
|
2128
|
+
{
|
|
2129
|
+
onClick: () => {
|
|
2130
|
+
setPhase("idle");
|
|
2131
|
+
setMerchantRef(null);
|
|
2132
|
+
},
|
|
2133
|
+
className: "hsk-checkout-cancel-btn",
|
|
2134
|
+
children: "Cancel and change phone number"
|
|
2135
|
+
}
|
|
2136
|
+
)
|
|
2137
|
+
] }) : phase === "failed" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-status-card failed", children: [
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-status-icon-wrap failed", children: "\u274C" }),
|
|
2139
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Payment failed or timed out" }),
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-checkout-error-text", children: payError || "Could not verify M-Pesa transaction." }),
|
|
2141
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => {
|
|
2142
|
+
setPhase("idle");
|
|
2143
|
+
setPayError(null);
|
|
2144
|
+
}, className: "hsk-pay-btn hsk-btn-primary", style: { marginTop: "1.5rem" }, children: "Try Again" })
|
|
2145
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-payment-form-wrap", children: [
|
|
2146
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { className: "hsk-checkout-section-title", children: "Payment details" }),
|
|
2147
|
+
loadingConfig ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-cart-loading", children: "Loading payment configuration..." }) : !hasPaymentMethods ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-checkout-error", children: "No payment methods configured for this store." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("form", { onSubmit: handlePay, className: "hsk-stripe-checkout-form", children: [
|
|
2148
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-form-group", children: [
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "hsk-form-label", children: "M-Pesa Mobile Number" }),
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-phone-input-container", children: [
|
|
2151
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-phone-prefix", children: "254" }),
|
|
2152
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2153
|
+
"input",
|
|
2154
|
+
{
|
|
2155
|
+
type: "tel",
|
|
2156
|
+
required: true,
|
|
2157
|
+
placeholder: "712345678",
|
|
2158
|
+
pattern: "[0-9]{9}",
|
|
2159
|
+
maxLength: 9,
|
|
2160
|
+
value: phone,
|
|
2161
|
+
onChange: (e) => setPhone(e.target.value.replace(/\D/g, "")),
|
|
2162
|
+
className: "hsk-phone-input-field"
|
|
2163
|
+
}
|
|
2164
|
+
)
|
|
2165
|
+
] }),
|
|
2166
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "hsk-form-hint", children: "Enter your 9-digit number (e.g. 712345678)" })
|
|
2167
|
+
] }),
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-form-group", children: [
|
|
2169
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "hsk-form-label", children: "Email address" }),
|
|
2170
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2171
|
+
"input",
|
|
2172
|
+
{
|
|
2173
|
+
type: "email",
|
|
2174
|
+
placeholder: "john.doe@example.com",
|
|
2175
|
+
value: email,
|
|
2176
|
+
onChange: (e) => setEmail(e.target.value),
|
|
2177
|
+
className: "hsk-form-input"
|
|
2178
|
+
}
|
|
2179
|
+
)
|
|
2180
|
+
] }),
|
|
2181
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-form-row", children: [
|
|
2182
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-form-group", children: [
|
|
2183
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "hsk-form-label", children: "First Name" }),
|
|
2184
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2185
|
+
"input",
|
|
2186
|
+
{
|
|
2187
|
+
type: "text",
|
|
2188
|
+
placeholder: "John",
|
|
2189
|
+
value: firstName,
|
|
2190
|
+
onChange: (e) => setFirstName(e.target.value),
|
|
2191
|
+
className: "hsk-form-input"
|
|
2192
|
+
}
|
|
2193
|
+
)
|
|
2194
|
+
] }),
|
|
2195
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-form-group", children: [
|
|
2196
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { className: "hsk-form-label", children: "Last Name" }),
|
|
2197
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2198
|
+
"input",
|
|
2199
|
+
{
|
|
2200
|
+
type: "text",
|
|
2201
|
+
placeholder: "Doe",
|
|
2202
|
+
value: lastName,
|
|
2203
|
+
onChange: (e) => setLastName(e.target.value),
|
|
2204
|
+
className: "hsk-form-input"
|
|
2205
|
+
}
|
|
2206
|
+
)
|
|
2207
|
+
] })
|
|
2208
|
+
] }),
|
|
2209
|
+
payError && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-form-error-banner", children: payError }),
|
|
2210
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "submit", className: "hsk-checkout-submit-btn", children: [
|
|
2211
|
+
"Pay ",
|
|
2212
|
+
currency,
|
|
2213
|
+
" ",
|
|
2214
|
+
total.toLocaleString()
|
|
2215
|
+
] }),
|
|
2216
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-footer-brand", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Powered by Huskel AI" }) })
|
|
1884
2217
|
] })
|
|
1885
|
-
] }) })
|
|
2218
|
+
] }) }) })
|
|
1886
2219
|
]
|
|
1887
2220
|
}
|
|
1888
2221
|
)
|
|
@@ -1900,11 +2233,11 @@ function CartDrawer({
|
|
|
1900
2233
|
theme
|
|
1901
2234
|
}) {
|
|
1902
2235
|
const { cart, loading } = useCart();
|
|
1903
|
-
const [open, setOpen] = (0,
|
|
1904
|
-
const [showCheckout, setShowCheckout] = (0,
|
|
1905
|
-
const [mounted, setMounted] = (0,
|
|
2236
|
+
const [open, setOpen] = (0, import_react14.useState)(false);
|
|
2237
|
+
const [showCheckout, setShowCheckout] = (0, import_react14.useState)(false);
|
|
2238
|
+
const [mounted, setMounted] = (0, import_react14.useState)(false);
|
|
1906
2239
|
const client = useHuskelContext();
|
|
1907
|
-
(0,
|
|
2240
|
+
(0, import_react14.useEffect)(() => {
|
|
1908
2241
|
setMounted(true);
|
|
1909
2242
|
const handleTriggerCheckout = () => {
|
|
1910
2243
|
setShowCheckout(true);
|
|
@@ -1915,7 +2248,7 @@ function CartDrawer({
|
|
|
1915
2248
|
window.removeEventListener("huskel:trigger_checkout", handleTriggerCheckout);
|
|
1916
2249
|
};
|
|
1917
2250
|
}, []);
|
|
1918
|
-
(0,
|
|
2251
|
+
(0, import_react14.useEffect)(() => {
|
|
1919
2252
|
if (open) {
|
|
1920
2253
|
document.body.style.overflow = "hidden";
|
|
1921
2254
|
} else {
|
|
@@ -1927,6 +2260,12 @@ function CartDrawer({
|
|
|
1927
2260
|
}, [open]);
|
|
1928
2261
|
const handleCheckout = async () => {
|
|
1929
2262
|
if (!cart || cart.items.length === 0) return;
|
|
2263
|
+
const event = new CustomEvent("huskel:trigger_checkout", { cancelable: true });
|
|
2264
|
+
window.dispatchEvent(event);
|
|
2265
|
+
if (event.defaultPrevented) {
|
|
2266
|
+
setOpen(false);
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
1930
2269
|
setShowCheckout(true);
|
|
1931
2270
|
};
|
|
1932
2271
|
const isStringTheme = typeof theme === "string";
|
|
@@ -2034,8 +2373,10 @@ function CartDrawer({
|
|
|
2034
2373
|
useCart,
|
|
2035
2374
|
useChat,
|
|
2036
2375
|
useHuskel,
|
|
2376
|
+
useHuskelContext,
|
|
2037
2377
|
useIngest,
|
|
2038
2378
|
usePageIngest,
|
|
2379
|
+
usePaymentPolling,
|
|
2039
2380
|
useSearch
|
|
2040
2381
|
});
|
|
2041
2382
|
//# sourceMappingURL=index.js.map
|