@lodashventure/medusa-review 1.4.18 → 1.4.19

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.
@@ -1,18 +1,216 @@
1
1
  "use strict";
2
2
  const jsxRuntime = require("react/jsx-runtime");
3
+ const react = require("react");
3
4
  const adminSdk = require("@medusajs/admin-sdk");
4
- const icons = require("@medusajs/icons");
5
5
  const ui = require("@medusajs/ui");
6
+ const icons = require("@medusajs/icons");
6
7
  const axios = require("axios");
7
- const Mentions = require("rc-mentions");
8
- const react = require("react");
9
8
  const reactRouterDom = require("react-router-dom");
9
+ const Mentions = require("rc-mentions");
10
10
  const Medusa = require("@medusajs/js-sdk");
11
11
  require("@medusajs/admin-shared");
12
12
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
13
13
  const axios__default = /* @__PURE__ */ _interopDefault(axios);
14
14
  const Mentions__default = /* @__PURE__ */ _interopDefault(Mentions);
15
15
  const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
16
+ const ProductReviewWidget = ({ data }) => {
17
+ const [reviews, setReviews] = react.useState([]);
18
+ const [isLoading, setIsLoading] = react.useState(true);
19
+ const [error, setError] = react.useState(null);
20
+ const [stats, setStats] = react.useState({
21
+ total: 0,
22
+ average: 0,
23
+ pending: 0,
24
+ approved: 0,
25
+ rejected: 0
26
+ });
27
+ const fetchReviews = async () => {
28
+ setIsLoading(true);
29
+ setError(null);
30
+ try {
31
+ const { data: response } = await axios__default.default.get(
32
+ `/admin/reviews`,
33
+ {
34
+ params: {
35
+ product_id: data.id,
36
+ limit: 5,
37
+ offset: 0,
38
+ order: "-created_at",
39
+ fields: "id,title,content,rating,status,created_at,first_name,last_name,is_admin"
40
+ }
41
+ }
42
+ );
43
+ setReviews(response.reviews);
44
+ const total = response.count || response.reviews.length;
45
+ const average = response.reviews.length > 0 ? response.reviews.reduce((sum, r) => sum + r.rating, 0) / response.reviews.length : 0;
46
+ const pending = response.reviews.filter(
47
+ (r) => r.status === "pending"
48
+ ).length;
49
+ const approved = response.reviews.filter(
50
+ (r) => r.status === "approved"
51
+ ).length;
52
+ const rejected = response.reviews.filter(
53
+ (r) => r.status === "rejected"
54
+ ).length;
55
+ setStats({ total, average, pending, approved, rejected });
56
+ } catch (err) {
57
+ setError(
58
+ err instanceof Error ? err.message : "Failed to fetch reviews"
59
+ );
60
+ } finally {
61
+ setIsLoading(false);
62
+ }
63
+ };
64
+ react.useEffect(() => {
65
+ fetchReviews();
66
+ }, [data.id]);
67
+ const handleStatusChange = async (reviewId, status) => {
68
+ try {
69
+ await axios__default.default.post("/admin/reviews/status", {
70
+ ids: [reviewId],
71
+ status
72
+ });
73
+ ui.toast.success(`Review ${status}`, {
74
+ description: `The review has been ${status} successfully`
75
+ });
76
+ fetchReviews();
77
+ } catch (err) {
78
+ ui.toast.error("Failed to update review", {
79
+ description: err instanceof Error ? err.message : "An error occurred"
80
+ });
81
+ }
82
+ };
83
+ const getStatusColor = (status) => {
84
+ switch (status) {
85
+ case "approved":
86
+ return "green";
87
+ case "rejected":
88
+ return "red";
89
+ case "pending":
90
+ return "orange";
91
+ default:
92
+ return "grey";
93
+ }
94
+ };
95
+ const renderStars = (rating) => {
96
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-0.5", children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsxRuntime.jsx(
97
+ "span",
98
+ {
99
+ className: `text-lg ${star <= rating ? "text-yellow-500" : "text-gray-300"}`,
100
+ children: "★"
101
+ },
102
+ star
103
+ )) });
104
+ };
105
+ if (error) {
106
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: error }) });
107
+ }
108
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "divide-y px-0 pb-0", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-6 space-y-6", children: [
109
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:justify-between", children: [
110
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
111
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ChatBubbleLeftRight, { className: "h-6 w-6 text-ui-fg-subtle" }),
112
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
113
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Product Reviews" }),
114
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-subtle", children: "Customer reviews and ratings for this product" })
115
+ ] })
116
+ ] }),
117
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
118
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { color: "blue", size: "small", children: [
119
+ stats.total,
120
+ " Total"
121
+ ] }),
122
+ stats.pending > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { color: "orange", size: "small", children: [
123
+ stats.pending,
124
+ " Pending"
125
+ ] })
126
+ ] })
127
+ ] }),
128
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border bg-ui-bg-base p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-muted", children: "Loading reviews..." }) }) : reviews.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-dashed bg-ui-bg-subtle p-6 text-center", children: [
129
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ChatBubbleLeftRight, { className: "h-8 w-8 mx-auto mb-2 text-ui-fg-muted" }),
130
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-muted", children: "No reviews yet for this product" }),
131
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-subtle", children: "Reviews will appear here once customers submit them" })
132
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
133
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border bg-ui-bg-base p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
134
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
135
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-subtle mb-1", children: "Average Rating" }),
136
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
137
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-2xl font-bold", children: stats.average.toFixed(1) }),
138
+ renderStars(Math.round(stats.average))
139
+ ] })
140
+ ] }),
141
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-right", children: [
142
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-subtle mb-1", children: "Status Breakdown" }),
143
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2 justify-end", children: [
144
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { color: "green", size: "small", children: [
145
+ stats.approved,
146
+ " Approved"
147
+ ] }),
148
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { color: "red", size: "small", children: [
149
+ stats.rejected,
150
+ " Rejected"
151
+ ] })
152
+ ] })
153
+ ] })
154
+ ] }) }),
155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
156
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
157
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-base", children: "Recent Reviews" }),
158
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: "/reviews", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "secondary", size: "small", children: [
159
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, { className: "mr-1 h-4 w-4" }),
160
+ "View All"
161
+ ] }) })
162
+ ] }),
163
+ reviews.map((review) => /* @__PURE__ */ jsxRuntime.jsx(
164
+ "div",
165
+ {
166
+ className: "rounded-lg border bg-ui-bg-subtle p-4 space-y-3",
167
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
168
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
169
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
170
+ renderStars(review.rating),
171
+ /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: getStatusColor(review.status), children: review.status.charAt(0).toUpperCase() + review.status.slice(1) })
172
+ ] }),
173
+ review.title && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium mb-1", children: review.title }),
174
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-sm text-ui-fg-subtle line-clamp-2", children: review.content }),
175
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
176
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: review.is_admin ? "Admin" : review.first_name ? `${review.first_name} ${review.last_name || ""}` : "Anonymous" }),
177
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-ui-fg-muted", children: "•" }),
178
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs text-ui-fg-muted", children: new Date(review.created_at).toLocaleDateString() })
179
+ ] })
180
+ ] }),
181
+ review.status === "pending" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 ml-4", children: [
182
+ /* @__PURE__ */ jsxRuntime.jsx(
183
+ ui.Button,
184
+ {
185
+ variant: "secondary",
186
+ size: "small",
187
+ onClick: () => handleStatusChange(review.id, "approved"),
188
+ title: "Approve review",
189
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircleSolid, { className: "h-4 w-4 text-green-600" })
190
+ }
191
+ ),
192
+ /* @__PURE__ */ jsxRuntime.jsx(
193
+ ui.Button,
194
+ {
195
+ variant: "secondary",
196
+ size: "small",
197
+ onClick: () => handleStatusChange(review.id, "rejected"),
198
+ title: "Reject review",
199
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XCircleSolid, { className: "h-4 w-4 text-red-600" })
200
+ }
201
+ )
202
+ ] })
203
+ ] })
204
+ },
205
+ review.id
206
+ ))
207
+ ] })
208
+ ] })
209
+ ] }) });
210
+ };
211
+ adminSdk.defineWidgetConfig({
212
+ zone: "product.details.after"
213
+ });
16
214
  const statusColor = (status) => status === "approved" ? "green" : status === "rejected" ? "red" : "grey";
17
215
  const mimeTypes = ["image", "video"];
18
216
  const MediaViewer = ({ media, className }) => {
@@ -459,7 +657,12 @@ const config = adminSdk.defineRouteConfig({
459
657
  label: "Reviews",
460
658
  icon: icons.ChatBubbleLeftRight
461
659
  });
462
- const widgetModule = { widgets: [] };
660
+ const widgetModule = { widgets: [
661
+ {
662
+ Component: ProductReviewWidget,
663
+ zone: ["product.details.after"]
664
+ }
665
+ ] };
463
666
  const routeModule = {
464
667
  routes: [
465
668
  {
@@ -1,13 +1,211 @@
1
- import { jsx, Fragment, jsxs } from "react/jsx-runtime";
2
- import { defineRouteConfig } from "@medusajs/admin-sdk";
3
- import { ChatBubbleLeftRight } from "@medusajs/icons";
4
- import { FocusModal, clx, ProgressAccordion, StatusBadge, Text, Button, createDataTableColumnHelper, Container, DataTable, Heading, Toaster, createDataTableCommandHelper, toast, useDataTable, Drawer } from "@medusajs/ui";
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from "react";
3
+ import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
4
+ import { Container, Alert, Heading, Text, Badge, Button, StatusBadge, toast, FocusModal, clx, ProgressAccordion, createDataTableColumnHelper, DataTable, Toaster, createDataTableCommandHelper, useDataTable, Drawer } from "@medusajs/ui";
5
+ import { ChatBubbleLeftRight, ArrowUpRightOnBox, CheckCircleSolid, XCircleSolid } from "@medusajs/icons";
5
6
  import axios from "axios";
6
- import Mentions from "rc-mentions";
7
- import { useState, useMemo, useEffect } from "react";
8
7
  import { Link } from "react-router-dom";
8
+ import Mentions from "rc-mentions";
9
9
  import Medusa from "@medusajs/js-sdk";
10
10
  import "@medusajs/admin-shared";
11
+ const ProductReviewWidget = ({ data }) => {
12
+ const [reviews, setReviews] = useState([]);
13
+ const [isLoading, setIsLoading] = useState(true);
14
+ const [error, setError] = useState(null);
15
+ const [stats, setStats] = useState({
16
+ total: 0,
17
+ average: 0,
18
+ pending: 0,
19
+ approved: 0,
20
+ rejected: 0
21
+ });
22
+ const fetchReviews = async () => {
23
+ setIsLoading(true);
24
+ setError(null);
25
+ try {
26
+ const { data: response } = await axios.get(
27
+ `/admin/reviews`,
28
+ {
29
+ params: {
30
+ product_id: data.id,
31
+ limit: 5,
32
+ offset: 0,
33
+ order: "-created_at",
34
+ fields: "id,title,content,rating,status,created_at,first_name,last_name,is_admin"
35
+ }
36
+ }
37
+ );
38
+ setReviews(response.reviews);
39
+ const total = response.count || response.reviews.length;
40
+ const average = response.reviews.length > 0 ? response.reviews.reduce((sum, r) => sum + r.rating, 0) / response.reviews.length : 0;
41
+ const pending = response.reviews.filter(
42
+ (r) => r.status === "pending"
43
+ ).length;
44
+ const approved = response.reviews.filter(
45
+ (r) => r.status === "approved"
46
+ ).length;
47
+ const rejected = response.reviews.filter(
48
+ (r) => r.status === "rejected"
49
+ ).length;
50
+ setStats({ total, average, pending, approved, rejected });
51
+ } catch (err) {
52
+ setError(
53
+ err instanceof Error ? err.message : "Failed to fetch reviews"
54
+ );
55
+ } finally {
56
+ setIsLoading(false);
57
+ }
58
+ };
59
+ useEffect(() => {
60
+ fetchReviews();
61
+ }, [data.id]);
62
+ const handleStatusChange = async (reviewId, status) => {
63
+ try {
64
+ await axios.post("/admin/reviews/status", {
65
+ ids: [reviewId],
66
+ status
67
+ });
68
+ toast.success(`Review ${status}`, {
69
+ description: `The review has been ${status} successfully`
70
+ });
71
+ fetchReviews();
72
+ } catch (err) {
73
+ toast.error("Failed to update review", {
74
+ description: err instanceof Error ? err.message : "An error occurred"
75
+ });
76
+ }
77
+ };
78
+ const getStatusColor = (status) => {
79
+ switch (status) {
80
+ case "approved":
81
+ return "green";
82
+ case "rejected":
83
+ return "red";
84
+ case "pending":
85
+ return "orange";
86
+ default:
87
+ return "grey";
88
+ }
89
+ };
90
+ const renderStars = (rating) => {
91
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ jsx(
92
+ "span",
93
+ {
94
+ className: `text-lg ${star <= rating ? "text-yellow-500" : "text-gray-300"}`,
95
+ children: "★"
96
+ },
97
+ star
98
+ )) });
99
+ };
100
+ if (error) {
101
+ return /* @__PURE__ */ jsx(Container, { className: "p-6", children: /* @__PURE__ */ jsx(Alert, { variant: "error", children: error }) });
102
+ }
103
+ return /* @__PURE__ */ jsx(Container, { className: "divide-y px-0 pb-0", children: /* @__PURE__ */ jsxs("div", { className: "px-6 py-6 space-y-6", children: [
104
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:justify-between", children: [
105
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
106
+ /* @__PURE__ */ jsx(ChatBubbleLeftRight, { className: "h-6 w-6 text-ui-fg-subtle" }),
107
+ /* @__PURE__ */ jsxs("div", { children: [
108
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Product Reviews" }),
109
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle", children: "Customer reviews and ratings for this product" })
110
+ ] })
111
+ ] }),
112
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
113
+ /* @__PURE__ */ jsxs(Badge, { color: "blue", size: "small", children: [
114
+ stats.total,
115
+ " Total"
116
+ ] }),
117
+ stats.pending > 0 && /* @__PURE__ */ jsxs(Badge, { color: "orange", size: "small", children: [
118
+ stats.pending,
119
+ " Pending"
120
+ ] })
121
+ ] })
122
+ ] }),
123
+ isLoading ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-ui-bg-base p-6 text-center", children: /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-muted", children: "Loading reviews..." }) }) : reviews.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed bg-ui-bg-subtle p-6 text-center", children: [
124
+ /* @__PURE__ */ jsx(ChatBubbleLeftRight, { className: "h-8 w-8 mx-auto mb-2 text-ui-fg-muted" }),
125
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-muted", children: "No reviews yet for this product" }),
126
+ /* @__PURE__ */ jsx(Text, { className: "text-xs text-ui-fg-subtle", children: "Reviews will appear here once customers submit them" })
127
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
128
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-ui-bg-base p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
129
+ /* @__PURE__ */ jsxs("div", { children: [
130
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle mb-1", children: "Average Rating" }),
131
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
132
+ /* @__PURE__ */ jsx("span", { className: "text-2xl font-bold", children: stats.average.toFixed(1) }),
133
+ renderStars(Math.round(stats.average))
134
+ ] })
135
+ ] }),
136
+ /* @__PURE__ */ jsxs("div", { className: "text-right", children: [
137
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle mb-1", children: "Status Breakdown" }),
138
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-end", children: [
139
+ /* @__PURE__ */ jsxs(Badge, { color: "green", size: "small", children: [
140
+ stats.approved,
141
+ " Approved"
142
+ ] }),
143
+ /* @__PURE__ */ jsxs(Badge, { color: "red", size: "small", children: [
144
+ stats.rejected,
145
+ " Rejected"
146
+ ] })
147
+ ] })
148
+ ] })
149
+ ] }) }),
150
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
151
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
152
+ /* @__PURE__ */ jsx(Heading, { level: "h3", className: "text-base", children: "Recent Reviews" }),
153
+ /* @__PURE__ */ jsx(Link, { to: "/reviews", children: /* @__PURE__ */ jsxs(Button, { variant: "secondary", size: "small", children: [
154
+ /* @__PURE__ */ jsx(ArrowUpRightOnBox, { className: "mr-1 h-4 w-4" }),
155
+ "View All"
156
+ ] }) })
157
+ ] }),
158
+ reviews.map((review) => /* @__PURE__ */ jsx(
159
+ "div",
160
+ {
161
+ className: "rounded-lg border bg-ui-bg-subtle p-4 space-y-3",
162
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between", children: [
163
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
164
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-1", children: [
165
+ renderStars(review.rating),
166
+ /* @__PURE__ */ jsx(StatusBadge, { color: getStatusColor(review.status), children: review.status.charAt(0).toUpperCase() + review.status.slice(1) })
167
+ ] }),
168
+ review.title && /* @__PURE__ */ jsx(Text, { className: "font-medium mb-1", children: review.title }),
169
+ /* @__PURE__ */ jsx(Text, { className: "text-sm text-ui-fg-subtle line-clamp-2", children: review.content }),
170
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-2", children: [
171
+ /* @__PURE__ */ jsx(Text, { className: "text-xs text-ui-fg-muted", children: review.is_admin ? "Admin" : review.first_name ? `${review.first_name} ${review.last_name || ""}` : "Anonymous" }),
172
+ /* @__PURE__ */ jsx("span", { className: "text-ui-fg-muted", children: "•" }),
173
+ /* @__PURE__ */ jsx(Text, { className: "text-xs text-ui-fg-muted", children: new Date(review.created_at).toLocaleDateString() })
174
+ ] })
175
+ ] }),
176
+ review.status === "pending" && /* @__PURE__ */ jsxs("div", { className: "flex gap-1 ml-4", children: [
177
+ /* @__PURE__ */ jsx(
178
+ Button,
179
+ {
180
+ variant: "secondary",
181
+ size: "small",
182
+ onClick: () => handleStatusChange(review.id, "approved"),
183
+ title: "Approve review",
184
+ children: /* @__PURE__ */ jsx(CheckCircleSolid, { className: "h-4 w-4 text-green-600" })
185
+ }
186
+ ),
187
+ /* @__PURE__ */ jsx(
188
+ Button,
189
+ {
190
+ variant: "secondary",
191
+ size: "small",
192
+ onClick: () => handleStatusChange(review.id, "rejected"),
193
+ title: "Reject review",
194
+ children: /* @__PURE__ */ jsx(XCircleSolid, { className: "h-4 w-4 text-red-600" })
195
+ }
196
+ )
197
+ ] })
198
+ ] })
199
+ },
200
+ review.id
201
+ ))
202
+ ] })
203
+ ] })
204
+ ] }) });
205
+ };
206
+ defineWidgetConfig({
207
+ zone: "product.details.after"
208
+ });
11
209
  const statusColor = (status) => status === "approved" ? "green" : status === "rejected" ? "red" : "grey";
12
210
  const mimeTypes = ["image", "video"];
13
211
  const MediaViewer = ({ media, className }) => {
@@ -454,7 +652,12 @@ const config = defineRouteConfig({
454
652
  label: "Reviews",
455
653
  icon: ChatBubbleLeftRight
456
654
  });
457
- const widgetModule = { widgets: [] };
655
+ const widgetModule = { widgets: [
656
+ {
657
+ Component: ProductReviewWidget,
658
+ zone: ["product.details.after"]
659
+ }
660
+ ] };
458
661
  const routeModule = {
459
662
  routes: [
460
663
  {
@@ -4,11 +4,15 @@ exports.POST = exports.GET = void 0;
4
4
  const getReviewsWorkflow_1 = require("../../../workflows/review/getReviewsWorkflow");
5
5
  const createReviewWorkflow_1 = require("../../../workflows/review/createReviewWorkflow");
6
6
  const GET = async (req, res) => {
7
+ // Extract filters from query parameters
8
+ const { product_id, status } = req.query;
7
9
  const { result } = await (0, getReviewsWorkflow_1.getReviewsWorkflow)().run({
8
10
  input: {
9
11
  queryConfig: req.queryConfig,
10
12
  filters: {
11
13
  parent: null,
14
+ ...(product_id && { product_id }),
15
+ ...(status && { status }),
12
16
  },
13
17
  },
14
18
  });
@@ -32,4 +36,4 @@ const POST = async (req, res) => {
32
36
  res.json(result);
33
37
  };
34
38
  exports.POST = POST;
35
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3Jldmlld3Mvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBS0EscUZBQWtGO0FBQ2xGLHlGQUFzRjtBQUcvRSxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDbkUsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBQSx1Q0FBa0IsR0FBRSxDQUFDLEdBQUcsQ0FBQztRQUNoRCxLQUFLLEVBQUU7WUFDTCxXQUFXLEVBQUUsR0FBRyxDQUFDLFdBQVc7WUFDNUIsT0FBTyxFQUFFO2dCQUNQLE1BQU0sRUFBRSxJQUFJO2FBQ2I7U0FDRjtLQUNGLENBQUMsQ0FBQztJQUVILEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBWFcsUUFBQSxHQUFHLE9BV2Q7QUFFSyxNQUFNLElBQUksR0FBRyxLQUFLLEVBQ3ZCLEdBQW1ELEVBQ25ELEdBQW1CLEVBQ25CLEVBQUU7SUFDRjs7O09BR0c7SUFDSCxNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsWUFBWSxFQUFFLFVBQVUsS0FBSyxNQUFNLENBQUM7SUFFeEQsTUFBTSxFQUFFLE1BQU0sRUFBRSxHQUFHLE1BQU0sSUFBQSwyQ0FBb0IsRUFBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsR0FBRyxDQUFDO1FBQzNELEtBQUssRUFBRTtZQUNMLEdBQUcsR0FBRyxDQUFDLGFBQWE7WUFDcEIsUUFBUSxFQUFFLE9BQU87WUFDakIsTUFBTSxFQUFFLFVBQVU7WUFDbEIsTUFBTSxFQUFFLEVBQUU7U0FDWDtLQUNGLENBQUMsQ0FBQztJQUVILEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBcEJXLFFBQUEsSUFBSSxRQW9CZiJ9
39
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3Jldmlld3Mvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBS0EscUZBQWtGO0FBQ2xGLHlGQUFzRjtBQUcvRSxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDbkUsd0NBQXdDO0lBQ3hDLE1BQU0sRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLEtBR2xDLENBQUM7SUFFRixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLHVDQUFrQixHQUFFLENBQUMsR0FBRyxDQUFDO1FBQ2hELEtBQUssRUFBRTtZQUNMLFdBQVcsRUFBRSxHQUFHLENBQUMsV0FBVztZQUM1QixPQUFPLEVBQUU7Z0JBQ1AsTUFBTSxFQUFFLElBQUk7Z0JBQ1osR0FBRyxDQUFDLFVBQVUsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFDO2dCQUNqQyxHQUFHLENBQUMsTUFBTSxJQUFJLEVBQUUsTUFBTSxFQUFFLENBQUM7YUFDMUI7U0FDRjtLQUNGLENBQUMsQ0FBQztJQUVILEdBQUcsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBbkJXLFFBQUEsR0FBRyxPQW1CZDtBQUVLLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFDdkIsR0FBbUQsRUFDbkQsR0FBbUIsRUFDbkIsRUFBRTtJQUNGOzs7T0FHRztJQUNILE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxZQUFZLEVBQUUsVUFBVSxLQUFLLE1BQU0sQ0FBQztJQUV4RCxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLDJDQUFvQixFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDM0QsS0FBSyxFQUFFO1lBQ0wsR0FBRyxHQUFHLENBQUMsYUFBYTtZQUNwQixRQUFRLEVBQUUsT0FBTztZQUNqQixNQUFNLEVBQUUsVUFBVTtZQUNsQixNQUFNLEVBQUUsRUFBRTtTQUNYO0tBQ0YsQ0FBQyxDQUFDO0lBRUgsR0FBRyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUNuQixDQUFDLENBQUM7QUFwQlcsUUFBQSxJQUFJLFFBb0JmIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodashventure/medusa-review",
3
- "version": "1.4.18",
3
+ "version": "1.4.19",
4
4
  "description": "A starter for Medusa plugins.",
5
5
  "author": "Medusa (https://medusajs.com)",
6
6
  "license": "MIT",