@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.cjs
CHANGED
|
@@ -42,7 +42,7 @@ __export(index_exports, {
|
|
|
42
42
|
SocialProofPopup: () => SocialProofPopup,
|
|
43
43
|
StarRating: () => StarRating,
|
|
44
44
|
TrustBadge: () => TrustBadge,
|
|
45
|
-
|
|
45
|
+
apiFetch: () => apiFetch,
|
|
46
46
|
useForm: () => useForm,
|
|
47
47
|
useHelpfulVote: () => useHelpfulVote,
|
|
48
48
|
useQnA: () => useQnA,
|
|
@@ -55,18 +55,27 @@ module.exports = __toCommonJS(index_exports);
|
|
|
55
55
|
// src/components/ReviewWidget.tsx
|
|
56
56
|
var import_react4 = require("react");
|
|
57
57
|
|
|
58
|
+
// src/hooks/useReviews.ts
|
|
59
|
+
var import_react = require("react");
|
|
60
|
+
|
|
58
61
|
// src/utils.ts
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
function apiFetch({ apiUrl, shop, apiToken }, path, params, init) {
|
|
63
|
+
const searchParams = new URLSearchParams({ shop, ...params });
|
|
64
|
+
return fetch(`${apiUrl}${path}?${searchParams}`, {
|
|
65
|
+
...init,
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: `Bearer ${apiToken}`,
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
...init?.headers ?? {}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
64
72
|
}
|
|
65
73
|
|
|
66
74
|
// src/hooks/useReviews.ts
|
|
67
|
-
var import_react = require("react");
|
|
68
75
|
function useReviews({
|
|
69
|
-
|
|
76
|
+
apiUrl,
|
|
77
|
+
shop,
|
|
78
|
+
apiToken,
|
|
70
79
|
productId,
|
|
71
80
|
page: initialPage = 1,
|
|
72
81
|
limit = 10,
|
|
@@ -75,24 +84,20 @@ function useReviews({
|
|
|
75
84
|
}) {
|
|
76
85
|
const [page, setPage] = (0, import_react.useState)(initialPage);
|
|
77
86
|
const [sort, setSort] = (0, import_react.useState)(initialSort);
|
|
78
|
-
const [state, setState] = (0, import_react.useState)({
|
|
79
|
-
data: null,
|
|
80
|
-
loading: true,
|
|
81
|
-
error: null
|
|
82
|
-
});
|
|
87
|
+
const [state, setState] = (0, import_react.useState)({ data: null, loading: true, error: null });
|
|
83
88
|
const [tick, setTick] = (0, import_react.useState)(0);
|
|
84
89
|
const refetch = (0, import_react.useCallback)(() => setTick((t) => t + 1), []);
|
|
85
90
|
(0, import_react.useEffect)(() => {
|
|
86
91
|
let cancelled = false;
|
|
87
92
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
88
|
-
const params =
|
|
93
|
+
const params = {
|
|
89
94
|
productId,
|
|
90
95
|
page: String(page),
|
|
91
96
|
limit: String(limit),
|
|
92
97
|
sort,
|
|
93
98
|
...locale ? { locale } : {}
|
|
94
|
-
}
|
|
95
|
-
|
|
99
|
+
};
|
|
100
|
+
apiFetch({ apiUrl, shop, apiToken }, "/api/reviews", params).then((res) => {
|
|
96
101
|
if (!res.ok) throw new Error(`Revova: reviews fetch failed (${res.status})`);
|
|
97
102
|
return res.json();
|
|
98
103
|
}).then((data) => {
|
|
@@ -104,15 +109,8 @@ function useReviews({
|
|
|
104
109
|
return () => {
|
|
105
110
|
cancelled = true;
|
|
106
111
|
};
|
|
107
|
-
}, [
|
|
108
|
-
return {
|
|
109
|
-
...state,
|
|
110
|
-
refetch,
|
|
111
|
-
setPage,
|
|
112
|
-
setSort,
|
|
113
|
-
currentPage: page,
|
|
114
|
-
currentSort: sort
|
|
115
|
-
};
|
|
112
|
+
}, [apiUrl, shop, apiToken, productId, page, limit, sort, locale, tick]);
|
|
113
|
+
return { ...state, refetch, setPage, setSort, currentPage: page, currentSort: sort };
|
|
116
114
|
}
|
|
117
115
|
|
|
118
116
|
// src/components/StarRating.tsx
|
|
@@ -163,44 +161,28 @@ var import_react3 = require("react");
|
|
|
163
161
|
|
|
164
162
|
// src/hooks/useSubmitReview.ts
|
|
165
163
|
var import_react2 = require("react");
|
|
166
|
-
var INITIAL_STATE = {
|
|
167
|
-
|
|
168
|
-
success: false,
|
|
169
|
-
error: null,
|
|
170
|
-
result: null
|
|
171
|
-
};
|
|
172
|
-
function useSubmitReview(proxyUrl) {
|
|
164
|
+
var INITIAL_STATE = { submitting: false, success: false, error: null, result: null };
|
|
165
|
+
function useSubmitReview(creds) {
|
|
173
166
|
const [state, setState] = (0, import_react2.useState)(INITIAL_STATE);
|
|
174
167
|
const submit = (0, import_react2.useCallback)(
|
|
175
168
|
async (payload) => {
|
|
176
169
|
setState({ submitting: true, success: false, error: null, result: null });
|
|
177
170
|
try {
|
|
178
|
-
const res = await
|
|
171
|
+
const res = await apiFetch(creds, "/api/reviews", {}, {
|
|
179
172
|
method: "POST",
|
|
180
|
-
headers: { "Content-Type": "application/json" },
|
|
181
173
|
body: JSON.stringify(payload)
|
|
182
174
|
});
|
|
183
175
|
const json = await res.json();
|
|
184
176
|
if (!res.ok) {
|
|
185
|
-
setState({
|
|
186
|
-
submitting: false,
|
|
187
|
-
success: false,
|
|
188
|
-
error: json.error ?? `Submission failed (${res.status})`,
|
|
189
|
-
result: null
|
|
190
|
-
});
|
|
177
|
+
setState({ submitting: false, success: false, error: json.error ?? `Submission failed (${res.status})`, result: null });
|
|
191
178
|
return;
|
|
192
179
|
}
|
|
193
180
|
setState({ submitting: false, success: true, error: null, result: json });
|
|
194
181
|
} catch (err) {
|
|
195
|
-
setState({
|
|
196
|
-
submitting: false,
|
|
197
|
-
success: false,
|
|
198
|
-
error: err instanceof Error ? err.message : "An unexpected error occurred.",
|
|
199
|
-
result: null
|
|
200
|
-
});
|
|
182
|
+
setState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An unexpected error occurred.", result: null });
|
|
201
183
|
}
|
|
202
184
|
},
|
|
203
|
-
[
|
|
185
|
+
[creds]
|
|
204
186
|
);
|
|
205
187
|
const reset = (0, import_react2.useCallback)(() => setState(INITIAL_STATE), []);
|
|
206
188
|
return { ...state, submit, reset };
|
|
@@ -488,9 +470,8 @@ function FieldRenderer(props) {
|
|
|
488
470
|
return null;
|
|
489
471
|
}
|
|
490
472
|
}
|
|
491
|
-
function ReviewForm({
|
|
492
|
-
const
|
|
493
|
-
const { submit, submitting, success, error, result } = useSubmitReview(resolvedUrl);
|
|
473
|
+
function ReviewForm({ apiUrl, shop, apiToken, productId, form, onSuccess, className }) {
|
|
474
|
+
const { submit, submitting, success, error, result } = useSubmitReview({ apiUrl, shop, apiToken });
|
|
494
475
|
const [answers, setAnswers] = (0, import_react3.useState)({});
|
|
495
476
|
const [email, setEmail] = (0, import_react3.useState)("");
|
|
496
477
|
const starField = form.fields.find((f) => f.type === "STAR_RATING");
|
|
@@ -597,8 +578,9 @@ function ReviewForm({ proxyUrl, storeDomain, productId, form, onSuccess, classNa
|
|
|
597
578
|
// src/components/ReviewWidget.tsx
|
|
598
579
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
599
580
|
function ReviewWidget({
|
|
600
|
-
|
|
601
|
-
|
|
581
|
+
apiUrl,
|
|
582
|
+
shop,
|
|
583
|
+
apiToken,
|
|
602
584
|
productId,
|
|
603
585
|
locale,
|
|
604
586
|
pageSize = 10,
|
|
@@ -606,10 +588,10 @@ function ReviewWidget({
|
|
|
606
588
|
starColor,
|
|
607
589
|
className
|
|
608
590
|
}) {
|
|
609
|
-
const
|
|
591
|
+
const creds = { apiUrl, shop, apiToken };
|
|
610
592
|
const [showingForm, setShowingForm] = (0, import_react4.useState)(false);
|
|
611
593
|
const { data, loading, error, setPage, setSort, currentPage, currentSort } = useReviews({
|
|
612
|
-
|
|
594
|
+
...creds,
|
|
613
595
|
productId,
|
|
614
596
|
limit: pageSize,
|
|
615
597
|
...locale !== void 0 ? { locale } : {}
|
|
@@ -644,7 +626,7 @@ function ReviewWidget({
|
|
|
644
626
|
showingForm && form && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { marginBottom: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
645
627
|
ReviewForm,
|
|
646
628
|
{
|
|
647
|
-
|
|
629
|
+
...creds,
|
|
648
630
|
productId,
|
|
649
631
|
form,
|
|
650
632
|
onSuccess: () => setShowingForm(false)
|
|
@@ -700,16 +682,11 @@ function ReviewWidget({
|
|
|
700
682
|
|
|
701
683
|
// src/hooks/useWidgetGlobals.ts
|
|
702
684
|
var import_react5 = require("react");
|
|
703
|
-
function useWidgetGlobals({
|
|
704
|
-
const [state, setState] = (0, import_react5.useState)({
|
|
705
|
-
data: null,
|
|
706
|
-
loading: true,
|
|
707
|
-
error: null
|
|
708
|
-
});
|
|
685
|
+
function useWidgetGlobals({ apiUrl, shop, apiToken, limit = 20 }) {
|
|
686
|
+
const [state, setState] = (0, import_react5.useState)({ data: null, loading: true, error: null });
|
|
709
687
|
(0, import_react5.useEffect)(() => {
|
|
710
688
|
let cancelled = false;
|
|
711
|
-
|
|
712
|
-
fetch(`${proxyUrl}/widget-globals?${params.toString()}`).then((res) => {
|
|
689
|
+
apiFetch({ apiUrl, shop, apiToken }, "/api/widget-globals", { limit: String(limit) }).then((res) => {
|
|
713
690
|
if (!res.ok) throw new Error(`Revova: widget-globals fetch failed (${res.status})`);
|
|
714
691
|
return res.json();
|
|
715
692
|
}).then((data) => {
|
|
@@ -721,15 +698,14 @@ function useWidgetGlobals({ proxyUrl, limit = 20 }) {
|
|
|
721
698
|
return () => {
|
|
722
699
|
cancelled = true;
|
|
723
700
|
};
|
|
724
|
-
}, [
|
|
701
|
+
}, [apiUrl, shop, apiToken, limit]);
|
|
725
702
|
return state;
|
|
726
703
|
}
|
|
727
704
|
|
|
728
705
|
// src/components/ReviewCount.tsx
|
|
729
706
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
730
|
-
function ReviewCount({
|
|
731
|
-
const
|
|
732
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl });
|
|
707
|
+
function ReviewCount({ apiUrl, shop, apiToken, starColor, starSize, className }) {
|
|
708
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
733
709
|
if (loading || !data?.stats?.averageRating) return null;
|
|
734
710
|
const avg = parseFloat(data.stats.averageRating);
|
|
735
711
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className, style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
@@ -747,16 +723,16 @@ function ReviewCount({ proxyUrl, storeDomain, starColor, starSize, className })
|
|
|
747
723
|
var import_react6 = __toESM(require("react"), 1);
|
|
748
724
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
749
725
|
function ReviewCarousel({
|
|
750
|
-
|
|
751
|
-
|
|
726
|
+
apiUrl,
|
|
727
|
+
shop,
|
|
728
|
+
apiToken,
|
|
752
729
|
limit = 10,
|
|
753
730
|
autoPlay = true,
|
|
754
731
|
intervalMs = 4e3,
|
|
755
732
|
starColor,
|
|
756
733
|
className
|
|
757
734
|
}) {
|
|
758
|
-
const
|
|
759
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
735
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
760
736
|
const [index, setIndex] = (0, import_react6.useState)(0);
|
|
761
737
|
const reviews = data?.reviews ?? [];
|
|
762
738
|
import_react6.default.useEffect(() => {
|
|
@@ -804,15 +780,15 @@ function ReviewCarousel({
|
|
|
804
780
|
var import_react7 = require("react");
|
|
805
781
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
806
782
|
function ReviewGallery({
|
|
807
|
-
|
|
808
|
-
|
|
783
|
+
apiUrl,
|
|
784
|
+
shop,
|
|
785
|
+
apiToken,
|
|
809
786
|
limit = 20,
|
|
810
787
|
columns = 3,
|
|
811
788
|
starColor,
|
|
812
789
|
className
|
|
813
790
|
}) {
|
|
814
|
-
const
|
|
815
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
791
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
816
792
|
const [lightbox, setLightbox] = (0, import_react7.useState)(null);
|
|
817
793
|
const items = (data?.reviews ?? []).filter((r) => r.image).map((r) => ({ url: r.image, review: r })).slice(0, limit);
|
|
818
794
|
if (loading) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className, children: "Loading gallery\u2026" });
|
|
@@ -929,7 +905,8 @@ var import_react9 = require("react");
|
|
|
929
905
|
// src/hooks/useQnA.ts
|
|
930
906
|
var import_react8 = require("react");
|
|
931
907
|
var INITIAL_SUBMIT = { submitting: false, success: false, error: null };
|
|
932
|
-
function useQnA({
|
|
908
|
+
function useQnA({ apiUrl, shop, apiToken, productId, page: initialPage = 1, sort = "recent" }) {
|
|
909
|
+
const creds = { apiUrl, shop, apiToken };
|
|
933
910
|
const [page, setPage] = (0, import_react8.useState)(initialPage);
|
|
934
911
|
const [tick, setTick] = (0, import_react8.useState)(0);
|
|
935
912
|
const [state, setState] = (0, import_react8.useState)({ data: null, loading: true, error: null });
|
|
@@ -938,8 +915,7 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
938
915
|
(0, import_react8.useEffect)(() => {
|
|
939
916
|
let cancelled = false;
|
|
940
917
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
941
|
-
|
|
942
|
-
fetch(`${proxyUrl}/qna?${params.toString()}`).then((res) => {
|
|
918
|
+
apiFetch(creds, "/api/qna", { productId, page: String(page), sort }).then((res) => {
|
|
943
919
|
if (!res.ok) throw new Error(`Revova: qna fetch failed (${res.status})`);
|
|
944
920
|
return res.json();
|
|
945
921
|
}).then((data) => {
|
|
@@ -951,15 +927,11 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
951
927
|
return () => {
|
|
952
928
|
cancelled = true;
|
|
953
929
|
};
|
|
954
|
-
}, [
|
|
955
|
-
const
|
|
930
|
+
}, [apiUrl, shop, apiToken, productId, page, sort, tick]);
|
|
931
|
+
const postQnA = (0, import_react8.useCallback)(async (payload) => {
|
|
956
932
|
setSubmitState({ submitting: true, success: false, error: null });
|
|
957
933
|
try {
|
|
958
|
-
const res = await
|
|
959
|
-
method: "POST",
|
|
960
|
-
headers: { "Content-Type": "application/json" },
|
|
961
|
-
body: JSON.stringify(payload)
|
|
962
|
-
});
|
|
934
|
+
const res = await apiFetch(creds, "/api/qna", {}, { method: "POST", body: JSON.stringify(payload) });
|
|
963
935
|
const json = await res.json();
|
|
964
936
|
if (!res.ok) {
|
|
965
937
|
setSubmitState({ submitting: false, success: false, error: json.error ?? `Failed (${res.status})` });
|
|
@@ -970,36 +942,27 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
970
942
|
} catch (err) {
|
|
971
943
|
setSubmitState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An error occurred." });
|
|
972
944
|
}
|
|
973
|
-
}, [
|
|
974
|
-
const submitAnswer = (0, import_react8.useCallback)(async (payload) => {
|
|
975
|
-
setSubmitState({ submitting: true, success: false, error: null });
|
|
976
|
-
try {
|
|
977
|
-
const res = await fetch(`${proxyUrl}/qna`, {
|
|
978
|
-
method: "POST",
|
|
979
|
-
headers: { "Content-Type": "application/json" },
|
|
980
|
-
body: JSON.stringify(payload)
|
|
981
|
-
});
|
|
982
|
-
const json = await res.json();
|
|
983
|
-
if (!res.ok) {
|
|
984
|
-
setSubmitState({ submitting: false, success: false, error: json.error ?? `Failed (${res.status})` });
|
|
985
|
-
return;
|
|
986
|
-
}
|
|
987
|
-
setSubmitState({ submitting: false, success: true, error: null });
|
|
988
|
-
refetch();
|
|
989
|
-
} catch (err) {
|
|
990
|
-
setSubmitState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An error occurred." });
|
|
991
|
-
}
|
|
992
|
-
}, [proxyUrl, refetch]);
|
|
945
|
+
}, [apiUrl, shop, apiToken, refetch]);
|
|
993
946
|
const resetSubmit = (0, import_react8.useCallback)(() => setSubmitState(INITIAL_SUBMIT), []);
|
|
994
|
-
return {
|
|
947
|
+
return {
|
|
948
|
+
...state,
|
|
949
|
+
setPage,
|
|
950
|
+
currentPage: page,
|
|
951
|
+
refetch,
|
|
952
|
+
submitQuestion: postQnA,
|
|
953
|
+
submitAnswer: postQnA,
|
|
954
|
+
submitState,
|
|
955
|
+
resetSubmit
|
|
956
|
+
};
|
|
995
957
|
}
|
|
996
958
|
|
|
997
959
|
// src/components/QnAWidget.tsx
|
|
998
960
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
999
|
-
function QnAWidget({
|
|
1000
|
-
const resolvedUrl = resolveProxyUrl({ proxyUrl, storeDomain });
|
|
961
|
+
function QnAWidget({ apiUrl, shop, apiToken, productId, className }) {
|
|
1001
962
|
const { data, loading, error, setPage, currentPage, submitQuestion, submitState, resetSubmit } = useQnA({
|
|
1002
|
-
|
|
963
|
+
apiUrl,
|
|
964
|
+
shop,
|
|
965
|
+
apiToken,
|
|
1003
966
|
productId
|
|
1004
967
|
});
|
|
1005
968
|
const [showForm, setShowForm] = (0, import_react9.useState)(false);
|
|
@@ -1091,16 +1054,16 @@ function QnAWidget({ proxyUrl, storeDomain, productId, className }) {
|
|
|
1091
1054
|
var import_react10 = require("react");
|
|
1092
1055
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1093
1056
|
function SocialProofPopup({
|
|
1094
|
-
|
|
1095
|
-
|
|
1057
|
+
apiUrl,
|
|
1058
|
+
shop,
|
|
1059
|
+
apiToken,
|
|
1096
1060
|
position = "bottom-left",
|
|
1097
1061
|
intervalMs = 8e3,
|
|
1098
1062
|
displayMs = 5e3,
|
|
1099
1063
|
starColor,
|
|
1100
1064
|
className
|
|
1101
1065
|
}) {
|
|
1102
|
-
const
|
|
1103
|
-
const { data } = useWidgetGlobals({ proxyUrl: resolvedUrl });
|
|
1066
|
+
const { data } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
1104
1067
|
const [current, setCurrent] = (0, import_react10.useState)(null);
|
|
1105
1068
|
const [visible, setVisible] = (0, import_react10.useState)(false);
|
|
1106
1069
|
const [dismissed, setDismissed] = (0, import_react10.useState)(false);
|
|
@@ -1202,15 +1165,15 @@ function SocialProofPopup({
|
|
|
1202
1165
|
var import_react11 = require("react");
|
|
1203
1166
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1204
1167
|
function ReviewTicker({
|
|
1205
|
-
|
|
1206
|
-
|
|
1168
|
+
apiUrl,
|
|
1169
|
+
shop,
|
|
1170
|
+
apiToken,
|
|
1207
1171
|
limit = 20,
|
|
1208
1172
|
speedSeconds = 30,
|
|
1209
1173
|
starColor,
|
|
1210
1174
|
className
|
|
1211
1175
|
}) {
|
|
1212
|
-
const
|
|
1213
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
1176
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
1214
1177
|
const trackRef = (0, import_react11.useRef)(null);
|
|
1215
1178
|
const reviews = data?.reviews ?? [];
|
|
1216
1179
|
if (loading || reviews.length === 0) return null;
|
|
@@ -1271,8 +1234,9 @@ function ReviewTicker({
|
|
|
1271
1234
|
var import_react12 = require("react");
|
|
1272
1235
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1273
1236
|
function FloatingReviewsTab({
|
|
1274
|
-
|
|
1275
|
-
|
|
1237
|
+
apiUrl,
|
|
1238
|
+
shop,
|
|
1239
|
+
apiToken,
|
|
1276
1240
|
label = "Reviews",
|
|
1277
1241
|
position = "right",
|
|
1278
1242
|
color = "#111827",
|
|
@@ -1280,8 +1244,7 @@ function FloatingReviewsTab({
|
|
|
1280
1244
|
starColor,
|
|
1281
1245
|
className
|
|
1282
1246
|
}) {
|
|
1283
|
-
const
|
|
1284
|
-
const { data } = useWidgetGlobals({ proxyUrl: resolvedUrl, limit });
|
|
1247
|
+
const { data } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
1285
1248
|
const [open, setOpen] = (0, import_react12.useState)(false);
|
|
1286
1249
|
const reviews = data?.reviews ?? [];
|
|
1287
1250
|
const stats = data?.stats;
|
|
@@ -1358,9 +1321,8 @@ function FloatingReviewsTab({
|
|
|
1358
1321
|
|
|
1359
1322
|
// src/components/TrustBadge.tsx
|
|
1360
1323
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1361
|
-
function TrustBadge({
|
|
1362
|
-
const
|
|
1363
|
-
const { data, loading } = useWidgetGlobals({ proxyUrl: resolvedUrl });
|
|
1324
|
+
function TrustBadge({ apiUrl, shop, apiToken, style: badgeStyle = "pill", starColor, className }) {
|
|
1325
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
1364
1326
|
if (loading || !data?.stats?.averageRating) return null;
|
|
1365
1327
|
const avg = parseFloat(data.stats.averageRating);
|
|
1366
1328
|
const count = data.stats.totalReviews;
|
|
@@ -1437,45 +1399,41 @@ var import_react14 = require("react");
|
|
|
1437
1399
|
|
|
1438
1400
|
// src/hooks/useForm.ts
|
|
1439
1401
|
var import_react13 = require("react");
|
|
1440
|
-
function useForm(
|
|
1402
|
+
function useForm(creds, productId) {
|
|
1441
1403
|
const [state, setState] = (0, import_react13.useState)({ form: null, loading: true, error: null });
|
|
1442
1404
|
(0, import_react13.useEffect)(() => {
|
|
1443
1405
|
let cancelled = false;
|
|
1444
|
-
|
|
1445
|
-
fetch(`${proxyUrl}/reviews?${params.toString()}`).then((res) => {
|
|
1406
|
+
apiFetch(creds, "/api/reviews", { productId, limit: "1", page: "1" }).then((res) => {
|
|
1446
1407
|
if (!res.ok) throw new Error(`Revova: form fetch failed (${res.status})`);
|
|
1447
1408
|
return res.json();
|
|
1448
1409
|
}).then(({ form }) => {
|
|
1449
1410
|
if (!cancelled) setState({ form, loading: false, error: null });
|
|
1450
1411
|
}).catch((err) => {
|
|
1451
1412
|
if (!cancelled)
|
|
1452
|
-
setState({
|
|
1453
|
-
form: null,
|
|
1454
|
-
loading: false,
|
|
1455
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1456
|
-
});
|
|
1413
|
+
setState({ form: null, loading: false, error: err instanceof Error ? err : new Error(String(err)) });
|
|
1457
1414
|
});
|
|
1458
1415
|
return () => {
|
|
1459
1416
|
cancelled = true;
|
|
1460
1417
|
};
|
|
1461
|
-
}, [
|
|
1418
|
+
}, [creds.apiUrl, creds.shop, creds.apiToken, productId]);
|
|
1462
1419
|
return state;
|
|
1463
1420
|
}
|
|
1464
1421
|
|
|
1465
1422
|
// src/components/FloatingReviewButton.tsx
|
|
1466
1423
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1467
1424
|
function FloatingReviewButton({
|
|
1468
|
-
|
|
1469
|
-
|
|
1425
|
+
apiUrl,
|
|
1426
|
+
shop,
|
|
1427
|
+
apiToken,
|
|
1470
1428
|
productId,
|
|
1471
1429
|
text = "Write a Review",
|
|
1472
1430
|
color = "#111827",
|
|
1473
1431
|
position = "bottom-right",
|
|
1474
1432
|
className
|
|
1475
1433
|
}) {
|
|
1476
|
-
const
|
|
1434
|
+
const creds = { apiUrl, shop, apiToken };
|
|
1477
1435
|
const [open, setOpen] = (0, import_react14.useState)(false);
|
|
1478
|
-
const { form, loading } = useForm(
|
|
1436
|
+
const { form, loading } = useForm(creds, productId);
|
|
1479
1437
|
const posStyle = position === "bottom-right" ? { bottom: 24, right: 24 } : { bottom: 24, left: 24 };
|
|
1480
1438
|
if (!loading && !form) return null;
|
|
1481
1439
|
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_jsx_runtime12.Fragment, { children: [
|
|
@@ -1553,7 +1511,7 @@ function FloatingReviewButton({
|
|
|
1553
1511
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
|
|
1554
1512
|
ReviewForm,
|
|
1555
1513
|
{
|
|
1556
|
-
|
|
1514
|
+
...creds,
|
|
1557
1515
|
productId,
|
|
1558
1516
|
form,
|
|
1559
1517
|
onSuccess: () => setOpen(false)
|
|
@@ -1569,7 +1527,7 @@ function FloatingReviewButton({
|
|
|
1569
1527
|
|
|
1570
1528
|
// src/hooks/useHelpfulVote.ts
|
|
1571
1529
|
var import_react15 = require("react");
|
|
1572
|
-
function useHelpfulVote(
|
|
1530
|
+
function useHelpfulVote(creds) {
|
|
1573
1531
|
const [loading, setLoading] = (0, import_react15.useState)(false);
|
|
1574
1532
|
const [voted, setVoted] = (0, import_react15.useState)(false);
|
|
1575
1533
|
const vote = (0, import_react15.useCallback)(
|
|
@@ -1577,9 +1535,8 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1577
1535
|
if (voted || loading) return;
|
|
1578
1536
|
setLoading(true);
|
|
1579
1537
|
try {
|
|
1580
|
-
await
|
|
1538
|
+
await apiFetch(creds, "/api/helpful", {}, {
|
|
1581
1539
|
method: "POST",
|
|
1582
|
-
headers: { "Content-Type": "application/json" },
|
|
1583
1540
|
body: JSON.stringify(payload)
|
|
1584
1541
|
});
|
|
1585
1542
|
setVoted(true);
|
|
@@ -1587,7 +1544,7 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1587
1544
|
setLoading(false);
|
|
1588
1545
|
}
|
|
1589
1546
|
},
|
|
1590
|
-
[
|
|
1547
|
+
[creds, voted, loading]
|
|
1591
1548
|
);
|
|
1592
1549
|
return { vote, loading, voted };
|
|
1593
1550
|
}
|
|
@@ -1605,7 +1562,7 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1605
1562
|
SocialProofPopup,
|
|
1606
1563
|
StarRating,
|
|
1607
1564
|
TrustBadge,
|
|
1608
|
-
|
|
1565
|
+
apiFetch,
|
|
1609
1566
|
useForm,
|
|
1610
1567
|
useHelpfulVote,
|
|
1611
1568
|
useQnA,
|