elseware-ui 2.36.9 → 2.37.0

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