@huskel/sdk 0.4.3 → 0.4.7
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 +91 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +20 -2
- package/dist/index.d.ts +20 -2
- package/dist/index.js +315 -89
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +324 -100
- 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);
|
|
@@ -207,6 +209,32 @@ var HuskelAPI = class {
|
|
|
207
209
|
if (!res.ok) throw new Error("Failed to fetch checkout config");
|
|
208
210
|
return res.json();
|
|
209
211
|
}
|
|
212
|
+
async initiatePayment(phoneNumber, email, firstName, lastName) {
|
|
213
|
+
const res = await fetch(`${this.apiUrl}/payment/initiate`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: this.buildHeaders(),
|
|
216
|
+
body: JSON.stringify({
|
|
217
|
+
siteId: this.siteId,
|
|
218
|
+
phoneNumber,
|
|
219
|
+
email,
|
|
220
|
+
firstName,
|
|
221
|
+
lastName
|
|
222
|
+
})
|
|
223
|
+
});
|
|
224
|
+
if (!res.ok) {
|
|
225
|
+
const errText = await res.text();
|
|
226
|
+
throw new Error("Failed to initiate payment: " + errText);
|
|
227
|
+
}
|
|
228
|
+
return res.json();
|
|
229
|
+
}
|
|
230
|
+
async getPaymentStatus(ref) {
|
|
231
|
+
const res = await fetch(`${this.apiUrl}/payment/status?ref=${ref}`, {
|
|
232
|
+
method: "GET",
|
|
233
|
+
headers: this.buildHeaders()
|
|
234
|
+
});
|
|
235
|
+
if (!res.ok) throw new Error("Failed to get payment status");
|
|
236
|
+
return res.json();
|
|
237
|
+
}
|
|
210
238
|
};
|
|
211
239
|
|
|
212
240
|
// src/client.ts
|
|
@@ -358,6 +386,9 @@ var _HuskelClient = class _HuskelClient {
|
|
|
358
386
|
} catch (e) {
|
|
359
387
|
}
|
|
360
388
|
}
|
|
389
|
+
reRegister() {
|
|
390
|
+
instance = this;
|
|
391
|
+
}
|
|
361
392
|
setShopperId(id) {
|
|
362
393
|
this.shopperId = id;
|
|
363
394
|
}
|
|
@@ -491,11 +522,17 @@ function HuskelProvider({ siteId, apiUrl, apiToken, shopperId, children }) {
|
|
|
491
522
|
const clientRef = (0, import_react2.useRef)(null);
|
|
492
523
|
if (!clientRef.current) {
|
|
493
524
|
clientRef.current = new HuskelClient({ siteId, apiUrl, apiToken, shopperId });
|
|
525
|
+
} else {
|
|
526
|
+
clientRef.current.reRegister();
|
|
494
527
|
}
|
|
495
528
|
(0, import_react2.useEffect)(() => {
|
|
496
529
|
var _a;
|
|
497
530
|
(_a = clientRef.current) == null ? void 0 : _a.setShopperId(shopperId);
|
|
498
531
|
}, [shopperId]);
|
|
532
|
+
(0, import_react2.useEffect)(() => {
|
|
533
|
+
var _a;
|
|
534
|
+
(_a = clientRef.current) == null ? void 0 : _a.reRegister();
|
|
535
|
+
}, []);
|
|
499
536
|
(0, import_react2.useEffect)(() => {
|
|
500
537
|
return () => {
|
|
501
538
|
var _a;
|
|
@@ -527,6 +564,7 @@ function useSearch() {
|
|
|
527
564
|
return;
|
|
528
565
|
}
|
|
529
566
|
const gen = ++genRef.current;
|
|
567
|
+
setLoading(true);
|
|
530
568
|
setError(null);
|
|
531
569
|
try {
|
|
532
570
|
const res = await client.api.searchAutocomplete(query, limit);
|
|
@@ -617,9 +655,10 @@ function useChat() {
|
|
|
617
655
|
const [sources, setSources] = (0, import_react6.useState)([]);
|
|
618
656
|
const [loading, setLoading] = (0, import_react6.useState)(false);
|
|
619
657
|
const [error, setError] = (0, import_react6.useState)(null);
|
|
658
|
+
const [lastAction, setLastAction] = (0, import_react6.useState)(null);
|
|
620
659
|
const abortRef = (0, import_react6.useRef)(null);
|
|
621
660
|
const send = (0, import_react6.useCallback)(async (query, displayQuery) => {
|
|
622
|
-
var _a, _b, _c, _d, _e;
|
|
661
|
+
var _a, _b, _c, _d, _e, _f;
|
|
623
662
|
if (!query.trim() || loading) return;
|
|
624
663
|
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
625
664
|
abortRef.current = new AbortController();
|
|
@@ -650,6 +689,7 @@ function useChat() {
|
|
|
650
689
|
}
|
|
651
690
|
if (signal.aborted) return;
|
|
652
691
|
setSources((_b = res.sources) != null ? _b : []);
|
|
692
|
+
if (res.action) setLastAction(res.action);
|
|
653
693
|
if (((_c = res.action) == null ? void 0 : _c.type) === "add_to_cart" || res.checkout) {
|
|
654
694
|
if (typeof window !== "undefined") {
|
|
655
695
|
window.dispatchEvent(new CustomEvent("huskel:cart_updated", { detail: res.checkout }));
|
|
@@ -660,12 +700,17 @@ function useChat() {
|
|
|
660
700
|
window.dispatchEvent(new CustomEvent("huskel:trigger_checkout", { detail: res.checkout }));
|
|
661
701
|
}
|
|
662
702
|
}
|
|
703
|
+
if (((_e = res.action) == null ? void 0 : _e.type) === "awaiting_payment") {
|
|
704
|
+
if (typeof window !== "undefined") {
|
|
705
|
+
window.dispatchEvent(new CustomEvent("huskel:awaiting_payment", { detail: res.action }));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
663
708
|
if (res.checkout && client.onCheckout) {
|
|
664
709
|
client.onCheckout(res.checkout);
|
|
665
710
|
}
|
|
666
711
|
} catch (e) {
|
|
667
712
|
if (signal.aborted) return;
|
|
668
|
-
let msg = (
|
|
713
|
+
let msg = (_f = e == null ? void 0 : e.message) != null ? _f : "Chat request failed";
|
|
669
714
|
try {
|
|
670
715
|
const parsed = JSON.parse(msg);
|
|
671
716
|
if (parsed && parsed.error) {
|
|
@@ -688,8 +733,9 @@ function useChat() {
|
|
|
688
733
|
setSources([]);
|
|
689
734
|
setError(null);
|
|
690
735
|
setLoading(false);
|
|
736
|
+
setLastAction(null);
|
|
691
737
|
}, []);
|
|
692
|
-
return { messages, sources, loading, error, send, reset };
|
|
738
|
+
return { messages, sources, loading, error, lastAction, send, reset };
|
|
693
739
|
}
|
|
694
740
|
|
|
695
741
|
// src/hooks/useCart.ts
|
|
@@ -728,8 +774,71 @@ function useCart() {
|
|
|
728
774
|
return { cart, loading, fetchCart };
|
|
729
775
|
}
|
|
730
776
|
|
|
731
|
-
// src/
|
|
777
|
+
// src/hooks/usePaymentPolling.ts
|
|
732
778
|
var import_react8 = require("react");
|
|
779
|
+
function usePaymentPolling({
|
|
780
|
+
client,
|
|
781
|
+
merchantReference,
|
|
782
|
+
onSuccess,
|
|
783
|
+
onFailure,
|
|
784
|
+
intervalMs = 3e3,
|
|
785
|
+
timeoutMs = 3e5
|
|
786
|
+
// 5 minutes default
|
|
787
|
+
}) {
|
|
788
|
+
const [status, setStatus] = (0, import_react8.useState)("IDLE");
|
|
789
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
790
|
+
const onSuccessRef = (0, import_react8.useRef)(onSuccess);
|
|
791
|
+
const onFailureRef = (0, import_react8.useRef)(onFailure);
|
|
792
|
+
(0, import_react8.useEffect)(() => {
|
|
793
|
+
onSuccessRef.current = onSuccess;
|
|
794
|
+
onFailureRef.current = onFailure;
|
|
795
|
+
}, [onSuccess, onFailure]);
|
|
796
|
+
(0, import_react8.useEffect)(() => {
|
|
797
|
+
if (!merchantReference) {
|
|
798
|
+
setStatus("IDLE");
|
|
799
|
+
setError(null);
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
setStatus("PENDING");
|
|
803
|
+
setError(null);
|
|
804
|
+
const startTime = Date.now();
|
|
805
|
+
let timerId = null;
|
|
806
|
+
async function checkStatus() {
|
|
807
|
+
try {
|
|
808
|
+
if (Date.now() - startTime >= timeoutMs) {
|
|
809
|
+
setStatus("FAILED");
|
|
810
|
+
setError("Payment session timed out");
|
|
811
|
+
if (onFailureRef.current) onFailureRef.current("Payment session timed out");
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
const res = await client.getPaymentStatus(merchantReference);
|
|
815
|
+
if (res.status === "COMPLETED") {
|
|
816
|
+
setStatus("COMPLETED");
|
|
817
|
+
if (onSuccessRef.current) onSuccessRef.current();
|
|
818
|
+
} else if (res.status === "FAILED") {
|
|
819
|
+
setStatus("FAILED");
|
|
820
|
+
setError("Payment failed");
|
|
821
|
+
if (onFailureRef.current) onFailureRef.current("Payment failed");
|
|
822
|
+
} else {
|
|
823
|
+
timerId = setTimeout(checkStatus, intervalMs);
|
|
824
|
+
}
|
|
825
|
+
} catch (err) {
|
|
826
|
+
console.error("[Huskel Polling Error]", err);
|
|
827
|
+
timerId = setTimeout(checkStatus, intervalMs);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
timerId = setTimeout(checkStatus, intervalMs);
|
|
831
|
+
return () => {
|
|
832
|
+
if (timerId) {
|
|
833
|
+
clearTimeout(timerId);
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
}, [client, merchantReference, intervalMs, timeoutMs]);
|
|
837
|
+
return { status, error };
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// src/components/SearchBar.tsx
|
|
841
|
+
var import_react9 = require("react");
|
|
733
842
|
|
|
734
843
|
// src/utils/cn.ts
|
|
735
844
|
var import_clsx = require("clsx");
|
|
@@ -747,7 +856,7 @@ var SearchIcon = () => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { wi
|
|
|
747
856
|
function SearchBar({
|
|
748
857
|
placeholder = "Search products\u2026",
|
|
749
858
|
limit = 10,
|
|
750
|
-
debounceMs =
|
|
859
|
+
debounceMs = 300,
|
|
751
860
|
onSelect,
|
|
752
861
|
className,
|
|
753
862
|
inputClassName,
|
|
@@ -756,25 +865,35 @@ function SearchBar({
|
|
|
756
865
|
theme,
|
|
757
866
|
classNames = {}
|
|
758
867
|
}) {
|
|
759
|
-
const [query, setQuery] = (0,
|
|
760
|
-
const [open, setOpen] = (0,
|
|
868
|
+
const [query, setQuery] = (0, import_react9.useState)("");
|
|
869
|
+
const [open, setOpen] = (0, import_react9.useState)(false);
|
|
870
|
+
const [isDebouncing, setIsDebouncing] = (0, import_react9.useState)(false);
|
|
761
871
|
const { results, loading, search, clear } = useSearch();
|
|
762
|
-
const
|
|
763
|
-
const
|
|
764
|
-
(0,
|
|
872
|
+
const client = useHuskelContext();
|
|
873
|
+
const timer = (0, import_react9.useRef)();
|
|
874
|
+
const wrap = (0, import_react9.useRef)(null);
|
|
875
|
+
const ignoreNextQueryChange = (0, import_react9.useRef)(false);
|
|
876
|
+
(0, import_react9.useEffect)(() => {
|
|
877
|
+
if (ignoreNextQueryChange.current) {
|
|
878
|
+
ignoreNextQueryChange.current = false;
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
765
881
|
clearTimeout(timer.current);
|
|
766
882
|
if (!query.trim()) {
|
|
767
883
|
clear();
|
|
768
884
|
setOpen(false);
|
|
885
|
+
setIsDebouncing(false);
|
|
769
886
|
return;
|
|
770
887
|
}
|
|
771
888
|
setOpen(true);
|
|
889
|
+
setIsDebouncing(true);
|
|
772
890
|
timer.current = setTimeout(() => {
|
|
891
|
+
setIsDebouncing(false);
|
|
773
892
|
search(query, limit);
|
|
774
893
|
}, debounceMs);
|
|
775
894
|
return () => clearTimeout(timer.current);
|
|
776
895
|
}, [query]);
|
|
777
|
-
(0,
|
|
896
|
+
(0, import_react9.useEffect)(() => {
|
|
778
897
|
const h = (e) => {
|
|
779
898
|
if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
|
|
780
899
|
};
|
|
@@ -782,10 +901,23 @@ function SearchBar({
|
|
|
782
901
|
return () => document.removeEventListener("mousedown", h);
|
|
783
902
|
}, []);
|
|
784
903
|
const handleSelect = (r) => {
|
|
904
|
+
if (query.trim()) {
|
|
905
|
+
client.api.searchVector(query, 1).catch(() => {
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
ignoreNextQueryChange.current = true;
|
|
785
909
|
setOpen(false);
|
|
786
910
|
setQuery(r.product.name);
|
|
787
911
|
onSelect == null ? void 0 : onSelect(r);
|
|
788
912
|
};
|
|
913
|
+
const handleCommitSearch = () => {
|
|
914
|
+
if (!query.trim()) return;
|
|
915
|
+
client.api.searchVector(query, 1).catch(() => {
|
|
916
|
+
});
|
|
917
|
+
if (results.length > 0) {
|
|
918
|
+
handleSelect(results[0]);
|
|
919
|
+
}
|
|
920
|
+
};
|
|
789
921
|
const showDrop = open && query.trim().length > 0;
|
|
790
922
|
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 });
|
|
791
923
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn("hsk-sb-wrap", classNames.root, className), ref: wrap, style: customStyles, children: [
|
|
@@ -799,51 +931,73 @@ function SearchBar({
|
|
|
799
931
|
placeholder,
|
|
800
932
|
onChange: (e) => setQuery(e.target.value),
|
|
801
933
|
onFocus: () => results.length > 0 && query.trim() && setOpen(true),
|
|
934
|
+
onKeyDown: (e) => {
|
|
935
|
+
if (e.key === "Enter") {
|
|
936
|
+
handleCommitSearch();
|
|
937
|
+
}
|
|
938
|
+
},
|
|
802
939
|
autoComplete: "off",
|
|
803
940
|
spellCheck: false
|
|
804
941
|
}
|
|
805
942
|
),
|
|
806
943
|
showDrop && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn("hsk-sb-drop", classNames.dropdown, dropdownClassName), style: { position: "absolute" }, children: [
|
|
807
|
-
loading && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-loading-bar" }),
|
|
808
|
-
results.length === 0
|
|
809
|
-
"
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
{
|
|
818
|
-
|
|
819
|
-
className: "hsk-sb-
|
|
820
|
-
style: {
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
"
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
944
|
+
(loading || isDebouncing) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-loading-bar" }),
|
|
945
|
+
(loading || isDebouncing) && results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
946
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-skeleton-row", children: [
|
|
947
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-skeleton-icon" }),
|
|
948
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-row-body", children: [
|
|
949
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-skeleton-text1" }),
|
|
950
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-skeleton-text2" })
|
|
951
|
+
] })
|
|
952
|
+
] }),
|
|
953
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-skeleton-row", children: [
|
|
954
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-skeleton-icon" }),
|
|
955
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-row-body", children: [
|
|
956
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-skeleton-text1", style: { width: "45%" } }),
|
|
957
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-skeleton-text2", style: { width: "25%" } })
|
|
958
|
+
] })
|
|
959
|
+
] })
|
|
960
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
961
|
+
results.length === 0 && !loading && !isDebouncing && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-empty", children: [
|
|
962
|
+
"No results for \u201C",
|
|
963
|
+
query,
|
|
964
|
+
"\u201D"
|
|
965
|
+
] }),
|
|
966
|
+
results.map((r, i) => {
|
|
967
|
+
var _a;
|
|
968
|
+
return renderResult ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
969
|
+
"div",
|
|
970
|
+
{
|
|
971
|
+
onClick: () => handleSelect(r),
|
|
972
|
+
className: "hsk-sb-fade",
|
|
973
|
+
style: { animationDelay: `${i * 18}ms` },
|
|
974
|
+
children: renderResult(r)
|
|
975
|
+
},
|
|
976
|
+
r.id
|
|
977
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
978
|
+
"div",
|
|
979
|
+
{
|
|
980
|
+
className: cn("hsk-sb-row hsk-sb-fade", classNames.row),
|
|
981
|
+
style: { animationDelay: `${i * 18}ms` },
|
|
982
|
+
onClick: () => handleSelect(r),
|
|
983
|
+
children: [
|
|
984
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "hsk-sb-row-icon", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SearchIcon, {}) }),
|
|
985
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "hsk-sb-row-body", children: [
|
|
986
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-row-title", children: r.product.name }),
|
|
987
|
+
(r.product.category || r.product.brand) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "hsk-sb-row-sub", children: (_a = r.product.category) != null ? _a : r.product.brand })
|
|
988
|
+
] })
|
|
989
|
+
]
|
|
990
|
+
},
|
|
991
|
+
r.id
|
|
992
|
+
);
|
|
993
|
+
})
|
|
994
|
+
] })
|
|
841
995
|
] })
|
|
842
996
|
] });
|
|
843
997
|
}
|
|
844
998
|
|
|
845
999
|
// src/components/Sparkle.tsx
|
|
846
|
-
var
|
|
1000
|
+
var import_react10 = require("react");
|
|
847
1001
|
var import_react_dom = require("react-dom");
|
|
848
1002
|
|
|
849
1003
|
// src/utils/markdown.tsx
|
|
@@ -975,14 +1129,14 @@ function SparkleModal({
|
|
|
975
1129
|
}) {
|
|
976
1130
|
var _a, _b, _c;
|
|
977
1131
|
const client = useHuskelContext();
|
|
978
|
-
const [fetchedProduct, setFetchedProduct] = (0,
|
|
1132
|
+
const [fetchedProduct, setFetchedProduct] = (0, import_react10.useState)(null);
|
|
979
1133
|
const displayProduct = initialProduct || fetchedProduct;
|
|
980
1134
|
const { results, loading: searchLoading, search } = useSearch();
|
|
981
1135
|
const { messages, sources, loading: chatLoading, error: chatError, send } = useChat();
|
|
982
|
-
const [chatInput, setChatInput] = (0,
|
|
983
|
-
const chatBottomRef = (0,
|
|
984
|
-
const chatTextareaRef = (0,
|
|
985
|
-
(0,
|
|
1136
|
+
const [chatInput, setChatInput] = (0, import_react10.useState)("");
|
|
1137
|
+
const chatBottomRef = (0, import_react10.useRef)(null);
|
|
1138
|
+
const chatTextareaRef = (0, import_react10.useRef)(null);
|
|
1139
|
+
(0, import_react10.useEffect)(() => {
|
|
986
1140
|
if (!initialProduct && !fetchedProduct) {
|
|
987
1141
|
client.api.searchVector(productName, 1).then((res) => {
|
|
988
1142
|
if (res.results && res.results.length > 0) {
|
|
@@ -992,17 +1146,17 @@ function SparkleModal({
|
|
|
992
1146
|
}
|
|
993
1147
|
search(productName, limit);
|
|
994
1148
|
}, [productName, initialProduct, fetchedProduct, client, limit, search]);
|
|
995
|
-
(0,
|
|
1149
|
+
(0, import_react10.useEffect)(() => {
|
|
996
1150
|
if (results.length > 0) onResult == null ? void 0 : onResult(results);
|
|
997
1151
|
}, [results, onResult]);
|
|
998
|
-
(0,
|
|
1152
|
+
(0, import_react10.useEffect)(() => {
|
|
999
1153
|
const h = (e) => {
|
|
1000
1154
|
if (e.key === "Escape") onClose();
|
|
1001
1155
|
};
|
|
1002
1156
|
document.addEventListener("keydown", h);
|
|
1003
1157
|
return () => document.removeEventListener("keydown", h);
|
|
1004
1158
|
}, [onClose]);
|
|
1005
|
-
(0,
|
|
1159
|
+
(0, import_react10.useEffect)(() => {
|
|
1006
1160
|
var _a2;
|
|
1007
1161
|
(_a2 = chatBottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1008
1162
|
}, [messages, chatLoading]);
|
|
@@ -1234,9 +1388,9 @@ function Sparkle({
|
|
|
1234
1388
|
classNames = {},
|
|
1235
1389
|
product
|
|
1236
1390
|
}) {
|
|
1237
|
-
const [open, setOpen] = (0,
|
|
1238
|
-
const [mounted, setMounted] = (0,
|
|
1239
|
-
(0,
|
|
1391
|
+
const [open, setOpen] = (0, import_react10.useState)(false);
|
|
1392
|
+
const [mounted, setMounted] = (0, import_react10.useState)(false);
|
|
1393
|
+
(0, import_react10.useEffect)(() => {
|
|
1240
1394
|
setMounted(true);
|
|
1241
1395
|
}, []);
|
|
1242
1396
|
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 });
|
|
@@ -1274,7 +1428,7 @@ function Sparkle({
|
|
|
1274
1428
|
}
|
|
1275
1429
|
|
|
1276
1430
|
// src/components/ChatWidget.tsx
|
|
1277
|
-
var
|
|
1431
|
+
var import_react11 = require("react");
|
|
1278
1432
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1279
1433
|
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" }) });
|
|
1280
1434
|
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: [
|
|
@@ -1307,10 +1461,10 @@ function ChatWidget({
|
|
|
1307
1461
|
onSelectSource
|
|
1308
1462
|
}) {
|
|
1309
1463
|
const { messages, sources, loading, error, send, reset } = useChat();
|
|
1310
|
-
const [input, setInput] = (0,
|
|
1311
|
-
const bottomRef = (0,
|
|
1312
|
-
const textareaRef = (0,
|
|
1313
|
-
(0,
|
|
1464
|
+
const [input, setInput] = (0, import_react11.useState)("");
|
|
1465
|
+
const bottomRef = (0, import_react11.useRef)(null);
|
|
1466
|
+
const textareaRef = (0, import_react11.useRef)(null);
|
|
1467
|
+
(0, import_react11.useEffect)(() => {
|
|
1314
1468
|
var _a;
|
|
1315
1469
|
(_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
|
|
1316
1470
|
}, [messages, loading]);
|
|
@@ -1407,7 +1561,7 @@ function ChatWidget({
|
|
|
1407
1561
|
}
|
|
1408
1562
|
|
|
1409
1563
|
// src/components/AIChatButton.tsx
|
|
1410
|
-
var
|
|
1564
|
+
var import_react12 = require("react");
|
|
1411
1565
|
var import_react_dom2 = require("react-dom");
|
|
1412
1566
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1413
1567
|
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" }) });
|
|
@@ -1427,15 +1581,15 @@ var DEFAULT_CHIPS = [
|
|
|
1427
1581
|
"Best laptop for students"
|
|
1428
1582
|
];
|
|
1429
1583
|
function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
1430
|
-
const railRef = (0,
|
|
1431
|
-
const [showNext, setShowNext] = (0,
|
|
1432
|
-
const measure = (0,
|
|
1584
|
+
const railRef = (0, import_react12.useRef)(null);
|
|
1585
|
+
const [showNext, setShowNext] = (0, import_react12.useState)(false);
|
|
1586
|
+
const measure = (0, import_react12.useCallback)(() => {
|
|
1433
1587
|
const el = railRef.current;
|
|
1434
1588
|
if (!el) return;
|
|
1435
1589
|
const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;
|
|
1436
1590
|
setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);
|
|
1437
1591
|
}, []);
|
|
1438
|
-
(0,
|
|
1592
|
+
(0, import_react12.useEffect)(() => {
|
|
1439
1593
|
measure();
|
|
1440
1594
|
const el = railRef.current;
|
|
1441
1595
|
if (!el) return;
|
|
@@ -1500,16 +1654,53 @@ function ChatModal({
|
|
|
1500
1654
|
classNames = {}
|
|
1501
1655
|
}) {
|
|
1502
1656
|
var _a, _b;
|
|
1503
|
-
const
|
|
1504
|
-
const
|
|
1505
|
-
const [
|
|
1506
|
-
const
|
|
1507
|
-
const
|
|
1508
|
-
(0,
|
|
1657
|
+
const client = useHuskelContext();
|
|
1658
|
+
const { messages, sources, loading, error, lastAction, send, reset } = useChat();
|
|
1659
|
+
const [input, setInput] = (0, import_react12.useState)("");
|
|
1660
|
+
const [selectedProduct, setSelectedProduct] = (0, import_react12.useState)(null);
|
|
1661
|
+
const bottomRef = (0, import_react12.useRef)(null);
|
|
1662
|
+
const textareaRef = (0, import_react12.useRef)(null);
|
|
1663
|
+
const [phoneInput, setPhoneInput] = (0, import_react12.useState)("");
|
|
1664
|
+
const [merchantRef, setMerchantRef] = (0, import_react12.useState)(null);
|
|
1665
|
+
const [paymentPhase, setPaymentPhase] = (0, import_react12.useState)("idle");
|
|
1666
|
+
const { status: pollStatus } = usePaymentPolling({
|
|
1667
|
+
client: client.api,
|
|
1668
|
+
merchantReference: merchantRef,
|
|
1669
|
+
onSuccess: () => {
|
|
1670
|
+
setPaymentPhase("done");
|
|
1671
|
+
setMerchantRef(null);
|
|
1672
|
+
},
|
|
1673
|
+
onFailure: () => {
|
|
1674
|
+
setPaymentPhase("failed");
|
|
1675
|
+
setMerchantRef(null);
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
(0, import_react12.useEffect)(() => {
|
|
1679
|
+
var _a2;
|
|
1680
|
+
if (!lastAction) return;
|
|
1681
|
+
if (lastAction.type === "request_phone") {
|
|
1682
|
+
setPaymentPhase("prompt_phone");
|
|
1683
|
+
} else if (lastAction.type === "awaiting_payment") {
|
|
1684
|
+
setMerchantRef((_a2 = lastAction.merchantReference) != null ? _a2 : null);
|
|
1685
|
+
setPaymentPhase("awaiting");
|
|
1686
|
+
}
|
|
1687
|
+
}, [lastAction]);
|
|
1688
|
+
const handlePhoneSubmit = async () => {
|
|
1689
|
+
if (!phoneInput.trim()) return;
|
|
1690
|
+
try {
|
|
1691
|
+
const res = await client.api.initiatePayment(phoneInput);
|
|
1692
|
+
setMerchantRef(res.merchantReference);
|
|
1693
|
+
setPaymentPhase("awaiting");
|
|
1694
|
+
} catch (e) {
|
|
1695
|
+
console.error("[Huskel] initiatePayment error", e);
|
|
1696
|
+
setPaymentPhase("failed");
|
|
1697
|
+
}
|
|
1698
|
+
};
|
|
1699
|
+
(0, import_react12.useEffect)(() => {
|
|
1509
1700
|
var _a2;
|
|
1510
1701
|
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1511
1702
|
}, [messages, loading, selectedProduct]);
|
|
1512
|
-
(0,
|
|
1703
|
+
(0, import_react12.useEffect)(() => {
|
|
1513
1704
|
const h = (e) => {
|
|
1514
1705
|
if (e.key === "Escape") onClose();
|
|
1515
1706
|
};
|
|
@@ -1625,6 +1816,39 @@ function ChatModal({
|
|
|
1625
1816
|
] })
|
|
1626
1817
|
] }),
|
|
1627
1818
|
error && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-error", children: error }),
|
|
1819
|
+
paymentPhase === "prompt_phone" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1820
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", children: "\u{1F4F1}" }),
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", children: "Enter your M-Pesa number to pay" }),
|
|
1822
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1823
|
+
"input",
|
|
1824
|
+
{
|
|
1825
|
+
type: "tel",
|
|
1826
|
+
className: "hsk-cb-phone-input",
|
|
1827
|
+
placeholder: "e.g. 0712 345 678",
|
|
1828
|
+
value: phoneInput,
|
|
1829
|
+
onChange: (e) => setPhoneInput(e.target.value),
|
|
1830
|
+
onKeyDown: (e) => e.key === "Enter" && handlePhoneSubmit()
|
|
1831
|
+
}
|
|
1832
|
+
),
|
|
1833
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-pay-submit", onClick: handlePhoneSubmit, children: "Send STK Push \u2192" })
|
|
1834
|
+
] }),
|
|
1835
|
+
paymentPhase === "awaiting" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1836
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", style: { fontSize: "2rem" }, children: "\u23F3" }),
|
|
1837
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", style: { fontWeight: 600 }, children: "Check your phone" }),
|
|
1838
|
+
/* @__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." })
|
|
1839
|
+
] }),
|
|
1840
|
+
paymentPhase === "done" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1841
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", style: { color: "#22c55e", fontSize: "2rem" }, children: "\u2705" }),
|
|
1842
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", children: "Payment complete! Thank you." })
|
|
1843
|
+
] }),
|
|
1844
|
+
paymentPhase === "failed" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-payment-prompt", children: [
|
|
1845
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "hsk-cb-payment-icon", style: { color: "#ef4444", fontSize: "2rem" }, children: "\u274C" }),
|
|
1846
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { className: "hsk-cb-payment-label", children: "Payment failed or timed out." }),
|
|
1847
|
+
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { className: "hsk-cb-pay-submit", onClick: () => {
|
|
1848
|
+
setPaymentPhase("idle");
|
|
1849
|
+
setMerchantRef(null);
|
|
1850
|
+
}, children: "Try again" })
|
|
1851
|
+
] }),
|
|
1628
1852
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { ref: bottomRef, style: { height: 1 } })
|
|
1629
1853
|
] }),
|
|
1630
1854
|
/* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "hsk-cb-input-wrap", children: [
|
|
@@ -1673,9 +1897,9 @@ function AIChatButton({
|
|
|
1673
1897
|
theme,
|
|
1674
1898
|
classNames = {}
|
|
1675
1899
|
}) {
|
|
1676
|
-
const [open, setOpen] = (0,
|
|
1677
|
-
const [mounted, setMounted] = (0,
|
|
1678
|
-
(0,
|
|
1900
|
+
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
1901
|
+
const [mounted, setMounted] = (0, import_react12.useState)(false);
|
|
1902
|
+
(0, import_react12.useEffect)(() => {
|
|
1679
1903
|
setMounted(true);
|
|
1680
1904
|
}, []);
|
|
1681
1905
|
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 });
|
|
@@ -1723,11 +1947,11 @@ function CartBadge({ className }) {
|
|
|
1723
1947
|
}
|
|
1724
1948
|
|
|
1725
1949
|
// src/components/CartDrawer.tsx
|
|
1726
|
-
var
|
|
1950
|
+
var import_react14 = require("react");
|
|
1727
1951
|
var import_react_dom4 = require("react-dom");
|
|
1728
1952
|
|
|
1729
1953
|
// src/components/CheckoutModal.tsx
|
|
1730
|
-
var
|
|
1954
|
+
var import_react13 = require("react");
|
|
1731
1955
|
var import_react_dom3 = require("react-dom");
|
|
1732
1956
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1733
1957
|
function CheckoutModal({
|
|
@@ -1739,11 +1963,11 @@ function CheckoutModal({
|
|
|
1739
1963
|
var _a, _b, _c, _d;
|
|
1740
1964
|
const { cart, loading: cartLoading } = useCart();
|
|
1741
1965
|
const client = useHuskelContext();
|
|
1742
|
-
const [config, setConfig] = (0,
|
|
1743
|
-
const [loading, setLoading] = (0,
|
|
1744
|
-
const [checkingOut, setCheckingOut] = (0,
|
|
1745
|
-
const [paymentSuccess, setPaymentSuccess] = (0,
|
|
1746
|
-
(0,
|
|
1966
|
+
const [config, setConfig] = (0, import_react13.useState)(null);
|
|
1967
|
+
const [loading, setLoading] = (0, import_react13.useState)(true);
|
|
1968
|
+
const [checkingOut, setCheckingOut] = (0, import_react13.useState)(false);
|
|
1969
|
+
const [paymentSuccess, setPaymentSuccess] = (0, import_react13.useState)(false);
|
|
1970
|
+
(0, import_react13.useEffect)(() => {
|
|
1747
1971
|
client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch((e) => console.error("[Huskel] Failed to fetch checkout config", e)).finally(() => setLoading(false));
|
|
1748
1972
|
}, [client]);
|
|
1749
1973
|
const handlePay = async (method) => {
|
|
@@ -1845,11 +2069,11 @@ function CartDrawer({
|
|
|
1845
2069
|
theme
|
|
1846
2070
|
}) {
|
|
1847
2071
|
const { cart, loading } = useCart();
|
|
1848
|
-
const [open, setOpen] = (0,
|
|
1849
|
-
const [showCheckout, setShowCheckout] = (0,
|
|
1850
|
-
const [mounted, setMounted] = (0,
|
|
2072
|
+
const [open, setOpen] = (0, import_react14.useState)(false);
|
|
2073
|
+
const [showCheckout, setShowCheckout] = (0, import_react14.useState)(false);
|
|
2074
|
+
const [mounted, setMounted] = (0, import_react14.useState)(false);
|
|
1851
2075
|
const client = useHuskelContext();
|
|
1852
|
-
(0,
|
|
2076
|
+
(0, import_react14.useEffect)(() => {
|
|
1853
2077
|
setMounted(true);
|
|
1854
2078
|
const handleTriggerCheckout = () => {
|
|
1855
2079
|
setShowCheckout(true);
|
|
@@ -1860,7 +2084,7 @@ function CartDrawer({
|
|
|
1860
2084
|
window.removeEventListener("huskel:trigger_checkout", handleTriggerCheckout);
|
|
1861
2085
|
};
|
|
1862
2086
|
}, []);
|
|
1863
|
-
(0,
|
|
2087
|
+
(0, import_react14.useEffect)(() => {
|
|
1864
2088
|
if (open) {
|
|
1865
2089
|
document.body.style.overflow = "hidden";
|
|
1866
2090
|
} else {
|
|
@@ -1979,8 +2203,10 @@ function CartDrawer({
|
|
|
1979
2203
|
useCart,
|
|
1980
2204
|
useChat,
|
|
1981
2205
|
useHuskel,
|
|
2206
|
+
useHuskelContext,
|
|
1982
2207
|
useIngest,
|
|
1983
2208
|
usePageIngest,
|
|
2209
|
+
usePaymentPolling,
|
|
1984
2210
|
useSearch
|
|
1985
2211
|
});
|
|
1986
2212
|
//# sourceMappingURL=index.js.map
|