elseware-ui 2.36.9 → 2.37.1

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/index.js CHANGED
@@ -20196,26 +20196,40 @@ var Drawer = ({
20196
20196
  side = "left",
20197
20197
  handleClose,
20198
20198
  styles,
20199
+ backdropStyles,
20199
20200
  showBackdrop = true,
20200
- closeOnBackdropClick = true
20201
+ closeOnBackdropClick = true,
20202
+ offsetTop = "0px",
20203
+ offsetBottom = "0px"
20201
20204
  }) => {
20202
20205
  const isHorizontal = side === "left" || side === "right";
20203
20206
  const isMobile = useIsMobile_default();
20207
+ const hasOffset = offsetTop !== "0px" || offsetBottom !== "0px";
20208
+ const drawerStyle = isHorizontal ? {
20209
+ top: offsetTop,
20210
+ bottom: offsetBottom,
20211
+ height: hasOffset ? `calc(100vh - ${offsetTop} - ${offsetBottom})` : void 0
20212
+ } : void 0;
20204
20213
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
20205
20214
  isMobile && showBackdrop && /* @__PURE__ */ jsxRuntime.jsx(TransitionFade_default, { visibility, leaveDuration: 200, children: /* @__PURE__ */ jsxRuntime.jsx(
20206
20215
  "div",
20207
20216
  {
20208
- className: "fixed inset-0 bg-black/50 z-40",
20217
+ className: classNames17__default.default(
20218
+ "fixed inset-0 bg-black/50 z-40",
20219
+ backdropStyles
20220
+ ),
20209
20221
  onClick: closeOnBackdropClick ? handleClose : void 0
20210
20222
  }
20211
20223
  ) }),
20212
20224
  /* @__PURE__ */ jsxRuntime.jsx(TransitionSlide_default, { visibility, side, leaveDuration: 200, children: /* @__PURE__ */ jsxRuntime.jsx(
20213
20225
  "div",
20214
20226
  {
20227
+ style: drawerStyle,
20215
20228
  className: classNames17__default.default(
20216
20229
  "fixed shadow-lg z-50",
20217
20230
  {
20218
- "h-full w-[300px] top-0": isHorizontal,
20231
+ "h-full w-[300px] top-0": isHorizontal && !hasOffset,
20232
+ "w-[300px]": isHorizontal && hasOffset,
20219
20233
  "w-full h-[300px] left-0": !isHorizontal,
20220
20234
  "left-0": side === "left",
20221
20235
  "right-0": side === "right",
@@ -22976,6 +22990,1563 @@ var TopNav = ({
22976
22990
  };
22977
22991
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center", children: items.map(renderNode) });
22978
22992
  };
22993
+ function ReviewForm({
22994
+ initialValues,
22995
+ placeholder = "Write your review...",
22996
+ submitLabel = "Submit",
22997
+ cancelLabel = "Cancel",
22998
+ loading = false,
22999
+ disabled = false,
23000
+ autoFocus = false,
23001
+ config,
23002
+ onSubmit,
23003
+ onCancel
23004
+ }) {
23005
+ const [content, setContent] = React2.useState(initialValues?.content ?? "");
23006
+ const [rating, setRating] = React2.useState(initialValues?.rating ?? 0);
23007
+ const [isRecommended, setIsRecommended] = React2.useState(
23008
+ initialValues?.isRecommended ?? true
23009
+ );
23010
+ const [error, setError] = React2.useState("");
23011
+ const contentLength = content.trim().length;
23012
+ const ratingItems = React2.useMemo(() => {
23013
+ return Array.from(
23014
+ {
23015
+ length: config.maxRating
23016
+ },
23017
+ (_, index) => index + 1
23018
+ );
23019
+ }, [config.maxRating]);
23020
+ React2.useEffect(() => {
23021
+ setContent(initialValues?.content ?? "");
23022
+ setRating(initialValues?.rating ?? 0);
23023
+ setIsRecommended(initialValues?.isRecommended ?? true);
23024
+ setError("");
23025
+ }, [
23026
+ initialValues?.content,
23027
+ initialValues?.rating,
23028
+ initialValues?.isRecommended
23029
+ ]);
23030
+ function validate() {
23031
+ const trimmedContent = content.trim();
23032
+ if (trimmedContent.length < config.minContentLength) {
23033
+ return `Review must be at least ${config.minContentLength} characters.`;
23034
+ }
23035
+ if (trimmedContent.length > config.maxContentLength) {
23036
+ return `Review cannot be more than ${config.maxContentLength} characters.`;
23037
+ }
23038
+ if (rating < config.minRating) {
23039
+ return "Please select a rating.";
23040
+ }
23041
+ return "";
23042
+ }
23043
+ async function handleSubmit() {
23044
+ const nextError = validate();
23045
+ if (nextError) {
23046
+ setError(nextError);
23047
+ return;
23048
+ }
23049
+ const values = {
23050
+ content: content.trim(),
23051
+ rating,
23052
+ isRecommended: config.allowRecommendation ? isRecommended : void 0
23053
+ };
23054
+ await onSubmit(values);
23055
+ }
23056
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
23057
+ /* @__PURE__ */ jsxRuntime.jsx(
23058
+ "textarea",
23059
+ {
23060
+ value: content,
23061
+ disabled: disabled || loading,
23062
+ autoFocus,
23063
+ placeholder,
23064
+ maxLength: config.maxContentLength,
23065
+ onChange: (event) => {
23066
+ setContent(event.target.value);
23067
+ setError("");
23068
+ },
23069
+ className: "min-h-[120px] w-full resize-none rounded-xl border border-gray-300 bg-white px-4 py-3 text-sm text-gray-900 outline-none transition-colors placeholder:text-gray-400 focus:border-blue-500 disabled:cursor-not-allowed disabled:opacity-60 dark:border-neutral-700 dark:bg-neutral-900 dark:text-gray-100 dark:placeholder:text-gray-500"
23070
+ }
23071
+ ),
23072
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 text-xs text-gray-500 dark:text-gray-400", children: [
23073
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
23074
+ contentLength,
23075
+ "/",
23076
+ config.maxContentLength
23077
+ ] }),
23078
+ error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-500", children: error })
23079
+ ] }),
23080
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
23081
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: "Rating" }),
23082
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1", children: ratingItems.map((item) => {
23083
+ const active = item <= rating;
23084
+ return /* @__PURE__ */ jsxRuntime.jsx(
23085
+ "button",
23086
+ {
23087
+ type: "button",
23088
+ disabled: disabled || loading,
23089
+ onClick: () => {
23090
+ setRating(item);
23091
+ setError("");
23092
+ },
23093
+ className: `
23094
+ text-2xl leading-none transition-transform disabled:cursor-not-allowed disabled:opacity-60
23095
+ ${active ? "text-yellow-400" : "text-gray-300 dark:text-neutral-700"}
23096
+ hover:scale-110
23097
+ `,
23098
+ "aria-label": `Rate ${item} out of ${config.maxRating}`,
23099
+ children: "\u2605"
23100
+ },
23101
+ item
23102
+ );
23103
+ }) })
23104
+ ] }),
23105
+ config.allowRecommendation && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
23106
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: "Do you recommend this?" }),
23107
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-3", children: [
23108
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex cursor-pointer items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [
23109
+ /* @__PURE__ */ jsxRuntime.jsx(
23110
+ "input",
23111
+ {
23112
+ type: "radio",
23113
+ checked: isRecommended,
23114
+ disabled: disabled || loading,
23115
+ onChange: () => setIsRecommended(true)
23116
+ }
23117
+ ),
23118
+ "Yes"
23119
+ ] }),
23120
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "inline-flex cursor-pointer items-center gap-2 text-sm text-gray-700 dark:text-gray-300", children: [
23121
+ /* @__PURE__ */ jsxRuntime.jsx(
23122
+ "input",
23123
+ {
23124
+ type: "radio",
23125
+ checked: !isRecommended,
23126
+ disabled: disabled || loading,
23127
+ onChange: () => setIsRecommended(false)
23128
+ }
23129
+ ),
23130
+ "No"
23131
+ ] })
23132
+ ] })
23133
+ ] }),
23134
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-end gap-2", children: [
23135
+ onCancel && /* @__PURE__ */ jsxRuntime.jsx(
23136
+ Button_default,
23137
+ {
23138
+ text: cancelLabel,
23139
+ variant: "secondary",
23140
+ appearance: "ghost",
23141
+ disabled: loading,
23142
+ onClick: onCancel
23143
+ }
23144
+ ),
23145
+ /* @__PURE__ */ jsxRuntime.jsx(
23146
+ Button_default,
23147
+ {
23148
+ text: submitLabel,
23149
+ variant: "primary",
23150
+ loading,
23151
+ disabled: disabled || !content.trim() || rating < config.minRating,
23152
+ onClick: handleSubmit
23153
+ }
23154
+ )
23155
+ ] })
23156
+ ] });
23157
+ }
23158
+ var ReviewForm_default = ReviewForm;
23159
+ function ReviewComposer({
23160
+ currentUser,
23161
+ placeholder = "Write your review...",
23162
+ submitLabel = "Post Review",
23163
+ loading = false,
23164
+ disabled = false,
23165
+ config,
23166
+ onLoginClick,
23167
+ onSubmit
23168
+ }) {
23169
+ if (!currentUser) {
23170
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-4 dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [
23171
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: "Please login to post your review." }),
23172
+ onLoginClick && /* @__PURE__ */ jsxRuntime.jsx(Button_default, { text: "Login", variant: "primary", onClick: onLoginClick })
23173
+ ] }) });
23174
+ }
23175
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-4 dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
23176
+ /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: currentUser.avatar, size: "md" }),
23177
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
23178
+ ReviewForm_default,
23179
+ {
23180
+ placeholder,
23181
+ submitLabel,
23182
+ loading,
23183
+ disabled,
23184
+ config,
23185
+ onSubmit
23186
+ }
23187
+ ) })
23188
+ ] }) });
23189
+ }
23190
+ var ReviewComposer_default = ReviewComposer;
23191
+
23192
+ // src/compositions/review-thread/utils/formatReviewDate.ts
23193
+ function formatReviewDate(date, format = "relative") {
23194
+ if (!date) {
23195
+ return "";
23196
+ }
23197
+ const target = new Date(date);
23198
+ if (Number.isNaN(target.getTime())) {
23199
+ return "";
23200
+ }
23201
+ if (format === "absolute") {
23202
+ return target.toLocaleString();
23203
+ }
23204
+ const now = /* @__PURE__ */ new Date();
23205
+ const seconds = Math.floor((now.getTime() - target.getTime()) / 1e3);
23206
+ if (seconds < 60) {
23207
+ return "Just now";
23208
+ }
23209
+ const intervals = [
23210
+ {
23211
+ label: "year",
23212
+ seconds: 31536e3
23213
+ },
23214
+ {
23215
+ label: "month",
23216
+ seconds: 2592e3
23217
+ },
23218
+ {
23219
+ label: "day",
23220
+ seconds: 86400
23221
+ },
23222
+ {
23223
+ label: "hour",
23224
+ seconds: 3600
23225
+ },
23226
+ {
23227
+ label: "minute",
23228
+ seconds: 60
23229
+ }
23230
+ ];
23231
+ for (const interval of intervals) {
23232
+ const count = Math.floor(seconds / interval.seconds);
23233
+ if (count >= 1) {
23234
+ return `${count} ${interval.label}${count > 1 ? "s" : ""} ago`;
23235
+ }
23236
+ }
23237
+ return "Just now";
23238
+ }
23239
+
23240
+ // src/compositions/review-thread/utils/reviewState.ts
23241
+ function prependReview(reviews, review) {
23242
+ return [review, ...reviews.filter((item) => item.id !== review.id)];
23243
+ }
23244
+ function appendReview(reviews, review) {
23245
+ return [...reviews.filter((item) => item.id !== review.id), review];
23246
+ }
23247
+ function updateReview(reviews, reviewId, updater) {
23248
+ return reviews.map(
23249
+ (review) => review.id === reviewId ? updater(review) : review
23250
+ );
23251
+ }
23252
+ function replaceReview(reviews, nextReview) {
23253
+ return updateReview(reviews, nextReview.id, () => nextReview);
23254
+ }
23255
+ function removeReview(reviews, reviewId) {
23256
+ return reviews.filter((review) => review.id !== reviewId);
23257
+ }
23258
+ function findReview(reviews, reviewId) {
23259
+ return reviews.find((review) => review.id === reviewId);
23260
+ }
23261
+ function upsertReview(reviews, review) {
23262
+ const exists = reviews.some((item) => item.id === review.id);
23263
+ if (!exists) {
23264
+ return prependReview(reviews, review);
23265
+ }
23266
+ return replaceReview(reviews, review);
23267
+ }
23268
+
23269
+ // src/compositions/review-thread/utils/reviewReplyState.ts
23270
+ function addReviewReply(review, reply, maxReplies = 1) {
23271
+ const replies = review.replies ?? [];
23272
+ return {
23273
+ ...review,
23274
+ replies: [reply, ...replies.filter((item) => item.id !== reply.id)].slice(
23275
+ 0,
23276
+ maxReplies
23277
+ )
23278
+ };
23279
+ }
23280
+ function updateReviewReply(review, replyId, updater) {
23281
+ return {
23282
+ ...review,
23283
+ replies: (review.replies ?? []).map(
23284
+ (reply) => reply.id === replyId ? updater(reply) : reply
23285
+ )
23286
+ };
23287
+ }
23288
+ function replaceReviewReply(review, nextReply) {
23289
+ return updateReviewReply(review, nextReply.id, () => nextReply);
23290
+ }
23291
+ function removeReviewReply(review, replyId) {
23292
+ return {
23293
+ ...review,
23294
+ replies: (review.replies ?? []).filter((reply) => reply.id !== replyId)
23295
+ };
23296
+ }
23297
+ function hasReviewReply(review, replyId) {
23298
+ return (review.replies ?? []).some((reply) => reply.id === replyId);
23299
+ }
23300
+
23301
+ // src/compositions/review-thread/utils/reviewPermissions.ts
23302
+ function canEditReview(review) {
23303
+ return review.permissions?.canEdit ?? false;
23304
+ }
23305
+ function canDeleteReview(review) {
23306
+ return review.permissions?.canDelete ?? false;
23307
+ }
23308
+ function canReportReview(review) {
23309
+ return review.permissions?.canReport ?? false;
23310
+ }
23311
+ function canReplyToReview(review) {
23312
+ return review.permissions?.canReply ?? false;
23313
+ }
23314
+ function canEditReviewReply(reply) {
23315
+ return reply.permissions?.canEdit ?? false;
23316
+ }
23317
+ function canDeleteReviewReply(reply) {
23318
+ return reply.permissions?.canDelete ?? false;
23319
+ }
23320
+ function canReportReviewReply(reply) {
23321
+ return reply.permissions?.canReport ?? false;
23322
+ }
23323
+
23324
+ // src/compositions/review-thread/utils/normalizeReview.ts
23325
+ function normalizeReviewAuthor(author) {
23326
+ if (!author) {
23327
+ return null;
23328
+ }
23329
+ return {
23330
+ id: String(author.id ?? author._id ?? ""),
23331
+ username: author.username,
23332
+ avatar: author.avatar,
23333
+ firstName: author.firstName,
23334
+ lastName: author.lastName,
23335
+ role: author.role
23336
+ };
23337
+ }
23338
+ function normalizeReviewReply(raw, fallback) {
23339
+ return {
23340
+ id: String(raw.id ?? raw._id ?? ""),
23341
+ targetId: String(raw.targetId ?? fallback.targetId),
23342
+ reviewId: String(raw.reviewId ?? fallback.reviewId),
23343
+ content: String(raw.content ?? raw.replyText ?? raw.responseText ?? ""),
23344
+ createdAt: String(raw.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()),
23345
+ updatedAt: raw.updatedAt,
23346
+ edited: raw.edited ?? false,
23347
+ author: normalizeReviewAuthor(raw.author ?? raw.repliedBy ?? null),
23348
+ authorLabel: raw.authorLabel,
23349
+ permissions: raw.permissions
23350
+ };
23351
+ }
23352
+ function normalizeReview(raw, fallbackTargetId = "") {
23353
+ const id = String(raw.id ?? raw._id ?? "");
23354
+ const targetId = String(
23355
+ raw.targetId ?? raw.gameSlug ?? raw.productSlug ?? raw.courseId ?? fallbackTargetId
23356
+ );
23357
+ const replies = raw.replies ? raw.replies.map(
23358
+ (reply) => normalizeReviewReply(reply, {
23359
+ targetId,
23360
+ reviewId: id
23361
+ })
23362
+ ) : raw.ownerReply ? [
23363
+ normalizeReviewReply(raw.ownerReply, {
23364
+ targetId,
23365
+ reviewId: id
23366
+ })
23367
+ ] : [];
23368
+ return {
23369
+ id,
23370
+ targetId,
23371
+ content: String(raw.content ?? raw.reviewText ?? ""),
23372
+ rating: Number(raw.rating ?? 0),
23373
+ isRecommended: raw.isRecommended,
23374
+ createdAt: String(raw.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()),
23375
+ updatedAt: raw.updatedAt,
23376
+ edited: raw.edited ?? false,
23377
+ author: normalizeReviewAuthor(raw.author ?? raw.reviewedBy ?? null),
23378
+ replies,
23379
+ permissions: raw.permissions
23380
+ };
23381
+ }
23382
+ function ReviewHeader({ review, config }) {
23383
+ const author = review.author;
23384
+ const displayName = author?.firstName || author?.lastName ? `${author?.firstName ?? ""} ${author?.lastName ?? ""}`.trim() : author?.username ?? "Unknown";
23385
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
23386
+ /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: author?.avatar, size: "sm" }),
23387
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
23388
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
23389
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-gray-900 dark:text-gray-100", children: displayName }),
23390
+ author?.username && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
23391
+ "@",
23392
+ author.username
23393
+ ] }),
23394
+ author?.role && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600 dark:bg-neutral-800 dark:text-gray-300", children: author.role })
23395
+ ] }),
23396
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-gray-500 dark:text-gray-400", children: [
23397
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatReviewDate(review.createdAt, config.dateFormat) }),
23398
+ review.edited && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "(edited)" })
23399
+ ] })
23400
+ ] })
23401
+ ] });
23402
+ }
23403
+ var ReviewHeader_default = ReviewHeader;
23404
+ function ReviewContent({ review }) {
23405
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap break-words text-sm leading-6 text-gray-700 dark:text-gray-300", children: review.content });
23406
+ }
23407
+ var ReviewContent_default = ReviewContent;
23408
+ function ReviewRating({ rating, maxRating }) {
23409
+ const items = Array.from(
23410
+ {
23411
+ length: maxRating
23412
+ },
23413
+ (_, index) => index + 1
23414
+ );
23415
+ return /* @__PURE__ */ jsxRuntime.jsx(
23416
+ "div",
23417
+ {
23418
+ className: "inline-flex items-center gap-0.5",
23419
+ "aria-label": `${rating} out of ${maxRating}`,
23420
+ children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
23421
+ "span",
23422
+ {
23423
+ className: item <= rating ? "text-sm text-yellow-400" : "text-sm text-gray-300 dark:text-neutral-700",
23424
+ children: "\u2605"
23425
+ },
23426
+ item
23427
+ ))
23428
+ }
23429
+ );
23430
+ }
23431
+ var ReviewRating_default = ReviewRating;
23432
+ function ReviewRecommendation({ isRecommended }) {
23433
+ if (typeof isRecommended !== "boolean") {
23434
+ return null;
23435
+ }
23436
+ return /* @__PURE__ */ jsxRuntime.jsxs(
23437
+ "span",
23438
+ {
23439
+ className: `
23440
+ inline-flex items-center gap-1 rounded-full px-2.5 py-1 text-xs font-medium
23441
+ ${isRecommended ? "bg-green-500/10 text-green-500" : "bg-red-500/10 text-red-500"}
23442
+ `,
23443
+ children: [
23444
+ isRecommended ? /* @__PURE__ */ jsxRuntime.jsx(bi.BiCheckCircle, { size: 16 }) : /* @__PURE__ */ jsxRuntime.jsx(bi.BiXCircle, { size: 16 }),
23445
+ isRecommended ? "Recommended" : "Not Recommended"
23446
+ ]
23447
+ }
23448
+ );
23449
+ }
23450
+ var ReviewRecommendation_default = ReviewRecommendation;
23451
+ function MyReviewPanel({
23452
+ currentUser,
23453
+ myReview,
23454
+ loading = false,
23455
+ disabled = false,
23456
+ config,
23457
+ components,
23458
+ onLoginClick,
23459
+ onCreateReview,
23460
+ onUpdateReview,
23461
+ onEditReview,
23462
+ onDeleteReview
23463
+ }) {
23464
+ const [editing, setEditing] = React2.useState(false);
23465
+ const ComposerComponent = components?.Composer ?? ReviewComposer_default;
23466
+ const FormComponent = components?.Form ?? ReviewForm_default;
23467
+ const HeaderComponent = components?.Header ?? ReviewHeader_default;
23468
+ const ContentComponent = components?.Content ?? ReviewContent_default;
23469
+ const RatingComponent = components?.Rating ?? ReviewRating_default;
23470
+ const RecommendationComponent = components?.Recommendation ?? ReviewRecommendation_default;
23471
+ async function handleCreateReview(values) {
23472
+ await onCreateReview(values);
23473
+ }
23474
+ async function handleUpdateReview(values) {
23475
+ if (!myReview) {
23476
+ return;
23477
+ }
23478
+ await onUpdateReview(myReview, values);
23479
+ setEditing(false);
23480
+ }
23481
+ function handleEdit() {
23482
+ if (!myReview) {
23483
+ return;
23484
+ }
23485
+ if (onEditReview) {
23486
+ onEditReview(myReview);
23487
+ return;
23488
+ }
23489
+ setEditing(true);
23490
+ }
23491
+ function handleDelete() {
23492
+ if (!myReview) {
23493
+ return;
23494
+ }
23495
+ onDeleteReview?.(myReview);
23496
+ }
23497
+ if (!config.showMyReviewPanel) {
23498
+ return null;
23499
+ }
23500
+ if (!myReview) {
23501
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
23502
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "My Review" }),
23503
+ /* @__PURE__ */ jsxRuntime.jsx(
23504
+ ComposerComponent,
23505
+ {
23506
+ currentUser,
23507
+ placeholder: "Write your review...",
23508
+ submitLabel: "Post Review",
23509
+ loading,
23510
+ disabled,
23511
+ config,
23512
+ onLoginClick,
23513
+ onSubmit: handleCreateReview
23514
+ }
23515
+ )
23516
+ ] });
23517
+ }
23518
+ if (editing) {
23519
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
23520
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "Update My Review" }),
23521
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-4 dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsx(
23522
+ FormComponent,
23523
+ {
23524
+ initialValues: {
23525
+ content: myReview.content,
23526
+ rating: myReview.rating,
23527
+ isRecommended: myReview.isRecommended
23528
+ },
23529
+ placeholder: "Update your review...",
23530
+ submitLabel: "Update Review",
23531
+ loading,
23532
+ disabled,
23533
+ autoFocus: true,
23534
+ config,
23535
+ onSubmit: handleUpdateReview,
23536
+ onCancel: () => setEditing(false)
23537
+ }
23538
+ ) })
23539
+ ] });
23540
+ }
23541
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
23542
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: "My Review" }),
23543
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-4 dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
23544
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
23545
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderComponent, { review: myReview, config }),
23546
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
23547
+ /* @__PURE__ */ jsxRuntime.jsx(
23548
+ Button_default,
23549
+ {
23550
+ text: "Edit",
23551
+ variant: "secondary",
23552
+ appearance: "ghost",
23553
+ disabled: disabled || loading,
23554
+ onClick: handleEdit
23555
+ }
23556
+ ),
23557
+ onDeleteReview && /* @__PURE__ */ jsxRuntime.jsx(
23558
+ Button_default,
23559
+ {
23560
+ text: "Delete",
23561
+ variant: "danger",
23562
+ appearance: "ghost",
23563
+ disabled: disabled || loading,
23564
+ onClick: handleDelete
23565
+ }
23566
+ )
23567
+ ] })
23568
+ ] }),
23569
+ /* @__PURE__ */ jsxRuntime.jsx(ContentComponent, { review: myReview }),
23570
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
23571
+ /* @__PURE__ */ jsxRuntime.jsx(
23572
+ RatingComponent,
23573
+ {
23574
+ rating: myReview.rating,
23575
+ maxRating: config.maxRating
23576
+ }
23577
+ ),
23578
+ config.allowRecommendation && /* @__PURE__ */ jsxRuntime.jsx(RecommendationComponent, { isRecommended: myReview.isRecommended })
23579
+ ] })
23580
+ ] }) })
23581
+ ] });
23582
+ }
23583
+ var MyReviewPanel_default = MyReviewPanel;
23584
+ function ReviewMenuItem({
23585
+ icon,
23586
+ name,
23587
+ badge,
23588
+ to,
23589
+ navigate,
23590
+ className,
23591
+ onClick,
23592
+ style
23593
+ }) {
23594
+ return /* @__PURE__ */ jsxRuntime.jsx(
23595
+ MenuItem,
23596
+ {
23597
+ icon,
23598
+ name,
23599
+ badge,
23600
+ to,
23601
+ navigate,
23602
+ onClick,
23603
+ style,
23604
+ className
23605
+ }
23606
+ );
23607
+ }
23608
+ function ReviewMenu({
23609
+ open,
23610
+ setOpen,
23611
+ canEdit,
23612
+ canDelete,
23613
+ canReport,
23614
+ canReply,
23615
+ onEdit,
23616
+ onDelete,
23617
+ onReport,
23618
+ onReply
23619
+ }) {
23620
+ const ref = React2.useRef(null);
23621
+ useClickOutside(ref, () => setOpen(false));
23622
+ const items = [];
23623
+ if (canEdit) {
23624
+ items.push({
23625
+ type: "item",
23626
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiEdit, {}),
23627
+ name: "Edit Review",
23628
+ component: ReviewMenuItem,
23629
+ onClick: () => {
23630
+ setOpen(false);
23631
+ onEdit?.();
23632
+ },
23633
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800"
23634
+ });
23635
+ }
23636
+ if (canDelete) {
23637
+ items.push({
23638
+ type: "item",
23639
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiTrash, {}),
23640
+ name: "Delete Review",
23641
+ component: ReviewMenuItem,
23642
+ onClick: () => {
23643
+ setOpen(false);
23644
+ onDelete?.();
23645
+ },
23646
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm text-red-500 hover:bg-gray-100 dark:hover:bg-neutral-800"
23647
+ });
23648
+ }
23649
+ if (canReport) {
23650
+ items.push({
23651
+ type: "item",
23652
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiFlag, {}),
23653
+ name: "Report Review",
23654
+ component: ReviewMenuItem,
23655
+ onClick: () => {
23656
+ setOpen(false);
23657
+ onReport?.();
23658
+ },
23659
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800"
23660
+ });
23661
+ }
23662
+ if (canReply) {
23663
+ items.push({
23664
+ type: "item",
23665
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiReply, {}),
23666
+ name: "Reply as Owner",
23667
+ component: ReviewMenuItem,
23668
+ onClick: () => {
23669
+ setOpen(false);
23670
+ onReply?.();
23671
+ },
23672
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800"
23673
+ });
23674
+ }
23675
+ if (!items.length) {
23676
+ return null;
23677
+ }
23678
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: "relative", children: [
23679
+ /* @__PURE__ */ jsxRuntime.jsx(
23680
+ "button",
23681
+ {
23682
+ type: "button",
23683
+ onClick: () => setOpen(!open),
23684
+ className: "text-gray-500 transition-colors hover:text-gray-900 dark:hover:text-gray-100",
23685
+ "aria-label": "Review options",
23686
+ children: /* @__PURE__ */ jsxRuntime.jsx(bi.BiDotsVerticalRounded, { size: 20 })
23687
+ }
23688
+ ),
23689
+ /* @__PURE__ */ jsxRuntime.jsx(Transition.TransitionDropdown, { visibility: open, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-full z-50 mt-2 min-w-[190px] overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xl dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsx(Menu, { items }) }) })
23690
+ ] });
23691
+ }
23692
+ var ReviewMenu_default = ReviewMenu;
23693
+ function ReviewActions({ canReply, onReply }) {
23694
+ if (!canReply) {
23695
+ return null;
23696
+ }
23697
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-4 text-sm text-gray-500 dark:text-gray-400", children: /* @__PURE__ */ jsxRuntime.jsxs(
23698
+ "button",
23699
+ {
23700
+ type: "button",
23701
+ onClick: onReply,
23702
+ className: "inline-flex items-center gap-1 font-medium transition-colors hover:text-gray-900 dark:hover:text-gray-100",
23703
+ children: [
23704
+ /* @__PURE__ */ jsxRuntime.jsx(bi.BiReply, { size: 18 }),
23705
+ "Reply as Owner"
23706
+ ]
23707
+ }
23708
+ ) });
23709
+ }
23710
+ var ReviewActions_default = ReviewActions;
23711
+ function ReviewReplyHeader({ reply, config }) {
23712
+ const author = reply.author;
23713
+ const displayName = author?.firstName || author?.lastName ? `${author?.firstName ?? ""} ${author?.lastName ?? ""}`.trim() : author?.username ?? "Unknown";
23714
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-center gap-3", children: [
23715
+ /* @__PURE__ */ jsxRuntime.jsx(Avatar, { src: author?.avatar, size: "sm" }),
23716
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
23717
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [
23718
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-gray-900 dark:text-gray-100", children: displayName }),
23719
+ (reply.authorLabel || author?.role) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-blue-500/10 px-2 py-0.5 text-xs font-medium text-blue-500", children: reply.authorLabel ?? author?.role }),
23720
+ author?.username && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
23721
+ "@",
23722
+ author.username
23723
+ ] })
23724
+ ] }),
23725
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 text-xs text-gray-500 dark:text-gray-400", children: [
23726
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatReviewDate(reply.createdAt, config.dateFormat) }),
23727
+ reply.edited && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "(edited)" })
23728
+ ] })
23729
+ ] })
23730
+ ] });
23731
+ }
23732
+ var ReviewReplyHeader_default = ReviewReplyHeader;
23733
+ function ReviewReplyContent({ reply }) {
23734
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "whitespace-pre-wrap break-words text-sm leading-6 text-gray-700 dark:text-gray-300", children: reply.content });
23735
+ }
23736
+ var ReviewReplyContent_default = ReviewReplyContent;
23737
+ function ReviewReplyMenuItem({
23738
+ icon,
23739
+ name,
23740
+ badge,
23741
+ to,
23742
+ navigate,
23743
+ className,
23744
+ onClick,
23745
+ style
23746
+ }) {
23747
+ return /* @__PURE__ */ jsxRuntime.jsx(
23748
+ MenuItem,
23749
+ {
23750
+ icon,
23751
+ name,
23752
+ badge,
23753
+ to,
23754
+ navigate,
23755
+ onClick,
23756
+ style,
23757
+ className
23758
+ }
23759
+ );
23760
+ }
23761
+ function ReviewReplyMenu({
23762
+ open,
23763
+ setOpen,
23764
+ canEdit,
23765
+ canDelete,
23766
+ canReport,
23767
+ onEdit,
23768
+ onDelete,
23769
+ onReport
23770
+ }) {
23771
+ const ref = React2.useRef(null);
23772
+ useClickOutside(ref, () => setOpen(false));
23773
+ const items = [];
23774
+ if (canEdit) {
23775
+ items.push({
23776
+ type: "item",
23777
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiEdit, {}),
23778
+ name: "Edit Reply",
23779
+ component: ReviewReplyMenuItem,
23780
+ onClick: () => {
23781
+ setOpen(false);
23782
+ onEdit?.();
23783
+ },
23784
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800"
23785
+ });
23786
+ }
23787
+ if (canDelete) {
23788
+ items.push({
23789
+ type: "item",
23790
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiTrash, {}),
23791
+ name: "Delete Reply",
23792
+ component: ReviewReplyMenuItem,
23793
+ onClick: () => {
23794
+ setOpen(false);
23795
+ onDelete?.();
23796
+ },
23797
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm text-red-500 hover:bg-gray-100 dark:hover:bg-neutral-800"
23798
+ });
23799
+ }
23800
+ if (canReport) {
23801
+ items.push({
23802
+ type: "item",
23803
+ icon: /* @__PURE__ */ jsxRuntime.jsx(bi.BiFlag, {}),
23804
+ name: "Report Reply",
23805
+ component: ReviewReplyMenuItem,
23806
+ onClick: () => {
23807
+ setOpen(false);
23808
+ onReport?.();
23809
+ },
23810
+ className: "flex w-full items-center gap-2 px-4 py-3 text-sm hover:bg-gray-100 dark:hover:bg-neutral-800"
23811
+ });
23812
+ }
23813
+ if (!items.length) {
23814
+ return null;
23815
+ }
23816
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, className: "relative", children: [
23817
+ /* @__PURE__ */ jsxRuntime.jsx(
23818
+ "button",
23819
+ {
23820
+ type: "button",
23821
+ onClick: () => setOpen(!open),
23822
+ className: "text-gray-500 transition-colors hover:text-gray-900 dark:hover:text-gray-100",
23823
+ "aria-label": "Review reply options",
23824
+ children: /* @__PURE__ */ jsxRuntime.jsx(bi.BiDotsVerticalRounded, { size: 18 })
23825
+ }
23826
+ ),
23827
+ /* @__PURE__ */ jsxRuntime.jsx(Transition.TransitionDropdown, { visibility: open, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute right-0 top-full z-50 mt-2 min-w-[180px] overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xl dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsx(Menu, { items }) }) })
23828
+ ] });
23829
+ }
23830
+ var ReviewReplyMenu_default = ReviewReplyMenu;
23831
+ function ReviewReplyItem({
23832
+ reply,
23833
+ parentReview,
23834
+ config,
23835
+ components,
23836
+ onEditReviewReply,
23837
+ onDeleteReviewReply,
23838
+ onReportReviewReply
23839
+ }) {
23840
+ const [menuOpen, setMenuOpen] = React2.useState(false);
23841
+ const HeaderComponent = components?.ReplyHeader ?? ReviewReplyHeader_default;
23842
+ const ContentComponent = components?.ReplyContent ?? ReviewReplyContent_default;
23843
+ const MenuComponent = components?.ReplyMenu ?? ReviewReplyMenu_default;
23844
+ const canEdit = canEditReviewReply(reply);
23845
+ const canDelete = canDeleteReviewReply(reply);
23846
+ const canReport = canReportReviewReply(reply);
23847
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-gray-50 p-4 dark:border-neutral-800 dark:bg-neutral-950", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
23848
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
23849
+ /* @__PURE__ */ jsxRuntime.jsx(
23850
+ HeaderComponent,
23851
+ {
23852
+ reply,
23853
+ parentReview,
23854
+ config
23855
+ }
23856
+ ),
23857
+ /* @__PURE__ */ jsxRuntime.jsx(
23858
+ MenuComponent,
23859
+ {
23860
+ reply,
23861
+ parentReview,
23862
+ open: menuOpen,
23863
+ setOpen: setMenuOpen,
23864
+ canEdit,
23865
+ canDelete,
23866
+ canReport,
23867
+ onEdit: () => onEditReviewReply?.(reply, parentReview),
23868
+ onDelete: () => onDeleteReviewReply?.(reply, parentReview),
23869
+ onReport: () => onReportReviewReply?.(reply, parentReview)
23870
+ }
23871
+ )
23872
+ ] }),
23873
+ /* @__PURE__ */ jsxRuntime.jsx(ContentComponent, { reply, parentReview })
23874
+ ] }) });
23875
+ }
23876
+ var ReviewReplyItem_default = ReviewReplyItem;
23877
+ function ReviewReplyList({
23878
+ review,
23879
+ replies,
23880
+ config,
23881
+ components,
23882
+ onEditReviewReply,
23883
+ onDeleteReviewReply,
23884
+ onReportReviewReply
23885
+ }) {
23886
+ const ReplyItemComponent = components?.ReplyItem ?? ReviewReplyItem_default;
23887
+ if (!config.allowReplies || !replies.length) {
23888
+ return null;
23889
+ }
23890
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-6 border-l border-gray-200 pl-4 dark:border-neutral-800", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3", children: replies.map((reply) => /* @__PURE__ */ jsxRuntime.jsx(
23891
+ ReplyItemComponent,
23892
+ {
23893
+ reply,
23894
+ parentReview: review,
23895
+ config,
23896
+ components,
23897
+ onEditReviewReply,
23898
+ onDeleteReviewReply,
23899
+ onReportReviewReply
23900
+ },
23901
+ reply.id
23902
+ )) }) });
23903
+ }
23904
+ var ReviewReplyList_default = ReviewReplyList;
23905
+ function ReviewItem({
23906
+ review,
23907
+ config,
23908
+ components,
23909
+ onEditReview,
23910
+ onDeleteReview,
23911
+ onReportReview,
23912
+ onReplyToReview,
23913
+ onEditReviewReply,
23914
+ onDeleteReviewReply,
23915
+ onReportReviewReply
23916
+ }) {
23917
+ const [menuOpen, setMenuOpen] = React2.useState(false);
23918
+ const HeaderComponent = components?.Header ?? ReviewHeader_default;
23919
+ const ContentComponent = components?.Content ?? ReviewContent_default;
23920
+ const RatingComponent = components?.Rating ?? ReviewRating_default;
23921
+ const RecommendationComponent = components?.Recommendation ?? ReviewRecommendation_default;
23922
+ const MenuComponent = components?.Menu ?? ReviewMenu_default;
23923
+ const ActionsComponent = components?.Actions ?? ReviewActions_default;
23924
+ const ReplyListComponent = components?.ReplyList ?? ReviewReplyList_default;
23925
+ const replies = review.replies ?? [];
23926
+ const canEdit = canEditReview(review);
23927
+ const canDelete = canDeleteReview(review);
23928
+ const canReport = canReportReview(review);
23929
+ const canReply = config.allowReplies && canReplyToReview(review) && replies.length < config.maxRepliesPerReview;
23930
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-gray-200 bg-white p-4 dark:border-neutral-800 dark:bg-neutral-900", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
23931
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3", children: [
23932
+ /* @__PURE__ */ jsxRuntime.jsx(HeaderComponent, { review, config }),
23933
+ /* @__PURE__ */ jsxRuntime.jsx(
23934
+ MenuComponent,
23935
+ {
23936
+ review,
23937
+ open: menuOpen,
23938
+ setOpen: setMenuOpen,
23939
+ canEdit,
23940
+ canDelete,
23941
+ canReport,
23942
+ canReply,
23943
+ onEdit: () => onEditReview?.(review),
23944
+ onDelete: () => onDeleteReview?.(review),
23945
+ onReport: () => onReportReview?.(review),
23946
+ onReply: () => onReplyToReview?.(review)
23947
+ }
23948
+ )
23949
+ ] }),
23950
+ /* @__PURE__ */ jsxRuntime.jsx(ContentComponent, { review }),
23951
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
23952
+ /* @__PURE__ */ jsxRuntime.jsx(
23953
+ RatingComponent,
23954
+ {
23955
+ rating: review.rating,
23956
+ maxRating: config.maxRating
23957
+ }
23958
+ ),
23959
+ config.allowRecommendation && /* @__PURE__ */ jsxRuntime.jsx(RecommendationComponent, { isRecommended: review.isRecommended })
23960
+ ] }),
23961
+ /* @__PURE__ */ jsxRuntime.jsx(
23962
+ ActionsComponent,
23963
+ {
23964
+ review,
23965
+ canReply,
23966
+ onReply: () => onReplyToReview?.(review)
23967
+ }
23968
+ ),
23969
+ /* @__PURE__ */ jsxRuntime.jsx(
23970
+ ReplyListComponent,
23971
+ {
23972
+ review,
23973
+ replies,
23974
+ config,
23975
+ components,
23976
+ onEditReviewReply,
23977
+ onDeleteReviewReply,
23978
+ onReportReviewReply
23979
+ }
23980
+ )
23981
+ ] }) });
23982
+ }
23983
+ var ReviewItem_default = ReviewItem;
23984
+ function ReviewEmptyState({
23985
+ title = "No reviews yet",
23986
+ text = "There are no reviews to show."
23987
+ }) {
23988
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-xl border border-dashed border-gray-300 bg-white p-8 text-center dark:border-neutral-800 dark:bg-neutral-900", children: [
23989
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: title }),
23990
+ text && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-2 text-sm text-gray-500 dark:text-gray-400", children: text })
23991
+ ] });
23992
+ }
23993
+ var ReviewEmptyState_default = ReviewEmptyState;
23994
+ function ReviewList({
23995
+ reviews,
23996
+ config,
23997
+ components,
23998
+ onEditReview,
23999
+ onDeleteReview,
24000
+ onReportReview,
24001
+ onReplyToReview,
24002
+ onEditReviewReply,
24003
+ onDeleteReviewReply,
24004
+ onReportReviewReply
24005
+ }) {
24006
+ const ItemComponent = components?.Item ?? ReviewItem_default;
24007
+ const EmptyStateComponent = components?.EmptyState ?? ReviewEmptyState_default;
24008
+ if (!reviews.length) {
24009
+ return /* @__PURE__ */ jsxRuntime.jsx(EmptyStateComponent, { title: config.emptyTitle, text: config.emptyText });
24010
+ }
24011
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-5", children: reviews.map((review) => /* @__PURE__ */ jsxRuntime.jsx(
24012
+ ItemComponent,
24013
+ {
24014
+ review,
24015
+ config,
24016
+ components,
24017
+ onEditReview,
24018
+ onDeleteReview,
24019
+ onReportReview,
24020
+ onReplyToReview,
24021
+ onEditReviewReply,
24022
+ onDeleteReviewReply,
24023
+ onReportReviewReply
24024
+ },
24025
+ review.id
24026
+ )) });
24027
+ }
24028
+ var ReviewList_default = ReviewList;
24029
+
24030
+ // src/compositions/review-thread/constants/review-thread.constants.ts
24031
+ var DEFAULT_REVIEW_THREAD_CONFIG = {
24032
+ reviewsPerPage: 10,
24033
+ dateFormat: "relative",
24034
+ minRating: 1,
24035
+ maxRating: 5,
24036
+ minContentLength: 8,
24037
+ maxContentLength: 2e3,
24038
+ allowRecommendation: true,
24039
+ showMyReviewPanel: true,
24040
+ showMyReviewInList: true,
24041
+ allowReplies: true,
24042
+ maxRepliesPerReview: 1,
24043
+ ownerReplyLabel: "Owner",
24044
+ emptyTitle: "No reviews yet",
24045
+ emptyText: "There are no reviews to show."
24046
+ };
24047
+ function isReviewEntity(value) {
24048
+ return Boolean(value && "rating" in value);
24049
+ }
24050
+ function updateReviewEverywhere(review, updater) {
24051
+ return updater(review);
24052
+ }
24053
+ function useReviewThreadState({
24054
+ targetId,
24055
+ dataSource,
24056
+ config,
24057
+ shouldLoadMyReview = true,
24058
+ loading = false,
24059
+ updatedReview,
24060
+ deletedReview,
24061
+ updatedReviewReply,
24062
+ deletedReviewReply,
24063
+ onReviewCreated,
24064
+ onReviewUpdated,
24065
+ onReviewDeleted
24066
+ }) {
24067
+ const [reviews, setReviews] = React2.useState([]);
24068
+ const [myReview, setMyReview] = React2.useState(null);
24069
+ const [isLoadingReviews, setIsLoadingReviews] = React2.useState(true);
24070
+ const [isLoadingMyReview, setIsLoadingMyReview] = React2.useState(
24071
+ Boolean(dataSource.getMyReview && shouldLoadMyReview)
24072
+ );
24073
+ const [isCreatingReview, setIsCreatingReview] = React2.useState(false);
24074
+ const [isUpdatingReview, setIsUpdatingReview] = React2.useState(false);
24075
+ const [isDeletingReview, setIsDeletingReview] = React2.useState(false);
24076
+ const [isCreatingReviewReply, setIsCreatingReviewReply] = React2.useState(false);
24077
+ const [isUpdatingReviewReply, setIsUpdatingReviewReply] = React2.useState(false);
24078
+ const [isDeletingReviewReply, setIsDeletingReviewReply] = React2.useState(false);
24079
+ const [reviewsError, setReviewsError] = React2.useState(null);
24080
+ const [myReviewError, setMyReviewError] = React2.useState(null);
24081
+ const error = reviewsError ?? myReviewError;
24082
+ const visibleReviews = React2.useMemo(() => {
24083
+ if (config.showMyReviewInList || !myReview) {
24084
+ return reviews;
24085
+ }
24086
+ return reviews.filter((review) => review.id !== myReview.id);
24087
+ }, [config.showMyReviewInList, myReview, reviews]);
24088
+ const applyReview = React2.useCallback((review) => {
24089
+ setReviews((prev) => upsertReview(prev, review));
24090
+ setMyReview((prev) => {
24091
+ if (!prev || prev.id !== review.id) {
24092
+ return prev;
24093
+ }
24094
+ return review;
24095
+ });
24096
+ }, []);
24097
+ const applyReviewUpdater = React2.useCallback(
24098
+ (reviewId, updater) => {
24099
+ setReviews((prev) => updateReview(prev, reviewId, updater));
24100
+ setMyReview((prev) => {
24101
+ if (!prev || prev.id !== reviewId) {
24102
+ return prev;
24103
+ }
24104
+ return updateReviewEverywhere(prev, updater);
24105
+ });
24106
+ },
24107
+ []
24108
+ );
24109
+ const removeReviewById = React2.useCallback((reviewId) => {
24110
+ setReviews((prev) => removeReview(prev, reviewId));
24111
+ setMyReview((prev) => {
24112
+ if (!prev || prev.id !== reviewId) {
24113
+ return prev;
24114
+ }
24115
+ return null;
24116
+ });
24117
+ }, []);
24118
+ const loadReviews = React2.useCallback(async () => {
24119
+ try {
24120
+ setIsLoadingReviews(true);
24121
+ setReviewsError(null);
24122
+ const result = await dataSource.getReviews({
24123
+ limit: config.reviewsPerPage
24124
+ });
24125
+ setReviews(result.items);
24126
+ } catch (error2) {
24127
+ setReviewsError(error2);
24128
+ } finally {
24129
+ setIsLoadingReviews(false);
24130
+ }
24131
+ }, [config.reviewsPerPage, dataSource, targetId]);
24132
+ const loadMyReview = React2.useCallback(async () => {
24133
+ if (!dataSource.getMyReview || !shouldLoadMyReview) {
24134
+ setMyReview(null);
24135
+ setIsLoadingMyReview(false);
24136
+ setMyReviewError(null);
24137
+ return;
24138
+ }
24139
+ try {
24140
+ setIsLoadingMyReview(true);
24141
+ setMyReviewError(null);
24142
+ const result = await dataSource.getMyReview();
24143
+ setMyReview(result);
24144
+ } catch (error2) {
24145
+ setMyReview(null);
24146
+ setMyReviewError(error2);
24147
+ } finally {
24148
+ setIsLoadingMyReview(false);
24149
+ }
24150
+ }, [dataSource, shouldLoadMyReview, targetId]);
24151
+ const reload = React2.useCallback(async () => {
24152
+ await Promise.all([loadReviews(), loadMyReview()]);
24153
+ }, [loadReviews, loadMyReview]);
24154
+ const createReview = React2.useCallback(
24155
+ async (values) => {
24156
+ try {
24157
+ setIsCreatingReview(true);
24158
+ const review = await dataSource.createReview({
24159
+ content: values.content,
24160
+ rating: values.rating,
24161
+ isRecommended: values.isRecommended
24162
+ });
24163
+ setMyReview(review);
24164
+ setReviews((prev) => prependReview(prev, review));
24165
+ onReviewCreated?.(review);
24166
+ return review;
24167
+ } finally {
24168
+ setIsCreatingReview(false);
24169
+ }
24170
+ },
24171
+ [dataSource, onReviewCreated]
24172
+ );
24173
+ const updateReview2 = React2.useCallback(
24174
+ async (review, values) => {
24175
+ try {
24176
+ setIsUpdatingReview(true);
24177
+ const updated = await dataSource.updateReview({
24178
+ id: review.id,
24179
+ content: values.content,
24180
+ rating: values.rating,
24181
+ isRecommended: values.isRecommended
24182
+ });
24183
+ setMyReview((prev) => {
24184
+ if (!prev || prev.id !== updated.id) {
24185
+ return prev;
24186
+ }
24187
+ return updated;
24188
+ });
24189
+ setReviews((prev) => upsertReview(prev, updated));
24190
+ onReviewUpdated?.(updated);
24191
+ return updated;
24192
+ } finally {
24193
+ setIsUpdatingReview(false);
24194
+ }
24195
+ },
24196
+ [dataSource, onReviewUpdated]
24197
+ );
24198
+ const deleteReview = React2.useCallback(
24199
+ async (review) => {
24200
+ try {
24201
+ setIsDeletingReview(true);
24202
+ await dataSource.deleteReview(review.id);
24203
+ removeReviewById(review.id);
24204
+ onReviewDeleted?.(review);
24205
+ } finally {
24206
+ setIsDeletingReview(false);
24207
+ }
24208
+ },
24209
+ [dataSource, onReviewDeleted, removeReviewById]
24210
+ );
24211
+ const createReviewReply = React2.useCallback(
24212
+ async (input) => {
24213
+ if (!dataSource.createReviewReply) {
24214
+ throw new Error("createReviewReply is not implemented by dataSource.");
24215
+ }
24216
+ try {
24217
+ setIsCreatingReviewReply(true);
24218
+ const result = await dataSource.createReviewReply(input);
24219
+ if (isReviewEntity(result)) {
24220
+ applyReview(result);
24221
+ } else {
24222
+ applyReviewUpdater(
24223
+ input.reviewId,
24224
+ (review) => addReviewReply(review, result, config.maxRepliesPerReview)
24225
+ );
24226
+ }
24227
+ return result;
24228
+ } finally {
24229
+ setIsCreatingReviewReply(false);
24230
+ }
24231
+ },
24232
+ [applyReview, applyReviewUpdater, config.maxRepliesPerReview, dataSource]
24233
+ );
24234
+ const updateReviewReply2 = React2.useCallback(
24235
+ async (input) => {
24236
+ if (!dataSource.updateReviewReply) {
24237
+ throw new Error("updateReviewReply is not implemented by dataSource.");
24238
+ }
24239
+ try {
24240
+ setIsUpdatingReviewReply(true);
24241
+ const result = await dataSource.updateReviewReply(input);
24242
+ if (isReviewEntity(result)) {
24243
+ applyReview(result);
24244
+ } else {
24245
+ applyReviewUpdater(
24246
+ input.reviewId,
24247
+ (review) => replaceReviewReply(review, result)
24248
+ );
24249
+ }
24250
+ return result;
24251
+ } finally {
24252
+ setIsUpdatingReviewReply(false);
24253
+ }
24254
+ },
24255
+ [applyReview, applyReviewUpdater, dataSource]
24256
+ );
24257
+ const deleteReviewReply = React2.useCallback(
24258
+ async (reviewId, replyId) => {
24259
+ if (!dataSource.deleteReviewReply) {
24260
+ throw new Error("deleteReviewReply is not implemented by dataSource.");
24261
+ }
24262
+ try {
24263
+ setIsDeletingReviewReply(true);
24264
+ const result = await dataSource.deleteReviewReply(reviewId, replyId);
24265
+ if (isReviewEntity(result)) {
24266
+ applyReview(result);
24267
+ } else {
24268
+ applyReviewUpdater(
24269
+ reviewId,
24270
+ (review) => removeReviewReply(review, replyId)
24271
+ );
24272
+ }
24273
+ return result;
24274
+ } finally {
24275
+ setIsDeletingReviewReply(false);
24276
+ }
24277
+ },
24278
+ [applyReview, applyReviewUpdater, dataSource]
24279
+ );
24280
+ React2.useEffect(() => {
24281
+ let cancelled = false;
24282
+ async function run() {
24283
+ try {
24284
+ setIsLoadingReviews(true);
24285
+ setReviewsError(null);
24286
+ const result = await dataSource.getReviews({
24287
+ limit: config.reviewsPerPage
24288
+ });
24289
+ if (!cancelled) {
24290
+ setReviews(result.items);
24291
+ }
24292
+ } catch (error2) {
24293
+ if (!cancelled) {
24294
+ setReviewsError(error2);
24295
+ }
24296
+ } finally {
24297
+ if (!cancelled) {
24298
+ setIsLoadingReviews(false);
24299
+ }
24300
+ }
24301
+ }
24302
+ void run();
24303
+ return () => {
24304
+ cancelled = true;
24305
+ };
24306
+ }, [config.reviewsPerPage, dataSource, targetId]);
24307
+ React2.useEffect(() => {
24308
+ let cancelled = false;
24309
+ async function run() {
24310
+ if (!dataSource.getMyReview || !shouldLoadMyReview) {
24311
+ setMyReview(null);
24312
+ setMyReviewError(null);
24313
+ setIsLoadingMyReview(false);
24314
+ return;
24315
+ }
24316
+ try {
24317
+ setIsLoadingMyReview(true);
24318
+ setMyReviewError(null);
24319
+ const result = await dataSource.getMyReview();
24320
+ if (!cancelled) {
24321
+ setMyReview(result);
24322
+ }
24323
+ } catch (error2) {
24324
+ if (!cancelled) {
24325
+ setMyReview(null);
24326
+ setMyReviewError(error2);
24327
+ }
24328
+ } finally {
24329
+ if (!cancelled) {
24330
+ setIsLoadingMyReview(false);
24331
+ }
24332
+ }
24333
+ }
24334
+ void run();
24335
+ return () => {
24336
+ cancelled = true;
24337
+ };
24338
+ }, [dataSource, shouldLoadMyReview, targetId]);
24339
+ React2.useEffect(() => {
24340
+ if (!updatedReview) {
24341
+ return;
24342
+ }
24343
+ applyReview(updatedReview);
24344
+ }, [applyReview, updatedReview]);
24345
+ React2.useEffect(() => {
24346
+ if (!deletedReview) {
24347
+ return;
24348
+ }
24349
+ removeReviewById(deletedReview.id);
24350
+ }, [deletedReview, removeReviewById]);
24351
+ React2.useEffect(() => {
24352
+ if (!updatedReviewReply) {
24353
+ return;
24354
+ }
24355
+ if (updatedReviewReply.review) {
24356
+ applyReview(updatedReviewReply.review);
24357
+ return;
24358
+ }
24359
+ if (!updatedReviewReply.reply) {
24360
+ return;
24361
+ }
24362
+ applyReviewUpdater(updatedReviewReply.reviewId, (review) => {
24363
+ if (hasReviewReply(review, updatedReviewReply.reply.id)) {
24364
+ return replaceReviewReply(review, updatedReviewReply.reply);
24365
+ }
24366
+ return addReviewReply(
24367
+ review,
24368
+ updatedReviewReply.reply,
24369
+ config.maxRepliesPerReview
24370
+ );
24371
+ });
24372
+ }, [
24373
+ applyReview,
24374
+ applyReviewUpdater,
24375
+ config.maxRepliesPerReview,
24376
+ updatedReviewReply
24377
+ ]);
24378
+ React2.useEffect(() => {
24379
+ if (!deletedReviewReply) {
24380
+ return;
24381
+ }
24382
+ if (deletedReviewReply.review) {
24383
+ applyReview(deletedReviewReply.review);
24384
+ return;
24385
+ }
24386
+ applyReviewUpdater(
24387
+ deletedReviewReply.reviewId,
24388
+ (review) => removeReviewReply(review, deletedReviewReply.replyId)
24389
+ );
24390
+ }, [applyReview, applyReviewUpdater, deletedReviewReply]);
24391
+ return {
24392
+ reviews,
24393
+ visibleReviews,
24394
+ myReview,
24395
+ isLoadingReviews,
24396
+ isLoadingMyReview,
24397
+ isInitialLoading: loading || isLoadingReviews || isLoadingMyReview,
24398
+ isCreatingReview,
24399
+ isUpdatingReview,
24400
+ isDeletingReview,
24401
+ isCreatingReviewReply,
24402
+ isUpdatingReviewReply,
24403
+ isDeletingReviewReply,
24404
+ reviewsError,
24405
+ myReviewError,
24406
+ error,
24407
+ setReviews,
24408
+ setMyReview,
24409
+ loadReviews,
24410
+ loadMyReview,
24411
+ reload,
24412
+ createReview,
24413
+ updateReview: updateReview2,
24414
+ deleteReview,
24415
+ createReviewReply,
24416
+ updateReviewReply: updateReviewReply2,
24417
+ deleteReviewReply,
24418
+ applyReview,
24419
+ removeReviewById
24420
+ };
24421
+ }
24422
+ function ReviewThread({
24423
+ targetId,
24424
+ currentUser,
24425
+ dataSource,
24426
+ loading = false,
24427
+ disabled = false,
24428
+ config,
24429
+ components,
24430
+ updatedReview,
24431
+ deletedReview,
24432
+ updatedReviewReply,
24433
+ deletedReviewReply,
24434
+ onLoginClick,
24435
+ onEditReview,
24436
+ onDeleteReview,
24437
+ onReportReview,
24438
+ onReplyToReview,
24439
+ onEditReviewReply,
24440
+ onDeleteReviewReply,
24441
+ onReportReviewReply,
24442
+ onReviewCreated,
24443
+ onReviewUpdated,
24444
+ onReviewDeleted
24445
+ }) {
24446
+ const resolvedConfig = {
24447
+ ...DEFAULT_REVIEW_THREAD_CONFIG,
24448
+ ...config
24449
+ };
24450
+ const reviewState = useReviewThreadState({
24451
+ targetId,
24452
+ dataSource,
24453
+ config: resolvedConfig,
24454
+ shouldLoadMyReview: Boolean(currentUser),
24455
+ loading,
24456
+ updatedReview,
24457
+ deletedReview,
24458
+ updatedReviewReply,
24459
+ deletedReviewReply,
24460
+ onReviewCreated,
24461
+ onReviewUpdated,
24462
+ onReviewDeleted
24463
+ });
24464
+ function handleEditReview(review) {
24465
+ onEditReview?.(review);
24466
+ }
24467
+ function handleDeleteReview(review) {
24468
+ if (onDeleteReview) {
24469
+ onDeleteReview(review);
24470
+ return;
24471
+ }
24472
+ void reviewState.deleteReview(review);
24473
+ }
24474
+ function handleReportReview(review) {
24475
+ onReportReview?.(review);
24476
+ }
24477
+ function handleReplyToReview(review) {
24478
+ onReplyToReview?.(review);
24479
+ }
24480
+ function handleEditReviewReply(reply, parentReview) {
24481
+ onEditReviewReply?.(reply, parentReview);
24482
+ }
24483
+ function handleDeleteReviewReply(reply, parentReview) {
24484
+ onDeleteReviewReply?.(reply, parentReview);
24485
+ }
24486
+ function handleReportReviewReply(reply, parentReview) {
24487
+ onReportReviewReply?.(reply, parentReview);
24488
+ }
24489
+ if (reviewState.isInitialLoading) {
24490
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
24491
+ resolvedConfig.showMyReviewPanel && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-40 animate-pulse rounded-xl bg-gray-100 dark:bg-neutral-900" }),
24492
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-36 animate-pulse rounded-xl bg-gray-100 dark:bg-neutral-900" }),
24493
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-36 animate-pulse rounded-xl bg-gray-100 dark:bg-neutral-900" })
24494
+ ] });
24495
+ }
24496
+ if (reviewState.error && !reviewState.reviews.length) {
24497
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-5 dark:border-red-500/20 dark:bg-red-500/10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
24498
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
24499
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-red-700 dark:text-red-400", children: "Failed to load reviews" }),
24500
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-red-600/80 dark:text-red-300/80", children: "Something went wrong while loading the review thread." })
24501
+ ] }),
24502
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(
24503
+ Button_default,
24504
+ {
24505
+ text: "Retry",
24506
+ variant: "danger",
24507
+ appearance: "ghost",
24508
+ onClick: () => {
24509
+ void reviewState.reload();
24510
+ }
24511
+ }
24512
+ ) })
24513
+ ] }) });
24514
+ }
24515
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-8", children: [
24516
+ resolvedConfig.showMyReviewPanel && /* @__PURE__ */ jsxRuntime.jsx(
24517
+ MyReviewPanel_default,
24518
+ {
24519
+ currentUser,
24520
+ myReview: reviewState.myReview,
24521
+ loading: reviewState.isCreatingReview || reviewState.isUpdatingReview || reviewState.isDeletingReview,
24522
+ disabled,
24523
+ config: resolvedConfig,
24524
+ components,
24525
+ onLoginClick,
24526
+ onCreateReview: reviewState.createReview,
24527
+ onUpdateReview: reviewState.updateReview,
24528
+ onEditReview: handleEditReview,
24529
+ onDeleteReview: handleDeleteReview
24530
+ }
24531
+ ),
24532
+ /* @__PURE__ */ jsxRuntime.jsx(
24533
+ ReviewList_default,
24534
+ {
24535
+ reviews: reviewState.visibleReviews,
24536
+ config: resolvedConfig,
24537
+ components,
24538
+ onEditReview: handleEditReview,
24539
+ onDeleteReview: handleDeleteReview,
24540
+ onReportReview: handleReportReview,
24541
+ onReplyToReview: handleReplyToReview,
24542
+ onEditReviewReply: handleEditReviewReply,
24543
+ onDeleteReviewReply: handleDeleteReviewReply,
24544
+ onReportReviewReply: handleReportReviewReply
24545
+ }
24546
+ )
24547
+ ] });
24548
+ }
24549
+ var ReviewThread_default = ReviewThread;
22979
24550
  function GenericLayout({
22980
24551
  children,
22981
24552
  styles,
@@ -23158,6 +24729,7 @@ exports.ConfiguratorError = ConfiguratorError;
23158
24729
  exports.Content = Content;
23159
24730
  exports.ContentArea = ContentArea_default;
23160
24731
  exports.DEFAULT_CHART_THEME = DEFAULT_CHART_THEME;
24732
+ exports.DEFAULT_REVIEW_THREAD_CONFIG = DEFAULT_REVIEW_THREAD_CONFIG;
23161
24733
  exports.DataView = DataView;
23162
24734
  exports.DataViewTable = DataViewTable_default;
23163
24735
  exports.DateSelector = DateSelector_default;
@@ -23235,6 +24807,7 @@ exports.ProgressBarRating = ProgressBarRating_default;
23235
24807
  exports.Quote = Quote;
23236
24808
  exports.Radio = Radio_default;
23237
24809
  exports.RealtimeChartDataSource = RealtimeChartDataSource;
24810
+ exports.ReviewThread = ReviewThread_default;
23238
24811
  exports.RouteTab = RouteTab_default;
23239
24812
  exports.RouteTabs = RouteTabs_default;
23240
24813
  exports.ScatterPlot = ScatterPlot;
@@ -23285,9 +24858,18 @@ exports.WorldMap = WorldMap;
23285
24858
  exports.WorldMapCountryTable = WorldMapCountryTable;
23286
24859
  exports.YoutubeVideoPlayer = YoutubeVideo_default;
23287
24860
  exports.addReplyToCache = addReplyToCache;
24861
+ exports.addReviewReply = addReviewReply;
24862
+ exports.appendReview = appendReview;
23288
24863
  exports.applyReactionState = applyReactionState;
23289
24864
  exports.assertAbsoluteURL = assertAbsoluteURL;
23290
24865
  exports.buildURL = buildURL;
24866
+ exports.canDeleteReview = canDeleteReview;
24867
+ exports.canDeleteReviewReply = canDeleteReviewReply;
24868
+ exports.canEditReview = canEditReview;
24869
+ exports.canEditReviewReply = canEditReviewReply;
24870
+ exports.canReplyToReview = canReplyToReview;
24871
+ exports.canReportReview = canReportReview;
24872
+ exports.canReportReviewReply = canReportReviewReply;
23291
24873
  exports.chartReducer = chartReducer;
23292
24874
  exports.cn = cn;
23293
24875
  exports.createConfigurator = createConfigurator;
@@ -23299,9 +24881,11 @@ exports.createURLResolver = createURLResolver;
23299
24881
  exports.decrementReplyCount = decrementReplyCount;
23300
24882
  exports.enumValues = enumValues;
23301
24883
  exports.euiToast = euiToast;
24884
+ exports.findReview = findReview;
23302
24885
  exports.formatChartValue = formatChartValue;
23303
24886
  exports.formatCommentDate = formatCommentDate;
23304
24887
  exports.formatConfigError = formatConfigError;
24888
+ exports.formatReviewDate = formatReviewDate;
23305
24889
  exports.getAllowedOrigins = getAllowedOrigins;
23306
24890
  exports.getBrowserHref = getBrowserHref;
23307
24891
  exports.getBrowserOrigin = getBrowserOrigin;
@@ -23311,6 +24895,7 @@ exports.getLayout = getLayout;
23311
24895
  exports.getOptimisticDislikeState = getOptimisticDislikeState;
23312
24896
  exports.getOptimisticLikeState = getOptimisticLikeState;
23313
24897
  exports.getSafeRedirect = getSafeRedirect;
24898
+ exports.hasReviewReply = hasReviewReply;
23314
24899
  exports.incrementReplyCount = incrementReplyCount;
23315
24900
  exports.isMatch = isMatch;
23316
24901
  exports.isRenderFn = isRenderFn;
@@ -23318,13 +24903,21 @@ exports.limitRealtimePoints = limitRealtimePoints;
23318
24903
  exports.normalize = normalize;
23319
24904
  exports.normalizeChartData = normalizeChartData;
23320
24905
  exports.normalizeChartDataPoint = normalizeChartDataPoint;
24906
+ exports.normalizeReview = normalizeReview;
24907
+ exports.normalizeReviewAuthor = normalizeReviewAuthor;
24908
+ exports.normalizeReviewReply = normalizeReviewReply;
23321
24909
  exports.parseJson = parseJson;
23322
24910
  exports.parseJsonRecord = parseJsonRecord;
23323
24911
  exports.parseUrlMap = parseUrlMap;
24912
+ exports.prependReview = prependReview;
23324
24913
  exports.registerLayout = registerLayout;
23325
24914
  exports.removeComment = removeComment;
23326
24915
  exports.removeCommentTreeFromCache = removeCommentTreeFromCache;
24916
+ exports.removeReview = removeReview;
24917
+ exports.removeReviewReply = removeReviewReply;
23327
24918
  exports.replaceRealtimePoints = replaceRealtimePoints;
24919
+ exports.replaceReview = replaceReview;
24920
+ exports.replaceReviewReply = replaceReviewReply;
23328
24921
  exports.resolveAppearanceStyles = resolveAppearanceStyles;
23329
24922
  exports.resolveChartColor = resolveChartColor;
23330
24923
  exports.resolveChartColors = resolveChartColors;
@@ -23333,6 +24926,9 @@ exports.sendToast = sendToast;
23333
24926
  exports.toConfiguratorError = toConfiguratorError;
23334
24927
  exports.updateComment = updateComment;
23335
24928
  exports.updateReplyCacheComment = updateReplyCacheComment;
24929
+ exports.updateReview = updateReview;
24930
+ exports.updateReviewReply = updateReviewReply;
24931
+ exports.upsertReview = upsertReview;
23336
24932
  exports.useChartContext = useChartContext;
23337
24933
  exports.useChartData = useChartData;
23338
24934
  exports.useChartPolling = useChartPolling;
@@ -23346,6 +24942,7 @@ exports.useDrawer = useDrawer_default;
23346
24942
  exports.useEUIConfig = useEUIConfig;
23347
24943
  exports.useIsMobile = useIsMobile_default;
23348
24944
  exports.useModal = useModal_default;
24945
+ exports.useReviewThreadState = useReviewThreadState;
23349
24946
  exports.useTheme = useTheme_default;
23350
24947
  //# sourceMappingURL=index.js.map
23351
24948
  //# sourceMappingURL=index.js.map