@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.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 = (_e = e == null ? void 0 : e.message) != null ? _e : "Chat request failed";
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/components/SearchBar.tsx
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, import_react8.useState)("");
770
- const [open, setOpen] = (0, import_react8.useState)(false);
771
- const [isDebouncing, setIsDebouncing] = (0, import_react8.useState)(false);
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, import_react8.useRef)();
775
- const wrap = (0, import_react8.useRef)(null);
776
- const ignoreNextQueryChange = (0, import_react8.useRef)(false);
777
- (0, import_react8.useEffect)(() => {
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, import_react8.useEffect)(() => {
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 import_react9 = require("react");
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, import_react9.useState)(null);
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, import_react9.useState)("");
1038
- const chatBottomRef = (0, import_react9.useRef)(null);
1039
- const chatTextareaRef = (0, import_react9.useRef)(null);
1040
- (0, import_react9.useEffect)(() => {
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, import_react9.useEffect)(() => {
1161
+ (0, import_react10.useEffect)(() => {
1051
1162
  if (results.length > 0) onResult == null ? void 0 : onResult(results);
1052
1163
  }, [results, onResult]);
1053
- (0, import_react9.useEffect)(() => {
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, import_react9.useEffect)(() => {
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, import_react9.useState)(false);
1293
- const [mounted, setMounted] = (0, import_react9.useState)(false);
1294
- (0, import_react9.useEffect)(() => {
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 import_react10 = require("react");
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, import_react10.useState)("");
1366
- const bottomRef = (0, import_react10.useRef)(null);
1367
- const textareaRef = (0, import_react10.useRef)(null);
1368
- (0, import_react10.useEffect)(() => {
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 import_react11 = require("react");
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, import_react11.useRef)(null);
1486
- const [showNext, setShowNext] = (0, import_react11.useState)(false);
1487
- const measure = (0, import_react11.useCallback)(() => {
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, import_react11.useEffect)(() => {
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 { messages, sources, loading, error, send, reset } = useChat();
1559
- const [input, setInput] = (0, import_react11.useState)("");
1560
- const [selectedProduct, setSelectedProduct] = (0, import_react11.useState)(null);
1561
- const bottomRef = (0, import_react11.useRef)(null);
1562
- const textareaRef = (0, import_react11.useRef)(null);
1563
- (0, import_react11.useEffect)(() => {
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, import_react11.useEffect)(() => {
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, import_react11.useState)(false);
1732
- const [mounted, setMounted] = (0, import_react11.useState)(false);
1733
- (0, import_react11.useEffect)(() => {
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 import_react13 = require("react");
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 import_react12 = require("react");
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, import_react12.useState)(null);
1798
- const [loading, setLoading] = (0, import_react12.useState)(true);
1799
- const [checkingOut, setCheckingOut] = (0, import_react12.useState)(false);
1800
- const [paymentSuccess, setPaymentSuccess] = (0, import_react12.useState)(false);
1801
- (0, import_react12.useEffect)(() => {
1802
- client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch((e) => console.error("[Huskel] Failed to fetch checkout config", e)).finally(() => setLoading(false));
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 handlePay = async (method) => {
1805
- setCheckingOut(true);
1806
- setTimeout(async () => {
1807
- try {
1808
- const payload = await client.api.checkoutCart();
1809
- if (client.onCheckout) {
1810
- client.onCheckout(payload);
1811
- }
1812
- setPaymentSuccess(true);
1813
- setTimeout(() => {
1814
- onClose();
1815
- }, 3e3);
1816
- } catch (e) {
1817
- console.error("[Huskel] Checkout failed", e);
1818
- setCheckingOut(false);
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
- }, 1500);
2053
+ } catch (err) {
2054
+ setPhase("failed");
2055
+ setPayError(err.message || "Could not connect to payment processor.");
2056
+ }
1821
2057
  };
1822
- const hasPaymentMethods = config && Object.values(config).some((m) => m.enabled);
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-cart-backdrop",
1828
- style: __spreadProps(__spreadValues({}, customStyles), { zIndex: 999999 }),
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-header", children: [
1840
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h2", { children: "Secure Checkout" }),
1841
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: onClose, className: "hsk-close-btn", children: "\xD7" })
1842
- ] }),
1843
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "hsk-checkout-content", children: paymentSuccess ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-success", children: [
1844
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "hsk-success-icon", children: [
1845
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
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)("h3", { children: "Payment Successful!" }),
1849
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { children: "Thank you for your order." })
1850
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-split", children: [
1851
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-checkout-summary", children: [
1852
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Order Summary" }),
1853
- cartLoading || !cart ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-cart-loading", children: "Loading order..." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1854
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("ul", { className: "hsk-checkout-items", children: cart.items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("li", { children: [
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.jsxs)("div", { className: "hsk-checkout-payment", children: [
1877
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: "Payment Method" }),
1878
- loading ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-cart-loading", children: "Loading secure payment methods..." }) : !hasPaymentMethods ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "hsk-checkout-error", children: "No payment methods are currently available for this store." }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "hsk-payment-options", children: [
1879
- ((_a = config == null ? void 0 : config.mpesa) == null ? void 0 : _a.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("mpesa"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-mpesa", children: checkingOut ? "Processing..." : "Pay with M-Pesa" }),
1880
- ((_b = config == null ? void 0 : config.equity) == null ? void 0 : _b.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("equity"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-equity", children: checkingOut ? "Processing..." : "Pay with Equity Bank" }),
1881
- ((_c = config == null ? void 0 : config.stripe) == null ? void 0 : _c.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("stripe"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-stripe", children: checkingOut ? "Processing..." : "Pay with Card (Stripe)" }),
1882
- ((_d = config == null ? void 0 : config.paypal) == null ? void 0 : _d.enabled) && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { onClick: () => handlePay("paypal"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-paypal", children: checkingOut ? "Processing..." : "Pay with PayPal" })
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, import_react13.useState)(false);
1904
- const [showCheckout, setShowCheckout] = (0, import_react13.useState)(false);
1905
- const [mounted, setMounted] = (0, import_react13.useState)(false);
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, import_react13.useEffect)(() => {
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, import_react13.useEffect)(() => {
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