@huskel/sdk 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +1060 -155
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +64 -27
- package/dist/index.d.ts +64 -27
- package/dist/index.js +912 -247
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +870 -208
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -1
package/dist/index.mjs
CHANGED
|
@@ -63,7 +63,15 @@ var HuskelAPI = class {
|
|
|
63
63
|
});
|
|
64
64
|
if (!res.ok) {
|
|
65
65
|
const text = await res.text();
|
|
66
|
-
|
|
66
|
+
let message = text;
|
|
67
|
+
try {
|
|
68
|
+
const parsed = JSON.parse(text);
|
|
69
|
+
if (parsed && typeof parsed.error === "string") {
|
|
70
|
+
message = parsed.error;
|
|
71
|
+
}
|
|
72
|
+
} catch (e) {
|
|
73
|
+
}
|
|
74
|
+
const err = { status: res.status, message };
|
|
67
75
|
if (res.status >= 400 && res.status < 500) {
|
|
68
76
|
log("error", `${path} failed [${res.status}]`, text);
|
|
69
77
|
throw err;
|
|
@@ -114,6 +122,52 @@ var HuskelAPI = class {
|
|
|
114
122
|
log("info", "chat query", query);
|
|
115
123
|
return this.post("/chat", { query, siteId: this.siteId, history });
|
|
116
124
|
}
|
|
125
|
+
// --- Cart System ---
|
|
126
|
+
buildHeaders() {
|
|
127
|
+
var _a, _b;
|
|
128
|
+
const headers = {
|
|
129
|
+
"Content-Type": "application/json",
|
|
130
|
+
"X-Huskel-Token": this.apiToken,
|
|
131
|
+
"X-Huskel-Site": this.siteId
|
|
132
|
+
};
|
|
133
|
+
const shopperId = (_a = this.getShopperId) == null ? void 0 : _a.call(this);
|
|
134
|
+
if (shopperId) headers["X-Huskel-Shopper-Id"] = shopperId;
|
|
135
|
+
const sessionId = (_b = this.getSessionId) == null ? void 0 : _b.call(this);
|
|
136
|
+
if (sessionId) headers["X-Huskel-Session-Id"] = sessionId;
|
|
137
|
+
return headers;
|
|
138
|
+
}
|
|
139
|
+
async getCart() {
|
|
140
|
+
const res = await fetch(`${this.apiUrl}/cart?siteId=${this.siteId}`, {
|
|
141
|
+
headers: this.buildHeaders()
|
|
142
|
+
});
|
|
143
|
+
if (!res.ok) throw new Error("Failed to fetch cart");
|
|
144
|
+
return res.json();
|
|
145
|
+
}
|
|
146
|
+
async clearCart() {
|
|
147
|
+
const res = await fetch(`${this.apiUrl}/cart?siteId=${this.siteId}`, {
|
|
148
|
+
method: "DELETE",
|
|
149
|
+
headers: this.buildHeaders()
|
|
150
|
+
});
|
|
151
|
+
if (!res.ok) throw new Error("Failed to clear cart");
|
|
152
|
+
return res.json();
|
|
153
|
+
}
|
|
154
|
+
async checkoutCart() {
|
|
155
|
+
const res = await fetch(`${this.apiUrl}/cart/checkout`, {
|
|
156
|
+
method: "POST",
|
|
157
|
+
headers: this.buildHeaders(),
|
|
158
|
+
body: JSON.stringify({ siteId: this.siteId })
|
|
159
|
+
});
|
|
160
|
+
if (!res.ok) throw new Error("Failed to checkout cart");
|
|
161
|
+
return res.json();
|
|
162
|
+
}
|
|
163
|
+
async getCheckoutConfig() {
|
|
164
|
+
const res = await fetch(`${this.apiUrl}/checkout/config?site_id=${this.siteId}`, {
|
|
165
|
+
method: "GET",
|
|
166
|
+
headers: this.buildHeaders()
|
|
167
|
+
});
|
|
168
|
+
if (!res.ok) throw new Error("Failed to fetch checkout config");
|
|
169
|
+
return res.json();
|
|
170
|
+
}
|
|
117
171
|
};
|
|
118
172
|
|
|
119
173
|
// src/client.ts
|
|
@@ -221,13 +275,14 @@ var _HuskelClient = class _HuskelClient {
|
|
|
221
275
|
if (!apiUrl) console.error('[Huskel] Missing apiUrl. Set it via <HuskelProvider apiUrl="..."> or NEXT_PUBLIC_HUSKEL_API_URL.');
|
|
222
276
|
if (!apiToken) console.error('[Huskel] Missing apiToken. Set it via <HuskelProvider apiToken="..."> or NEXT_PUBLIC_HUSKEL_API_TOKEN.');
|
|
223
277
|
this.shopperId = config.shopperId;
|
|
278
|
+
this.onCheckout = config.onCheckout;
|
|
224
279
|
this.initSession();
|
|
225
280
|
this.loadIngestedCache();
|
|
226
281
|
this.api = new HuskelAPI(
|
|
227
282
|
apiUrl,
|
|
228
283
|
siteId,
|
|
229
284
|
apiToken,
|
|
230
|
-
() => this.
|
|
285
|
+
() => this.getShopperId(),
|
|
231
286
|
() => this.sessionId
|
|
232
287
|
);
|
|
233
288
|
instance = this;
|
|
@@ -268,7 +323,7 @@ var _HuskelClient = class _HuskelClient {
|
|
|
268
323
|
this.shopperId = id;
|
|
269
324
|
}
|
|
270
325
|
getShopperId() {
|
|
271
|
-
return this.shopperId;
|
|
326
|
+
return this.shopperId || "guest_" + this.sessionId;
|
|
272
327
|
}
|
|
273
328
|
getSessionId() {
|
|
274
329
|
return this.sessionId;
|
|
@@ -441,7 +496,15 @@ function useSearch() {
|
|
|
441
496
|
}
|
|
442
497
|
} catch (e) {
|
|
443
498
|
if (gen === genRef.current) {
|
|
444
|
-
|
|
499
|
+
let msg = (_b = e == null ? void 0 : e.message) != null ? _b : "Search failed";
|
|
500
|
+
try {
|
|
501
|
+
const parsed = JSON.parse(msg);
|
|
502
|
+
if (parsed && parsed.error) {
|
|
503
|
+
msg = parsed.error;
|
|
504
|
+
}
|
|
505
|
+
} catch (e2) {
|
|
506
|
+
}
|
|
507
|
+
setError(msg);
|
|
445
508
|
}
|
|
446
509
|
} finally {
|
|
447
510
|
if (gen === genRef.current) setLoading(false);
|
|
@@ -516,38 +579,127 @@ function useChat() {
|
|
|
516
579
|
const [loading, setLoading] = useState3(false);
|
|
517
580
|
const [error, setError] = useState3(null);
|
|
518
581
|
const abortRef = useRef5(null);
|
|
519
|
-
const send = useCallback3(async (query) => {
|
|
520
|
-
var _a, _b, _c;
|
|
582
|
+
const send = useCallback3(async (query, displayQuery) => {
|
|
583
|
+
var _a, _b, _c, _d, _e;
|
|
521
584
|
if (!query.trim() || loading) return;
|
|
522
585
|
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
523
586
|
abortRef.current = new AbortController();
|
|
524
|
-
const
|
|
587
|
+
const signal = abortRef.current.signal;
|
|
588
|
+
const userMsg = { role: "user", content: displayQuery != null ? displayQuery : query };
|
|
525
589
|
setMessages((prev) => [...prev, userMsg]);
|
|
526
590
|
setLoading(true);
|
|
527
591
|
setError(null);
|
|
528
592
|
try {
|
|
529
593
|
const history = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
530
594
|
const res = await client.api.chat(query, history);
|
|
531
|
-
|
|
532
|
-
|
|
595
|
+
if (signal.aborted) return;
|
|
596
|
+
const fullAnswer = res.answer || "";
|
|
597
|
+
const words = fullAnswer.split(/(\s+)/);
|
|
598
|
+
setMessages((prev) => [...prev, { role: "assistant", content: "" }]);
|
|
599
|
+
let currentContent = "";
|
|
600
|
+
for (const word of words) {
|
|
601
|
+
if (signal.aborted) return;
|
|
602
|
+
currentContent += word;
|
|
603
|
+
setMessages((prev) => {
|
|
604
|
+
const next = [...prev];
|
|
605
|
+
if (next.length > 0) {
|
|
606
|
+
next[next.length - 1] = { role: "assistant", content: currentContent };
|
|
607
|
+
}
|
|
608
|
+
return next;
|
|
609
|
+
});
|
|
610
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
611
|
+
}
|
|
612
|
+
if (signal.aborted) return;
|
|
533
613
|
setSources((_b = res.sources) != null ? _b : []);
|
|
614
|
+
if (((_c = res.action) == null ? void 0 : _c.type) === "add_to_cart" || res.checkout) {
|
|
615
|
+
if (typeof window !== "undefined") {
|
|
616
|
+
window.dispatchEvent(new CustomEvent("huskel:cart_updated", { detail: res.checkout }));
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
if (((_d = res.action) == null ? void 0 : _d.type) === "checkout") {
|
|
620
|
+
if (typeof window !== "undefined") {
|
|
621
|
+
window.dispatchEvent(new CustomEvent("huskel:trigger_checkout", { detail: res.checkout }));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (res.checkout && client.onCheckout) {
|
|
625
|
+
client.onCheckout(res.checkout);
|
|
626
|
+
}
|
|
534
627
|
} catch (e) {
|
|
535
|
-
|
|
628
|
+
if (signal.aborted) return;
|
|
629
|
+
let msg = (_e = e == null ? void 0 : e.message) != null ? _e : "Chat request failed";
|
|
630
|
+
try {
|
|
631
|
+
const parsed = JSON.parse(msg);
|
|
632
|
+
if (parsed && parsed.error) {
|
|
633
|
+
msg = parsed.error;
|
|
634
|
+
}
|
|
635
|
+
} catch (e2) {
|
|
636
|
+
}
|
|
637
|
+
setError(msg);
|
|
536
638
|
setMessages((prev) => prev.slice(0, -1));
|
|
537
639
|
} finally {
|
|
538
|
-
|
|
640
|
+
if (!signal.aborted) {
|
|
641
|
+
setLoading(false);
|
|
642
|
+
}
|
|
539
643
|
}
|
|
540
644
|
}, [client, messages, loading]);
|
|
541
645
|
const reset = useCallback3(() => {
|
|
646
|
+
var _a;
|
|
647
|
+
(_a = abortRef.current) == null ? void 0 : _a.abort();
|
|
542
648
|
setMessages([]);
|
|
543
649
|
setSources([]);
|
|
544
650
|
setError(null);
|
|
651
|
+
setLoading(false);
|
|
545
652
|
}, []);
|
|
546
653
|
return { messages, sources, loading, error, send, reset };
|
|
547
654
|
}
|
|
548
655
|
|
|
656
|
+
// src/hooks/useCart.ts
|
|
657
|
+
import { useState as useState4, useEffect as useEffect3, useCallback as useCallback4 } from "react";
|
|
658
|
+
function useCart() {
|
|
659
|
+
const client = useHuskelContext();
|
|
660
|
+
const [cart, setCart] = useState4(null);
|
|
661
|
+
const [loading, setLoading] = useState4(false);
|
|
662
|
+
const shopperId = client.getShopperId();
|
|
663
|
+
const fetchCart = useCallback4(async () => {
|
|
664
|
+
if (!shopperId) return;
|
|
665
|
+
setLoading(true);
|
|
666
|
+
try {
|
|
667
|
+
const res = await client.api.getCart();
|
|
668
|
+
setCart(res);
|
|
669
|
+
} catch (e) {
|
|
670
|
+
console.error("[Huskel] Failed to fetch cart", e);
|
|
671
|
+
} finally {
|
|
672
|
+
setLoading(false);
|
|
673
|
+
}
|
|
674
|
+
}, [client, shopperId]);
|
|
675
|
+
useEffect3(() => {
|
|
676
|
+
fetchCart();
|
|
677
|
+
const handleCartUpdate = (e) => {
|
|
678
|
+
if (e.detail) {
|
|
679
|
+
setCart(e.detail);
|
|
680
|
+
} else {
|
|
681
|
+
fetchCart();
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
if (typeof window !== "undefined") {
|
|
685
|
+
window.addEventListener("huskel:cart_updated", handleCartUpdate);
|
|
686
|
+
return () => window.removeEventListener("huskel:cart_updated", handleCartUpdate);
|
|
687
|
+
}
|
|
688
|
+
}, [fetchCart, shopperId]);
|
|
689
|
+
return { cart, loading, fetchCart };
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/components/SearchBar.tsx
|
|
693
|
+
import { useState as useState5, useEffect as useEffect4, useRef as useRef6 } from "react";
|
|
694
|
+
|
|
695
|
+
// src/utils/cn.ts
|
|
696
|
+
import { clsx } from "clsx";
|
|
697
|
+
import { twMerge } from "tailwind-merge";
|
|
698
|
+
function cn(...inputs) {
|
|
699
|
+
return twMerge(clsx(inputs));
|
|
700
|
+
}
|
|
701
|
+
|
|
549
702
|
// src/components/SearchBar.tsx
|
|
550
|
-
import { useState as useState4, useEffect as useEffect3, useRef as useRef6 } from "react";
|
|
551
703
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
552
704
|
var SearchIcon = () => /* @__PURE__ */ jsxs("svg", { width: "15", height: "15", viewBox: "0 0 20 20", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
553
705
|
/* @__PURE__ */ jsx2("circle", { cx: "8.5", cy: "8.5", r: "5.5" }),
|
|
@@ -565,12 +717,12 @@ function SearchBar({
|
|
|
565
717
|
theme,
|
|
566
718
|
classNames = {}
|
|
567
719
|
}) {
|
|
568
|
-
const [query, setQuery] =
|
|
569
|
-
const [open, setOpen] =
|
|
720
|
+
const [query, setQuery] = useState5("");
|
|
721
|
+
const [open, setOpen] = useState5(false);
|
|
570
722
|
const { results, loading, search, clear } = useSearch();
|
|
571
723
|
const timer = useRef6();
|
|
572
724
|
const wrap = useRef6(null);
|
|
573
|
-
|
|
725
|
+
useEffect4(() => {
|
|
574
726
|
clearTimeout(timer.current);
|
|
575
727
|
if (!query.trim()) {
|
|
576
728
|
clear();
|
|
@@ -583,7 +735,7 @@ function SearchBar({
|
|
|
583
735
|
}, debounceMs);
|
|
584
736
|
return () => clearTimeout(timer.current);
|
|
585
737
|
}, [query]);
|
|
586
|
-
|
|
738
|
+
useEffect4(() => {
|
|
587
739
|
const h = (e) => {
|
|
588
740
|
if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
|
|
589
741
|
};
|
|
@@ -596,13 +748,13 @@ function SearchBar({
|
|
|
596
748
|
onSelect == null ? void 0 : onSelect(r);
|
|
597
749
|
};
|
|
598
750
|
const showDrop = open && query.trim().length > 0;
|
|
599
|
-
const customStyles = __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 });
|
|
600
|
-
return /* @__PURE__ */ jsxs("div", { className:
|
|
751
|
+
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 });
|
|
752
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-wrap", classNames.root, className), ref: wrap, style: customStyles, children: [
|
|
601
753
|
/* @__PURE__ */ jsx2("span", { className: "hsk-sb-icon", children: /* @__PURE__ */ jsx2(SearchIcon, {}) }),
|
|
602
754
|
/* @__PURE__ */ jsx2(
|
|
603
755
|
"input",
|
|
604
756
|
{
|
|
605
|
-
className:
|
|
757
|
+
className: cn("hsk-sb-input", classNames.input, inputClassName),
|
|
606
758
|
type: "text",
|
|
607
759
|
value: query,
|
|
608
760
|
placeholder,
|
|
@@ -612,7 +764,7 @@ function SearchBar({
|
|
|
612
764
|
spellCheck: false
|
|
613
765
|
}
|
|
614
766
|
),
|
|
615
|
-
showDrop && /* @__PURE__ */ jsxs("div", { className:
|
|
767
|
+
showDrop && /* @__PURE__ */ jsxs("div", { className: cn("hsk-sb-drop", classNames.dropdown, dropdownClassName), style: { position: "absolute" }, children: [
|
|
616
768
|
loading && /* @__PURE__ */ jsx2("div", { className: "hsk-sb-loading-bar" }),
|
|
617
769
|
results.length === 0 && !loading && /* @__PURE__ */ jsxs("div", { className: "hsk-sb-empty", children: [
|
|
618
770
|
"No results for \u201C",
|
|
@@ -633,7 +785,7 @@ function SearchBar({
|
|
|
633
785
|
) : /* @__PURE__ */ jsxs(
|
|
634
786
|
"div",
|
|
635
787
|
{
|
|
636
|
-
className:
|
|
788
|
+
className: cn("hsk-sb-row hsk-sb-fade", classNames.row),
|
|
637
789
|
style: { animationDelay: `${i * 18}ms` },
|
|
638
790
|
onClick: () => handleSelect(r),
|
|
639
791
|
children: [
|
|
@@ -652,14 +804,124 @@ function SearchBar({
|
|
|
652
804
|
}
|
|
653
805
|
|
|
654
806
|
// src/components/Sparkle.tsx
|
|
655
|
-
import { useState as
|
|
807
|
+
import { useState as useState6, useEffect as useEffect5, useRef as useRef7 } from "react";
|
|
656
808
|
import { createPortal } from "react-dom";
|
|
657
|
-
|
|
658
|
-
|
|
809
|
+
|
|
810
|
+
// src/utils/markdown.tsx
|
|
811
|
+
import { Fragment, jsx as jsx3 } from "react/jsx-runtime";
|
|
812
|
+
var parseInline = (text, keyPrefix) => {
|
|
813
|
+
const tokenRegex = /(\[[^\]]+\]\([^)]+\)|\*\*[^*]+\*\*|`[^`]+`)/g;
|
|
814
|
+
const parts = text.split(tokenRegex);
|
|
815
|
+
return parts.map((part, index) => {
|
|
816
|
+
if (!part) return null;
|
|
817
|
+
const key = `${keyPrefix}-inline-${index}`;
|
|
818
|
+
if (part.startsWith("`") && part.endsWith("`")) {
|
|
819
|
+
return /* @__PURE__ */ jsx3("code", { className: "hsk-markdown-code", children: part.slice(1, -1) }, key);
|
|
820
|
+
}
|
|
821
|
+
if (part.startsWith("**") && part.endsWith("**")) {
|
|
822
|
+
return /* @__PURE__ */ jsx3("strong", { children: parseInline(part.slice(2, -2), key) }, key);
|
|
823
|
+
}
|
|
824
|
+
const linkMatch = part.match(/^\[([^\]]+)\]\(([^)]+)\)$/);
|
|
825
|
+
if (linkMatch) {
|
|
826
|
+
const url = linkMatch[2];
|
|
827
|
+
const isSafeUrl = /^(https?|mailto|tel):/i.test(url) || url.startsWith("/");
|
|
828
|
+
if (isSafeUrl) {
|
|
829
|
+
return /* @__PURE__ */ jsx3("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: "hsk-markdown-link", children: parseInline(linkMatch[1], key) }, key);
|
|
830
|
+
}
|
|
831
|
+
return /* @__PURE__ */ jsx3("span", { children: parseInline(linkMatch[1], key) }, key);
|
|
832
|
+
}
|
|
833
|
+
return part;
|
|
834
|
+
});
|
|
835
|
+
};
|
|
836
|
+
function renderMarkdown(content) {
|
|
837
|
+
const lines = content.split("\n");
|
|
838
|
+
const elements = [];
|
|
839
|
+
let i = 0;
|
|
840
|
+
while (i < lines.length) {
|
|
841
|
+
const line = lines[i];
|
|
842
|
+
const key = `md-line-${i}`;
|
|
843
|
+
if (!line.trim()) {
|
|
844
|
+
i++;
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
const headerMatch = line.match(/^(#{1,3})\s+(.*)/);
|
|
848
|
+
if (headerMatch) {
|
|
849
|
+
const level = headerMatch[1].length;
|
|
850
|
+
const Tag = `h${level + 3}`;
|
|
851
|
+
elements.push(/* @__PURE__ */ jsx3(Tag, { className: `hsk-markdown-h${level}`, children: parseInline(headerMatch[2], key) }, key));
|
|
852
|
+
i++;
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (line.match(/^[-*]\s+/)) {
|
|
856
|
+
const listItems = [];
|
|
857
|
+
while (i < lines.length && lines[i].match(/^[-*]\s+/)) {
|
|
858
|
+
const itemText = lines[i].replace(/^[-*]\s+/, "");
|
|
859
|
+
listItems.push(/* @__PURE__ */ jsx3("li", { children: parseInline(itemText, `li-${i}`) }, `li-${i}`));
|
|
860
|
+
i++;
|
|
861
|
+
}
|
|
862
|
+
elements.push(/* @__PURE__ */ jsx3("ul", { className: "hsk-markdown-list", children: listItems }, `ul-${key}`));
|
|
863
|
+
continue;
|
|
864
|
+
}
|
|
865
|
+
if (line.trim().startsWith("|")) {
|
|
866
|
+
const tableRows = [];
|
|
867
|
+
let isHeader = true;
|
|
868
|
+
while (i < lines.length && lines[i].trim().startsWith("|")) {
|
|
869
|
+
const rowLine = lines[i].trim();
|
|
870
|
+
if (rowLine.match(/^\|[-:| ]+\|$/)) {
|
|
871
|
+
i++;
|
|
872
|
+
isHeader = false;
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
const cells = rowLine.split("|").slice(1, -1).map((c) => c.trim());
|
|
876
|
+
const Tag = isHeader ? "th" : "td";
|
|
877
|
+
tableRows.push(
|
|
878
|
+
/* @__PURE__ */ jsx3("tr", { children: cells.map((cell, cIdx) => /* @__PURE__ */ jsx3(Tag, { children: parseInline(cell, `td-${i}-${cIdx}`) }, `td-${i}-${cIdx}`)) }, `tr-${i}`)
|
|
879
|
+
);
|
|
880
|
+
i++;
|
|
881
|
+
}
|
|
882
|
+
elements.push(
|
|
883
|
+
/* @__PURE__ */ jsx3("div", { className: "hsk-table-wrapper", children: /* @__PURE__ */ jsx3("table", { className: "hsk-markdown-table", children: /* @__PURE__ */ jsx3("tbody", { children: tableRows }) }) }, `table-wrapper-${key}`)
|
|
884
|
+
);
|
|
885
|
+
continue;
|
|
886
|
+
}
|
|
887
|
+
elements.push(
|
|
888
|
+
/* @__PURE__ */ jsx3("p", { className: "hsk-markdown-p", children: parseInline(line, key) }, key)
|
|
889
|
+
);
|
|
890
|
+
i++;
|
|
891
|
+
}
|
|
892
|
+
return /* @__PURE__ */ jsx3(Fragment, { children: elements });
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// src/components/Sparkle.tsx
|
|
896
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
897
|
+
var SparkleIcon = ({ className }) => /* @__PURE__ */ jsx4("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx4("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" }) });
|
|
659
898
|
var CloseIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
660
|
-
/* @__PURE__ */
|
|
661
|
-
/* @__PURE__ */
|
|
899
|
+
/* @__PURE__ */ jsx4("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
900
|
+
/* @__PURE__ */ jsx4("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
662
901
|
] });
|
|
902
|
+
var ArrowUpIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
903
|
+
/* @__PURE__ */ jsx4("path", { d: "m5 12 7-7 7 7" }),
|
|
904
|
+
/* @__PURE__ */ jsx4("path", { d: "M12 19V5" })
|
|
905
|
+
] });
|
|
906
|
+
var getFriendlyError = (err) => {
|
|
907
|
+
let str = "";
|
|
908
|
+
if (typeof err === "string") str = err;
|
|
909
|
+
else if (err && typeof err === "object" && err.message) str = err.message;
|
|
910
|
+
else try {
|
|
911
|
+
str = JSON.stringify(err);
|
|
912
|
+
} catch (e) {
|
|
913
|
+
str = String(err);
|
|
914
|
+
}
|
|
915
|
+
if (str.toLowerCase().includes("token limit")) {
|
|
916
|
+
return "You've reached your usage limit. Please update your billing limits in your dashboard to continue.";
|
|
917
|
+
}
|
|
918
|
+
try {
|
|
919
|
+
const parsed = JSON.parse(str);
|
|
920
|
+
return parsed.error || parsed.message || str;
|
|
921
|
+
} catch (e) {
|
|
922
|
+
return str;
|
|
923
|
+
}
|
|
924
|
+
};
|
|
663
925
|
function SparkleModal({
|
|
664
926
|
productName,
|
|
665
927
|
limit,
|
|
@@ -669,26 +931,42 @@ function SparkleModal({
|
|
|
669
931
|
onNavigate,
|
|
670
932
|
onResult,
|
|
671
933
|
theme,
|
|
672
|
-
classNames = {}
|
|
934
|
+
classNames = {},
|
|
935
|
+
product: initialProduct
|
|
673
936
|
}) {
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
937
|
+
var _a, _b, _c;
|
|
938
|
+
const client = useHuskelContext();
|
|
939
|
+
const [fetchedProduct, setFetchedProduct] = useState6(null);
|
|
940
|
+
const displayProduct = initialProduct || fetchedProduct;
|
|
941
|
+
const { results, loading: searchLoading, search } = useSearch();
|
|
942
|
+
const { messages, sources, loading: chatLoading, error: chatError, send } = useChat();
|
|
943
|
+
const [chatInput, setChatInput] = useState6("");
|
|
944
|
+
const chatBottomRef = useRef7(null);
|
|
945
|
+
const chatTextareaRef = useRef7(null);
|
|
946
|
+
useEffect5(() => {
|
|
947
|
+
if (!initialProduct && !fetchedProduct) {
|
|
948
|
+
client.api.searchVector(productName, 1).then((res) => {
|
|
949
|
+
if (res.results && res.results.length > 0) {
|
|
950
|
+
setFetchedProduct(res.results[0].product);
|
|
951
|
+
}
|
|
952
|
+
}).catch((err) => console.error("[Huskel] Failed to fetch product details", err));
|
|
680
953
|
}
|
|
681
|
-
|
|
682
|
-
|
|
954
|
+
search(productName, limit);
|
|
955
|
+
}, [productName, initialProduct, fetchedProduct, client, limit, search]);
|
|
956
|
+
useEffect5(() => {
|
|
683
957
|
if (results.length > 0) onResult == null ? void 0 : onResult(results);
|
|
684
|
-
}, [results]);
|
|
685
|
-
|
|
958
|
+
}, [results, onResult]);
|
|
959
|
+
useEffect5(() => {
|
|
686
960
|
const h = (e) => {
|
|
687
961
|
if (e.key === "Escape") onClose();
|
|
688
962
|
};
|
|
689
963
|
document.addEventListener("keydown", h);
|
|
690
964
|
return () => document.removeEventListener("keydown", h);
|
|
691
|
-
}, []);
|
|
965
|
+
}, [onClose]);
|
|
966
|
+
useEffect5(() => {
|
|
967
|
+
var _a2;
|
|
968
|
+
(_a2 = chatBottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
969
|
+
}, [messages, chatLoading]);
|
|
692
970
|
const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "16px";
|
|
693
971
|
const bg = backdropColor != null ? backdropColor : void 0;
|
|
694
972
|
const handleNav = (r) => {
|
|
@@ -698,83 +976,209 @@ function SparkleModal({
|
|
|
698
976
|
if (r.product.url) window.location.href = r.product.url;
|
|
699
977
|
}
|
|
700
978
|
};
|
|
701
|
-
const
|
|
702
|
-
|
|
979
|
+
const handleSend = async (text) => {
|
|
980
|
+
const q = (text != null ? text : chatInput).trim();
|
|
981
|
+
if (!q || chatLoading) return;
|
|
982
|
+
setChatInput("");
|
|
983
|
+
if (chatTextareaRef.current) {
|
|
984
|
+
chatTextareaRef.current.style.height = "auto";
|
|
985
|
+
}
|
|
986
|
+
if (messages.length === 0 && displayProduct) {
|
|
987
|
+
const contextQuery = `[Context: Shopper is viewing "${displayProduct.name}". Price: ${displayProduct.price}. Description: ${displayProduct.description || ""}]
|
|
988
|
+
|
|
989
|
+
Question: ${q}`;
|
|
990
|
+
await send(contextQuery, q);
|
|
991
|
+
} else {
|
|
992
|
+
await send(q);
|
|
993
|
+
}
|
|
994
|
+
};
|
|
995
|
+
const handleKeyDown = (e) => {
|
|
996
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
997
|
+
e.preventDefault();
|
|
998
|
+
handleSend();
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
const handleInput = (e) => {
|
|
1002
|
+
setChatInput(e.target.value);
|
|
1003
|
+
const t = e.target;
|
|
1004
|
+
t.style.height = "auto";
|
|
1005
|
+
t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
|
|
1006
|
+
};
|
|
1007
|
+
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 });
|
|
1008
|
+
const displayMessages = messages.length === 0 && displayProduct ? [
|
|
1009
|
+
{
|
|
1010
|
+
role: "assistant",
|
|
1011
|
+
content: `Hi! I can help you with **${displayProduct.name}**. Ask me about its specifications, features, compare it with other options, or find alternatives!`
|
|
1012
|
+
}
|
|
1013
|
+
] : messages;
|
|
1014
|
+
return /* @__PURE__ */ jsx4(
|
|
703
1015
|
"div",
|
|
704
1016
|
{
|
|
705
|
-
className:
|
|
1017
|
+
className: cn("hsk-sp-backdrop", classNames.backdrop),
|
|
706
1018
|
onClick: onClose,
|
|
707
1019
|
style: __spreadValues({
|
|
708
1020
|
backdropFilter: `blur(${blurVal})`,
|
|
709
1021
|
WebkitBackdropFilter: `blur(${blurVal})`,
|
|
710
1022
|
background: bg != null ? bg : void 0
|
|
711
1023
|
}, customStyles),
|
|
712
|
-
children: /* @__PURE__ */ jsxs2("div", { className:
|
|
1024
|
+
children: /* @__PURE__ */ jsxs2("div", { className: cn("hsk-sp-card hsk-sp-fullscreen", classNames.card), onClick: (e) => e.stopPropagation(), children: [
|
|
713
1025
|
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-header", children: [
|
|
714
|
-
/* @__PURE__ */
|
|
1026
|
+
/* @__PURE__ */ jsx4("span", { className: "hsk-sp-header-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx4(SparkleIcon, {}) }),
|
|
715
1027
|
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-header-body", children: [
|
|
716
|
-
/* @__PURE__ */
|
|
717
|
-
|
|
718
|
-
productName,
|
|
719
|
-
"\u201D"
|
|
720
|
-
] }),
|
|
721
|
-
/* @__PURE__ */ jsx3("div", { className: "hsk-sp-header-sub", children: "AI vector similarity \xB7 instant results" })
|
|
1028
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-header-title", children: (displayProduct == null ? void 0 : displayProduct.name) || productName }),
|
|
1029
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-header-sub", children: "Ask questions, compare specs, or check similar products" })
|
|
722
1030
|
] }),
|
|
723
|
-
/* @__PURE__ */
|
|
1031
|
+
/* @__PURE__ */ jsx4("button", { className: "hsk-sp-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx4(CloseIcon, {}) })
|
|
724
1032
|
] }),
|
|
725
|
-
|
|
726
|
-
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1033
|
+
searchLoading && /* @__PURE__ */ jsx4("div", { className: "hsk-sp-bar" }),
|
|
1034
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-body", children: [
|
|
1035
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-details-pane", children: [
|
|
1036
|
+
displayProduct && /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-product-profile-container", children: [
|
|
1037
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-product-profile", children: [
|
|
1038
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-details-imgwrap", children: ((_a = displayProduct.images) == null ? void 0 : _a[0]) ? /* @__PURE__ */ jsx4("img", { src: displayProduct.images[0], alt: displayProduct.name }) : /* @__PURE__ */ jsx4("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
|
|
1039
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-details-meta", children: [
|
|
1040
|
+
displayProduct.brand && /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-brand", children: displayProduct.brand }),
|
|
1041
|
+
displayProduct.category && /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-cat", children: displayProduct.category }),
|
|
1042
|
+
/* @__PURE__ */ jsx4("h2", { className: "hsk-sp-details-name", children: displayProduct.name }),
|
|
1043
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-price-row", children: [
|
|
1044
|
+
/* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-currency", children: (_b = displayProduct.currency) != null ? _b : "KES" }),
|
|
1045
|
+
/* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-price", children: parseFloat(((_c = displayProduct.price) == null ? void 0 : _c.replace(/[^0-9.]/g, "")) || "0").toLocaleString() }),
|
|
1046
|
+
displayProduct.originalPrice && /* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-original-price", children: parseFloat(displayProduct.originalPrice.replace(/[^0-9.]/g, "") || "0").toLocaleString() }),
|
|
1047
|
+
displayProduct.discount && /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-item-discount", children: [
|
|
1048
|
+
"(",
|
|
1049
|
+
displayProduct.discount,
|
|
1050
|
+
")"
|
|
1051
|
+
] })
|
|
1052
|
+
] }),
|
|
1053
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-meta-badges", children: [
|
|
1054
|
+
displayProduct.rating && /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-rating", children: [
|
|
1055
|
+
"\u2605 ",
|
|
1056
|
+
parseFloat(displayProduct.rating.toString()).toFixed(1),
|
|
1057
|
+
" ",
|
|
1058
|
+
displayProduct.reviewCount ? `(${displayProduct.reviewCount})` : ""
|
|
745
1059
|
] }),
|
|
746
|
-
/* @__PURE__ */
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
className: "hsk-sp-action hsk-sp-action-primary",
|
|
751
|
-
onClick: () => handleNav(r),
|
|
752
|
-
children: "View Product"
|
|
753
|
-
}
|
|
754
|
-
),
|
|
755
|
-
/* @__PURE__ */ jsx3(
|
|
756
|
-
"button",
|
|
757
|
-
{
|
|
758
|
-
className: "hsk-sp-action hsk-sp-action-secondary",
|
|
759
|
-
onClick: () => onClose(),
|
|
760
|
-
children: "Add to Cart"
|
|
761
|
-
}
|
|
762
|
-
)
|
|
1060
|
+
displayProduct.availability && /* @__PURE__ */ jsx4("span", { className: `hsk-sp-meta-badge hsk-sp-meta-badge-avail ${displayProduct.availability.toLowerCase().includes("in") ? "in-stock" : "out-stock"}`, children: displayProduct.availability }),
|
|
1061
|
+
displayProduct.stock && !displayProduct.availability && /* @__PURE__ */ jsxs2("span", { className: "hsk-sp-meta-badge hsk-sp-meta-badge-stock", children: [
|
|
1062
|
+
"Stock: ",
|
|
1063
|
+
displayProduct.stock
|
|
763
1064
|
] })
|
|
764
1065
|
] })
|
|
765
|
-
]
|
|
766
|
-
},
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
1066
|
+
] })
|
|
1067
|
+
] }),
|
|
1068
|
+
displayProduct.specs && Object.keys(displayProduct.specs).length > 0 && /* @__PURE__ */ jsx4("div", { className: "hsk-sp-specs-horizontal", children: Object.entries(displayProduct.specs).map(([key, val]) => /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-spec-item-horizontal", children: [
|
|
1069
|
+
/* @__PURE__ */ jsxs2("span", { className: "hsk-sp-spec-label-horizontal", children: [
|
|
1070
|
+
key,
|
|
1071
|
+
":"
|
|
1072
|
+
] }),
|
|
1073
|
+
/* @__PURE__ */ jsx4("span", { className: "hsk-sp-spec-value-horizontal", title: val, children: val })
|
|
1074
|
+
] }, key)) }),
|
|
1075
|
+
displayProduct.description && /* @__PURE__ */ jsxs2("div", { className: "hsk-sp-details-desc", children: [
|
|
1076
|
+
/* @__PURE__ */ jsx4("h4", { children: "Description" }),
|
|
1077
|
+
/* @__PURE__ */ jsx4("p", { children: displayProduct.description })
|
|
1078
|
+
] })
|
|
1079
|
+
] }),
|
|
1080
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-similar-section", children: [
|
|
1081
|
+
/* @__PURE__ */ jsx4("h3", { children: "Similar Products" }),
|
|
1082
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-results", children: (() => {
|
|
1083
|
+
const similarProducts = results.filter(
|
|
1084
|
+
(r) => {
|
|
1085
|
+
var _a2;
|
|
1086
|
+
const isSameName = r.product.name.toLowerCase() === ((_a2 = displayProduct == null ? void 0 : displayProduct.name) == null ? void 0 : _a2.toLowerCase());
|
|
1087
|
+
const isSameSlug = r.product.slug && (displayProduct == null ? void 0 : displayProduct.slug) && r.product.slug.toLowerCase() === displayProduct.slug.toLowerCase();
|
|
1088
|
+
return !isSameName && !isSameSlug;
|
|
1089
|
+
}
|
|
1090
|
+
);
|
|
1091
|
+
if (!searchLoading && similarProducts.length === 0) {
|
|
1092
|
+
return /* @__PURE__ */ jsx4("div", { className: "hsk-sp-empty", children: "No similar products found." });
|
|
1093
|
+
}
|
|
1094
|
+
return similarProducts.map((r, i) => {
|
|
1095
|
+
var _a2, _b2, _c2;
|
|
1096
|
+
const price = parseFloat(((_a2 = r.product.price) == null ? void 0 : _a2.replace(/[^0-9.]/g, "")) || "0");
|
|
1097
|
+
const currency = (_b2 = r.product.currency) != null ? _b2 : "KES";
|
|
1098
|
+
return /* @__PURE__ */ jsxs2(
|
|
1099
|
+
"div",
|
|
1100
|
+
{
|
|
1101
|
+
className: cn("hsk-sp-item", classNames.item),
|
|
1102
|
+
style: { animationDelay: `${i * 55}ms` },
|
|
1103
|
+
children: [
|
|
1104
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-img-wrap", children: ((_c2 = r.product.images) == null ? void 0 : _c2[0]) ? /* @__PURE__ */ jsx4("img", { src: r.product.images[0], alt: r.product.name }) : /* @__PURE__ */ jsx4("span", { className: "hsk-sp-img-placeholder", children: "\u{1F6CD}" }) }),
|
|
1105
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-body", children: [
|
|
1106
|
+
/* @__PURE__ */ jsxs2("div", { children: [
|
|
1107
|
+
r.product.category && /* @__PURE__ */ jsx4("div", { className: "hsk-sp-item-cat", children: r.product.category }),
|
|
1108
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-item-name", title: r.product.name, children: r.product.name })
|
|
1109
|
+
] }),
|
|
1110
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-item-price-row", children: [
|
|
1111
|
+
/* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-currency", children: currency }),
|
|
1112
|
+
/* @__PURE__ */ jsx4("span", { className: "hsk-sp-item-price", children: price.toLocaleString() })
|
|
1113
|
+
] }),
|
|
1114
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-actions", children: /* @__PURE__ */ jsx4(
|
|
1115
|
+
"button",
|
|
1116
|
+
{
|
|
1117
|
+
className: "hsk-sp-action hsk-sp-action-primary",
|
|
1118
|
+
onClick: () => handleNav(r),
|
|
1119
|
+
children: "View"
|
|
1120
|
+
}
|
|
1121
|
+
) })
|
|
1122
|
+
] })
|
|
1123
|
+
]
|
|
1124
|
+
},
|
|
1125
|
+
r.id
|
|
1126
|
+
);
|
|
1127
|
+
});
|
|
1128
|
+
})() })
|
|
1129
|
+
] })
|
|
775
1130
|
] }),
|
|
776
|
-
/* @__PURE__ */
|
|
777
|
-
|
|
1131
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-sp-chat-pane", children: [
|
|
1132
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-cb-msgs", children: [
|
|
1133
|
+
displayMessages.map((msg, idx) => {
|
|
1134
|
+
const isUser = msg.role === "user";
|
|
1135
|
+
return /* @__PURE__ */ jsx4("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ jsx4("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ jsx4("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-ai-msg", children: [
|
|
1136
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx4(SparkleIcon, {}) }),
|
|
1137
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-body", children: /* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }) })
|
|
1138
|
+
] }) }, idx);
|
|
1139
|
+
}),
|
|
1140
|
+
chatLoading && /* @__PURE__ */ jsxs2("div", { className: "hsk-cb-typing-row", children: [
|
|
1141
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx4(SparkleIcon, {}) }),
|
|
1142
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-cb-typing", children: [
|
|
1143
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-dot" }),
|
|
1144
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-dot" }),
|
|
1145
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-dot" })
|
|
1146
|
+
] })
|
|
1147
|
+
] }),
|
|
1148
|
+
chatError && /* @__PURE__ */ jsx4("div", { className: "hsk-cb-error", children: getFriendlyError(chatError) }),
|
|
1149
|
+
/* @__PURE__ */ jsx4("div", { ref: chatBottomRef, style: { height: 1 } })
|
|
1150
|
+
] }),
|
|
1151
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-cb-input-wrap", children: [
|
|
1152
|
+
/* @__PURE__ */ jsxs2("div", { className: "hsk-cb-input-box", children: [
|
|
1153
|
+
/* @__PURE__ */ jsx4(
|
|
1154
|
+
"textarea",
|
|
1155
|
+
{
|
|
1156
|
+
ref: chatTextareaRef,
|
|
1157
|
+
className: "hsk-cb-textarea",
|
|
1158
|
+
value: chatInput,
|
|
1159
|
+
onChange: handleInput,
|
|
1160
|
+
onKeyDown: handleKeyDown,
|
|
1161
|
+
placeholder: "Ask about this product, specs, or comparison...",
|
|
1162
|
+
rows: 1,
|
|
1163
|
+
disabled: chatLoading
|
|
1164
|
+
}
|
|
1165
|
+
),
|
|
1166
|
+
/* @__PURE__ */ jsx4(
|
|
1167
|
+
"button",
|
|
1168
|
+
{
|
|
1169
|
+
className: "hsk-cb-send",
|
|
1170
|
+
onClick: () => handleSend(),
|
|
1171
|
+
disabled: !chatInput.trim() || chatLoading,
|
|
1172
|
+
"aria-label": "Send message",
|
|
1173
|
+
children: /* @__PURE__ */ jsx4(ArrowUpIcon, {})
|
|
1174
|
+
}
|
|
1175
|
+
)
|
|
1176
|
+
] }),
|
|
1177
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 instant product knowledge" })
|
|
1178
|
+
] })
|
|
1179
|
+
] })
|
|
1180
|
+
] }),
|
|
1181
|
+
/* @__PURE__ */ jsx4("div", { className: "hsk-sp-footer", children: /* @__PURE__ */ jsx4("span", { className: "hsk-sp-esc", children: "Esc to close" }) })
|
|
778
1182
|
] })
|
|
779
1183
|
}
|
|
780
1184
|
);
|
|
@@ -788,39 +1192,41 @@ function Sparkle({
|
|
|
788
1192
|
className,
|
|
789
1193
|
onNavigate,
|
|
790
1194
|
theme,
|
|
791
|
-
classNames = {}
|
|
1195
|
+
classNames = {},
|
|
1196
|
+
product
|
|
792
1197
|
}) {
|
|
793
|
-
const [open, setOpen] =
|
|
794
|
-
const [mounted, setMounted] =
|
|
795
|
-
|
|
1198
|
+
const [open, setOpen] = useState6(false);
|
|
1199
|
+
const [mounted, setMounted] = useState6(false);
|
|
1200
|
+
useEffect5(() => {
|
|
796
1201
|
setMounted(true);
|
|
797
1202
|
}, []);
|
|
798
|
-
const customStyles = __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 });
|
|
799
|
-
return /* @__PURE__ */ jsxs2(
|
|
800
|
-
/* @__PURE__ */
|
|
1203
|
+
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 });
|
|
1204
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1205
|
+
/* @__PURE__ */ jsx4(
|
|
801
1206
|
"button",
|
|
802
1207
|
{
|
|
803
|
-
className:
|
|
1208
|
+
className: cn("hsk-sp-btn", classNames.button, className),
|
|
804
1209
|
onClick: () => setOpen(true),
|
|
805
1210
|
style: customStyles,
|
|
806
1211
|
title: "Find similar products",
|
|
807
1212
|
"aria-label": "Find similar products",
|
|
808
|
-
children: /* @__PURE__ */
|
|
1213
|
+
children: /* @__PURE__ */ jsx4(SparkleIcon, {})
|
|
809
1214
|
}
|
|
810
1215
|
),
|
|
811
1216
|
open && mounted && createPortal(
|
|
812
|
-
/* @__PURE__ */
|
|
1217
|
+
/* @__PURE__ */ jsx4(
|
|
813
1218
|
SparkleModal,
|
|
814
1219
|
{
|
|
815
1220
|
productName,
|
|
816
1221
|
limit,
|
|
1222
|
+
onResult,
|
|
817
1223
|
backdropColor,
|
|
818
1224
|
backdropBlur,
|
|
819
1225
|
onClose: () => setOpen(false),
|
|
820
|
-
onResult,
|
|
821
1226
|
onNavigate,
|
|
822
1227
|
theme,
|
|
823
|
-
classNames
|
|
1228
|
+
classNames,
|
|
1229
|
+
product
|
|
824
1230
|
}
|
|
825
1231
|
),
|
|
826
1232
|
document.body
|
|
@@ -829,19 +1235,19 @@ function Sparkle({
|
|
|
829
1235
|
}
|
|
830
1236
|
|
|
831
1237
|
// src/components/ChatWidget.tsx
|
|
832
|
-
import { useState as
|
|
833
|
-
import { jsx as
|
|
834
|
-
var SparkleIcon2 = () => /* @__PURE__ */
|
|
835
|
-
var
|
|
836
|
-
/* @__PURE__ */
|
|
837
|
-
/* @__PURE__ */
|
|
1238
|
+
import { useState as useState7, useRef as useRef8, useEffect as useEffect6 } from "react";
|
|
1239
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1240
|
+
var SparkleIcon2 = () => /* @__PURE__ */ jsx5("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("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" }) });
|
|
1241
|
+
var ArrowUpIcon2 = () => /* @__PURE__ */ jsxs3("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1242
|
+
/* @__PURE__ */ jsx5("path", { d: "m5 12 7-7 7 7" }),
|
|
1243
|
+
/* @__PURE__ */ jsx5("path", { d: "M12 19V5" })
|
|
838
1244
|
] });
|
|
839
1245
|
function SourceCard({ source, defaultCurrency, onSelect }) {
|
|
840
1246
|
var _a;
|
|
841
1247
|
return /* @__PURE__ */ jsxs3("div", { className: "hsk-source-card", onClick: () => onSelect == null ? void 0 : onSelect(source), children: [
|
|
842
|
-
source.image && /* @__PURE__ */
|
|
1248
|
+
source.image && /* @__PURE__ */ jsx5("img", { src: source.image, alt: source.name, className: "hsk-source-img" }),
|
|
843
1249
|
/* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
|
|
844
|
-
/* @__PURE__ */
|
|
1250
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-source-name", children: source.name }),
|
|
845
1251
|
source.price && /* @__PURE__ */ jsxs3("div", { className: "hsk-source-price", children: [
|
|
846
1252
|
(_a = source.currency) != null ? _a : defaultCurrency,
|
|
847
1253
|
" ",
|
|
@@ -862,10 +1268,10 @@ function ChatWidget({
|
|
|
862
1268
|
onSelectSource
|
|
863
1269
|
}) {
|
|
864
1270
|
const { messages, sources, loading, error, send, reset } = useChat();
|
|
865
|
-
const [input, setInput] =
|
|
1271
|
+
const [input, setInput] = useState7("");
|
|
866
1272
|
const bottomRef = useRef8(null);
|
|
867
1273
|
const textareaRef = useRef8(null);
|
|
868
|
-
|
|
1274
|
+
useEffect6(() => {
|
|
869
1275
|
var _a;
|
|
870
1276
|
(_a = bottomRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth" });
|
|
871
1277
|
}, [messages, loading]);
|
|
@@ -888,48 +1294,55 @@ function ChatWidget({
|
|
|
888
1294
|
t.style.height = "auto";
|
|
889
1295
|
t.style.height = Math.min(t.scrollHeight, 120) + "px";
|
|
890
1296
|
};
|
|
891
|
-
const customStyles = __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 });
|
|
1297
|
+
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 });
|
|
892
1298
|
return /* @__PURE__ */ jsxs3(
|
|
893
1299
|
"div",
|
|
894
1300
|
{
|
|
895
|
-
className:
|
|
1301
|
+
className: cn("hsk-chat-widget", classNames.root, className),
|
|
896
1302
|
style: customStyles,
|
|
897
1303
|
children: [
|
|
898
|
-
/* @__PURE__ */ jsxs3("div", { className:
|
|
899
|
-
/* @__PURE__ */
|
|
900
|
-
/* @__PURE__ */
|
|
901
|
-
/* @__PURE__ */
|
|
902
|
-
messages.length > 0 && /* @__PURE__ */
|
|
1304
|
+
/* @__PURE__ */ jsxs3("div", { className: cn("hsk-chat-header", classNames.header), children: [
|
|
1305
|
+
/* @__PURE__ */ jsx5("span", { className: "hsk-chat-header-icon", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
|
|
1306
|
+
/* @__PURE__ */ jsx5("span", { className: "hsk-chat-title", children: title }),
|
|
1307
|
+
/* @__PURE__ */ jsx5("span", { className: "hsk-chat-badge", children: "AI" }),
|
|
1308
|
+
messages.length > 0 && /* @__PURE__ */ jsx5("button", { className: "hsk-chat-reset", onClick: reset, style: { marginLeft: "auto" }, children: "Clear" })
|
|
903
1309
|
] }),
|
|
904
1310
|
/* @__PURE__ */ jsxs3("div", { className: "hsk-chat-messages", children: [
|
|
905
1311
|
messages.length === 0 ? /* @__PURE__ */ jsxs3("div", { className: "hsk-chat-empty", children: [
|
|
906
|
-
/* @__PURE__ */
|
|
907
|
-
/* @__PURE__ */
|
|
908
|
-
/* @__PURE__ */
|
|
1312
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-chat-empty-icon", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
|
|
1313
|
+
/* @__PURE__ */ jsx5("div", { children: emptyStateText }),
|
|
1314
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-chat-empty-suggestions", children: emptyStateSuggestions })
|
|
909
1315
|
] }) : messages.map((msg, idx) => /* @__PURE__ */ jsxs3("div", { children: [
|
|
910
1316
|
/* @__PURE__ */ jsxs3("div", { className: `hsk-msg-row ${msg.role}`, children: [
|
|
911
|
-
/* @__PURE__ */
|
|
912
|
-
/* @__PURE__ */
|
|
1317
|
+
/* @__PURE__ */ jsx5("div", { className: cn("hsk-msg-avatar", msg.role === "assistant" ? "ai" : "user"), children: msg.role === "assistant" ? /* @__PURE__ */ jsx5(SparkleIcon2, {}) : "U" }),
|
|
1318
|
+
/* @__PURE__ */ jsx5("div", { className: cn("hsk-msg-bubble", msg.role, classNames.messageBubble), children: renderMarkdown(msg.content) })
|
|
913
1319
|
] }),
|
|
914
|
-
msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */
|
|
1320
|
+
msg.role === "assistant" && idx === messages.length - 1 && sources.length > 0 && /* @__PURE__ */ jsx5("div", { className: "hsk-sources-container", children: /* @__PURE__ */ jsx5("div", { className: "hsk-sources", children: sources.map((src, si) => /* @__PURE__ */ jsx5(SourceCard, { source: src, defaultCurrency, onSelect: onSelectSource }, si)) }) })
|
|
915
1321
|
] }, idx)),
|
|
916
1322
|
loading && /* @__PURE__ */ jsxs3("div", { className: "hsk-msg-row", children: [
|
|
917
|
-
/* @__PURE__ */
|
|
1323
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-msg-avatar ai", children: /* @__PURE__ */ jsx5(SparkleIcon2, {}) }),
|
|
918
1324
|
/* @__PURE__ */ jsxs3("div", { className: "hsk-typing", children: [
|
|
919
|
-
/* @__PURE__ */
|
|
920
|
-
/* @__PURE__ */
|
|
921
|
-
/* @__PURE__ */
|
|
1325
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-typing-dot" }),
|
|
1326
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-typing-dot" }),
|
|
1327
|
+
/* @__PURE__ */ jsx5("div", { className: "hsk-typing-dot" })
|
|
922
1328
|
] })
|
|
923
1329
|
] }),
|
|
924
|
-
error && /* @__PURE__ */
|
|
925
|
-
|
|
1330
|
+
error && /* @__PURE__ */ jsx5("div", { className: "hsk-chat-error", children: (() => {
|
|
1331
|
+
try {
|
|
1332
|
+
const parsed = JSON.parse(error);
|
|
1333
|
+
return parsed.error || parsed.message || error;
|
|
1334
|
+
} catch (e) {
|
|
1335
|
+
return error;
|
|
1336
|
+
}
|
|
1337
|
+
})() }),
|
|
1338
|
+
/* @__PURE__ */ jsx5("div", { ref: bottomRef })
|
|
926
1339
|
] }),
|
|
927
1340
|
/* @__PURE__ */ jsxs3("div", { className: "hsk-chat-input-area", children: [
|
|
928
|
-
/* @__PURE__ */
|
|
1341
|
+
/* @__PURE__ */ jsx5(
|
|
929
1342
|
"textarea",
|
|
930
1343
|
{
|
|
931
1344
|
ref: textareaRef,
|
|
932
|
-
className:
|
|
1345
|
+
className: cn("hsk-chat-input", classNames.input),
|
|
933
1346
|
value: input,
|
|
934
1347
|
onChange: handleInput,
|
|
935
1348
|
onKeyDown: handleKey,
|
|
@@ -938,14 +1351,14 @@ function ChatWidget({
|
|
|
938
1351
|
disabled: loading
|
|
939
1352
|
}
|
|
940
1353
|
),
|
|
941
|
-
/* @__PURE__ */
|
|
1354
|
+
/* @__PURE__ */ jsx5(
|
|
942
1355
|
"button",
|
|
943
1356
|
{
|
|
944
1357
|
className: "hsk-chat-send",
|
|
945
1358
|
onClick: handleSend,
|
|
946
1359
|
disabled: !input.trim() || loading,
|
|
947
1360
|
"aria-label": "Send message",
|
|
948
|
-
children: /* @__PURE__ */
|
|
1361
|
+
children: /* @__PURE__ */ jsx5(ArrowUpIcon2, {})
|
|
949
1362
|
}
|
|
950
1363
|
)
|
|
951
1364
|
] })
|
|
@@ -955,19 +1368,19 @@ function ChatWidget({
|
|
|
955
1368
|
}
|
|
956
1369
|
|
|
957
1370
|
// src/components/AIChatButton.tsx
|
|
958
|
-
import { useState as
|
|
1371
|
+
import { useState as useState8, useEffect as useEffect7, useRef as useRef9, useCallback as useCallback5 } from "react";
|
|
959
1372
|
import { createPortal as createPortal2 } from "react-dom";
|
|
960
|
-
import { Fragment as
|
|
961
|
-
var SparkleIcon3 = ({ className }) => /* @__PURE__ */
|
|
962
|
-
var
|
|
963
|
-
/* @__PURE__ */
|
|
964
|
-
/* @__PURE__ */
|
|
1373
|
+
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1374
|
+
var SparkleIcon3 = ({ className }) => /* @__PURE__ */ jsx6("svg", { className, width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("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" }) });
|
|
1375
|
+
var ArrowUpIcon3 = () => /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1376
|
+
/* @__PURE__ */ jsx6("path", { d: "m5 12 7-7 7 7" }),
|
|
1377
|
+
/* @__PURE__ */ jsx6("path", { d: "M12 19V5" })
|
|
965
1378
|
] });
|
|
966
1379
|
var CloseIcon2 = () => /* @__PURE__ */ jsxs4("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
967
|
-
/* @__PURE__ */
|
|
968
|
-
/* @__PURE__ */
|
|
1380
|
+
/* @__PURE__ */ jsx6("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
1381
|
+
/* @__PURE__ */ jsx6("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
969
1382
|
] });
|
|
970
|
-
var ChevronRightIcon = () => /* @__PURE__ */
|
|
1383
|
+
var ChevronRightIcon = () => /* @__PURE__ */ jsx6("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx6("path", { d: "m9 18 6-6-6-6" }) });
|
|
971
1384
|
var DEFAULT_CHIPS = [
|
|
972
1385
|
"Cheapest smartphone",
|
|
973
1386
|
"Smart TV under KSh 20,000",
|
|
@@ -976,14 +1389,14 @@ var DEFAULT_CHIPS = [
|
|
|
976
1389
|
];
|
|
977
1390
|
function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
978
1391
|
const railRef = useRef9(null);
|
|
979
|
-
const [showNext, setShowNext] =
|
|
980
|
-
const measure =
|
|
1392
|
+
const [showNext, setShowNext] = useState8(false);
|
|
1393
|
+
const measure = useCallback5(() => {
|
|
981
1394
|
const el = railRef.current;
|
|
982
1395
|
if (!el) return;
|
|
983
1396
|
const atEnd = el.scrollLeft + el.clientWidth >= el.scrollWidth - 8;
|
|
984
1397
|
setShowNext(el.scrollWidth > el.clientWidth + 4 && !atEnd);
|
|
985
1398
|
}, []);
|
|
986
|
-
|
|
1399
|
+
useEffect7(() => {
|
|
987
1400
|
measure();
|
|
988
1401
|
const el = railRef.current;
|
|
989
1402
|
if (!el) return;
|
|
@@ -1000,7 +1413,7 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1000
1413
|
(_a = railRef.current) == null ? void 0 : _a.scrollBy({ left: 170, behavior: "smooth" });
|
|
1001
1414
|
};
|
|
1002
1415
|
return /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-sources-wrap", children: [
|
|
1003
|
-
/* @__PURE__ */
|
|
1416
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-sources", ref: railRef, children: sources.map((src, si) => {
|
|
1004
1417
|
var _a;
|
|
1005
1418
|
return /* @__PURE__ */ jsxs4(
|
|
1006
1419
|
"div",
|
|
@@ -1009,9 +1422,9 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1009
1422
|
style: { animationDelay: `${si * 50}ms` },
|
|
1010
1423
|
onClick: () => onSelectSource == null ? void 0 : onSelectSource(src),
|
|
1011
1424
|
children: [
|
|
1012
|
-
src.image ? /* @__PURE__ */
|
|
1425
|
+
src.image ? /* @__PURE__ */ jsx6("div", { className: "hsk-cb-src-imgwrap", children: /* @__PURE__ */ jsx6("img", { src: src.image, alt: src.name, loading: "lazy" }) }) : /* @__PURE__ */ jsx6("div", { className: "hsk-cb-src-imgwrap-empty", children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
|
|
1013
1426
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-info", children: [
|
|
1014
|
-
/* @__PURE__ */
|
|
1427
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-src-name", children: src.name }),
|
|
1015
1428
|
src.price && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-src-price", children: [
|
|
1016
1429
|
(_a = src.currency) != null ? _a : defaultCurrency,
|
|
1017
1430
|
" ",
|
|
@@ -1023,20 +1436,20 @@ function SourcesCarousel({ sources, defaultCurrency, onSelectSource }) {
|
|
|
1023
1436
|
si
|
|
1024
1437
|
);
|
|
1025
1438
|
}) }),
|
|
1026
|
-
showNext && /* @__PURE__ */ jsxs4(
|
|
1027
|
-
/* @__PURE__ */
|
|
1439
|
+
showNext && /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1440
|
+
/* @__PURE__ */ jsx6(
|
|
1028
1441
|
"div",
|
|
1029
1442
|
{
|
|
1030
1443
|
className: "hsk-cb-sources-fade",
|
|
1031
1444
|
style: { background: "linear-gradient(to right, transparent, var(--hsk-fade-bg, #0e0e0f))" }
|
|
1032
1445
|
}
|
|
1033
1446
|
),
|
|
1034
|
-
/* @__PURE__ */
|
|
1447
|
+
/* @__PURE__ */ jsx6("button", { className: "hsk-cb-sources-next", onClick: scrollNext, "aria-label": "See more", children: /* @__PURE__ */ jsx6(ChevronRightIcon, {}) })
|
|
1035
1448
|
] })
|
|
1036
1449
|
] });
|
|
1037
1450
|
}
|
|
1038
1451
|
function ChatModal({
|
|
1039
|
-
title = "
|
|
1452
|
+
title = "Shopping Assistant",
|
|
1040
1453
|
placeholder = "Ask me anything \u2014 gifts, budget, use case\u2026",
|
|
1041
1454
|
backdropColor,
|
|
1042
1455
|
backdropBlur,
|
|
@@ -1049,15 +1462,15 @@ function ChatModal({
|
|
|
1049
1462
|
}) {
|
|
1050
1463
|
var _a, _b;
|
|
1051
1464
|
const { messages, sources, loading, error, send, reset } = useChat();
|
|
1052
|
-
const [input, setInput] =
|
|
1053
|
-
const [selectedProduct, setSelectedProduct] =
|
|
1465
|
+
const [input, setInput] = useState8("");
|
|
1466
|
+
const [selectedProduct, setSelectedProduct] = useState8(null);
|
|
1054
1467
|
const bottomRef = useRef9(null);
|
|
1055
1468
|
const textareaRef = useRef9(null);
|
|
1056
|
-
|
|
1469
|
+
useEffect7(() => {
|
|
1057
1470
|
var _a2;
|
|
1058
1471
|
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
|
|
1059
1472
|
}, [messages, loading, selectedProduct]);
|
|
1060
|
-
|
|
1473
|
+
useEffect7(() => {
|
|
1061
1474
|
const h = (e) => {
|
|
1062
1475
|
if (e.key === "Escape") onClose();
|
|
1063
1476
|
};
|
|
@@ -1094,36 +1507,32 @@ function ChatModal({
|
|
|
1094
1507
|
t.style.height = `${Math.min(t.scrollHeight, 140)}px`;
|
|
1095
1508
|
};
|
|
1096
1509
|
const blurVal = typeof backdropBlur === "number" ? `${backdropBlur}px` : backdropBlur != null ? backdropBlur : "20px";
|
|
1097
|
-
const customStyles = __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 });
|
|
1098
|
-
return /* @__PURE__ */
|
|
1510
|
+
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 });
|
|
1511
|
+
return /* @__PURE__ */ jsx6(
|
|
1099
1512
|
"div",
|
|
1100
1513
|
{
|
|
1101
|
-
className:
|
|
1514
|
+
className: cn("hsk-cb-overlay", classNames.overlay),
|
|
1102
1515
|
onClick: onClose,
|
|
1103
1516
|
style: __spreadValues(__spreadValues({
|
|
1104
1517
|
backdropFilter: `blur(${blurVal})`,
|
|
1105
1518
|
WebkitBackdropFilter: `blur(${blurVal})`
|
|
1106
1519
|
}, backdropColor ? { background: backdropColor } : {}), customStyles),
|
|
1107
|
-
children: /* @__PURE__ */ jsxs4("div", { className:
|
|
1520
|
+
children: /* @__PURE__ */ jsxs4("div", { className: cn("hsk-cb-panel", classNames.panel), onClick: (e) => e.stopPropagation(), children: [
|
|
1108
1521
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar", children: [
|
|
1109
1522
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar-left", children: [
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
/* @__PURE__ */ jsx5("div", { className: "hsk-cb-topbar-title", children: title }),
|
|
1113
|
-
/* @__PURE__ */ jsx5("div", { className: "hsk-cb-topbar-sub", children: "Powered by Huskel AI \xB7 searches the whole catalogue" })
|
|
1114
|
-
] })
|
|
1523
|
+
/* @__PURE__ */ jsx6("span", { className: "hsk-cb-topbar-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
|
|
1524
|
+
/* @__PURE__ */ jsx6("div", { children: /* @__PURE__ */ jsx6("div", { className: "hsk-cb-topbar-title", children: title }) })
|
|
1115
1525
|
] }),
|
|
1116
1526
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-topbar-actions", children: [
|
|
1117
|
-
messages.length > 0 && /* @__PURE__ */
|
|
1118
|
-
/* @__PURE__ */
|
|
1527
|
+
messages.length > 0 && /* @__PURE__ */ jsx6("button", { className: "hsk-cb-topbar-btn", onClick: reset, children: "Clear chat" }),
|
|
1528
|
+
/* @__PURE__ */ jsx6("button", { className: "hsk-cb-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx6(CloseIcon2, {}) })
|
|
1119
1529
|
] })
|
|
1120
1530
|
] }),
|
|
1121
1531
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-msgs", children: [
|
|
1122
1532
|
messages.length === 0 ? /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-empty", children: [
|
|
1123
|
-
/* @__PURE__ */
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
/* @__PURE__ */
|
|
1126
|
-
/* @__PURE__ */ jsx5("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ jsx5(
|
|
1533
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-empty-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
|
|
1534
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-empty-title", children: "Find exactly what you need" }),
|
|
1535
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-chips", children: chips.map((chip) => /* @__PURE__ */ jsx6(
|
|
1127
1536
|
"button",
|
|
1128
1537
|
{
|
|
1129
1538
|
className: "hsk-cb-chip",
|
|
@@ -1135,11 +1544,11 @@ function ChatModal({
|
|
|
1135
1544
|
] }) : messages.map((msg, idx) => {
|
|
1136
1545
|
const isLast = idx === messages.length - 1;
|
|
1137
1546
|
const isUser = msg.role === "user";
|
|
1138
|
-
return /* @__PURE__ */
|
|
1139
|
-
/* @__PURE__ */
|
|
1547
|
+
return /* @__PURE__ */ jsx6("div", { className: "hsk-cb-msg-group", children: isUser ? /* @__PURE__ */ jsx6("div", { className: "hsk-cb-user-msg", children: /* @__PURE__ */ jsx6("div", { className: "hsk-cb-user-bubble", children: msg.content }) }) : /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-ai-msg", children: [
|
|
1548
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
|
|
1140
1549
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-ai-body", children: [
|
|
1141
|
-
/* @__PURE__ */
|
|
1142
|
-
isLast && sources.length > 0 && /* @__PURE__ */
|
|
1550
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-text", children: renderMarkdown(msg.content) }),
|
|
1551
|
+
isLast && sources.length > 0 && /* @__PURE__ */ jsx6(
|
|
1143
1552
|
SourcesCarousel,
|
|
1144
1553
|
{
|
|
1145
1554
|
sources,
|
|
@@ -1156,9 +1565,9 @@ function ChatModal({
|
|
|
1156
1565
|
className: "hsk-cb-selected-product",
|
|
1157
1566
|
onClick: () => selectedProduct.url && window.open(selectedProduct.url, "_blank"),
|
|
1158
1567
|
children: [
|
|
1159
|
-
selectedProduct.image && /* @__PURE__ */
|
|
1568
|
+
selectedProduct.image && /* @__PURE__ */ jsx6("img", { className: "hsk-cb-selected-img", src: selectedProduct.image, alt: selectedProduct.name }),
|
|
1160
1569
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-selected-info", children: [
|
|
1161
|
-
/* @__PURE__ */
|
|
1570
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-selected-name", children: selectedProduct.name }),
|
|
1162
1571
|
selectedProduct.price && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-selected-price", children: [
|
|
1163
1572
|
(_a = selectedProduct.currency) != null ? _a : defaultCurrency,
|
|
1164
1573
|
" ",
|
|
@@ -1169,23 +1578,23 @@ function ChatModal({
|
|
|
1169
1578
|
}
|
|
1170
1579
|
),
|
|
1171
1580
|
loading && /* @__PURE__ */ jsxs4("div", { className: "hsk-cb-typing-row", children: [
|
|
1172
|
-
/* @__PURE__ */
|
|
1581
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-ai-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
|
|
1173
1582
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-typing", children: [
|
|
1174
|
-
/* @__PURE__ */
|
|
1175
|
-
/* @__PURE__ */
|
|
1176
|
-
/* @__PURE__ */
|
|
1583
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" }),
|
|
1584
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" }),
|
|
1585
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-dot" })
|
|
1177
1586
|
] })
|
|
1178
1587
|
] }),
|
|
1179
|
-
error && /* @__PURE__ */
|
|
1180
|
-
/* @__PURE__ */
|
|
1588
|
+
error && /* @__PURE__ */ jsx6("div", { className: "hsk-cb-error", children: error }),
|
|
1589
|
+
/* @__PURE__ */ jsx6("div", { ref: bottomRef, style: { height: 1 } })
|
|
1181
1590
|
] }),
|
|
1182
1591
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-input-wrap", children: [
|
|
1183
1592
|
/* @__PURE__ */ jsxs4("div", { className: "hsk-cb-input-box", children: [
|
|
1184
|
-
/* @__PURE__ */
|
|
1593
|
+
/* @__PURE__ */ jsx6(
|
|
1185
1594
|
"textarea",
|
|
1186
1595
|
{
|
|
1187
1596
|
ref: textareaRef,
|
|
1188
|
-
className:
|
|
1597
|
+
className: cn("hsk-cb-textarea", classNames.input),
|
|
1189
1598
|
value: input,
|
|
1190
1599
|
onChange: handleInput,
|
|
1191
1600
|
onKeyDown: handleKeyDown,
|
|
@@ -1195,18 +1604,18 @@ function ChatModal({
|
|
|
1195
1604
|
autoFocus: true
|
|
1196
1605
|
}
|
|
1197
1606
|
),
|
|
1198
|
-
/* @__PURE__ */
|
|
1607
|
+
/* @__PURE__ */ jsx6(
|
|
1199
1608
|
"button",
|
|
1200
1609
|
{
|
|
1201
|
-
className:
|
|
1610
|
+
className: cn("hsk-cb-send", classNames.sendButton),
|
|
1202
1611
|
onClick: () => handleSend(),
|
|
1203
1612
|
disabled: !input.trim() || loading,
|
|
1204
1613
|
"aria-label": "Send message",
|
|
1205
|
-
children: /* @__PURE__ */
|
|
1614
|
+
children: /* @__PURE__ */ jsx6(ArrowUpIcon3, {})
|
|
1206
1615
|
}
|
|
1207
1616
|
)
|
|
1208
1617
|
] }),
|
|
1209
|
-
/* @__PURE__ */
|
|
1618
|
+
/* @__PURE__ */ jsx6("div", { className: "hsk-cb-hint", children: "Huskel AI \xB7 searches the whole catalogue in real time" })
|
|
1210
1619
|
] })
|
|
1211
1620
|
] })
|
|
1212
1621
|
}
|
|
@@ -1225,28 +1634,28 @@ function AIChatButton({
|
|
|
1225
1634
|
theme,
|
|
1226
1635
|
classNames = {}
|
|
1227
1636
|
}) {
|
|
1228
|
-
const [open, setOpen] =
|
|
1229
|
-
const [mounted, setMounted] =
|
|
1230
|
-
|
|
1637
|
+
const [open, setOpen] = useState8(false);
|
|
1638
|
+
const [mounted, setMounted] = useState8(false);
|
|
1639
|
+
useEffect7(() => {
|
|
1231
1640
|
setMounted(true);
|
|
1232
1641
|
}, []);
|
|
1233
|
-
const customStyles = __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 });
|
|
1234
|
-
return /* @__PURE__ */ jsxs4(
|
|
1642
|
+
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 });
|
|
1643
|
+
return /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1235
1644
|
/* @__PURE__ */ jsxs4(
|
|
1236
1645
|
"button",
|
|
1237
1646
|
{
|
|
1238
|
-
className:
|
|
1647
|
+
className: cn("hsk-cb-btn", classNames.button, className),
|
|
1239
1648
|
onClick: () => setOpen(true),
|
|
1240
1649
|
style: customStyles,
|
|
1241
1650
|
"aria-label": "Open AI chat",
|
|
1242
1651
|
children: [
|
|
1243
|
-
/* @__PURE__ */
|
|
1652
|
+
/* @__PURE__ */ jsx6("span", { className: "hsk-cb-btn-icon", style: { display: "flex", alignItems: "center" }, children: /* @__PURE__ */ jsx6(SparkleIcon3, {}) }),
|
|
1244
1653
|
label !== void 0 ? label : null
|
|
1245
1654
|
]
|
|
1246
1655
|
}
|
|
1247
1656
|
),
|
|
1248
1657
|
open && mounted && createPortal2(
|
|
1249
|
-
/* @__PURE__ */
|
|
1658
|
+
/* @__PURE__ */ jsx6(
|
|
1250
1659
|
ChatModal,
|
|
1251
1660
|
{
|
|
1252
1661
|
title,
|
|
@@ -1265,8 +1674,260 @@ function AIChatButton({
|
|
|
1265
1674
|
)
|
|
1266
1675
|
] });
|
|
1267
1676
|
}
|
|
1677
|
+
|
|
1678
|
+
// src/components/CartBadge.tsx
|
|
1679
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1680
|
+
function CartBadge({ className }) {
|
|
1681
|
+
const { cart } = useCart();
|
|
1682
|
+
if (!cart || cart.item_count === 0) return null;
|
|
1683
|
+
return /* @__PURE__ */ jsx7("span", { className: cn("hsk-cart-badge", className), children: cart.item_count });
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// src/components/CartDrawer.tsx
|
|
1687
|
+
import { useState as useState10, useEffect as useEffect9 } from "react";
|
|
1688
|
+
import { createPortal as createPortal4 } from "react-dom";
|
|
1689
|
+
|
|
1690
|
+
// src/components/CheckoutModal.tsx
|
|
1691
|
+
import { useState as useState9, useEffect as useEffect8 } from "react";
|
|
1692
|
+
import { createPortal as createPortal3 } from "react-dom";
|
|
1693
|
+
import { Fragment as Fragment4, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1694
|
+
function CheckoutModal({
|
|
1695
|
+
onClose,
|
|
1696
|
+
theme,
|
|
1697
|
+
customStyles,
|
|
1698
|
+
hskThemeAttr
|
|
1699
|
+
}) {
|
|
1700
|
+
var _a, _b, _c, _d;
|
|
1701
|
+
const { cart, loading: cartLoading } = useCart();
|
|
1702
|
+
const client = useHuskelContext();
|
|
1703
|
+
const [config, setConfig] = useState9(null);
|
|
1704
|
+
const [loading, setLoading] = useState9(true);
|
|
1705
|
+
const [checkingOut, setCheckingOut] = useState9(false);
|
|
1706
|
+
const [paymentSuccess, setPaymentSuccess] = useState9(false);
|
|
1707
|
+
useEffect8(() => {
|
|
1708
|
+
client.api.getCheckoutConfig().then((res) => setConfig(res.payment_methods)).catch((e) => console.error("[Huskel] Failed to fetch checkout config", e)).finally(() => setLoading(false));
|
|
1709
|
+
}, [client]);
|
|
1710
|
+
const handlePay = async (method) => {
|
|
1711
|
+
setCheckingOut(true);
|
|
1712
|
+
setTimeout(async () => {
|
|
1713
|
+
try {
|
|
1714
|
+
const payload = await client.api.checkoutCart();
|
|
1715
|
+
if (client.onCheckout) {
|
|
1716
|
+
client.onCheckout(payload);
|
|
1717
|
+
}
|
|
1718
|
+
setPaymentSuccess(true);
|
|
1719
|
+
setTimeout(() => {
|
|
1720
|
+
onClose();
|
|
1721
|
+
}, 3e3);
|
|
1722
|
+
} catch (e) {
|
|
1723
|
+
console.error("[Huskel] Checkout failed", e);
|
|
1724
|
+
setCheckingOut(false);
|
|
1725
|
+
}
|
|
1726
|
+
}, 1500);
|
|
1727
|
+
};
|
|
1728
|
+
const hasPaymentMethods = config && Object.values(config).some((m) => m.enabled);
|
|
1729
|
+
return createPortal3(
|
|
1730
|
+
/* @__PURE__ */ jsx8(
|
|
1731
|
+
"div",
|
|
1732
|
+
{
|
|
1733
|
+
className: "hsk-cart-backdrop",
|
|
1734
|
+
style: __spreadProps(__spreadValues({}, customStyles), { zIndex: 999999 }),
|
|
1735
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1736
|
+
onClick: onClose,
|
|
1737
|
+
children: /* @__PURE__ */ jsxs5(
|
|
1738
|
+
"div",
|
|
1739
|
+
{
|
|
1740
|
+
className: "hsk-checkout-modal",
|
|
1741
|
+
style: customStyles,
|
|
1742
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1743
|
+
onClick: (e) => e.stopPropagation(),
|
|
1744
|
+
children: [
|
|
1745
|
+
/* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-header", children: [
|
|
1746
|
+
/* @__PURE__ */ jsx8("h2", { children: "Secure Checkout" }),
|
|
1747
|
+
/* @__PURE__ */ jsx8("button", { onClick: onClose, className: "hsk-close-btn", children: "\xD7" })
|
|
1748
|
+
] }),
|
|
1749
|
+
/* @__PURE__ */ jsx8("div", { className: "hsk-checkout-content", children: paymentSuccess ? /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-success", children: [
|
|
1750
|
+
/* @__PURE__ */ jsxs5("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "hsk-success-icon", children: [
|
|
1751
|
+
/* @__PURE__ */ jsx8("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }),
|
|
1752
|
+
/* @__PURE__ */ jsx8("polyline", { points: "22 4 12 14.01 9 11.01" })
|
|
1753
|
+
] }),
|
|
1754
|
+
/* @__PURE__ */ jsx8("h3", { children: "Payment Successful!" }),
|
|
1755
|
+
/* @__PURE__ */ jsx8("p", { children: "Thank you for your order." })
|
|
1756
|
+
] }) : /* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-split", children: [
|
|
1757
|
+
/* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-summary", children: [
|
|
1758
|
+
/* @__PURE__ */ jsx8("h3", { children: "Order Summary" }),
|
|
1759
|
+
cartLoading || !cart ? /* @__PURE__ */ jsx8("p", { className: "hsk-cart-loading", children: "Loading order..." }) : /* @__PURE__ */ jsxs5(Fragment4, { children: [
|
|
1760
|
+
/* @__PURE__ */ jsx8("ul", { className: "hsk-checkout-items", children: cart.items.map((item) => /* @__PURE__ */ jsxs5("li", { children: [
|
|
1761
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1762
|
+
item.quantity,
|
|
1763
|
+
"x ",
|
|
1764
|
+
item.name
|
|
1765
|
+
] }),
|
|
1766
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1767
|
+
item.currency,
|
|
1768
|
+
" ",
|
|
1769
|
+
(item.price_numeric * item.quantity).toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1770
|
+
] })
|
|
1771
|
+
] }, item.id)) }),
|
|
1772
|
+
/* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-total", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx8("span", { children: "Total" }),
|
|
1774
|
+
/* @__PURE__ */ jsxs5("span", { children: [
|
|
1775
|
+
cart.currency,
|
|
1776
|
+
" ",
|
|
1777
|
+
cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1778
|
+
] })
|
|
1779
|
+
] })
|
|
1780
|
+
] })
|
|
1781
|
+
] }),
|
|
1782
|
+
/* @__PURE__ */ jsxs5("div", { className: "hsk-checkout-payment", children: [
|
|
1783
|
+
/* @__PURE__ */ jsx8("h3", { children: "Payment Method" }),
|
|
1784
|
+
loading ? /* @__PURE__ */ jsx8("p", { className: "hsk-cart-loading", children: "Loading secure payment methods..." }) : !hasPaymentMethods ? /* @__PURE__ */ jsx8("p", { className: "hsk-checkout-error", children: "No payment methods are currently available for this store." }) : /* @__PURE__ */ jsxs5("div", { className: "hsk-payment-options", children: [
|
|
1785
|
+
((_a = config == null ? void 0 : config.mpesa) == null ? void 0 : _a.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("mpesa"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-mpesa", children: checkingOut ? "Processing..." : "Pay with M-Pesa" }),
|
|
1786
|
+
((_b = config == null ? void 0 : config.equity) == null ? void 0 : _b.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("equity"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-equity", children: checkingOut ? "Processing..." : "Pay with Equity Bank" }),
|
|
1787
|
+
((_c = config == null ? void 0 : config.stripe) == null ? void 0 : _c.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("stripe"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-stripe", children: checkingOut ? "Processing..." : "Pay with Card (Stripe)" }),
|
|
1788
|
+
((_d = config == null ? void 0 : config.paypal) == null ? void 0 : _d.enabled) && /* @__PURE__ */ jsx8("button", { onClick: () => handlePay("paypal"), disabled: checkingOut, className: "hsk-pay-btn hsk-pay-paypal", children: checkingOut ? "Processing..." : "Pay with PayPal" })
|
|
1789
|
+
] })
|
|
1790
|
+
] })
|
|
1791
|
+
] }) })
|
|
1792
|
+
]
|
|
1793
|
+
}
|
|
1794
|
+
)
|
|
1795
|
+
}
|
|
1796
|
+
),
|
|
1797
|
+
document.body
|
|
1798
|
+
);
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
// src/components/CartDrawer.tsx
|
|
1802
|
+
import { Fragment as Fragment5, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1803
|
+
function CartDrawer({
|
|
1804
|
+
trigger,
|
|
1805
|
+
className,
|
|
1806
|
+
theme
|
|
1807
|
+
}) {
|
|
1808
|
+
const { cart, loading } = useCart();
|
|
1809
|
+
const [open, setOpen] = useState10(false);
|
|
1810
|
+
const [showCheckout, setShowCheckout] = useState10(false);
|
|
1811
|
+
const [mounted, setMounted] = useState10(false);
|
|
1812
|
+
const client = useHuskelContext();
|
|
1813
|
+
useEffect9(() => {
|
|
1814
|
+
setMounted(true);
|
|
1815
|
+
const handleTriggerCheckout = () => {
|
|
1816
|
+
setShowCheckout(true);
|
|
1817
|
+
setOpen(false);
|
|
1818
|
+
};
|
|
1819
|
+
window.addEventListener("huskel:trigger_checkout", handleTriggerCheckout);
|
|
1820
|
+
return () => {
|
|
1821
|
+
window.removeEventListener("huskel:trigger_checkout", handleTriggerCheckout);
|
|
1822
|
+
};
|
|
1823
|
+
}, []);
|
|
1824
|
+
useEffect9(() => {
|
|
1825
|
+
if (open) {
|
|
1826
|
+
document.body.style.overflow = "hidden";
|
|
1827
|
+
} else {
|
|
1828
|
+
document.body.style.overflow = "";
|
|
1829
|
+
}
|
|
1830
|
+
return () => {
|
|
1831
|
+
document.body.style.overflow = "";
|
|
1832
|
+
};
|
|
1833
|
+
}, [open]);
|
|
1834
|
+
const handleCheckout = async () => {
|
|
1835
|
+
if (!cart || cart.items.length === 0) return;
|
|
1836
|
+
setShowCheckout(true);
|
|
1837
|
+
};
|
|
1838
|
+
const isStringTheme = typeof theme === "string";
|
|
1839
|
+
const hskThemeAttr = isStringTheme ? theme : void 0;
|
|
1840
|
+
const customStyles = !isStringTheme && theme ? __spreadValues(__spreadValues(__spreadValues(__spreadValues(__spreadValues({}, (theme == null ? void 0 : theme.primaryColor) && { "--hsk-primary": theme.primaryColor, "--hsk-primary-color": 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 }) : void 0;
|
|
1841
|
+
return /* @__PURE__ */ jsxs6(Fragment5, { children: [
|
|
1842
|
+
/* @__PURE__ */ jsx9("div", { onClick: () => setOpen(true), style: { display: "inline-block" }, children: trigger || /* @__PURE__ */ jsxs6(
|
|
1843
|
+
"button",
|
|
1844
|
+
{
|
|
1845
|
+
className: cn("hsk-cart-trigger", className),
|
|
1846
|
+
style: customStyles,
|
|
1847
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1848
|
+
"aria-label": "Open cart",
|
|
1849
|
+
children: [
|
|
1850
|
+
/* @__PURE__ */ jsxs6("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
1851
|
+
/* @__PURE__ */ jsx9("circle", { cx: "9", cy: "21", r: "1" }),
|
|
1852
|
+
/* @__PURE__ */ jsx9("circle", { cx: "20", cy: "21", r: "1" }),
|
|
1853
|
+
/* @__PURE__ */ jsx9("path", { d: "M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6" })
|
|
1854
|
+
] }),
|
|
1855
|
+
cart && cart.item_count > 0 ? /* @__PURE__ */ jsx9("span", { className: "hsk-cart-trigger-badge", children: cart.item_count }) : null
|
|
1856
|
+
]
|
|
1857
|
+
}
|
|
1858
|
+
) }),
|
|
1859
|
+
open && mounted && createPortal4(
|
|
1860
|
+
/* @__PURE__ */ jsx9(
|
|
1861
|
+
"div",
|
|
1862
|
+
{
|
|
1863
|
+
className: "hsk-cart-backdrop",
|
|
1864
|
+
style: customStyles,
|
|
1865
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1866
|
+
onClick: () => setOpen(false),
|
|
1867
|
+
children: /* @__PURE__ */ jsxs6(
|
|
1868
|
+
"div",
|
|
1869
|
+
{
|
|
1870
|
+
className: "hsk-cart-bottom-sheet",
|
|
1871
|
+
style: customStyles,
|
|
1872
|
+
"data-hsk-theme": hskThemeAttr,
|
|
1873
|
+
onClick: (e) => e.stopPropagation(),
|
|
1874
|
+
children: [
|
|
1875
|
+
/* @__PURE__ */ jsx9("div", { className: "hsk-cart-sheet-handle" }),
|
|
1876
|
+
/* @__PURE__ */ jsxs6("div", { className: "hsk-cart-sheet-header", children: [
|
|
1877
|
+
/* @__PURE__ */ jsx9("h2", { children: "Your Cart" }),
|
|
1878
|
+
/* @__PURE__ */ jsx9("button", { onClick: () => setOpen(false), className: "hsk-close-btn", children: "\xD7" })
|
|
1879
|
+
] }),
|
|
1880
|
+
/* @__PURE__ */ jsx9("div", { className: "hsk-cart-sheet-content", children: loading && !cart ? /* @__PURE__ */ jsx9("div", { className: "hsk-cart-loading", children: "Loading cart..." }) : !cart || cart.items.length === 0 ? /* @__PURE__ */ jsx9("div", { className: "hsk-cart-empty", children: "Your cart is empty." }) : /* @__PURE__ */ jsx9("ul", { className: "hsk-cart-items", children: cart.items.map((item) => /* @__PURE__ */ jsxs6("li", { className: "hsk-cart-item", children: [
|
|
1881
|
+
item.image && /* @__PURE__ */ jsx9("img", { src: item.image, alt: item.name, className: "hsk-cart-item-img" }),
|
|
1882
|
+
/* @__PURE__ */ jsxs6("div", { className: "hsk-cart-item-info", children: [
|
|
1883
|
+
/* @__PURE__ */ jsx9("span", { className: "hsk-cart-item-name", children: item.name }),
|
|
1884
|
+
/* @__PURE__ */ jsxs6("span", { className: "hsk-cart-item-price", children: [
|
|
1885
|
+
item.currency,
|
|
1886
|
+
" ",
|
|
1887
|
+
item.price_numeric.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1888
|
+
] })
|
|
1889
|
+
] }),
|
|
1890
|
+
/* @__PURE__ */ jsxs6("div", { className: "hsk-cart-item-qty", children: [
|
|
1891
|
+
"x",
|
|
1892
|
+
item.quantity
|
|
1893
|
+
] })
|
|
1894
|
+
] }, item.id)) }) }),
|
|
1895
|
+
cart && cart.items.length > 0 && /* @__PURE__ */ jsxs6("div", { className: "hsk-cart-sheet-footer", children: [
|
|
1896
|
+
/* @__PURE__ */ jsxs6("div", { className: "hsk-cart-total", children: [
|
|
1897
|
+
/* @__PURE__ */ jsx9("span", { children: "Total" }),
|
|
1898
|
+
/* @__PURE__ */ jsxs6("span", { children: [
|
|
1899
|
+
cart.currency,
|
|
1900
|
+
" ",
|
|
1901
|
+
cart.total.toLocaleString(void 0, { minimumFractionDigits: 2 })
|
|
1902
|
+
] })
|
|
1903
|
+
] }),
|
|
1904
|
+
/* @__PURE__ */ jsx9("button", { onClick: handleCheckout, className: "hsk-checkout-btn", children: "Checkout securely" })
|
|
1905
|
+
] })
|
|
1906
|
+
]
|
|
1907
|
+
}
|
|
1908
|
+
)
|
|
1909
|
+
}
|
|
1910
|
+
),
|
|
1911
|
+
document.body
|
|
1912
|
+
),
|
|
1913
|
+
showCheckout && mounted && /* @__PURE__ */ jsx9(
|
|
1914
|
+
CheckoutModal,
|
|
1915
|
+
{
|
|
1916
|
+
onClose: () => {
|
|
1917
|
+
setShowCheckout(false);
|
|
1918
|
+
setOpen(false);
|
|
1919
|
+
},
|
|
1920
|
+
theme: isStringTheme ? theme : void 0,
|
|
1921
|
+
customStyles,
|
|
1922
|
+
hskThemeAttr
|
|
1923
|
+
}
|
|
1924
|
+
)
|
|
1925
|
+
] });
|
|
1926
|
+
}
|
|
1268
1927
|
export {
|
|
1269
1928
|
AIChatButton,
|
|
1929
|
+
CartBadge,
|
|
1930
|
+
CartDrawer,
|
|
1270
1931
|
ChatWidget,
|
|
1271
1932
|
HuskelAPI,
|
|
1272
1933
|
HuskelClient,
|
|
@@ -1275,6 +1936,7 @@ export {
|
|
|
1275
1936
|
Sparkle,
|
|
1276
1937
|
getHuskelClient,
|
|
1277
1938
|
initHuskel,
|
|
1939
|
+
useCart,
|
|
1278
1940
|
useChat,
|
|
1279
1941
|
useHuskel,
|
|
1280
1942
|
useIngest,
|