@burdenoff/microfe-subscriberbot 2026.625.1 → 2026.626.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/dist/subscriberbot/pages/HomePage.js +19 -14
- package/dist/subscriberbot/pages/HomePage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/AdminAnalyticsPage.js +66 -60
- package/dist/subscriberbot/pages/admin/AdminAnalyticsPage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/AdminDashboard.js +83 -44
- package/dist/subscriberbot/pages/admin/AdminDashboard.js.map +1 -1
- package/dist/subscriberbot/pages/admin/AdminFinancialPage.js +38 -32
- package/dist/subscriberbot/pages/admin/AdminFinancialPage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/AdminOrdersPage.js +226 -221
- package/dist/subscriberbot/pages/admin/AdminOrdersPage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/AdminProfilePage.js +127 -125
- package/dist/subscriberbot/pages/admin/AdminProfilePage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/AdminSettingsPage.js +235 -230
- package/dist/subscriberbot/pages/admin/AdminSettingsPage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/CartManagementPage.js +267 -262
- package/dist/subscriberbot/pages/admin/CartManagementPage.js.map +1 -1
- package/dist/subscriberbot/pages/admin/UserManagementPage.js +267 -262
- package/dist/subscriberbot/pages/admin/UserManagementPage.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/AnalyticsDashboard.js +46 -41
- package/dist/subscriberbot/pages/cart-owner/AnalyticsDashboard.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/BusinessSettingsPage.js +257 -252
- package/dist/subscriberbot/pages/cart-owner/BusinessSettingsPage.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/CartOwnerDashboard.js +231 -194
- package/dist/subscriberbot/pages/cart-owner/CartOwnerDashboard.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/CartOwnerProfilePage.js +167 -162
- package/dist/subscriberbot/pages/cart-owner/CartOwnerProfilePage.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/CartServiceConfigPage.js +128 -126
- package/dist/subscriberbot/pages/cart-owner/CartServiceConfigPage.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/InventoryManagementPage.js +297 -300
- package/dist/subscriberbot/pages/cart-owner/InventoryManagementPage.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/OrderManagementPage.js +220 -223
- package/dist/subscriberbot/pages/cart-owner/OrderManagementPage.js.map +1 -1
- package/dist/subscriberbot/pages/cart-owner/StaffManagementPage.js +250 -245
- package/dist/subscriberbot/pages/cart-owner/StaffManagementPage.js.map +1 -1
- package/dist/subscriberbot/pages/common/HelpSupportPage.js +94 -96
- package/dist/subscriberbot/pages/common/HelpSupportPage.js.map +1 -1
- package/dist/subscriberbot/pages/common/NotificationCenterPage.js +48 -42
- package/dist/subscriberbot/pages/common/NotificationCenterPage.js.map +1 -1
- package/dist/subscriberbot/pages/common/SettingsPage.js +745 -736
- package/dist/subscriberbot/pages/common/SettingsPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/BookingDetailsPage.js +353 -321
- package/dist/subscriberbot/pages/customer/BookingDetailsPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/CartDetailPage.js +258 -225
- package/dist/subscriberbot/pages/customer/CartDetailPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/CartDiscoveryPage.js +82 -76
- package/dist/subscriberbot/pages/customer/CartDiscoveryPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/CheckoutPage.js +212 -215
- package/dist/subscriberbot/pages/customer/CheckoutPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/CustomerDashboard.js +416 -381
- package/dist/subscriberbot/pages/customer/CustomerDashboard.js.map +1 -1
- package/dist/subscriberbot/pages/customer/CustomerProfilePage.js +488 -479
- package/dist/subscriberbot/pages/customer/CustomerProfilePage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/DirectServiceBookingPage.js +521 -512
- package/dist/subscriberbot/pages/customer/DirectServiceBookingPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/FavoritesPage.js +57 -51
- package/dist/subscriberbot/pages/customer/FavoritesPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/OrderHistoryPage.js +56 -50
- package/dist/subscriberbot/pages/customer/OrderHistoryPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/OrderTrackingPage.js +65 -60
- package/dist/subscriberbot/pages/customer/OrderTrackingPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/PaymentMethodsPage.js +79 -73
- package/dist/subscriberbot/pages/customer/PaymentMethodsPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/ReviewsPage.js +68 -62
- package/dist/subscriberbot/pages/customer/ReviewsPage.js.map +1 -1
- package/dist/subscriberbot/pages/customer/SavedAddressesPage.js +60 -54
- package/dist/subscriberbot/pages/customer/SavedAddressesPage.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestmentDetailsPage.js +292 -255
- package/dist/subscriberbot/pages/investor/InvestmentDetailsPage.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestmentOpportunitiesPage.js +237 -231
- package/dist/subscriberbot/pages/investor/InvestmentOpportunitiesPage.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestorAnalyticsPage.js +60 -54
- package/dist/subscriberbot/pages/investor/InvestorAnalyticsPage.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestorDashboard.js +350 -319
- package/dist/subscriberbot/pages/investor/InvestorDashboard.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestorInvestmentsPage.js +106 -100
- package/dist/subscriberbot/pages/investor/InvestorInvestmentsPage.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestorPortfolioPage.js +51 -45
- package/dist/subscriberbot/pages/investor/InvestorPortfolioPage.js.map +1 -1
- package/dist/subscriberbot/pages/investor/InvestorProfilePage.js +387 -382
- package/dist/subscriberbot/pages/investor/InvestorProfilePage.js.map +1 -1
- package/dist/subscriberbot/pages/worker/TaskDetailsPage.js +320 -282
- package/dist/subscriberbot/pages/worker/TaskDetailsPage.js.map +1 -1
- package/dist/subscriberbot/pages/worker/WorkerDashboard.js +92 -61
- package/dist/subscriberbot/pages/worker/WorkerDashboard.js.map +1 -1
- package/dist/subscriberbot/pages/worker/WorkerOrdersPage.js +82 -77
- package/dist/subscriberbot/pages/worker/WorkerOrdersPage.js.map +1 -1
- package/dist/subscriberbot/pages/worker/WorkerProfilePage.js +82 -77
- package/dist/subscriberbot/pages/worker/WorkerProfilePage.js.map +1 -1
- package/dist/subscriberbot/pages/worker/WorkerSchedulePage.js +348 -340
- package/dist/subscriberbot/pages/worker/WorkerSchedulePage.js.map +1 -1
- package/package.json +3 -3
|
@@ -7,17 +7,18 @@ import { RatingDisplay as a } from "../../components/RatingDisplay.js";
|
|
|
7
7
|
import { useCallback as o, useState as s } from "react";
|
|
8
8
|
import { CheckCircle2 as c, Info as l, MessageCircle as u, MessageSquare as d, PenSquare as f, Search as p, Star as m, ThumbsUp as h, X as g } from "lucide-react";
|
|
9
9
|
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
10
|
+
import { EmphasisPanel as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/subscriberbot/pages/customer/ReviewsPage.tsx
|
|
11
|
-
var
|
|
12
|
+
var x = "user-001", S = {
|
|
12
13
|
1: "Terrible",
|
|
13
14
|
2: "Poor",
|
|
14
15
|
3: "Average",
|
|
15
16
|
4: "Good",
|
|
16
17
|
5: "Excellent"
|
|
17
|
-
},
|
|
18
|
+
}, C = [
|
|
18
19
|
{
|
|
19
20
|
id: "review-001",
|
|
20
|
-
userId:
|
|
21
|
+
userId: x,
|
|
21
22
|
userName: "Rahul Sharma",
|
|
22
23
|
userAvatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100",
|
|
23
24
|
itemId: "FOOD-001",
|
|
@@ -80,7 +81,7 @@ var y = "user-001", b = {
|
|
|
80
81
|
},
|
|
81
82
|
{
|
|
82
83
|
id: "review-004",
|
|
83
|
-
userId:
|
|
84
|
+
userId: x,
|
|
84
85
|
userName: "Rahul Sharma",
|
|
85
86
|
userAvatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100",
|
|
86
87
|
itemId: "prod-001",
|
|
@@ -122,7 +123,7 @@ var y = "user-001", b = {
|
|
|
122
123
|
createdAt: "2024-06-12T12:20:00Z"
|
|
123
124
|
}
|
|
124
125
|
];
|
|
125
|
-
function
|
|
126
|
+
function w({ value: e, onChange: n }) {
|
|
126
127
|
let [r, i] = s(0), a = r || e;
|
|
127
128
|
return /* @__PURE__ */ v("div", {
|
|
128
129
|
className: "flex items-center gap-3",
|
|
@@ -144,33 +145,33 @@ function S({ value: e, onChange: n }) {
|
|
|
144
145
|
}, e))
|
|
145
146
|
}), /* @__PURE__ */ _("span", {
|
|
146
147
|
className: "text-sm font-medium text-text-secondary",
|
|
147
|
-
children:
|
|
148
|
+
children: S[a] ?? ""
|
|
148
149
|
})]
|
|
149
150
|
});
|
|
150
151
|
}
|
|
151
|
-
function
|
|
152
|
-
let { navigateTo:
|
|
152
|
+
function T() {
|
|
153
|
+
let { navigateTo: S } = e(), [T, E] = s(C), [D, O] = s(!1), [k, A] = s({
|
|
153
154
|
rating: 5,
|
|
154
155
|
title: "",
|
|
155
156
|
comment: ""
|
|
156
|
-
}), [
|
|
157
|
+
}), [j, M] = s({
|
|
157
158
|
searchQuery: "",
|
|
158
159
|
type: "all",
|
|
159
160
|
rating: "",
|
|
160
161
|
sortBy: "recent"
|
|
161
|
-
}), [
|
|
162
|
-
|
|
163
|
-
},
|
|
164
|
-
let e = [...
|
|
165
|
-
if (
|
|
166
|
-
let t = parseInt(
|
|
162
|
+
}), [N, P] = s(null), F = (e) => {
|
|
163
|
+
P(e), setTimeout(() => P(null), 3e3);
|
|
164
|
+
}, I = o(() => {
|
|
165
|
+
let e = [...T];
|
|
166
|
+
if (j.type === "my-reviews" ? e = e.filter((e) => e.userId === x) : j.type !== "all" && (e = e.filter((e) => e.itemType === j.type)), j.rating) {
|
|
167
|
+
let t = parseInt(j.rating, 10);
|
|
167
168
|
e = e.filter((e) => e.rating === t);
|
|
168
169
|
}
|
|
169
|
-
if (
|
|
170
|
-
let t =
|
|
170
|
+
if (j.searchQuery) {
|
|
171
|
+
let t = j.searchQuery.toLowerCase();
|
|
171
172
|
e = e.filter((e) => e.itemName.toLowerCase().includes(t) || e.title.toLowerCase().includes(t) || e.comment.toLowerCase().includes(t) || e.cartName.toLowerCase().includes(t));
|
|
172
173
|
}
|
|
173
|
-
switch (
|
|
174
|
+
switch (j.sortBy) {
|
|
174
175
|
case "recent":
|
|
175
176
|
e.sort((e, t) => new Date(t.createdAt).getTime() - new Date(e.createdAt).getTime());
|
|
176
177
|
break;
|
|
@@ -185,14 +186,14 @@ function C() {
|
|
|
185
186
|
break;
|
|
186
187
|
}
|
|
187
188
|
return e;
|
|
188
|
-
}, [
|
|
189
|
-
if (!
|
|
189
|
+
}, [T, j])(), L = T.filter((e) => e.userId === x).length, R = T.length > 0 ? (T.reduce((e, t) => e + t.rating, 0) / T.length).toFixed(1) : "0", z = () => {
|
|
190
|
+
if (!k.title.trim() || !k.comment.trim()) {
|
|
190
191
|
alert("Please fill in the Review Title and Comment.");
|
|
191
192
|
return;
|
|
192
193
|
}
|
|
193
194
|
let e = {
|
|
194
195
|
id: `review-${Date.now()}`,
|
|
195
|
-
userId:
|
|
196
|
+
userId: x,
|
|
196
197
|
userName: "Rahul Sharma",
|
|
197
198
|
userAvatar: "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100",
|
|
198
199
|
itemId: "FOOD-001",
|
|
@@ -201,21 +202,21 @@ function C() {
|
|
|
201
202
|
cartId: "FOOD-001",
|
|
202
203
|
cartName: "Dosa Express",
|
|
203
204
|
orderId: `ORD-2024-${Date.now()}`,
|
|
204
|
-
rating:
|
|
205
|
-
title:
|
|
206
|
-
comment:
|
|
205
|
+
rating: k.rating,
|
|
206
|
+
title: k.title,
|
|
207
|
+
comment: k.comment,
|
|
207
208
|
helpfulCount: 0,
|
|
208
209
|
isHelpful: !1,
|
|
209
210
|
isVerified: !0,
|
|
210
211
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
211
212
|
};
|
|
212
|
-
|
|
213
|
+
E((t) => [e, ...t]), A({
|
|
213
214
|
rating: 5,
|
|
214
215
|
title: "",
|
|
215
216
|
comment: ""
|
|
216
|
-
}),
|
|
217
|
-
},
|
|
218
|
-
|
|
217
|
+
}), O(!1), F("Review submitted successfully!");
|
|
218
|
+
}, B = (e) => {
|
|
219
|
+
E((t) => t.map((t) => t.id === e ? {
|
|
219
220
|
...t,
|
|
220
221
|
isHelpful: !t.isHelpful,
|
|
221
222
|
helpfulCount: t.isHelpful ? t.helpfulCount - 1 : t.helpfulCount + 1
|
|
@@ -223,31 +224,36 @@ function C() {
|
|
|
223
224
|
};
|
|
224
225
|
return /* @__PURE__ */ v(n, {
|
|
225
226
|
title: "Reviews & Ratings",
|
|
226
|
-
description: `${
|
|
227
|
+
description: `${I.length} reviews found`,
|
|
227
228
|
actions: /* @__PURE__ */ v("button", {
|
|
228
229
|
type: "button",
|
|
229
|
-
onClick: () =>
|
|
230
|
+
onClick: () => O(!0),
|
|
230
231
|
className: t("flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity", "bg-foreground text-background hover:opacity-90"),
|
|
231
232
|
children: [/* @__PURE__ */ _(f, { className: "h-4 w-4" }), "Write Review"]
|
|
232
233
|
}),
|
|
233
234
|
children: [
|
|
234
|
-
|
|
235
|
+
/* @__PURE__ */ _(b, {
|
|
236
|
+
icon: /* @__PURE__ */ _(m, { className: "h-4 w-4" }),
|
|
237
|
+
className: "mb-6",
|
|
238
|
+
children: "Share your experience and read what others think. Write reviews for carts you've ordered from to help the community find great service, and revisit your own ratings any time. Honest feedback keeps quality high and helps good carts stand out."
|
|
239
|
+
}),
|
|
240
|
+
N && /* @__PURE__ */ v("div", {
|
|
235
241
|
className: "mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg/10 p-3 flex items-center gap-2",
|
|
236
242
|
children: [/* @__PURE__ */ _(c, { className: "h-4 w-4 text-status-success-text shrink-0" }), /* @__PURE__ */ _("p", {
|
|
237
243
|
className: "text-sm text-status-success-text",
|
|
238
|
-
children:
|
|
244
|
+
children: N
|
|
239
245
|
})]
|
|
240
246
|
}),
|
|
241
|
-
/* @__PURE__ */ v(
|
|
247
|
+
/* @__PURE__ */ v(y, {
|
|
242
248
|
className: "flex flex-wrap items-center gap-4 text-sm text-text-secondary mb-6",
|
|
243
249
|
children: [
|
|
244
|
-
/* @__PURE__ */ v("span", { children: [
|
|
245
|
-
/* @__PURE__ */ v("span", { children: [
|
|
250
|
+
/* @__PURE__ */ v("span", { children: [T.length, " total reviews"] }),
|
|
251
|
+
/* @__PURE__ */ v("span", { children: [L, " your reviews"] }),
|
|
246
252
|
/* @__PURE__ */ v("span", {
|
|
247
253
|
className: "flex items-center gap-1",
|
|
248
254
|
children: [
|
|
249
255
|
"Average: ",
|
|
250
|
-
|
|
256
|
+
R,
|
|
251
257
|
/* @__PURE__ */ _(m, { className: "h-3.5 w-3.5 fill-status-warning-text text-status-warning-text" })
|
|
252
258
|
]
|
|
253
259
|
})
|
|
@@ -263,8 +269,8 @@ function C() {
|
|
|
263
269
|
children: [/* @__PURE__ */ _(p, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary" }), /* @__PURE__ */ _("input", {
|
|
264
270
|
type: "text",
|
|
265
271
|
placeholder: "Search reviews...",
|
|
266
|
-
value:
|
|
267
|
-
onChange: (e) =>
|
|
272
|
+
value: j.searchQuery,
|
|
273
|
+
onChange: (e) => M((t) => ({
|
|
268
274
|
...t,
|
|
269
275
|
searchQuery: e.target.value
|
|
270
276
|
})),
|
|
@@ -272,8 +278,8 @@ function C() {
|
|
|
272
278
|
})]
|
|
273
279
|
}),
|
|
274
280
|
/* @__PURE__ */ v("select", {
|
|
275
|
-
value:
|
|
276
|
-
onChange: (e) =>
|
|
281
|
+
value: j.type,
|
|
282
|
+
onChange: (e) => M((t) => ({
|
|
277
283
|
...t,
|
|
278
284
|
type: e.target.value
|
|
279
285
|
})),
|
|
@@ -298,8 +304,8 @@ function C() {
|
|
|
298
304
|
]
|
|
299
305
|
}),
|
|
300
306
|
/* @__PURE__ */ v("select", {
|
|
301
|
-
value:
|
|
302
|
-
onChange: (e) =>
|
|
307
|
+
value: j.rating,
|
|
308
|
+
onChange: (e) => M((t) => ({
|
|
303
309
|
...t,
|
|
304
310
|
rating: e.target.value
|
|
305
311
|
})),
|
|
@@ -332,8 +338,8 @@ function C() {
|
|
|
332
338
|
]
|
|
333
339
|
}),
|
|
334
340
|
/* @__PURE__ */ v("select", {
|
|
335
|
-
value:
|
|
336
|
-
onChange: (e) =>
|
|
341
|
+
value: j.sortBy,
|
|
342
|
+
onChange: (e) => M((t) => ({
|
|
337
343
|
...t,
|
|
338
344
|
sortBy: e.target.value
|
|
339
345
|
})),
|
|
@@ -360,21 +366,21 @@ function C() {
|
|
|
360
366
|
]
|
|
361
367
|
})
|
|
362
368
|
}),
|
|
363
|
-
|
|
369
|
+
I.length === 0 ? /* @__PURE__ */ _(r, {
|
|
364
370
|
illustration: "empty-data",
|
|
365
371
|
icon: /* @__PURE__ */ _(d, { className: "h-12 w-12" }),
|
|
366
372
|
title: "No reviews found",
|
|
367
373
|
description: "Be the first to write a review!",
|
|
368
374
|
action: /* @__PURE__ */ _("button", {
|
|
369
375
|
type: "button",
|
|
370
|
-
onClick: () =>
|
|
376
|
+
onClick: () => O(!0),
|
|
371
377
|
className: "rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity",
|
|
372
378
|
children: "Write a Review"
|
|
373
379
|
})
|
|
374
380
|
}) : /* @__PURE__ */ _("div", {
|
|
375
381
|
className: "space-y-4",
|
|
376
|
-
children:
|
|
377
|
-
let n = e.userId ===
|
|
382
|
+
children: I.map((e) => {
|
|
383
|
+
let n = e.userId === x;
|
|
378
384
|
return /* @__PURE__ */ v("div", {
|
|
379
385
|
className: t("rounded-lg border bg-bg-surface p-5", n ? "border-foreground/20" : "border-border-seam"),
|
|
380
386
|
children: [
|
|
@@ -471,7 +477,7 @@ function C() {
|
|
|
471
477
|
className: "flex items-center justify-between pt-3 border-t border-border-seam",
|
|
472
478
|
children: [/* @__PURE__ */ v("button", {
|
|
473
479
|
type: "button",
|
|
474
|
-
onClick: () =>
|
|
480
|
+
onClick: () => B(e.id),
|
|
475
481
|
className: t("flex items-center gap-1.5 text-xs transition-colors", e.isHelpful ? "text-status-info-text font-medium" : "text-text-secondary hover:text-text-primary"),
|
|
476
482
|
children: [
|
|
477
483
|
/* @__PURE__ */ _(h, { className: t("h-3.5 w-3.5", e.isHelpful && "fill-current") }),
|
|
@@ -483,7 +489,7 @@ function C() {
|
|
|
483
489
|
className: "flex items-center gap-3 text-xs text-text-secondary",
|
|
484
490
|
children: [/* @__PURE__ */ v("span", { children: ["Order: ", e.orderId] }), /* @__PURE__ */ _("button", {
|
|
485
491
|
type: "button",
|
|
486
|
-
onClick: () =>
|
|
492
|
+
onClick: () => S(`/customer/carts/${e.cartId}`),
|
|
487
493
|
className: "font-medium hover:text-text-primary transition-colors",
|
|
488
494
|
children: "View Cart"
|
|
489
495
|
})]
|
|
@@ -493,7 +499,7 @@ function C() {
|
|
|
493
499
|
}, e.id);
|
|
494
500
|
})
|
|
495
501
|
}),
|
|
496
|
-
|
|
502
|
+
D && /* @__PURE__ */ _("div", {
|
|
497
503
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/50 p-4",
|
|
498
504
|
children: /* @__PURE__ */ _("div", {
|
|
499
505
|
className: "w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-seam bg-bg-surface shadow-lg",
|
|
@@ -507,7 +513,7 @@ function C() {
|
|
|
507
513
|
children: "Write a Review"
|
|
508
514
|
}), /* @__PURE__ */ _("button", {
|
|
509
515
|
type: "button",
|
|
510
|
-
onClick: () =>
|
|
516
|
+
onClick: () => O(!1),
|
|
511
517
|
className: "rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors",
|
|
512
518
|
children: /* @__PURE__ */ _(g, { className: "h-4 w-4" })
|
|
513
519
|
})]
|
|
@@ -521,9 +527,9 @@ function C() {
|
|
|
521
527
|
className: "text-status-error-text",
|
|
522
528
|
children: "*"
|
|
523
529
|
})]
|
|
524
|
-
}), /* @__PURE__ */ _(
|
|
525
|
-
value:
|
|
526
|
-
onChange: (e) =>
|
|
530
|
+
}), /* @__PURE__ */ _(w, {
|
|
531
|
+
value: k.rating,
|
|
532
|
+
onChange: (e) => A((t) => ({
|
|
527
533
|
...t,
|
|
528
534
|
rating: e
|
|
529
535
|
}))
|
|
@@ -537,8 +543,8 @@ function C() {
|
|
|
537
543
|
}), /* @__PURE__ */ _("input", {
|
|
538
544
|
type: "text",
|
|
539
545
|
placeholder: "Summarize your experience",
|
|
540
|
-
value:
|
|
541
|
-
onChange: (e) =>
|
|
546
|
+
value: k.title,
|
|
547
|
+
onChange: (e) => A((t) => ({
|
|
542
548
|
...t,
|
|
543
549
|
title: e.target.value
|
|
544
550
|
})),
|
|
@@ -553,8 +559,8 @@ function C() {
|
|
|
553
559
|
}), /* @__PURE__ */ _("textarea", {
|
|
554
560
|
rows: 5,
|
|
555
561
|
placeholder: "Share your experience with others...",
|
|
556
|
-
value:
|
|
557
|
-
onChange: (e) =>
|
|
562
|
+
value: k.comment,
|
|
563
|
+
onChange: (e) => A((t) => ({
|
|
558
564
|
...t,
|
|
559
565
|
comment: e.target.value
|
|
560
566
|
})),
|
|
@@ -584,12 +590,12 @@ function C() {
|
|
|
584
590
|
className: "flex items-center gap-3 pt-5",
|
|
585
591
|
children: [/* @__PURE__ */ _("button", {
|
|
586
592
|
type: "button",
|
|
587
|
-
onClick:
|
|
593
|
+
onClick: z,
|
|
588
594
|
className: "rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity",
|
|
589
595
|
children: "Submit Review"
|
|
590
596
|
}), /* @__PURE__ */ _("button", {
|
|
591
597
|
type: "button",
|
|
592
|
-
onClick: () =>
|
|
598
|
+
onClick: () => O(!1),
|
|
593
599
|
className: "rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors",
|
|
594
600
|
children: "Cancel"
|
|
595
601
|
})]
|
|
@@ -602,6 +608,6 @@ function C() {
|
|
|
602
608
|
});
|
|
603
609
|
}
|
|
604
610
|
//#endregion
|
|
605
|
-
export {
|
|
611
|
+
export { T as ReviewsPage };
|
|
606
612
|
|
|
607
613
|
//# sourceMappingURL=ReviewsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReviewsPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/customer/ReviewsPage.tsx"],"sourcesContent":["/**\n * Reviews Page\n * @module subscriberbot/pages/customer\n *\n * Full-featured reviews: Write Review modal (interactive star rating with\n * text labels, title, comment, guidelines), Owner Response callout,\n * review images, verified badge, Mark as Helpful toggle, 4 filters\n * (search, type, rating, sort), Your Review label, 5 mock reviews.\n */\n\nimport { useState, useCallback } from 'react';\nimport {\n Star,\n MessageSquare,\n ThumbsUp,\n PenSquare,\n X,\n CheckCircle2,\n Image,\n MessageCircle,\n Info,\n Search,\n} from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { RatingDisplay } from '../../components/RatingDisplay';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { cn } from '../../utils/cn';\nimport { formatDate } from '../../utils/formatters';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface Review {\n id: string;\n userId: string;\n userName: string;\n userAvatar: string;\n itemId: string;\n itemName: string;\n itemType: 'cart' | 'product';\n cartId: string;\n cartName: string;\n orderId: string;\n rating: number;\n title: string;\n comment: string;\n images?: string[];\n helpfulCount: number;\n isHelpful: boolean;\n isVerified: boolean;\n createdAt: string;\n response?: {\n message: string;\n respondedBy: string;\n respondedAt: string;\n };\n}\n\ninterface ReviewFormData {\n rating: number;\n title: string;\n comment: string;\n}\n\ninterface ReviewFilters {\n searchQuery: string;\n type: 'all' | 'cart' | 'product' | 'my-reviews';\n rating: string;\n sortBy: 'recent' | 'helpful' | 'rating-high' | 'rating-low';\n}\n\nconst CURRENT_USER_ID = 'user-001';\n\nconst ratingLabels: Record<number, string> = {\n 1: 'Terrible',\n 2: 'Poor',\n 3: 'Average',\n 4: 'Good',\n 5: 'Excellent',\n};\n\n// ============================================================================\n// Mock Data\n// ============================================================================\n\nconst mockReviews: Review[] = [\n {\n id: 'review-001',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-001',\n rating: 5,\n title: 'Excellent breakfast spot!',\n comment:\n 'Amazing dosas and filter coffee. The masala dosa was perfectly crispy and the potato filling was delicious. Service was quick and friendly. Highly recommend for authentic South Indian breakfast!',\n helpfulCount: 12,\n isHelpful: true,\n isVerified: true,\n createdAt: '2024-06-20T09:30:00Z',\n response: {\n message:\n \"Thank you so much for your wonderful review! We're thrilled you enjoyed the dosas. We look forward to serving you again soon!\",\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-20T15:45:00Z',\n },\n },\n {\n id: 'review-002',\n userId: 'user-002',\n userName: 'Priya Patel',\n userAvatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100',\n itemId: 'prod-002',\n itemName: 'Pani Puri',\n itemType: 'product',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-002',\n rating: 4,\n title: 'Good but could be spicier',\n comment:\n 'The pani puri was good overall. The puris were crispy and fresh. However, the pani could have been more spicy and tangy. The portions were generous though.',\n helpfulCount: 8,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-18T19:15:00Z',\n },\n {\n id: 'review-003',\n userId: 'user-003',\n userName: 'Amit Kumar',\n userAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100',\n itemId: 'FOOD-002',\n itemName: 'Chaat Corner',\n itemType: 'cart',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-003',\n rating: 5,\n title: 'Authentic Mumbai street food!',\n comment:\n 'This place brings back memories of Mumbai streets. Every item I ordered was authentic and delicious. The bhel puri and vada pav were exceptional. Will definitely order again!',\n images: ['https://images.unsplash.com/photo-1601050690597-df0568f70950?w=400'],\n helpfulCount: 15,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-17T20:30:00Z',\n },\n {\n id: 'review-004',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'prod-001',\n itemName: 'Masala Dosa',\n itemType: 'product',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-004',\n rating: 5,\n title: 'Perfect masala dosa!',\n comment:\n 'The masala dosa here is absolutely perfect. Crispy, golden brown, and the potato masala inside is perfectly spiced. Comes with great chutneys and sambar.',\n helpfulCount: 6,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-15T08:45:00Z',\n response: {\n message: 'Glad you loved it, Rahul! Our chef takes great care with every dosa. See you soon!',\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-15T14:00:00Z',\n },\n },\n {\n id: 'review-005',\n userId: 'user-004',\n userName: 'Sneha Reddy',\n userAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-005',\n rating: 3,\n title: 'Average experience',\n comment:\n 'The food was okay but delivery took longer than expected. The taste was good but not exceptional. The packaging could be better to keep food warm.',\n helpfulCount: 4,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-12T12:20:00Z',\n },\n];\n\n// ============================================================================\n// Star Rating Interactive Component\n// ============================================================================\n\nfunction InteractiveStarRating({\n value,\n onChange,\n}: {\n value: number;\n onChange: (v: number) => void;\n}) {\n const [hoverValue, setHoverValue] = useState(0);\n const displayValue = hoverValue || value;\n\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHoverValue(star)}\n onMouseLeave={() => setHoverValue(0)}\n className=\"p-0.5 transition-transform\"\n >\n <Star\n className={cn(\n 'h-6 w-6',\n star <= displayValue\n ? 'fill-status-warning-text text-status-warning-text'\n : 'fill-none text-text-secondary/40'\n )}\n />\n </button>\n ))}\n </div>\n <span className=\"text-sm font-medium text-text-secondary\">\n {ratingLabels[displayValue] ?? ''}\n </span>\n </div>\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ReviewsPage() {\n const { navigateTo } = useSubscriberBot();\n const [reviews, setReviews] = useState<Review[]>(mockReviews);\n const [showWriteReview, setShowWriteReview] = useState(false);\n const [reviewForm, setReviewForm] = useState<ReviewFormData>({\n rating: 5,\n title: '',\n comment: '',\n });\n const [filters, setFilters] = useState<ReviewFilters>({\n searchQuery: '',\n type: 'all',\n rating: '',\n sortBy: 'recent',\n });\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n // --- Filtered reviews ---\n const filteredReviews = useCallback(() => {\n let result = [...reviews];\n\n // Type filter\n if (filters.type === 'my-reviews') {\n result = result.filter((r) => r.userId === CURRENT_USER_ID);\n } else if (filters.type !== 'all') {\n result = result.filter((r) => r.itemType === filters.type);\n }\n\n // Rating filter\n if (filters.rating) {\n const ratingVal = parseInt(filters.rating, 10);\n result = result.filter((r) => r.rating === ratingVal);\n }\n\n // Search\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n (r) =>\n r.itemName.toLowerCase().includes(q) ||\n r.title.toLowerCase().includes(q) ||\n r.comment.toLowerCase().includes(q) ||\n r.cartName.toLowerCase().includes(q)\n );\n }\n\n // Sort\n switch (filters.sortBy) {\n case 'recent':\n result.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n break;\n case 'helpful':\n result.sort((a, b) => b.helpfulCount - a.helpfulCount);\n break;\n case 'rating-high':\n result.sort((a, b) => b.rating - a.rating);\n break;\n case 'rating-low':\n result.sort((a, b) => a.rating - b.rating);\n break;\n }\n\n return result;\n }, [reviews, filters])();\n\n // --- Stats ---\n const myReviewCount = reviews.filter((r) => r.userId === CURRENT_USER_ID).length;\n const avgRating =\n reviews.length > 0\n ? (reviews.reduce((s, r) => s + r.rating, 0) / reviews.length).toFixed(1)\n : '0';\n\n // --- Handlers ---\n const handleSubmitReview = () => {\n if (!reviewForm.title.trim() || !reviewForm.comment.trim()) {\n alert('Please fill in the Review Title and Comment.');\n return;\n }\n\n const newReview: Review = {\n id: `review-${Date.now()}`,\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: `ORD-2024-${Date.now()}`,\n rating: reviewForm.rating,\n title: reviewForm.title,\n comment: reviewForm.comment,\n helpfulCount: 0,\n isHelpful: false,\n isVerified: true,\n createdAt: new Date().toISOString(),\n };\n\n setReviews((prev) => [newReview, ...prev]);\n setReviewForm({ rating: 5, title: '', comment: '' });\n setShowWriteReview(false);\n showStatus('Review submitted successfully!');\n };\n\n const handleMarkHelpful = (reviewId: string) => {\n setReviews((prev) =>\n prev.map((r) =>\n r.id === reviewId\n ? {\n ...r,\n isHelpful: !r.isHelpful,\n helpfulCount: r.isHelpful ? r.helpfulCount - 1 : r.helpfulCount + 1,\n }\n : r\n )\n );\n };\n\n return (\n <PageLayout\n title=\"Reviews & Ratings\"\n description={`${filteredReviews.length} reviews found`}\n actions={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className={cn(\n 'flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity',\n 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <PenSquare className=\"h-4 w-4\" />\n Write Review\n </button>\n }\n >\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg/10 p-3 flex items-center gap-2\">\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">{statusMessage}</p>\n </div>\n )}\n\n {/* Stats */}\n <div className=\"flex flex-wrap items-center gap-4 text-sm text-text-secondary mb-6\">\n <span>{reviews.length} total reviews</span>\n <span>{myReviewCount} your reviews</span>\n <span className=\"flex items-center gap-1\">\n Average: {avgRating}\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n </span>\n </div>\n\n {/* Filters */}\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4 mb-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3\">\n {/* Search */}\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search reviews...\"\n value={filters.searchQuery}\n onChange={(e) => setFilters((p) => ({ ...p, searchQuery: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* Type */}\n <select\n value={filters.type}\n onChange={(e) =>\n setFilters((p) => ({ ...p, type: e.target.value as ReviewFilters['type'] }))\n }\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"all\">All Reviews</option>\n <option value=\"cart\">Cart Reviews</option>\n <option value=\"product\">Product Reviews</option>\n <option value=\"my-reviews\">My Reviews</option>\n </select>\n\n {/* Rating */}\n <select\n value={filters.rating}\n onChange={(e) => setFilters((p) => ({ ...p, rating: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Ratings</option>\n <option value=\"5\">5 Stars</option>\n <option value=\"4\">4 Stars</option>\n <option value=\"3\">3 Stars</option>\n <option value=\"2\">2 Stars</option>\n <option value=\"1\">1 Star</option>\n </select>\n\n {/* Sort */}\n <select\n value={filters.sortBy}\n onChange={(e) =>\n setFilters((p) => ({ ...p, sortBy: e.target.value as ReviewFilters['sortBy'] }))\n }\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"recent\">Most Recent</option>\n <option value=\"helpful\">Most Helpful</option>\n <option value=\"rating-high\">Highest Rating</option>\n <option value=\"rating-low\">Lowest Rating</option>\n </select>\n </div>\n </div>\n\n {/* Reviews List */}\n {filteredReviews.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MessageSquare className=\"h-12 w-12\" />}\n title=\"No reviews found\"\n description=\"Be the first to write a review!\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Write a Review\n </button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {filteredReviews.map((review) => {\n const isMyReview = review.userId === CURRENT_USER_ID;\n return (\n <div\n key={review.id}\n className={cn(\n 'rounded-lg border bg-bg-surface p-5',\n isMyReview ? 'border-foreground/20' : 'border-border-seam'\n )}\n >\n {/* Header */}\n <div className=\"flex items-start gap-3 mb-3\">\n <img\n src={review.userAvatar}\n alt={review.userName}\n className=\"h-10 w-10 rounded-full object-cover shrink-0\"\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {review.userName}\n </span>\n {review.isVerified && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n {isMyReview && (\n <span className=\"inline-flex items-center rounded-full bg-status-info-bg/10 px-2 py-0.5 text-[10px] font-medium text-status-info-text\">\n Your Review\n </span>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary mt-0.5\">\n <span>\n {review.itemType === 'cart' ? (\n <MessageSquare className=\"inline h-3 w-3 mr-0.5\" />\n ) : (\n <Star className=\"inline h-3 w-3 mr-0.5\" />\n )}\n {review.itemName}\n </span>\n <span>\n {formatDate(review.createdAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n </div>\n <div className=\"shrink-0\">\n <RatingDisplay value={review.rating} size=\"sm\" />\n <p className=\"text-xs text-text-secondary mt-0.5 text-right\">\n From: {review.cartName}\n </p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mb-3\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-1\">{review.title}</h4>\n <p className=\"text-sm text-text-primary leading-relaxed\">{review.comment}</p>\n </div>\n\n {/* Review Images */}\n {review.images && review.images.length > 0 && (\n <div className=\"mb-3 flex gap-2 overflow-x-auto pb-1\">\n {review.images.map((img, idx) => (\n <img\n key={idx}\n src={img}\n alt={`Review image ${idx + 1}`}\n className=\"h-20 w-20 rounded-lg object-cover shrink-0\"\n />\n ))}\n </div>\n )}\n\n {/* Owner Response */}\n {review.response && (\n <div className=\"mb-3 rounded-lg border border-status-info-border/20 bg-status-info-bg/5 p-3\">\n <div className=\"flex flex-wrap items-center gap-2 mb-1.5\">\n <MessageCircle className=\"h-3.5 w-3.5 text-status-info-text\" />\n <span className=\"text-xs font-medium text-status-info-text\">\n Response from {review.response.respondedBy}\n </span>\n <span className=\"text-[10px] text-status-info-text/70\">\n {formatDate(review.response.respondedAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n <p className=\"text-xs text-status-info-text leading-relaxed\">\n {review.response.message}\n </p>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between pt-3 border-t border-border-seam\">\n <button\n type=\"button\"\n onClick={() => handleMarkHelpful(review.id)}\n className={cn(\n 'flex items-center gap-1.5 text-xs transition-colors',\n review.isHelpful\n ? 'text-status-info-text font-medium'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <ThumbsUp className={cn('h-3.5 w-3.5', review.isHelpful && 'fill-current')} />\n Helpful ({review.helpfulCount})\n </button>\n\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n <span>Order: {review.orderId}</span>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/carts/${review.cartId}`)}\n className=\"font-medium hover:text-text-primary transition-colors\"\n >\n View Cart\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Write Review Modal */}\n {showWriteReview && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/50 p-4\">\n <div className=\"w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-seam bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-5\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Write a Review</h3>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(false)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n {/* Rating */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Rating <span className=\"text-status-error-text\">*</span>\n </label>\n <InteractiveStarRating\n value={reviewForm.rating}\n onChange={(v) => setReviewForm((p) => ({ ...p, rating: v }))}\n />\n </div>\n\n {/* Title */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Review Title <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"Summarize your experience\"\n value={reviewForm.title}\n onChange={(e) => setReviewForm((p) => ({ ...p, title: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* Comment */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Your Review <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={5}\n placeholder=\"Share your experience with others...\"\n value={reviewForm.comment}\n onChange={(e) => setReviewForm((p) => ({ ...p, comment: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n {/* Guidelines */}\n <div className=\"rounded-lg bg-bg-sunken p-4\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Info className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-primary\">Review Guidelines</h4>\n </div>\n <ul className=\"text-xs text-text-secondary space-y-1\">\n <li>- Be honest and helpful to other customers</li>\n <li>- Focus on your experience with the food and service</li>\n <li>- Avoid personal information or inappropriate content</li>\n <li>- Reviews are public and cannot be deleted once submitted</li>\n </ul>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-5\">\n <button\n type=\"button\"\n onClick={handleSubmitReview}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Submit Review\n </button>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(false)}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReviewsPage.js","names":[],"sources":["../../../../src/subscriberbot/pages/customer/ReviewsPage.tsx"],"sourcesContent":["/**\n * Reviews Page\n * @module subscriberbot/pages/customer\n *\n * Full-featured reviews: Write Review modal (interactive star rating with\n * text labels, title, comment, guidelines), Owner Response callout,\n * review images, verified badge, Mark as Helpful toggle, 4 filters\n * (search, type, rating, sort), Your Review label, 5 mock reviews.\n */\n\nimport { useState, useCallback } from 'react';\nimport {\n Star,\n MessageSquare,\n ThumbsUp,\n PenSquare,\n X,\n CheckCircle2,\n Image,\n MessageCircle,\n Info,\n Search,\n} from 'lucide-react';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { RatingDisplay } from '../../components/RatingDisplay';\nimport { EmptyState } from '../../components/EmptyState';\nimport { useSubscriberBot } from '../../context/SubscriberBotContext';\nimport { cn } from '../../utils/cn';\nimport { formatDate } from '../../utils/formatters';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface Review {\n id: string;\n userId: string;\n userName: string;\n userAvatar: string;\n itemId: string;\n itemName: string;\n itemType: 'cart' | 'product';\n cartId: string;\n cartName: string;\n orderId: string;\n rating: number;\n title: string;\n comment: string;\n images?: string[];\n helpfulCount: number;\n isHelpful: boolean;\n isVerified: boolean;\n createdAt: string;\n response?: {\n message: string;\n respondedBy: string;\n respondedAt: string;\n };\n}\n\ninterface ReviewFormData {\n rating: number;\n title: string;\n comment: string;\n}\n\ninterface ReviewFilters {\n searchQuery: string;\n type: 'all' | 'cart' | 'product' | 'my-reviews';\n rating: string;\n sortBy: 'recent' | 'helpful' | 'rating-high' | 'rating-low';\n}\n\nconst CURRENT_USER_ID = 'user-001';\n\nconst ratingLabels: Record<number, string> = {\n 1: 'Terrible',\n 2: 'Poor',\n 3: 'Average',\n 4: 'Good',\n 5: 'Excellent',\n};\n\n// ============================================================================\n// Mock Data\n// ============================================================================\n\nconst mockReviews: Review[] = [\n {\n id: 'review-001',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-001',\n rating: 5,\n title: 'Excellent breakfast spot!',\n comment:\n 'Amazing dosas and filter coffee. The masala dosa was perfectly crispy and the potato filling was delicious. Service was quick and friendly. Highly recommend for authentic South Indian breakfast!',\n helpfulCount: 12,\n isHelpful: true,\n isVerified: true,\n createdAt: '2024-06-20T09:30:00Z',\n response: {\n message:\n \"Thank you so much for your wonderful review! We're thrilled you enjoyed the dosas. We look forward to serving you again soon!\",\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-20T15:45:00Z',\n },\n },\n {\n id: 'review-002',\n userId: 'user-002',\n userName: 'Priya Patel',\n userAvatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100',\n itemId: 'prod-002',\n itemName: 'Pani Puri',\n itemType: 'product',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-002',\n rating: 4,\n title: 'Good but could be spicier',\n comment:\n 'The pani puri was good overall. The puris were crispy and fresh. However, the pani could have been more spicy and tangy. The portions were generous though.',\n helpfulCount: 8,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-18T19:15:00Z',\n },\n {\n id: 'review-003',\n userId: 'user-003',\n userName: 'Amit Kumar',\n userAvatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100',\n itemId: 'FOOD-002',\n itemName: 'Chaat Corner',\n itemType: 'cart',\n cartId: 'FOOD-002',\n cartName: 'Chaat Corner',\n orderId: 'ORD-2024-003',\n rating: 5,\n title: 'Authentic Mumbai street food!',\n comment:\n 'This place brings back memories of Mumbai streets. Every item I ordered was authentic and delicious. The bhel puri and vada pav were exceptional. Will definitely order again!',\n images: ['https://images.unsplash.com/photo-1601050690597-df0568f70950?w=400'],\n helpfulCount: 15,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-17T20:30:00Z',\n },\n {\n id: 'review-004',\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'prod-001',\n itemName: 'Masala Dosa',\n itemType: 'product',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-004',\n rating: 5,\n title: 'Perfect masala dosa!',\n comment:\n 'The masala dosa here is absolutely perfect. Crispy, golden brown, and the potato masala inside is perfectly spiced. Comes with great chutneys and sambar.',\n helpfulCount: 6,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-15T08:45:00Z',\n response: {\n message: 'Glad you loved it, Rahul! Our chef takes great care with every dosa. See you soon!',\n respondedBy: 'Dosa Express Team',\n respondedAt: '2024-06-15T14:00:00Z',\n },\n },\n {\n id: 'review-005',\n userId: 'user-004',\n userName: 'Sneha Reddy',\n userAvatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: 'ORD-2024-005',\n rating: 3,\n title: 'Average experience',\n comment:\n 'The food was okay but delivery took longer than expected. The taste was good but not exceptional. The packaging could be better to keep food warm.',\n helpfulCount: 4,\n isHelpful: false,\n isVerified: true,\n createdAt: '2024-06-12T12:20:00Z',\n },\n];\n\n// ============================================================================\n// Star Rating Interactive Component\n// ============================================================================\n\nfunction InteractiveStarRating({\n value,\n onChange,\n}: {\n value: number;\n onChange: (v: number) => void;\n}) {\n const [hoverValue, setHoverValue] = useState(0);\n const displayValue = hoverValue || value;\n\n return (\n <div className=\"flex items-center gap-3\">\n <div className=\"flex items-center gap-0.5\">\n {[1, 2, 3, 4, 5].map((star) => (\n <button\n key={star}\n type=\"button\"\n onClick={() => onChange(star)}\n onMouseEnter={() => setHoverValue(star)}\n onMouseLeave={() => setHoverValue(0)}\n className=\"p-0.5 transition-transform\"\n >\n <Star\n className={cn(\n 'h-6 w-6',\n star <= displayValue\n ? 'fill-status-warning-text text-status-warning-text'\n : 'fill-none text-text-secondary/40'\n )}\n />\n </button>\n ))}\n </div>\n <span className=\"text-sm font-medium text-text-secondary\">\n {ratingLabels[displayValue] ?? ''}\n </span>\n </div>\n );\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function ReviewsPage() {\n const { navigateTo } = useSubscriberBot();\n const [reviews, setReviews] = useState<Review[]>(mockReviews);\n const [showWriteReview, setShowWriteReview] = useState(false);\n const [reviewForm, setReviewForm] = useState<ReviewFormData>({\n rating: 5,\n title: '',\n comment: '',\n });\n const [filters, setFilters] = useState<ReviewFilters>({\n searchQuery: '',\n type: 'all',\n rating: '',\n sortBy: 'recent',\n });\n const [statusMessage, setStatusMessage] = useState<string | null>(null);\n\n const showStatus = (msg: string) => {\n setStatusMessage(msg);\n setTimeout(() => setStatusMessage(null), 3000);\n };\n\n // --- Filtered reviews ---\n const filteredReviews = useCallback(() => {\n let result = [...reviews];\n\n // Type filter\n if (filters.type === 'my-reviews') {\n result = result.filter((r) => r.userId === CURRENT_USER_ID);\n } else if (filters.type !== 'all') {\n result = result.filter((r) => r.itemType === filters.type);\n }\n\n // Rating filter\n if (filters.rating) {\n const ratingVal = parseInt(filters.rating, 10);\n result = result.filter((r) => r.rating === ratingVal);\n }\n\n // Search\n if (filters.searchQuery) {\n const q = filters.searchQuery.toLowerCase();\n result = result.filter(\n (r) =>\n r.itemName.toLowerCase().includes(q) ||\n r.title.toLowerCase().includes(q) ||\n r.comment.toLowerCase().includes(q) ||\n r.cartName.toLowerCase().includes(q)\n );\n }\n\n // Sort\n switch (filters.sortBy) {\n case 'recent':\n result.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());\n break;\n case 'helpful':\n result.sort((a, b) => b.helpfulCount - a.helpfulCount);\n break;\n case 'rating-high':\n result.sort((a, b) => b.rating - a.rating);\n break;\n case 'rating-low':\n result.sort((a, b) => a.rating - b.rating);\n break;\n }\n\n return result;\n }, [reviews, filters])();\n\n // --- Stats ---\n const myReviewCount = reviews.filter((r) => r.userId === CURRENT_USER_ID).length;\n const avgRating =\n reviews.length > 0\n ? (reviews.reduce((s, r) => s + r.rating, 0) / reviews.length).toFixed(1)\n : '0';\n\n // --- Handlers ---\n const handleSubmitReview = () => {\n if (!reviewForm.title.trim() || !reviewForm.comment.trim()) {\n alert('Please fill in the Review Title and Comment.');\n return;\n }\n\n const newReview: Review = {\n id: `review-${Date.now()}`,\n userId: CURRENT_USER_ID,\n userName: 'Rahul Sharma',\n userAvatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100',\n itemId: 'FOOD-001',\n itemName: 'Dosa Express',\n itemType: 'cart',\n cartId: 'FOOD-001',\n cartName: 'Dosa Express',\n orderId: `ORD-2024-${Date.now()}`,\n rating: reviewForm.rating,\n title: reviewForm.title,\n comment: reviewForm.comment,\n helpfulCount: 0,\n isHelpful: false,\n isVerified: true,\n createdAt: new Date().toISOString(),\n };\n\n setReviews((prev) => [newReview, ...prev]);\n setReviewForm({ rating: 5, title: '', comment: '' });\n setShowWriteReview(false);\n showStatus('Review submitted successfully!');\n };\n\n const handleMarkHelpful = (reviewId: string) => {\n setReviews((prev) =>\n prev.map((r) =>\n r.id === reviewId\n ? {\n ...r,\n isHelpful: !r.isHelpful,\n helpfulCount: r.isHelpful ? r.helpfulCount - 1 : r.helpfulCount + 1,\n }\n : r\n )\n );\n };\n\n return (\n <PageLayout\n title=\"Reviews & Ratings\"\n description={`${filteredReviews.length} reviews found`}\n actions={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className={cn(\n 'flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-opacity',\n 'bg-foreground text-background hover:opacity-90'\n )}\n >\n <PenSquare className=\"h-4 w-4\" />\n Write Review\n </button>\n }\n >\n <PagePurpose icon={<Star className=\"h-4 w-4\" />} className=\"mb-6\">\n Share your experience and read what others think. Write reviews for carts you've\n ordered from to help the community find great service, and revisit your own ratings\n any time. Honest feedback keeps quality high and helps good carts stand out.\n </PagePurpose>\n\n {/* Status message */}\n {statusMessage && (\n <div className=\"mb-4 rounded-lg border border-status-success-border/30 bg-status-success-bg/10 p-3 flex items-center gap-2\">\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text shrink-0\" />\n <p className=\"text-sm text-status-success-text\">{statusMessage}</p>\n </div>\n )}\n\n {/* Stats — the ONE emphasis zone */}\n <EmphasisPanel className=\"flex flex-wrap items-center gap-4 text-sm text-text-secondary mb-6\">\n <span>{reviews.length} total reviews</span>\n <span>{myReviewCount} your reviews</span>\n <span className=\"flex items-center gap-1\">\n Average: {avgRating}\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n </span>\n </EmphasisPanel>\n\n {/* Filters */}\n <div className=\"rounded-lg border border-border-seam bg-bg-surface p-4 mb-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3\">\n {/* Search */}\n <div className=\"relative\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-text-secondary\" />\n <input\n type=\"text\"\n placeholder=\"Search reviews...\"\n value={filters.searchQuery}\n onChange={(e) => setFilters((p) => ({ ...p, searchQuery: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface py-2 pl-9 pr-3 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* Type */}\n <select\n value={filters.type}\n onChange={(e) =>\n setFilters((p) => ({ ...p, type: e.target.value as ReviewFilters['type'] }))\n }\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"all\">All Reviews</option>\n <option value=\"cart\">Cart Reviews</option>\n <option value=\"product\">Product Reviews</option>\n <option value=\"my-reviews\">My Reviews</option>\n </select>\n\n {/* Rating */}\n <select\n value={filters.rating}\n onChange={(e) => setFilters((p) => ({ ...p, rating: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">All Ratings</option>\n <option value=\"5\">5 Stars</option>\n <option value=\"4\">4 Stars</option>\n <option value=\"3\">3 Stars</option>\n <option value=\"2\">2 Stars</option>\n <option value=\"1\">1 Star</option>\n </select>\n\n {/* Sort */}\n <select\n value={filters.sortBy}\n onChange={(e) =>\n setFilters((p) => ({ ...p, sortBy: e.target.value as ReviewFilters['sortBy'] }))\n }\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"recent\">Most Recent</option>\n <option value=\"helpful\">Most Helpful</option>\n <option value=\"rating-high\">Highest Rating</option>\n <option value=\"rating-low\">Lowest Rating</option>\n </select>\n </div>\n </div>\n\n {/* Reviews List */}\n {filteredReviews.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<MessageSquare className=\"h-12 w-12\" />}\n title=\"No reviews found\"\n description=\"Be the first to write a review!\"\n action={\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(true)}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Write a Review\n </button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {filteredReviews.map((review) => {\n const isMyReview = review.userId === CURRENT_USER_ID;\n return (\n <div\n key={review.id}\n className={cn(\n 'rounded-lg border bg-bg-surface p-5',\n isMyReview ? 'border-foreground/20' : 'border-border-seam'\n )}\n >\n {/* Header */}\n <div className=\"flex items-start gap-3 mb-3\">\n <img\n src={review.userAvatar}\n alt={review.userName}\n className=\"h-10 w-10 rounded-full object-cover shrink-0\"\n />\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {review.userName}\n </span>\n {review.isVerified && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg/10 px-2 py-0.5 text-[10px] font-medium text-status-success-text\">\n <CheckCircle2 className=\"h-3 w-3\" />\n Verified\n </span>\n )}\n {isMyReview && (\n <span className=\"inline-flex items-center rounded-full bg-status-info-bg/10 px-2 py-0.5 text-[10px] font-medium text-status-info-text\">\n Your Review\n </span>\n )}\n </div>\n <div className=\"flex flex-wrap items-center gap-2 text-xs text-text-secondary mt-0.5\">\n <span>\n {review.itemType === 'cart' ? (\n <MessageSquare className=\"inline h-3 w-3 mr-0.5\" />\n ) : (\n <Star className=\"inline h-3 w-3 mr-0.5\" />\n )}\n {review.itemName}\n </span>\n <span>\n {formatDate(review.createdAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n </div>\n <div className=\"shrink-0\">\n <RatingDisplay value={review.rating} size=\"sm\" />\n <p className=\"text-xs text-text-secondary mt-0.5 text-right\">\n From: {review.cartName}\n </p>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mb-3\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-1\">{review.title}</h4>\n <p className=\"text-sm text-text-primary leading-relaxed\">{review.comment}</p>\n </div>\n\n {/* Review Images */}\n {review.images && review.images.length > 0 && (\n <div className=\"mb-3 flex gap-2 overflow-x-auto pb-1\">\n {review.images.map((img, idx) => (\n <img\n key={idx}\n src={img}\n alt={`Review image ${idx + 1}`}\n className=\"h-20 w-20 rounded-lg object-cover shrink-0\"\n />\n ))}\n </div>\n )}\n\n {/* Owner Response */}\n {review.response && (\n <div className=\"mb-3 rounded-lg border border-status-info-border/20 bg-status-info-bg/5 p-3\">\n <div className=\"flex flex-wrap items-center gap-2 mb-1.5\">\n <MessageCircle className=\"h-3.5 w-3.5 text-status-info-text\" />\n <span className=\"text-xs font-medium text-status-info-text\">\n Response from {review.response.respondedBy}\n </span>\n <span className=\"text-[10px] text-status-info-text/70\">\n {formatDate(review.response.respondedAt, {\n hour: undefined,\n minute: undefined,\n hour12: undefined,\n })}\n </span>\n </div>\n <p className=\"text-xs text-status-info-text leading-relaxed\">\n {review.response.message}\n </p>\n </div>\n )}\n\n {/* Footer */}\n <div className=\"flex items-center justify-between pt-3 border-t border-border-seam\">\n <button\n type=\"button\"\n onClick={() => handleMarkHelpful(review.id)}\n className={cn(\n 'flex items-center gap-1.5 text-xs transition-colors',\n review.isHelpful\n ? 'text-status-info-text font-medium'\n : 'text-text-secondary hover:text-text-primary'\n )}\n >\n <ThumbsUp className={cn('h-3.5 w-3.5', review.isHelpful && 'fill-current')} />\n Helpful ({review.helpfulCount})\n </button>\n\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n <span>Order: {review.orderId}</span>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/customer/carts/${review.cartId}`)}\n className=\"font-medium hover:text-text-primary transition-colors\"\n >\n View Cart\n </button>\n </div>\n </div>\n </div>\n );\n })}\n </div>\n )}\n\n {/* Write Review Modal */}\n {showWriteReview && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-canvas/50 p-4\">\n <div className=\"w-full max-w-2xl max-h-[90vh] overflow-y-auto rounded-lg border border-border-seam bg-bg-surface shadow-lg\">\n <div className=\"p-5\">\n {/* Header */}\n <div className=\"flex items-center justify-between mb-5\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Write a Review</h3>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(false)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-accent hover:text-text-primary transition-colors\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"space-y-4\">\n {/* Rating */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-2\">\n Rating <span className=\"text-status-error-text\">*</span>\n </label>\n <InteractiveStarRating\n value={reviewForm.rating}\n onChange={(v) => setReviewForm((p) => ({ ...p, rating: v }))}\n />\n </div>\n\n {/* Title */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Review Title <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n type=\"text\"\n placeholder=\"Summarize your experience\"\n value={reviewForm.title}\n onChange={(e) => setReviewForm((p) => ({ ...p, title: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n\n {/* Comment */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1.5\">\n Your Review <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n rows={5}\n placeholder=\"Share your experience with others...\"\n value={reviewForm.comment}\n onChange={(e) => setReviewForm((p) => ({ ...p, comment: e.target.value }))}\n className=\"w-full rounded-lg border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n\n {/* Guidelines */}\n <div className=\"rounded-lg bg-bg-sunken p-4\">\n <div className=\"flex items-center gap-2 mb-2\">\n <Info className=\"h-4 w-4 text-text-secondary\" />\n <h4 className=\"text-sm font-medium text-text-primary\">Review Guidelines</h4>\n </div>\n <ul className=\"text-xs text-text-secondary space-y-1\">\n <li>- Be honest and helpful to other customers</li>\n <li>- Focus on your experience with the food and service</li>\n <li>- Avoid personal information or inappropriate content</li>\n <li>- Reviews are public and cannot be deleted once submitted</li>\n </ul>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-5\">\n <button\n type=\"button\"\n onClick={handleSubmitReview}\n className=\"rounded-lg bg-foreground px-4 py-2 text-sm font-medium text-background hover:opacity-90 transition-opacity\"\n >\n Submit Review\n </button>\n <button\n type=\"button\"\n onClick={() => setShowWriteReview(false)}\n className=\"rounded-lg border border-border-seam px-4 py-2 text-sm font-medium text-text-primary hover:bg-accent transition-colors\"\n >\n Cancel\n </button>\n </div>\n </div>\n </div>\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|