@revova/hydrogen 1.0.0 → 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 +136 -88
- package/dist/index.cjs +102 -117
- package/dist/index.d.cts +59 -58
- package/dist/index.d.ts +59 -58
- package/dist/index.js +101 -117
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,8 +3,25 @@ import { useState as useState4 } from "react";
|
|
|
3
3
|
|
|
4
4
|
// src/hooks/useReviews.ts
|
|
5
5
|
import { useEffect, useState, useCallback } from "react";
|
|
6
|
+
|
|
7
|
+
// src/utils.ts
|
|
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
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/hooks/useReviews.ts
|
|
6
21
|
function useReviews({
|
|
7
|
-
|
|
22
|
+
apiUrl,
|
|
23
|
+
shop,
|
|
24
|
+
apiToken,
|
|
8
25
|
productId,
|
|
9
26
|
page: initialPage = 1,
|
|
10
27
|
limit = 10,
|
|
@@ -13,24 +30,20 @@ function useReviews({
|
|
|
13
30
|
}) {
|
|
14
31
|
const [page, setPage] = useState(initialPage);
|
|
15
32
|
const [sort, setSort] = useState(initialSort);
|
|
16
|
-
const [state, setState] = useState({
|
|
17
|
-
data: null,
|
|
18
|
-
loading: true,
|
|
19
|
-
error: null
|
|
20
|
-
});
|
|
33
|
+
const [state, setState] = useState({ data: null, loading: true, error: null });
|
|
21
34
|
const [tick, setTick] = useState(0);
|
|
22
35
|
const refetch = useCallback(() => setTick((t) => t + 1), []);
|
|
23
36
|
useEffect(() => {
|
|
24
37
|
let cancelled = false;
|
|
25
38
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
26
|
-
const params =
|
|
39
|
+
const params = {
|
|
27
40
|
productId,
|
|
28
41
|
page: String(page),
|
|
29
42
|
limit: String(limit),
|
|
30
43
|
sort,
|
|
31
44
|
...locale ? { locale } : {}
|
|
32
|
-
}
|
|
33
|
-
|
|
45
|
+
};
|
|
46
|
+
apiFetch({ apiUrl, shop, apiToken }, "/api/reviews", params).then((res) => {
|
|
34
47
|
if (!res.ok) throw new Error(`Revova: reviews fetch failed (${res.status})`);
|
|
35
48
|
return res.json();
|
|
36
49
|
}).then((data) => {
|
|
@@ -42,15 +55,8 @@ function useReviews({
|
|
|
42
55
|
return () => {
|
|
43
56
|
cancelled = true;
|
|
44
57
|
};
|
|
45
|
-
}, [
|
|
46
|
-
return {
|
|
47
|
-
...state,
|
|
48
|
-
refetch,
|
|
49
|
-
setPage,
|
|
50
|
-
setSort,
|
|
51
|
-
currentPage: page,
|
|
52
|
-
currentSort: sort
|
|
53
|
-
};
|
|
58
|
+
}, [apiUrl, shop, apiToken, productId, page, limit, sort, locale, tick]);
|
|
59
|
+
return { ...state, refetch, setPage, setSort, currentPage: page, currentSort: sort };
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
// src/components/StarRating.tsx
|
|
@@ -101,44 +107,28 @@ import { useState as useState3 } from "react";
|
|
|
101
107
|
|
|
102
108
|
// src/hooks/useSubmitReview.ts
|
|
103
109
|
import { useState as useState2, useCallback as useCallback2 } from "react";
|
|
104
|
-
var INITIAL_STATE = {
|
|
105
|
-
|
|
106
|
-
success: false,
|
|
107
|
-
error: null,
|
|
108
|
-
result: null
|
|
109
|
-
};
|
|
110
|
-
function useSubmitReview(proxyUrl) {
|
|
110
|
+
var INITIAL_STATE = { submitting: false, success: false, error: null, result: null };
|
|
111
|
+
function useSubmitReview(creds) {
|
|
111
112
|
const [state, setState] = useState2(INITIAL_STATE);
|
|
112
113
|
const submit = useCallback2(
|
|
113
114
|
async (payload) => {
|
|
114
115
|
setState({ submitting: true, success: false, error: null, result: null });
|
|
115
116
|
try {
|
|
116
|
-
const res = await
|
|
117
|
+
const res = await apiFetch(creds, "/api/reviews", {}, {
|
|
117
118
|
method: "POST",
|
|
118
|
-
headers: { "Content-Type": "application/json" },
|
|
119
119
|
body: JSON.stringify(payload)
|
|
120
120
|
});
|
|
121
121
|
const json = await res.json();
|
|
122
122
|
if (!res.ok) {
|
|
123
|
-
setState({
|
|
124
|
-
submitting: false,
|
|
125
|
-
success: false,
|
|
126
|
-
error: json.error ?? `Submission failed (${res.status})`,
|
|
127
|
-
result: null
|
|
128
|
-
});
|
|
123
|
+
setState({ submitting: false, success: false, error: json.error ?? `Submission failed (${res.status})`, result: null });
|
|
129
124
|
return;
|
|
130
125
|
}
|
|
131
126
|
setState({ submitting: false, success: true, error: null, result: json });
|
|
132
127
|
} catch (err) {
|
|
133
|
-
setState({
|
|
134
|
-
submitting: false,
|
|
135
|
-
success: false,
|
|
136
|
-
error: err instanceof Error ? err.message : "An unexpected error occurred.",
|
|
137
|
-
result: null
|
|
138
|
-
});
|
|
128
|
+
setState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An unexpected error occurred.", result: null });
|
|
139
129
|
}
|
|
140
130
|
},
|
|
141
|
-
[
|
|
131
|
+
[creds]
|
|
142
132
|
);
|
|
143
133
|
const reset = useCallback2(() => setState(INITIAL_STATE), []);
|
|
144
134
|
return { ...state, submit, reset };
|
|
@@ -426,8 +416,8 @@ function FieldRenderer(props) {
|
|
|
426
416
|
return null;
|
|
427
417
|
}
|
|
428
418
|
}
|
|
429
|
-
function ReviewForm({
|
|
430
|
-
const { submit, submitting, success, error, result } = useSubmitReview(
|
|
419
|
+
function ReviewForm({ apiUrl, shop, apiToken, productId, form, onSuccess, className }) {
|
|
420
|
+
const { submit, submitting, success, error, result } = useSubmitReview({ apiUrl, shop, apiToken });
|
|
431
421
|
const [answers, setAnswers] = useState3({});
|
|
432
422
|
const [email, setEmail] = useState3("");
|
|
433
423
|
const starField = form.fields.find((f) => f.type === "STAR_RATING");
|
|
@@ -534,7 +524,9 @@ function ReviewForm({ proxyUrl, productId, form, onSuccess, className }) {
|
|
|
534
524
|
// src/components/ReviewWidget.tsx
|
|
535
525
|
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
536
526
|
function ReviewWidget({
|
|
537
|
-
|
|
527
|
+
apiUrl,
|
|
528
|
+
shop,
|
|
529
|
+
apiToken,
|
|
538
530
|
productId,
|
|
539
531
|
locale,
|
|
540
532
|
pageSize = 10,
|
|
@@ -542,9 +534,10 @@ function ReviewWidget({
|
|
|
542
534
|
starColor,
|
|
543
535
|
className
|
|
544
536
|
}) {
|
|
537
|
+
const creds = { apiUrl, shop, apiToken };
|
|
545
538
|
const [showingForm, setShowingForm] = useState4(false);
|
|
546
539
|
const { data, loading, error, setPage, setSort, currentPage, currentSort } = useReviews({
|
|
547
|
-
|
|
540
|
+
...creds,
|
|
548
541
|
productId,
|
|
549
542
|
limit: pageSize,
|
|
550
543
|
...locale !== void 0 ? { locale } : {}
|
|
@@ -579,7 +572,7 @@ function ReviewWidget({
|
|
|
579
572
|
showingForm && form && /* @__PURE__ */ jsx3("div", { style: { marginBottom: 24 }, children: /* @__PURE__ */ jsx3(
|
|
580
573
|
ReviewForm,
|
|
581
574
|
{
|
|
582
|
-
|
|
575
|
+
...creds,
|
|
583
576
|
productId,
|
|
584
577
|
form,
|
|
585
578
|
onSuccess: () => setShowingForm(false)
|
|
@@ -635,16 +628,11 @@ function ReviewWidget({
|
|
|
635
628
|
|
|
636
629
|
// src/hooks/useWidgetGlobals.ts
|
|
637
630
|
import { useEffect as useEffect2, useState as useState5 } from "react";
|
|
638
|
-
function useWidgetGlobals({
|
|
639
|
-
const [state, setState] = useState5({
|
|
640
|
-
data: null,
|
|
641
|
-
loading: true,
|
|
642
|
-
error: null
|
|
643
|
-
});
|
|
631
|
+
function useWidgetGlobals({ apiUrl, shop, apiToken, limit = 20 }) {
|
|
632
|
+
const [state, setState] = useState5({ data: null, loading: true, error: null });
|
|
644
633
|
useEffect2(() => {
|
|
645
634
|
let cancelled = false;
|
|
646
|
-
|
|
647
|
-
fetch(`${proxyUrl}/widget-globals?${params.toString()}`).then((res) => {
|
|
635
|
+
apiFetch({ apiUrl, shop, apiToken }, "/api/widget-globals", { limit: String(limit) }).then((res) => {
|
|
648
636
|
if (!res.ok) throw new Error(`Revova: widget-globals fetch failed (${res.status})`);
|
|
649
637
|
return res.json();
|
|
650
638
|
}).then((data) => {
|
|
@@ -656,14 +644,14 @@ function useWidgetGlobals({ proxyUrl, limit = 20 }) {
|
|
|
656
644
|
return () => {
|
|
657
645
|
cancelled = true;
|
|
658
646
|
};
|
|
659
|
-
}, [
|
|
647
|
+
}, [apiUrl, shop, apiToken, limit]);
|
|
660
648
|
return state;
|
|
661
649
|
}
|
|
662
650
|
|
|
663
651
|
// src/components/ReviewCount.tsx
|
|
664
652
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
665
|
-
function ReviewCount({
|
|
666
|
-
const { data, loading } = useWidgetGlobals({
|
|
653
|
+
function ReviewCount({ apiUrl, shop, apiToken, starColor, starSize, className }) {
|
|
654
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
667
655
|
if (loading || !data?.stats?.averageRating) return null;
|
|
668
656
|
const avg = parseFloat(data.stats.averageRating);
|
|
669
657
|
return /* @__PURE__ */ jsxs4("span", { className, style: { display: "inline-flex", alignItems: "center", gap: 6 }, children: [
|
|
@@ -681,14 +669,16 @@ function ReviewCount({ proxyUrl, starColor, starSize, className }) {
|
|
|
681
669
|
import React3, { useState as useState6 } from "react";
|
|
682
670
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
683
671
|
function ReviewCarousel({
|
|
684
|
-
|
|
672
|
+
apiUrl,
|
|
673
|
+
shop,
|
|
674
|
+
apiToken,
|
|
685
675
|
limit = 10,
|
|
686
676
|
autoPlay = true,
|
|
687
677
|
intervalMs = 4e3,
|
|
688
678
|
starColor,
|
|
689
679
|
className
|
|
690
680
|
}) {
|
|
691
|
-
const { data, loading } = useWidgetGlobals({
|
|
681
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
692
682
|
const [index, setIndex] = useState6(0);
|
|
693
683
|
const reviews = data?.reviews ?? [];
|
|
694
684
|
React3.useEffect(() => {
|
|
@@ -736,13 +726,15 @@ function ReviewCarousel({
|
|
|
736
726
|
import { useState as useState7 } from "react";
|
|
737
727
|
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
738
728
|
function ReviewGallery({
|
|
739
|
-
|
|
729
|
+
apiUrl,
|
|
730
|
+
shop,
|
|
731
|
+
apiToken,
|
|
740
732
|
limit = 20,
|
|
741
733
|
columns = 3,
|
|
742
734
|
starColor,
|
|
743
735
|
className
|
|
744
736
|
}) {
|
|
745
|
-
const { data, loading } = useWidgetGlobals({
|
|
737
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
746
738
|
const [lightbox, setLightbox] = useState7(null);
|
|
747
739
|
const items = (data?.reviews ?? []).filter((r) => r.image).map((r) => ({ url: r.image, review: r })).slice(0, limit);
|
|
748
740
|
if (loading) return /* @__PURE__ */ jsx6("div", { className, children: "Loading gallery\u2026" });
|
|
@@ -859,7 +851,8 @@ import { useState as useState9 } from "react";
|
|
|
859
851
|
// src/hooks/useQnA.ts
|
|
860
852
|
import { useEffect as useEffect3, useState as useState8, useCallback as useCallback3 } from "react";
|
|
861
853
|
var INITIAL_SUBMIT = { submitting: false, success: false, error: null };
|
|
862
|
-
function useQnA({
|
|
854
|
+
function useQnA({ apiUrl, shop, apiToken, productId, page: initialPage = 1, sort = "recent" }) {
|
|
855
|
+
const creds = { apiUrl, shop, apiToken };
|
|
863
856
|
const [page, setPage] = useState8(initialPage);
|
|
864
857
|
const [tick, setTick] = useState8(0);
|
|
865
858
|
const [state, setState] = useState8({ data: null, loading: true, error: null });
|
|
@@ -868,8 +861,7 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
868
861
|
useEffect3(() => {
|
|
869
862
|
let cancelled = false;
|
|
870
863
|
setState((s) => ({ ...s, loading: true, error: null }));
|
|
871
|
-
|
|
872
|
-
fetch(`${proxyUrl}/qna?${params.toString()}`).then((res) => {
|
|
864
|
+
apiFetch(creds, "/api/qna", { productId, page: String(page), sort }).then((res) => {
|
|
873
865
|
if (!res.ok) throw new Error(`Revova: qna fetch failed (${res.status})`);
|
|
874
866
|
return res.json();
|
|
875
867
|
}).then((data) => {
|
|
@@ -881,15 +873,11 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
881
873
|
return () => {
|
|
882
874
|
cancelled = true;
|
|
883
875
|
};
|
|
884
|
-
}, [
|
|
885
|
-
const
|
|
876
|
+
}, [apiUrl, shop, apiToken, productId, page, sort, tick]);
|
|
877
|
+
const postQnA = useCallback3(async (payload) => {
|
|
886
878
|
setSubmitState({ submitting: true, success: false, error: null });
|
|
887
879
|
try {
|
|
888
|
-
const res = await
|
|
889
|
-
method: "POST",
|
|
890
|
-
headers: { "Content-Type": "application/json" },
|
|
891
|
-
body: JSON.stringify(payload)
|
|
892
|
-
});
|
|
880
|
+
const res = await apiFetch(creds, "/api/qna", {}, { method: "POST", body: JSON.stringify(payload) });
|
|
893
881
|
const json = await res.json();
|
|
894
882
|
if (!res.ok) {
|
|
895
883
|
setSubmitState({ submitting: false, success: false, error: json.error ?? `Failed (${res.status})` });
|
|
@@ -900,35 +888,27 @@ function useQnA({ proxyUrl, productId, page: initialPage = 1, sort = "recent" })
|
|
|
900
888
|
} catch (err) {
|
|
901
889
|
setSubmitState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An error occurred." });
|
|
902
890
|
}
|
|
903
|
-
}, [
|
|
904
|
-
const submitAnswer = useCallback3(async (payload) => {
|
|
905
|
-
setSubmitState({ submitting: true, success: false, error: null });
|
|
906
|
-
try {
|
|
907
|
-
const res = await fetch(`${proxyUrl}/qna`, {
|
|
908
|
-
method: "POST",
|
|
909
|
-
headers: { "Content-Type": "application/json" },
|
|
910
|
-
body: JSON.stringify(payload)
|
|
911
|
-
});
|
|
912
|
-
const json = await res.json();
|
|
913
|
-
if (!res.ok) {
|
|
914
|
-
setSubmitState({ submitting: false, success: false, error: json.error ?? `Failed (${res.status})` });
|
|
915
|
-
return;
|
|
916
|
-
}
|
|
917
|
-
setSubmitState({ submitting: false, success: true, error: null });
|
|
918
|
-
refetch();
|
|
919
|
-
} catch (err) {
|
|
920
|
-
setSubmitState({ submitting: false, success: false, error: err instanceof Error ? err.message : "An error occurred." });
|
|
921
|
-
}
|
|
922
|
-
}, [proxyUrl, refetch]);
|
|
891
|
+
}, [apiUrl, shop, apiToken, refetch]);
|
|
923
892
|
const resetSubmit = useCallback3(() => setSubmitState(INITIAL_SUBMIT), []);
|
|
924
|
-
return {
|
|
893
|
+
return {
|
|
894
|
+
...state,
|
|
895
|
+
setPage,
|
|
896
|
+
currentPage: page,
|
|
897
|
+
refetch,
|
|
898
|
+
submitQuestion: postQnA,
|
|
899
|
+
submitAnswer: postQnA,
|
|
900
|
+
submitState,
|
|
901
|
+
resetSubmit
|
|
902
|
+
};
|
|
925
903
|
}
|
|
926
904
|
|
|
927
905
|
// src/components/QnAWidget.tsx
|
|
928
906
|
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
929
|
-
function QnAWidget({
|
|
907
|
+
function QnAWidget({ apiUrl, shop, apiToken, productId, className }) {
|
|
930
908
|
const { data, loading, error, setPage, currentPage, submitQuestion, submitState, resetSubmit } = useQnA({
|
|
931
|
-
|
|
909
|
+
apiUrl,
|
|
910
|
+
shop,
|
|
911
|
+
apiToken,
|
|
932
912
|
productId
|
|
933
913
|
});
|
|
934
914
|
const [showForm, setShowForm] = useState9(false);
|
|
@@ -1020,14 +1000,16 @@ function QnAWidget({ proxyUrl, productId, className }) {
|
|
|
1020
1000
|
import { useEffect as useEffect4, useState as useState10 } from "react";
|
|
1021
1001
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1022
1002
|
function SocialProofPopup({
|
|
1023
|
-
|
|
1003
|
+
apiUrl,
|
|
1004
|
+
shop,
|
|
1005
|
+
apiToken,
|
|
1024
1006
|
position = "bottom-left",
|
|
1025
1007
|
intervalMs = 8e3,
|
|
1026
1008
|
displayMs = 5e3,
|
|
1027
1009
|
starColor,
|
|
1028
1010
|
className
|
|
1029
1011
|
}) {
|
|
1030
|
-
const { data } = useWidgetGlobals({
|
|
1012
|
+
const { data } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
1031
1013
|
const [current, setCurrent] = useState10(null);
|
|
1032
1014
|
const [visible, setVisible] = useState10(false);
|
|
1033
1015
|
const [dismissed, setDismissed] = useState10(false);
|
|
@@ -1129,13 +1111,15 @@ function SocialProofPopup({
|
|
|
1129
1111
|
import { useRef } from "react";
|
|
1130
1112
|
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1131
1113
|
function ReviewTicker({
|
|
1132
|
-
|
|
1114
|
+
apiUrl,
|
|
1115
|
+
shop,
|
|
1116
|
+
apiToken,
|
|
1133
1117
|
limit = 20,
|
|
1134
1118
|
speedSeconds = 30,
|
|
1135
1119
|
starColor,
|
|
1136
1120
|
className
|
|
1137
1121
|
}) {
|
|
1138
|
-
const { data, loading } = useWidgetGlobals({
|
|
1122
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
1139
1123
|
const trackRef = useRef(null);
|
|
1140
1124
|
const reviews = data?.reviews ?? [];
|
|
1141
1125
|
if (loading || reviews.length === 0) return null;
|
|
@@ -1196,7 +1180,9 @@ function ReviewTicker({
|
|
|
1196
1180
|
import { useState as useState11 } from "react";
|
|
1197
1181
|
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1198
1182
|
function FloatingReviewsTab({
|
|
1199
|
-
|
|
1183
|
+
apiUrl,
|
|
1184
|
+
shop,
|
|
1185
|
+
apiToken,
|
|
1200
1186
|
label = "Reviews",
|
|
1201
1187
|
position = "right",
|
|
1202
1188
|
color = "#111827",
|
|
@@ -1204,7 +1190,7 @@ function FloatingReviewsTab({
|
|
|
1204
1190
|
starColor,
|
|
1205
1191
|
className
|
|
1206
1192
|
}) {
|
|
1207
|
-
const { data } = useWidgetGlobals({
|
|
1193
|
+
const { data } = useWidgetGlobals({ apiUrl, shop, apiToken, limit });
|
|
1208
1194
|
const [open, setOpen] = useState11(false);
|
|
1209
1195
|
const reviews = data?.reviews ?? [];
|
|
1210
1196
|
const stats = data?.stats;
|
|
@@ -1281,8 +1267,8 @@ function FloatingReviewsTab({
|
|
|
1281
1267
|
|
|
1282
1268
|
// src/components/TrustBadge.tsx
|
|
1283
1269
|
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1284
|
-
function TrustBadge({
|
|
1285
|
-
const { data, loading } = useWidgetGlobals({
|
|
1270
|
+
function TrustBadge({ apiUrl, shop, apiToken, style: badgeStyle = "pill", starColor, className }) {
|
|
1271
|
+
const { data, loading } = useWidgetGlobals({ apiUrl, shop, apiToken });
|
|
1286
1272
|
if (loading || !data?.stats?.averageRating) return null;
|
|
1287
1273
|
const avg = parseFloat(data.stats.averageRating);
|
|
1288
1274
|
const count = data.stats.totalReviews;
|
|
@@ -1359,43 +1345,41 @@ import { useState as useState13 } from "react";
|
|
|
1359
1345
|
|
|
1360
1346
|
// src/hooks/useForm.ts
|
|
1361
1347
|
import { useEffect as useEffect5, useState as useState12 } from "react";
|
|
1362
|
-
function useForm(
|
|
1348
|
+
function useForm(creds, productId) {
|
|
1363
1349
|
const [state, setState] = useState12({ form: null, loading: true, error: null });
|
|
1364
1350
|
useEffect5(() => {
|
|
1365
1351
|
let cancelled = false;
|
|
1366
|
-
|
|
1367
|
-
fetch(`${proxyUrl}/reviews?${params.toString()}`).then((res) => {
|
|
1352
|
+
apiFetch(creds, "/api/reviews", { productId, limit: "1", page: "1" }).then((res) => {
|
|
1368
1353
|
if (!res.ok) throw new Error(`Revova: form fetch failed (${res.status})`);
|
|
1369
1354
|
return res.json();
|
|
1370
1355
|
}).then(({ form }) => {
|
|
1371
1356
|
if (!cancelled) setState({ form, loading: false, error: null });
|
|
1372
1357
|
}).catch((err) => {
|
|
1373
1358
|
if (!cancelled)
|
|
1374
|
-
setState({
|
|
1375
|
-
form: null,
|
|
1376
|
-
loading: false,
|
|
1377
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1378
|
-
});
|
|
1359
|
+
setState({ form: null, loading: false, error: err instanceof Error ? err : new Error(String(err)) });
|
|
1379
1360
|
});
|
|
1380
1361
|
return () => {
|
|
1381
1362
|
cancelled = true;
|
|
1382
1363
|
};
|
|
1383
|
-
}, [
|
|
1364
|
+
}, [creds.apiUrl, creds.shop, creds.apiToken, productId]);
|
|
1384
1365
|
return state;
|
|
1385
1366
|
}
|
|
1386
1367
|
|
|
1387
1368
|
// src/components/FloatingReviewButton.tsx
|
|
1388
1369
|
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1389
1370
|
function FloatingReviewButton({
|
|
1390
|
-
|
|
1371
|
+
apiUrl,
|
|
1372
|
+
shop,
|
|
1373
|
+
apiToken,
|
|
1391
1374
|
productId,
|
|
1392
1375
|
text = "Write a Review",
|
|
1393
1376
|
color = "#111827",
|
|
1394
1377
|
position = "bottom-right",
|
|
1395
1378
|
className
|
|
1396
1379
|
}) {
|
|
1380
|
+
const creds = { apiUrl, shop, apiToken };
|
|
1397
1381
|
const [open, setOpen] = useState13(false);
|
|
1398
|
-
const { form, loading } = useForm(
|
|
1382
|
+
const { form, loading } = useForm(creds, productId);
|
|
1399
1383
|
const posStyle = position === "bottom-right" ? { bottom: 24, right: 24 } : { bottom: 24, left: 24 };
|
|
1400
1384
|
if (!loading && !form) return null;
|
|
1401
1385
|
return /* @__PURE__ */ jsxs12(Fragment5, { children: [
|
|
@@ -1473,7 +1457,7 @@ function FloatingReviewButton({
|
|
|
1473
1457
|
/* @__PURE__ */ jsx12(
|
|
1474
1458
|
ReviewForm,
|
|
1475
1459
|
{
|
|
1476
|
-
|
|
1460
|
+
...creds,
|
|
1477
1461
|
productId,
|
|
1478
1462
|
form,
|
|
1479
1463
|
onSuccess: () => setOpen(false)
|
|
@@ -1489,7 +1473,7 @@ function FloatingReviewButton({
|
|
|
1489
1473
|
|
|
1490
1474
|
// src/hooks/useHelpfulVote.ts
|
|
1491
1475
|
import { useState as useState14, useCallback as useCallback4 } from "react";
|
|
1492
|
-
function useHelpfulVote(
|
|
1476
|
+
function useHelpfulVote(creds) {
|
|
1493
1477
|
const [loading, setLoading] = useState14(false);
|
|
1494
1478
|
const [voted, setVoted] = useState14(false);
|
|
1495
1479
|
const vote = useCallback4(
|
|
@@ -1497,9 +1481,8 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1497
1481
|
if (voted || loading) return;
|
|
1498
1482
|
setLoading(true);
|
|
1499
1483
|
try {
|
|
1500
|
-
await
|
|
1484
|
+
await apiFetch(creds, "/api/helpful", {}, {
|
|
1501
1485
|
method: "POST",
|
|
1502
|
-
headers: { "Content-Type": "application/json" },
|
|
1503
1486
|
body: JSON.stringify(payload)
|
|
1504
1487
|
});
|
|
1505
1488
|
setVoted(true);
|
|
@@ -1507,7 +1490,7 @@ function useHelpfulVote(proxyUrl) {
|
|
|
1507
1490
|
setLoading(false);
|
|
1508
1491
|
}
|
|
1509
1492
|
},
|
|
1510
|
-
[
|
|
1493
|
+
[creds, voted, loading]
|
|
1511
1494
|
);
|
|
1512
1495
|
return { vote, loading, voted };
|
|
1513
1496
|
}
|
|
@@ -1524,6 +1507,7 @@ export {
|
|
|
1524
1507
|
SocialProofPopup,
|
|
1525
1508
|
StarRating,
|
|
1526
1509
|
TrustBadge,
|
|
1510
|
+
apiFetch,
|
|
1527
1511
|
useForm,
|
|
1528
1512
|
useHelpfulVote,
|
|
1529
1513
|
useQnA,
|