@revova/hydrogen 1.0.1 → 1.0.2
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/README.md +78 -122
- package/dist/index.cjs +100 -143
- package/dist/index.d.cts +45 -57
- package/dist/index.d.ts +45 -57
- package/dist/index.js +99 -142
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
// src/components/ReviewWidget.tsx
|
|
2
2
|
import { useState as useState4 } from "react";
|
|
3
3
|
|
|
4
|
+
// src/hooks/useReviews.ts
|
|
5
|
+
import { useEffect, useState, useCallback } from "react";
|
|
6
|
+
|
|
4
7
|
// src/utils.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
function apiFetch({ apiUrl, shop, apiToken }, path, params, init) {
|
|
9
|
+
const searchParams = new URLSearchParams({ shop, ...params });
|
|
10
|
+
return fetch(`${apiUrl}${path}?${searchParams}`, {
|
|
11
|
+
...init,
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${apiToken}`,
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
...init?.headers ?? {}
|
|
16
|
+
}
|
|
17
|
+
});
|
|
10
18
|
}
|
|
11
19
|
|
|
12
20
|
// src/hooks/useReviews.ts
|
|
13
|
-
import { useEffect, useState, useCallback } from "react";
|
|
14
21
|
function useReviews({
|
|
15
|
-
|
|
22
|
+
apiUrl,
|
|
23
|
+
shop,
|
|
24
|
+
apiToken,
|
|
16
25
|
productId,
|
|
17
26
|
page: initialPage = 1,
|
|
18
27
|
limit = 10,
|
|
@@ -21,24 +30,20 @@ function useReviews({
|
|
|
21
30
|
}) {
|
|
22
31
|
const [page, setPage] = useState(initialPage);
|
|
23
32
|
const [sort, setSort] = useState(initialSort);
|
|
24
|
-
const [state, setState] = useState({
|
|
25
|
-
data: null,
|
|
26
|
-
loading: true,
|
|
27
|
-
error: null
|
|
28
|
-
});
|
|
33
|
+
const [state, setState] = useState({ data: null, loading: true, error: null });
|
|
29
34
|
const [tick, setTick] = useState(0);
|
|
30
35
|
const refetch = useCallback(() => setTick((t) => t + 1), []);
|
|
31
36
|
useEffect(() => {
|
|
32
37
|
let cancelled = false;
|
|
33
38
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
34
|
-
const params =
|
|
39
|
+
const params = {
|
|
35
40
|
productId,
|
|
36
41
|
page: String(page),
|
|
37
42
|
limit: String(limit),
|
|
38
43
|
sort,
|
|
39
44
|
...locale ? { locale } : {}
|
|
40
|
-
}
|
|
41
|
-
|
|
45
|
+
};
|
|
46
|
+
apiFetch({ apiUrl, shop, apiToken }, "/api/reviews", params).then((res) => {
|
|
42
47
|
if (!res.ok) throw new Error(`Revova: reviews fetch failed (${res.status})`);
|
|
43
48
|
return res.json();
|
|
44
49
|
}).then((data) => {
|
|
@@ -50,15 +55,8 @@ function useReviews({
|
|
|
50
55
|
return () => {
|
|
51
56
|
cancelled = true;
|
|
52
57
|
};
|
|
53
|
-
}, [
|
|
54
|
-
return {
|
|
55
|
-
...state,
|
|
56
|
-
refetch,
|
|
57
|
-
setPage,
|
|
58
|
-
setSort,
|
|
59
|
-
currentPage: page,
|
|
60
|
-
currentSort: sort
|
|
61
|
-
};
|
|
58
|
+
}, [apiUrl, shop, apiToken, productId, page, limit, sort, locale, tick]);
|
|
59
|
+
return { ...state, refetch, setPage, setSort, currentPage: page, currentSort: sort };
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
// src/components/StarRating.tsx
|
|
@@ -109,44 +107,28 @@ import { useState as useState3 } from "react";
|
|
|
109
107
|
|
|
110
108
|
// src/hooks/useSubmitReview.ts
|
|
111
109
|
import { useState as useState2, useCallback as useCallback2 } from "react";
|
|
112
|
-
var INITIAL_STATE = {
|
|
113
|
-
|
|
114
|
-
success: false,
|
|
115
|
-
error: null,
|
|
116
|
-
result: null
|
|
117
|
-
};
|
|
118
|
-
function useSubmitReview(proxyUrl) {
|
|
110
|
+
var INITIAL_STATE = { submitting: false, success: false, error: null, result: null };
|
|
111
|
+
function useSubmitReview(creds) {
|
|
119
112
|
const [state, setState] = useState2(INITIAL_STATE);
|
|
120
113
|
const submit = useCallback2(
|
|
121
114
|
async (payload) => {
|
|
122
115
|
setState({ submitting: true, success: false, error: null, result: null });
|
|
123
116
|
try {
|
|
124
|
-
const res = await
|
|
117
|
+
const res = await apiFetch(creds, "/api/reviews", {}, {
|
|
125
118
|
method: "POST",
|
|
126
|
-
headers: { "Content-Type": "application/json" },
|
|
127
119
|
body: JSON.stringify(payload)
|
|
128
120
|
});
|
|
129
121
|
const json = await res.json();
|
|
130
122
|
if (!res.ok) {
|
|
131
|
-
setState({
|
|
132
|
-
submitting: false,
|
|
133
|
-
success: false,
|
|
134
|
-
error: json.error ?? `Submission failed (${res.status})`,
|
|
135
|
-
result: null
|
|
136
|
-
});
|
|
123
|
+
setState({ submitting: false, success: false, error: json.error ?? `Submission failed (${res.status})`, result: null });
|
|
137
124
|
return;
|
|
138
125
|
}
|
|
139
126
|
setState({ submitting: false, success: true, error: null, result: json });
|
|
140
127
|
} catch (err) {
|
|
141
|
-
setState({
|
|
142
|
-
submitting: false,
|
|
143
|
-
success: false,
|
|
144
|
-
error: err instanceof Error ? err.message : "An unexpected error occurred.",
|
|
145
|
-
result: null
|
|
146
|
-
});
|
|
128
|
+
setState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An unexpected error occurred.", result: null });
|
|
147
129
|
}
|
|
148
130
|
},
|
|
149
|
-
[
|
|
131
|
+
[creds]
|
|
150
132
|
);
|
|
151
133
|
const reset = useCallback2(() => setState(INITIAL_STATE), []);
|
|
152
134
|
return { ...state, submit, reset };
|
|
@@ -434,9 +416,8 @@ function FieldRenderer(props) {
|
|
|
434
416
|
return null;
|
|
435
417
|
}
|
|
436
418
|
}
|
|
437
|
-
function ReviewForm({
|
|
438
|
-
const
|
|
439
|
-
const { submit, submitting, success, error, result } = useSubmitReview(resolvedUrl);
|
|
419
|
+
function ReviewForm({ apiUrl, shop, apiToken, productId, form, onSuccess, className }) {
|
|
420
|
+
const { submit, submitting, success, error, result } = useSubmitReview({ apiUrl, shop, apiToken });
|
|
440
421
|
const [answers, setAnswers] = useState3({});
|
|
441
422
|
const [email, setEmail] = useState3("");
|
|
442
423
|
const starField = form.fields.find((f) => f.type === "STAR_RATING");
|
|
@@ -543,8 +524,9 @@ function ReviewForm({ proxyUrl, storeDomain, productId, form, onSuccess, classNa
|
|
|
543
524
|
// src/components/ReviewWidget.tsx
|
|
544
525
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
545
526
|
function ReviewWidget({
|
|
546
|
-
|
|
547
|
-
|
|
527
|
+
apiUrl,
|
|
528
|
+
shop,
|
|
529
|
+
apiToken,
|
|
548
530
|
productId,
|
|
549
531
|
locale,
|
|
550
532
|
pageSize = 10,
|
|
@@ -552,10 +534,10 @@ function ReviewWidget({
|
|
|
552
534
|
starColor,
|
|
553
535
|
className
|
|
554
536
|
}) {
|
|
555
|
-
const
|
|
537
|
+
const creds = { apiUrl, shop, apiToken };
|
|
556
538
|
const [showingForm, setShowingForm] = useState4(false);
|
|
557
539
|
const { data, loading, error, setPage, setSort, currentPage, currentSort } = useReviews({
|
|
558
|
-
|
|
540
|
+
...creds,
|
|
559
541
|
productId,
|
|
560
542
|
limit: pageSize,
|
|
561
543
|
...locale !== void 0 ? { locale } : {}
|
|
@@ -590,7 +572,7 @@ function ReviewWidget({
|
|
|
590
572
|
showingForm && form && /* @__PURE__ */ jsx3("div", { style: { marginBottom: 24 }, children: /* @__PURE__ */ jsx3(
|
|
591
573
|
ReviewForm,
|
|
592
574
|
{
|
|
593
|
-
|
|
575
|
+
...creds,
|
|
594
576
|
productId,
|
|
595
577
|
form,
|
|
596
578
|
onSuccess: () => setShowingForm(false)
|
|
@@ -646,16 +628,11 @@ function ReviewWidget({
|
|
|
646
628
|
|
|
647
629
|
// src/hooks/useWidgetGlobals.ts
|
|
648
630
|
import { useEffect as useEffect2, useState as useState5 } from "react";
|
|
649
|
-
function useWidgetGlobals({
|
|
650
|
-
const [state, setState] = useState5({
|
|
651
|
-
data: null,
|
|
652
|
-
loading: true,
|
|
653
|
-
error: null
|
|
654
|
-
});
|
|
631
|
+
function useWidgetGlobals({ apiUrl, shop, apiToken, limit = 20 }) {
|
|
632
|
+
const [state, setState] = useState5({ data: null, loading: true, error: null });
|
|
655
633
|
useEffect2(() => {
|
|
656
634
|
let cancelled = false;
|
|
657
|
-
|
|
658
|
-
fetch(`${proxyUrl}/widget-globals?${params.toString()}`).then((res) => {
|
|
635
|
+
apiFetch({ apiUrl, shop, apiToken }, "/api/widget-globals", { limit: String(limit) }).then((res) => {
|
|
659
636
|
if (!res.ok) throw new Error(`Revova: widget-globals fetch failed (${res.status})`);
|
|
660
637
|
return res.json();
|
|
661
638
|
}).then((data) => {
|
|
@@ -667,15 +644,14 @@ function useWidgetGlobals({ proxyUrl, limit = 20 }) {
|
|
|
667
644
|
return () => {
|
|
668
645
|
cancelled = true;
|
|
669
646
|
};
|
|
670
|
-
}, [
|
|
647
|
+
}, [apiUrl, shop, apiToken, limit]);
|
|
671
648
|
return state;
|
|
672
649
|
}
|
|
673
650
|
|
|
674
651
|
// src/components/ReviewCount.tsx
|
|
675
652
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
676
|
-
function ReviewCount({
|
|
677
|
-
const
|
|
678
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl });
|
|
653
|
+
function ReviewCount({ apiUrl, shop, apiToken, starColor, starSize, className }) {
|
|
654
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
679
655
|
if (loading || !data?.stats?.averageRating) return null;
|
|
680
656
|
const avg = parseFloat(data.stats.averageRating);
|
|
681
657
|
return /* @__PURE__ */ jsxs4("span", { className, style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
@@ -693,16 +669,16 @@ function ReviewCount({ proxyUrl, storeDomain, starColor, starSize, className })
|
|
|
693
669
|
import React3, { useState as useState6 } from "react";
|
|
694
670
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
695
671
|
function ReviewCarousel({
|
|
696
|
-
|
|
697
|
-
|
|
672
|
+
apiUrl,
|
|
673
|
+
shop,
|
|
674
|
+
apiToken,
|
|
698
675
|
limit = 10,
|
|
699
676
|
autoPlay = true,
|
|
700
677
|
intervalMs = 4e3,
|
|
701
678
|
starColor,
|
|
702
679
|
className
|
|
703
680
|
}) {
|
|
704
|
-
const
|
|
705
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
681
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
706
682
|
const [index, setIndex] = useState6(0);
|
|
707
683
|
const reviews = data?.reviews ?? [];
|
|
708
684
|
React3.useEffect(() => {
|
|
@@ -750,15 +726,15 @@ function ReviewCarousel({
|
|
|
750
726
|
import { useState as useState7 } from "react";
|
|
751
727
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
752
728
|
function ReviewGallery({
|
|
753
|
-
|
|
754
|
-
|
|
729
|
+
apiUrl,
|
|
730
|
+
shop,
|
|
731
|
+
apiToken,
|
|
755
732
|
limit = 20,
|
|
756
733
|
columns = 3,
|
|
757
734
|
starColor,
|
|
758
735
|
className
|
|
759
736
|
}) {
|
|
760
|
-
const
|
|
761
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
737
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
762
738
|
const [lightbox, setLightbox] = useState7(null);
|
|
763
739
|
const items = (data?.reviews ?? []).filter((r) => r.image).map((r) => ({ url: r.image, review: r })).slice(0, limit);
|
|
764
740
|
if (loading) return /* @__PURE__ */ jsx6("div", { className, children: "Loading gallery\u2026" });
|
|
@@ -875,7 +851,8 @@ import { useState as useState9 } from "react";
|
|
|
875
851
|
// src/hooks/useQnA.ts
|
|
876
852
|
import { useEffect as useEffect3, useState as useState8, useCallback as useCallback3 } from "react";
|
|
877
853
|
var INITIAL_SUBMIT = { submitting: false, success: false, error: null };
|
|
878
|
-
function useQnA({
|
|
854
|
+
function useQnA({ apiUrl, shop, apiToken, productId, page: initialPage = 1, sort = "recent" }) {
|
|
855
|
+
const creds = { apiUrl, shop, apiToken };
|
|
879
856
|
const [page, setPage] = useState8(initialPage);
|
|
880
857
|
const [tick, setTick] = useState8(0);
|
|
881
858
|
const [state, setState] = useState8({ data: null, loading: true, error: null });
|
|
@@ -884,8 +861,7 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
884
861
|
useEffect3(() => {
|
|
885
862
|
let cancelled = false;
|
|
886
863
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
887
|
-
|
|
888
|
-
fetch(`${proxyUrl}/qna?${params.toString()}`).then((res) => {
|
|
864
|
+
apiFetch(creds, "/api/qna", { productId, page: String(page), sort }).then((res) => {
|
|
889
865
|
if (!res.ok) throw new Error(`Revova: qna fetch failed (${res.status})`);
|
|
890
866
|
return res.json();
|
|
891
867
|
}).then((data) => {
|
|
@@ -897,15 +873,11 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
897
873
|
return () => {
|
|
898
874
|
cancelled = true;
|
|
899
875
|
};
|
|
900
|
-
}, [
|
|
901
|
-
const
|
|
876
|
+
}, [apiUrl, shop, apiToken, productId, page, sort, tick]);
|
|
877
|
+
const postQnA = useCallback3(async (payload) => {
|
|
902
878
|
setSubmitState({ submitting: true, success: false, error: null });
|
|
903
879
|
try {
|
|
904
|
-
const res = await
|
|
905
|
-
method: "POST",
|
|
906
|
-
headers: { "Content-Type": "application/json" },
|
|
907
|
-
body: JSON.stringify(payload)
|
|
908
|
-
});
|
|
880
|
+
const res = await apiFetch(creds, "/api/qna", {}, { method: "POST", body: JSON.stringify(payload) });
|
|
909
881
|
const json = await res.json();
|
|
910
882
|
if (!res.ok) {
|
|
911
883
|
setSubmitState({ submitting: false, success: false, error: json.error ?? `Failed (${res.status})` });
|
|
@@ -916,36 +888,27 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
916
888
|
} catch (err) {
|
|
917
889
|
setSubmitState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An error occurred." });
|
|
918
890
|
}
|
|
919
|
-
}, [
|
|
920
|
-
const submitAnswer = useCallback3(async (payload) => {
|
|
921
|
-
setSubmitState({ submitting: true, success: false, error: null });
|
|
922
|
-
try {
|
|
923
|
-
const res = await fetch(`${proxyUrl}/qna`, {
|
|
924
|
-
method: "POST",
|
|
925
|
-
headers: { "Content-Type": "application/json" },
|
|
926
|
-
body: JSON.stringify(payload)
|
|
927
|
-
});
|
|
928
|
-
const json = await res.json();
|
|
929
|
-
if (!res.ok) {
|
|
930
|
-
setSubmitState({ submitting: false, success: false, error: json.error ?? `Failed (${res.status})` });
|
|
931
|
-
return;
|
|
932
|
-
}
|
|
933
|
-
setSubmitState({ submitting: false, success: true, error: null });
|
|
934
|
-
refetch();
|
|
935
|
-
} catch (err) {
|
|
936
|
-
setSubmitState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An error occurred." });
|
|
937
|
-
}
|
|
938
|
-
}, [proxyUrl, refetch]);
|
|
891
|
+
}, [apiUrl, shop, apiToken, refetch]);
|
|
939
892
|
const resetSubmit = useCallback3(() => setSubmitState(INITIAL_SUBMIT), []);
|
|
940
|
-
return {
|
|
893
|
+
return {
|
|
894
|
+
...state,
|
|
895
|
+
setPage,
|
|
896
|
+
currentPage: page,
|
|
897
|
+
refetch,
|
|
898
|
+
submitQuestion: postQnA,
|
|
899
|
+
submitAnswer: postQnA,
|
|
900
|
+
submitState,
|
|
901
|
+
resetSubmit
|
|
902
|
+
};
|
|
941
903
|
}
|
|
942
904
|
|
|
943
905
|
// src/components/QnAWidget.tsx
|
|
944
906
|
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
945
|
-
function QnAWidget({
|
|
946
|
-
const resolvedUrl = resolveProxyUrl({ proxyUrl, storeDomain });
|
|
907
|
+
function QnAWidget({ apiUrl, shop, apiToken, productId, className }) {
|
|
947
908
|
const { data, loading, error, setPage, currentPage, submitQuestion, submitState, resetSubmit } = useQnA({
|
|
948
|
-
|
|
909
|
+
apiUrl,
|
|
910
|
+
shop,
|
|
911
|
+
apiToken,
|
|
949
912
|
productId
|
|
950
913
|
});
|
|
951
914
|
const [showForm, setShowForm] = useState9(false);
|
|
@@ -1037,16 +1000,16 @@ function QnAWidget({ proxyUrl, storeDomain, productId, className }) {
|
|
|
1037
1000
|
import { useEffect as useEffect4, useState as useState10 } from "react";
|
|
1038
1001
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1039
1002
|
function SocialProofPopup({
|
|
1040
|
-
|
|
1041
|
-
|
|
1003
|
+
apiUrl,
|
|
1004
|
+
shop,
|
|
1005
|
+
apiToken,
|
|
1042
1006
|
position = "bottom-left",
|
|
1043
1007
|
intervalMs = 8e3,
|
|
1044
1008
|
displayMs = 5e3,
|
|
1045
1009
|
starColor,
|
|
1046
1010
|
className
|
|
1047
1011
|
}) {
|
|
1048
|
-
const
|
|
1049
|
-
const { data } = useWidgetGlobals({ proxyUrl: resolvedUrl });
|
|
1012
|
+
const { data } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
1050
1013
|
const [current, setCurrent] = useState10(null);
|
|
1051
1014
|
const [visible, setVisible] = useState10(false);
|
|
1052
1015
|
const [dismissed, setDismissed] = useState10(false);
|
|
@@ -1148,15 +1111,15 @@ function SocialProofPopup({
|
|
|
1148
1111
|
import { useRef } from "react";
|
|
1149
1112
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1150
1113
|
function ReviewTicker({
|
|
1151
|
-
|
|
1152
|
-
|
|
1114
|
+
apiUrl,
|
|
1115
|
+
shop,
|
|
1116
|
+
apiToken,
|
|
1153
1117
|
limit = 20,
|
|
1154
1118
|
speedSeconds = 30,
|
|
1155
1119
|
starColor,
|
|
1156
1120
|
className
|
|
1157
1121
|
}) {
|
|
1158
|
-
const
|
|
1159
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
1122
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
1160
1123
|
const trackRef = useRef(null);
|
|
1161
1124
|
const reviews = data?.reviews ?? [];
|
|
1162
1125
|
if (loading || reviews.length === 0) return null;
|
|
@@ -1217,8 +1180,9 @@ function ReviewTicker({
|
|
|
1217
1180
|
import { useState as useState11 } from "react";
|
|
1218
1181
|
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1219
1182
|
function FloatingReviewsTab({
|
|
1220
|
-
|
|
1221
|
-
|
|
1183
|
+
apiUrl,
|
|
1184
|
+
shop,
|
|
1185
|
+
apiToken,
|
|
1222
1186
|
label = "Reviews",
|
|
1223
1187
|
position = "right",
|
|
1224
1188
|
color = "#111827",
|
|
@@ -1226,8 +1190,7 @@ function FloatingReviewsTab({
|
|
|
1226
1190
|
starColor,
|
|
1227
1191
|
className
|
|
1228
1192
|
}) {
|
|
1229
|
-
const
|
|
1230
|
-
const { data } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
1193
|
+
const { data } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
1231
1194
|
const [open, setOpen] = useState11(false);
|
|
1232
1195
|
const reviews = data?.reviews ?? [];
|
|
1233
1196
|
const stats = data?.stats;
|
|
@@ -1304,9 +1267,8 @@ function FloatingReviewsTab({
|
|
|
1304
1267
|
|
|
1305
1268
|
// src/components/TrustBadge.tsx
|
|
1306
1269
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1307
|
-
function TrustBadge({
|
|
1308
|
-
const
|
|
1309
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl });
|
|
1270
|
+
function TrustBadge({ apiUrl, shop, apiToken, style: badgeStyle = "pill", starColor, className }) {
|
|
1271
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
1310
1272
|
if (loading || !data?.stats?.averageRating) return null;
|
|
1311
1273
|
const avg = parseFloat(data.stats.averageRating);
|
|
1312
1274
|
const count = data.stats.totalReviews;
|
|
@@ -1383,45 +1345,41 @@ import { useState as useState13 } from "react";
|
|
|
1383
1345
|
|
|
1384
1346
|
// src/hooks/useForm.ts
|
|
1385
1347
|
import { useEffect as useEffect5, useState as useState12 } from "react";
|
|
1386
|
-
function useForm(
|
|
1348
|
+
function useForm(creds, productId) {
|
|
1387
1349
|
const [state, setState] = useState12({ form: null, loading: true, error: null });
|
|
1388
1350
|
useEffect5(() => {
|
|
1389
1351
|
let cancelled = false;
|
|
1390
|
-
|
|
1391
|
-
fetch(`${proxyUrl}/reviews?${params.toString()}`).then((res) => {
|
|
1352
|
+
apiFetch(creds, "/api/reviews", { productId, limit: "1", page: "1" }).then((res) => {
|
|
1392
1353
|
if (!res.ok) throw new Error(`Revova: form fetch failed (${res.status})`);
|
|
1393
1354
|
return res.json();
|
|
1394
1355
|
}).then(({ form }) => {
|
|
1395
1356
|
if (!cancelled) setState({ form, loading: false, error: null });
|
|
1396
1357
|
}).catch((err) => {
|
|
1397
1358
|
if (!cancelled)
|
|
1398
|
-
setState({
|
|
1399
|
-
form: null,
|
|
1400
|
-
loading: false,
|
|
1401
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1402
|
-
});
|
|
1359
|
+
setState({ form: null, loading: false, error: err instanceof Error ? err : new Error(String(err)) });
|
|
1403
1360
|
});
|
|
1404
1361
|
return () => {
|
|
1405
1362
|
cancelled = true;
|
|
1406
1363
|
};
|
|
1407
|
-
}, [
|
|
1364
|
+
}, [creds.apiUrl, creds.shop, creds.apiToken, productId]);
|
|
1408
1365
|
return state;
|
|
1409
1366
|
}
|
|
1410
1367
|
|
|
1411
1368
|
// src/components/FloatingReviewButton.tsx
|
|
1412
1369
|
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1413
1370
|
function FloatingReviewButton({
|
|
1414
|
-
|
|
1415
|
-
|
|
1371
|
+
apiUrl,
|
|
1372
|
+
shop,
|
|
1373
|
+
apiToken,
|
|
1416
1374
|
productId,
|
|
1417
1375
|
text = "Write a Review",
|
|
1418
1376
|
color = "#111827",
|
|
1419
1377
|
position = "bottom-right",
|
|
1420
1378
|
className
|
|
1421
1379
|
}) {
|
|
1422
|
-
const
|
|
1380
|
+
const creds = { apiUrl, shop, apiToken };
|
|
1423
1381
|
const [open, setOpen] = useState13(false);
|
|
1424
|
-
const { form, loading } = useForm(
|
|
1382
|
+
const { form, loading } = useForm(creds, productId);
|
|
1425
1383
|
const posStyle = position === "bottom-right" ? { bottom: 24, right: 24 } : { bottom: 24, left: 24 };
|
|
1426
1384
|
if (!loading && !form) return null;
|
|
1427
1385
|
return /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
@@ -1499,7 +1457,7 @@ function FloatingReviewButton({
|
|
|
1499
1457
|
/* @__PURE__ */ jsx12(
|
|
1500
1458
|
ReviewForm,
|
|
1501
1459
|
{
|
|
1502
|
-
|
|
1460
|
+
...creds,
|
|
1503
1461
|
productId,
|
|
1504
1462
|
form,
|
|
1505
1463
|
onSuccess: () => setOpen(false)
|
|
@@ -1515,7 +1473,7 @@ function FloatingReviewButton({
|
|
|
1515
1473
|
|
|
1516
1474
|
// src/hooks/useHelpfulVote.ts
|
|
1517
1475
|
import { useState as useState14, useCallback as useCallback4 } from "react";
|
|
1518
|
-
function useHelpfulVote(
|
|
1476
|
+
function useHelpfulVote(creds) {
|
|
1519
1477
|
const [loading, setLoading] = useState14(false);
|
|
1520
1478
|
const [voted, setVoted] = useState14(false);
|
|
1521
1479
|
const vote = useCallback4(
|
|
@@ -1523,9 +1481,8 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1523
1481
|
if (voted || loading) return;
|
|
1524
1482
|
setLoading(true);
|
|
1525
1483
|
try {
|
|
1526
|
-
await
|
|
1484
|
+
await apiFetch(creds, "/api/helpful", {}, {
|
|
1527
1485
|
method: "POST",
|
|
1528
|
-
headers: { "Content-Type": "application/json" },
|
|
1529
1486
|
body: JSON.stringify(payload)
|
|
1530
1487
|
});
|
|
1531
1488
|
setVoted(true);
|
|
@@ -1533,7 +1490,7 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1533
1490
|
setLoading(false);
|
|
1534
1491
|
}
|
|
1535
1492
|
},
|
|
1536
|
-
[
|
|
1493
|
+
[creds, voted, loading]
|
|
1537
1494
|
);
|
|
1538
1495
|
return { vote, loading, voted };
|
|
1539
1496
|
}
|
|
@@ -1550,7 +1507,7 @@ export {
|
|
|
1550
1507
|
SocialProofPopup,
|
|
1551
1508
|
StarRating,
|
|
1552
1509
|
TrustBadge,
|
|
1553
|
-
|
|
1510
|
+
apiFetch,
|
|
1554
1511
|
useForm,
|
|
1555
1512
|
useHelpfulVote,
|
|
1556
1513
|
useQnA,
|