@jsm406/medusa-product-reviews 1.0.0

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.
Files changed (96) hide show
  1. package/.medusa/server/src/admin/index.js +562 -0
  2. package/.medusa/server/src/admin/index.mjs +543 -0
  3. package/.medusa/server/src/api/admin/product-review-stats/middlewares.d.ts +47 -0
  4. package/.medusa/server/src/api/admin/product-review-stats/middlewares.js +46 -0
  5. package/.medusa/server/src/api/admin/product-review-stats/route.d.ts +2 -0
  6. package/.medusa/server/src/api/admin/product-review-stats/route.js +19 -0
  7. package/.medusa/server/src/api/admin/product-reviews/[id]/response/middlewares.d.ts +7 -0
  8. package/.medusa/server/src/api/admin/product-reviews/[id]/response/middlewares.js +28 -0
  9. package/.medusa/server/src/api/admin/product-reviews/[id]/response/route.d.ts +73 -0
  10. package/.medusa/server/src/api/admin/product-reviews/[id]/response/route.js +80 -0
  11. package/.medusa/server/src/api/admin/product-reviews/[id]/status/middlewares.d.ts +11 -0
  12. package/.medusa/server/src/api/admin/product-reviews/[id]/status/middlewares.js +18 -0
  13. package/.medusa/server/src/api/admin/product-reviews/[id]/status/route.d.ts +3 -0
  14. package/.medusa/server/src/api/admin/product-reviews/[id]/status/route.js +19 -0
  15. package/.medusa/server/src/api/admin/product-reviews/middlewares.d.ts +58 -0
  16. package/.medusa/server/src/api/admin/product-reviews/middlewares.js +52 -0
  17. package/.medusa/server/src/api/admin/product-reviews/route.d.ts +2 -0
  18. package/.medusa/server/src/api/admin/product-reviews/route.js +15 -0
  19. package/.medusa/server/src/api/middlewares.d.ts +2 -0
  20. package/.medusa/server/src/api/middlewares.js +23 -0
  21. package/.medusa/server/src/api/store/product-review-stats/middlewares.d.ts +47 -0
  22. package/.medusa/server/src/api/store/product-review-stats/middlewares.js +47 -0
  23. package/.medusa/server/src/api/store/product-review-stats/route.d.ts +2 -0
  24. package/.medusa/server/src/api/store/product-review-stats/route.js +19 -0
  25. package/.medusa/server/src/api/store/product-reviews/middlewares.d.ts +68 -0
  26. package/.medusa/server/src/api/store/product-reviews/middlewares.js +76 -0
  27. package/.medusa/server/src/api/store/product-reviews/route.d.ts +4 -0
  28. package/.medusa/server/src/api/store/product-reviews/route.js +35 -0
  29. package/.medusa/server/src/api/store/product-reviews/uploads/middlewares.d.ts +2 -0
  30. package/.medusa/server/src/api/store/product-reviews/uploads/middlewares.js +16 -0
  31. package/.medusa/server/src/api/store/product-reviews/uploads/route.d.ts +3 -0
  32. package/.medusa/server/src/api/store/product-reviews/uploads/route.js +25 -0
  33. package/.medusa/server/src/links/product-review-order-line-item.d.ts +2 -0
  34. package/.medusa/server/src/links/product-review-order-line-item.js +16 -0
  35. package/.medusa/server/src/links/product-review-order.d.ts +2 -0
  36. package/.medusa/server/src/links/product-review-order.js +16 -0
  37. package/.medusa/server/src/links/product-review-product.d.ts +2 -0
  38. package/.medusa/server/src/links/product-review-product.js +15 -0
  39. package/.medusa/server/src/links/product-review-stats-product.d.ts +2 -0
  40. package/.medusa/server/src/links/product-review-stats-product.js +17 -0
  41. package/.medusa/server/src/modules/product-review/index.d.ts +63 -0
  42. package/.medusa/server/src/modules/product-review/index.js +15 -0
  43. package/.medusa/server/src/modules/product-review/loaders/validate.d.ts +3 -0
  44. package/.medusa/server/src/modules/product-review/loaders/validate.js +12 -0
  45. package/.medusa/server/src/modules/product-review/migrations/Migration20250212230212.d.ts +5 -0
  46. package/.medusa/server/src/modules/product-review/migrations/Migration20250212230212.js +33 -0
  47. package/.medusa/server/src/modules/product-review/migrations/Migration20250421143843.d.ts +5 -0
  48. package/.medusa/server/src/modules/product-review/migrations/Migration20250421143843.js +16 -0
  49. package/.medusa/server/src/modules/product-review/models/index.d.ts +4 -0
  50. package/.medusa/server/src/modules/product-review/models/index.js +12 -0
  51. package/.medusa/server/src/modules/product-review/models/product-review-image.d.ts +25 -0
  52. package/.medusa/server/src/modules/product-review/models/product-review-image.js +13 -0
  53. package/.medusa/server/src/modules/product-review/models/product-review-response.d.ts +21 -0
  54. package/.medusa/server/src/modules/product-review/models/product-review-response.js +13 -0
  55. package/.medusa/server/src/modules/product-review/models/product-review-stats.d.ts +11 -0
  56. package/.medusa/server/src/modules/product-review/models/product-review-stats.js +22 -0
  57. package/.medusa/server/src/modules/product-review/models/product-review.d.ts +25 -0
  58. package/.medusa/server/src/modules/product-review/models/product-review.js +32 -0
  59. package/.medusa/server/src/modules/product-review/service.d.ts +112 -0
  60. package/.medusa/server/src/modules/product-review/service.js +73 -0
  61. package/.medusa/server/src/modules/product-review/types/common.d.ts +7 -0
  62. package/.medusa/server/src/modules/product-review/types/common.js +3 -0
  63. package/.medusa/server/src/modules/product-review/types/index.d.ts +2 -0
  64. package/.medusa/server/src/modules/product-review/types/index.js +19 -0
  65. package/.medusa/server/src/modules/product-review/types/mutations.d.ts +35 -0
  66. package/.medusa/server/src/modules/product-review/types/mutations.js +3 -0
  67. package/.medusa/server/src/workflows/create-product-review-responses.d.ts +33 -0
  68. package/.medusa/server/src/workflows/create-product-review-responses.js +21 -0
  69. package/.medusa/server/src/workflows/create-product-reviews.d.ts +33 -0
  70. package/.medusa/server/src/workflows/create-product-reviews.js +28 -0
  71. package/.medusa/server/src/workflows/delete-product-review-responses.d.ts +4 -0
  72. package/.medusa/server/src/workflows/delete-product-review-responses.js +21 -0
  73. package/.medusa/server/src/workflows/refresh-product-review-stats.d.ts +3 -0
  74. package/.medusa/server/src/workflows/refresh-product-review-stats.js +12 -0
  75. package/.medusa/server/src/workflows/steps/create-missing-product-review-stats.d.ts +16 -0
  76. package/.medusa/server/src/workflows/steps/create-missing-product-review-stats.js +16 -0
  77. package/.medusa/server/src/workflows/steps/create-product-review-responses.d.ts +34 -0
  78. package/.medusa/server/src/workflows/steps/create-product-review-responses.js +18 -0
  79. package/.medusa/server/src/workflows/steps/create-product-reviews.d.ts +34 -0
  80. package/.medusa/server/src/workflows/steps/create-product-reviews.js +33 -0
  81. package/.medusa/server/src/workflows/steps/delete-product-review-responses.d.ts +4 -0
  82. package/.medusa/server/src/workflows/steps/delete-product-review-responses.js +20 -0
  83. package/.medusa/server/src/workflows/steps/recalculate-product-review-stats.d.ts +16 -0
  84. package/.medusa/server/src/workflows/steps/recalculate-product-review-stats.js +15 -0
  85. package/.medusa/server/src/workflows/steps/update-product-review-response.d.ts +34 -0
  86. package/.medusa/server/src/workflows/steps/update-product-review-response.js +15 -0
  87. package/.medusa/server/src/workflows/steps/update-product-reviews.d.ts +34 -0
  88. package/.medusa/server/src/workflows/steps/update-product-reviews.js +21 -0
  89. package/.medusa/server/src/workflows/update-product-review-responses.d.ts +33 -0
  90. package/.medusa/server/src/workflows/update-product-review-responses.js +21 -0
  91. package/.medusa/server/src/workflows/update-product-reviews.d.ts +33 -0
  92. package/.medusa/server/src/workflows/update-product-reviews.js +26 -0
  93. package/.medusa/server/src/workflows/upsert-product-reviews.d.ts +16 -0
  94. package/.medusa/server/src/workflows/upsert-product-reviews.js +80 -0
  95. package/README.md +0 -0
  96. package/package.json +121 -0
@@ -0,0 +1,562 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ const jsxRuntime = require("react/jsx-runtime");
6
+ const adminSdk = require("@medusajs/admin-sdk");
7
+ const ui = require("@medusajs/ui");
8
+ const icons = require("@medusajs/icons");
9
+ const luxon = require("luxon");
10
+ const react = require("react");
11
+ const reactQuery = require("@tanstack/react-query");
12
+ const Medusa = require("@medusajs/js-sdk");
13
+ const reactHookForm = require("react-hook-form");
14
+ const z = require("zod");
15
+ const reactRouterDom = require("react-router-dom");
16
+ require("@medusajs/admin-shared");
17
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
18
+ function _interopNamespace(e) {
19
+ if (e && e.__esModule) return e;
20
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
21
+ if (e) {
22
+ for (const k in e) {
23
+ if (k !== "default") {
24
+ const d = Object.getOwnPropertyDescriptor(e, k);
25
+ Object.defineProperty(n, k, d.get ? d : {
26
+ enumerable: true,
27
+ get: () => e[k]
28
+ });
29
+ }
30
+ }
31
+ }
32
+ n.default = e;
33
+ return Object.freeze(n);
34
+ }
35
+ const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
36
+ const z__namespace = /* @__PURE__ */ _interopNamespace(z);
37
+ const Container = (props) => {
38
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { ...props, className: ui.clx("divide-y p-0", props.className) });
39
+ };
40
+ const backendUrl = __BACKEND_URL__ ?? "http://localhost:9000";
41
+ class AdminProductReviewsResource {
42
+ constructor(client) {
43
+ this.client = client;
44
+ }
45
+ async list(query) {
46
+ return this.client.fetch(`/admin/product-reviews`, {
47
+ method: "GET",
48
+ query
49
+ });
50
+ }
51
+ async updateStatus(productReviewId, status) {
52
+ return this.client.fetch(
53
+ `/admin/product-reviews/${productReviewId}/status`,
54
+ {
55
+ method: "PUT",
56
+ body: { status }
57
+ }
58
+ );
59
+ }
60
+ async createResponse(productReviewId, data) {
61
+ return this.client.fetch(
62
+ `/admin/product-reviews/${productReviewId}/response`,
63
+ {
64
+ method: "POST",
65
+ body: data
66
+ }
67
+ );
68
+ }
69
+ async updateResponse(productReviewId, data) {
70
+ return this.client.fetch(
71
+ `/admin/product-reviews/${productReviewId}/response`,
72
+ {
73
+ method: "PUT",
74
+ body: data
75
+ }
76
+ );
77
+ }
78
+ async deleteResponse(productReviewId) {
79
+ return this.client.fetch(
80
+ `/admin/product-reviews/${productReviewId}/response`,
81
+ {
82
+ method: "DELETE"
83
+ }
84
+ );
85
+ }
86
+ }
87
+ class StoreProductReviewsResource {
88
+ constructor(client) {
89
+ this.client = client;
90
+ }
91
+ async upsert(data, headers) {
92
+ return this.client.fetch(`/store/product-reviews`, {
93
+ method: "POST",
94
+ body: data,
95
+ headers
96
+ });
97
+ }
98
+ async list(query, headers) {
99
+ return this.client.fetch(`/store/product-reviews`, {
100
+ method: "GET",
101
+ query,
102
+ headers
103
+ });
104
+ }
105
+ async listStats(query, headers) {
106
+ return this.client.fetch(`/store/product-review-stats`, {
107
+ method: "GET",
108
+ query,
109
+ headers
110
+ });
111
+ }
112
+ }
113
+ class ExtendedAdminSDK extends Medusa.Admin {
114
+ constructor(client) {
115
+ super(client);
116
+ __publicField(this, "productReviews");
117
+ this.productReviews = new AdminProductReviewsResource(client);
118
+ }
119
+ }
120
+ class ExtendedStorefrontSDK extends Medusa.Store {
121
+ constructor(client) {
122
+ super(client);
123
+ __publicField(this, "productReviews");
124
+ this.productReviews = new StoreProductReviewsResource(client);
125
+ }
126
+ }
127
+ class MedusaPluginsSDK extends Medusa__default.default {
128
+ constructor(config) {
129
+ super(config);
130
+ __publicField(this, "admin");
131
+ __publicField(this, "store");
132
+ this.admin = new ExtendedAdminSDK(this.client);
133
+ this.store = new ExtendedStorefrontSDK(this.client);
134
+ }
135
+ }
136
+ const sdk = new MedusaPluginsSDK({
137
+ baseUrl: backendUrl,
138
+ auth: {
139
+ type: "session"
140
+ }
141
+ });
142
+ const QUERY_KEY = ["product-reviews"];
143
+ const useAdminListProductReviews = (query) => {
144
+ return reactQuery.useQuery({
145
+ queryKey: [...QUERY_KEY, query],
146
+ placeholderData: (previousData) => previousData,
147
+ queryFn: async () => {
148
+ return sdk.admin.productReviews.list(query);
149
+ }
150
+ });
151
+ };
152
+ const useAdminCreateProductReviewResponseMutation = (reviewId) => {
153
+ const queryClient = reactQuery.useQueryClient();
154
+ return reactQuery.useMutation({
155
+ mutationFn: async (body) => {
156
+ return await sdk.admin.productReviews.createResponse(reviewId, body);
157
+ },
158
+ onSuccess: () => {
159
+ queryClient.invalidateQueries({ queryKey: QUERY_KEY });
160
+ }
161
+ });
162
+ };
163
+ const useAdminUpdateProductReviewResponseMutation = (reviewId) => {
164
+ const queryClient = reactQuery.useQueryClient();
165
+ return reactQuery.useMutation({
166
+ mutationFn: async (body) => {
167
+ return await sdk.admin.productReviews.updateResponse(reviewId, body);
168
+ },
169
+ onSuccess: () => {
170
+ queryClient.invalidateQueries({ queryKey: QUERY_KEY });
171
+ }
172
+ });
173
+ };
174
+ const useAdminUpdateProductReviewStatusMutation = () => {
175
+ const queryClient = reactQuery.useQueryClient();
176
+ return reactQuery.useMutation({
177
+ mutationFn: async ({
178
+ reviewId,
179
+ status
180
+ }) => {
181
+ return await sdk.admin.productReviews.updateStatus(reviewId, status);
182
+ },
183
+ onSuccess: () => {
184
+ queryClient.invalidateQueries({ queryKey: QUERY_KEY });
185
+ }
186
+ });
187
+ };
188
+ z__namespace.object({
189
+ content: z__namespace.string().min(1).refine((val) => val.trim().split(/\s+/).length >= 1, {
190
+ message: "Response must contain at least one word"
191
+ })
192
+ });
193
+ const ProductReviewResponseDrawer = ({
194
+ review,
195
+ open,
196
+ setOpen
197
+ }) => {
198
+ var _a;
199
+ const title = (review == null ? void 0 : review.response) ? "Edit Response" : "Add Response";
200
+ const { mutate: createResponse } = useAdminCreateProductReviewResponseMutation((review == null ? void 0 : review.id) ?? "");
201
+ const { mutate: updateResponse } = useAdminUpdateProductReviewResponseMutation((review == null ? void 0 : review.id) ?? "");
202
+ const form = reactHookForm.useForm({
203
+ defaultValues: {
204
+ content: ((_a = review == null ? void 0 : review.response) == null ? void 0 : _a.content) ?? ""
205
+ }
206
+ // resolver: zodResolver(schema),
207
+ });
208
+ if (!review) return null;
209
+ const onSubmit = async (data) => {
210
+ if (review.response) {
211
+ await updateResponse(data);
212
+ } else {
213
+ await createResponse(data);
214
+ }
215
+ setOpen(false);
216
+ };
217
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
218
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { className: "font-medium", children: title }) }),
219
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Body, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-col gap-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
220
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { className: "font-medium", children: "Response" }),
221
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Textarea, { placeholder: "Write your response...", rows: 4, ...form.register("content") }),
222
+ form.formState.errors.content && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-red-500", size: "small", children: form.formState.errors.content.message })
223
+ ] }) }) }),
224
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
225
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Cancel" }) }),
226
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: form.handleSubmit(onSubmit), children: "Save" })
227
+ ] })
228
+ ] }) });
229
+ };
230
+ const ReviewStars = ({ rating }) => {
231
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-0.5", children: [...Array(5)].map((_, index) => /* @__PURE__ */ jsxRuntime.jsx(
232
+ "svg",
233
+ {
234
+ className: `h-5 w-5 ${index < rating ? "text-yellow-400" : "text-gray-200"}`,
235
+ xmlns: "http://www.w3.org/2000/svg",
236
+ viewBox: "0 0 24 24",
237
+ fill: "currentColor",
238
+ children: /* @__PURE__ */ jsxRuntime.jsx(
239
+ "path",
240
+ {
241
+ fillRule: "evenodd",
242
+ d: "M10.788 3.21c.448-1.077 1.976-1.077 2.424 0l2.082 5.007 5.404.433c1.164.093 1.636 1.545.749 2.305l-4.117 3.527 1.257 5.273c.271 1.136-.964 2.033-1.96 1.425L12 18.354 7.373 21.18c-.996.608-2.231-.29-1.96-1.425l1.257-5.273-4.117-3.527c-.887-.76-.415-2.212.749-2.305l5.404-.433 2.082-5.006z",
243
+ clipRule: "evenodd"
244
+ }
245
+ )
246
+ },
247
+ index
248
+ )) });
249
+ };
250
+ const SectionRow = ({ title, value, actions }) => {
251
+ const isValueString = typeof value === "string" || !value;
252
+ return /* @__PURE__ */ jsxRuntime.jsxs(
253
+ "div",
254
+ {
255
+ className: ui.clx("text-ui-fg-subtle grid grid-cols-2 items-center px-6 py-4", {
256
+ "grid-cols-[1fr_1fr_28px]": !!actions
257
+ }),
258
+ children: [
259
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", leading: "compact", children: title }),
260
+ isValueString ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", leading: "compact", className: "whitespace-pre-line text-pretty", children: value ?? "-" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: value }),
261
+ actions && /* @__PURE__ */ jsxRuntime.jsx("div", { children: actions })
262
+ ]
263
+ }
264
+ );
265
+ };
266
+ const ProductReviewDetailsDrawer = ({
267
+ review,
268
+ open,
269
+ setOpen
270
+ }) => {
271
+ var _a;
272
+ if (!review) return null;
273
+ const ProductValue = () => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
274
+ review.product.thumbnail ? /* @__PURE__ */ jsxRuntime.jsx(
275
+ "img",
276
+ {
277
+ className: "h-12 w-12 flex-shrink-0 rounded-md",
278
+ src: review.product.thumbnail,
279
+ alt: review.product.title
280
+ }
281
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-12 w-12 flex-shrink-0 rounded-md bg-gray-200" }),
282
+ /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/products/${review.product.id}`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "hover:underline", children: review.product.title }) })
283
+ ] });
284
+ const OrderValue = () => /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/orders/${review.order.id}`, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "hover:underline", children: [
285
+ "#",
286
+ review.order.display_id
287
+ ] }) });
288
+ const StatusValue = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle text-sm", children: review.status });
289
+ const CreatedAtValue = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle text-sm", children: luxon.DateTime.fromISO(review.created_at).toFormat("LLL dd yyyy hh:mm a") });
290
+ const CustomerValue = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: review.name }) });
291
+ const ReviewContent = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "whitespace-pre-wrap", children: review.content }) });
292
+ const ImagesValue = () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-2", children: review.images.map((image, index) => /* @__PURE__ */ jsxRuntime.jsx(
293
+ "img",
294
+ {
295
+ src: image.url,
296
+ alt: `Review image ${index + 1}`,
297
+ className: "w-full h-full object-cover"
298
+ }
299
+ )) });
300
+ const ResponseValue = () => {
301
+ var _a2;
302
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "whitespace-pre-wrap", children: (_a2 = review.response) == null ? void 0 : _a2.content }) });
303
+ };
304
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
305
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { className: "font-medium", children: "Review Details" }) }),
306
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Body, { className: "flex flex-col divide-y", children: [
307
+ /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Customer", value: /* @__PURE__ */ jsxRuntime.jsx(CustomerValue, {}) }),
308
+ /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Status", value: /* @__PURE__ */ jsxRuntime.jsx(StatusValue, {}) }),
309
+ /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Created At", value: /* @__PURE__ */ jsxRuntime.jsx(CreatedAtValue, {}) }),
310
+ /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Product", value: /* @__PURE__ */ jsxRuntime.jsx(ProductValue, {}) }),
311
+ /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Order", value: /* @__PURE__ */ jsxRuntime.jsx(OrderValue, {}) }),
312
+ /* @__PURE__ */ jsxRuntime.jsx(
313
+ SectionRow,
314
+ {
315
+ title: "Rating",
316
+ value: /* @__PURE__ */ jsxRuntime.jsx(ReviewStars, { rating: review.rating })
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Review", value: /* @__PURE__ */ jsxRuntime.jsx(ReviewContent, {}) }),
320
+ review.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(SectionRow, { title: "Images", value: /* @__PURE__ */ jsxRuntime.jsx(ImagesValue, {}) }),
321
+ /* @__PURE__ */ jsxRuntime.jsx(
322
+ SectionRow,
323
+ {
324
+ title: "Response",
325
+ value: review.response ? /* @__PURE__ */ jsxRuntime.jsx(ResponseValue, {}) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "No response" })
326
+ }
327
+ ),
328
+ !!((_a = review.response) == null ? void 0 : _a.created_at) && /* @__PURE__ */ jsxRuntime.jsx(
329
+ SectionRow,
330
+ {
331
+ title: "Responded At",
332
+ value: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: luxon.DateTime.fromISO(review.response.created_at).toFormat(
333
+ "LLL dd yyyy hh:mm a"
334
+ ) })
335
+ }
336
+ )
337
+ ] }),
338
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Footer, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Close" }) }) })
339
+ ] }) });
340
+ };
341
+ const PRODUCT_REVIEW_STATUSES = ["approved", "flagged", "pending"];
342
+ const columnHelper = ui.createDataTableColumnHelper();
343
+ const getColumns = (showColumns, actions) => {
344
+ const updateStatusActions = (review) => {
345
+ return PRODUCT_REVIEW_STATUSES.filter((status) => status !== review.status).map((status) => ({
346
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.CheckCircle, { className: "h-4 w-4" }),
347
+ label: `Mark as ${status}`,
348
+ onClick: () => actions.updateStatus({ reviewId: review.id, status })
349
+ }));
350
+ };
351
+ const allColumns = [
352
+ columnHelper.accessor("product", {
353
+ id: "product",
354
+ header: "Product",
355
+ enableSorting: false,
356
+ cell: ({ row }) => {
357
+ const product = row.original.product;
358
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4 py-2 w-full min-w-[200px] max-w-[300px]", children: [
359
+ product.thumbnail ? /* @__PURE__ */ jsxRuntime.jsx("img", { className: "h-8 w-8 flex-shrink-0 rounded-md my-1", src: product.thumbnail, alt: product.title }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-8 flex-shrink-0 rounded-md bg-gray-200" }),
360
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/products/${product.id}`, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm whitespace-normal break-words hover:underline line-clamp-3", children: product.title }) }) })
361
+ ] });
362
+ }
363
+ }),
364
+ columnHelper.accessor("order", {
365
+ id: "order",
366
+ header: "Order",
367
+ enableSorting: false,
368
+ cell: ({ row }) => {
369
+ return /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/orders/${row.original.order.id}`, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm whitespace-normal break-words hover:underline", children: [
370
+ "#",
371
+ row.original.order.display_id
372
+ ] }) });
373
+ }
374
+ }),
375
+ columnHelper.accessor("status", {
376
+ id: "status",
377
+ header: "Status",
378
+ enableSorting: false,
379
+ cell: ({ row }) => {
380
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { children: row.original.status });
381
+ }
382
+ }),
383
+ columnHelper.accessor("created_at", {
384
+ id: "created_at",
385
+ header: "Created At",
386
+ enableSorting: false,
387
+ cell: ({ row }) => {
388
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[160px] max-w-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: luxon.DateTime.fromISO(row.original.created_at).toFormat("LLL dd yyyy hh:mm a") }) });
389
+ }
390
+ }),
391
+ columnHelper.accessor("name", {
392
+ id: "name",
393
+ header: "Customer",
394
+ enableSorting: false,
395
+ cell: ({ row }) => {
396
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[120px] max-w-[200px]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "whitespace-normal break-words", children: row.original.name }) });
397
+ }
398
+ }),
399
+ columnHelper.accessor("content", {
400
+ id: "content",
401
+ header: "Review",
402
+ enableSorting: false,
403
+ cell: ({ row }) => {
404
+ const rating = row.original.rating;
405
+ const content = row.original.content;
406
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 my-2 min-w-[200px] max-w-[400px]", children: [
407
+ /* @__PURE__ */ jsxRuntime.jsx(ReviewStars, { rating }),
408
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-700 whitespace-normal break-words line-clamp-3", children: content })
409
+ ] });
410
+ }
411
+ }),
412
+ columnHelper.accessor("images", {
413
+ id: "images",
414
+ header: "Images",
415
+ enableSorting: false,
416
+ cell: ({ row }) => {
417
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2", children: row.original.images.length });
418
+ }
419
+ }),
420
+ columnHelper.accessor("response", {
421
+ id: "response",
422
+ header: "Response",
423
+ enableSorting: false,
424
+ cell: ({ row }) => {
425
+ var _a;
426
+ const content = (_a = row.original.response) == null ? void 0 : _a.content;
427
+ if (!content) {
428
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-[160px] max-w-[300px]", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-gray-400", children: "No response" }) });
429
+ }
430
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-2 my-2 min-w-[160px] max-w-[300px]", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-gray-700 whitespace-normal break-words line-clamp-3", children: content }) });
431
+ }
432
+ }),
433
+ columnHelper.action({
434
+ // @ts-ignore
435
+ id: "actions",
436
+ actions: ({ row }) => [
437
+ {
438
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, { className: "h-4 w-4" }),
439
+ label: "View details",
440
+ onClick: () => {
441
+ actions.setSelectedReviewForDetails(row.original);
442
+ }
443
+ },
444
+ {
445
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.ChatBubble, { className: "h-4 w-4" }),
446
+ label: row.original.response ? "Edit response" : "Add response",
447
+ onClick: () => {
448
+ actions.setSelectedReview(row.original);
449
+ }
450
+ },
451
+ ...updateStatusActions(row.original)
452
+ ]
453
+ })
454
+ ];
455
+ if (!showColumns) return allColumns;
456
+ return showColumns.map((c) => allColumns.find((column) => column.id === c)).filter((c) => !!c);
457
+ };
458
+ const ProductReviewDataTable = ({
459
+ defaultQuery,
460
+ showColumns
461
+ }) => {
462
+ const defaultLimit = defaultQuery.limit ?? 5;
463
+ const defaultOffset = defaultQuery.offset ?? 0;
464
+ const [selectedReview, setSelectedReview] = react.useState(null);
465
+ const [selectedReviewForDetails, setSelectedReviewForDetails] = react.useState(null);
466
+ const [query, setQuery] = react.useState({ ...defaultQuery, limit: defaultLimit, offset: defaultOffset });
467
+ const { mutate: updateStatus } = useAdminUpdateProductReviewStatusMutation();
468
+ const { data, isLoading } = useAdminListProductReviews({ ...query, limit: defaultLimit });
469
+ const table = ui.useDataTable({
470
+ columns: getColumns(showColumns, { setSelectedReview, setSelectedReviewForDetails, updateStatus }),
471
+ data: (data == null ? void 0 : data.product_reviews) ?? [],
472
+ getRowId: (review) => review.id,
473
+ rowCount: (data == null ? void 0 : data.count) ?? 0,
474
+ isLoading,
475
+ search: {
476
+ state: query.q ?? "",
477
+ onSearchChange: (q) => {
478
+ setQuery({ ...query, q });
479
+ }
480
+ },
481
+ pagination: {
482
+ state: {
483
+ pageIndex: Math.floor(query.offset / query.limit),
484
+ pageSize: query.limit
485
+ },
486
+ onPaginationChange: (pagination) => {
487
+ const newQuery = { ...query, offset: pagination.pageIndex * pagination.pageSize, limit: pagination.pageSize };
488
+ setQuery(newQuery);
489
+ }
490
+ }
491
+ });
492
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
493
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
494
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "flex flex-col items-start justify-between gap-2 md:flex-row md:items-center", children: [
495
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Product Reviews" }),
496
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Search, {})
497
+ ] }),
498
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
499
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
500
+ ] }),
501
+ selectedReview && /* @__PURE__ */ jsxRuntime.jsx(
502
+ ProductReviewResponseDrawer,
503
+ {
504
+ review: selectedReview,
505
+ open: selectedReview !== null,
506
+ setOpen: (open) => setSelectedReview(open ? selectedReview : null)
507
+ }
508
+ ),
509
+ selectedReviewForDetails && /* @__PURE__ */ jsxRuntime.jsx(
510
+ ProductReviewDetailsDrawer,
511
+ {
512
+ review: selectedReviewForDetails,
513
+ open: selectedReviewForDetails !== null,
514
+ setOpen: (open) => setSelectedReviewForDetails(open ? selectedReviewForDetails : null)
515
+ }
516
+ )
517
+ ] });
518
+ };
519
+ const OrderDetailsProductReviewsWidget$1 = ({ data: order }) => {
520
+ return /* @__PURE__ */ jsxRuntime.jsx(Container, { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(ProductReviewDataTable, { defaultQuery: { order_id: order.id } }) });
521
+ };
522
+ adminSdk.defineWidgetConfig({
523
+ zone: "order.details.after"
524
+ });
525
+ const OrderDetailsProductReviewsWidget = ({
526
+ data: product
527
+ }) => {
528
+ return /* @__PURE__ */ jsxRuntime.jsx(Container, { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(ProductReviewDataTable, { defaultQuery: { product_id: product.id } }) });
529
+ };
530
+ adminSdk.defineWidgetConfig({
531
+ zone: "product.details.after"
532
+ });
533
+ const widgetModule = { widgets: [
534
+ {
535
+ Component: OrderDetailsProductReviewsWidget$1,
536
+ zone: ["order.details.after"]
537
+ },
538
+ {
539
+ Component: OrderDetailsProductReviewsWidget,
540
+ zone: ["product.details.after"]
541
+ }
542
+ ] };
543
+ const routeModule = {
544
+ routes: []
545
+ };
546
+ const menuItemModule = {
547
+ menuItems: []
548
+ };
549
+ const formModule = { customFields: {} };
550
+ const displayModule = {
551
+ displays: {}
552
+ };
553
+ const i18nModule = { resources: {} };
554
+ const plugin = {
555
+ widgetModule,
556
+ routeModule,
557
+ menuItemModule,
558
+ formModule,
559
+ displayModule,
560
+ i18nModule
561
+ };
562
+ module.exports = plugin;