@mohasinac/appkit 4.11.2 → 4.11.3
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/_internal/server/features/checkout/actions.d.ts +4 -0
- package/dist/_internal/server/features/checkout/actions.js +111 -4
- package/dist/_internal/server/features/checkout/data.d.ts +10 -0
- package/dist/_internal/server/features/orders/adapters.js +7 -0
- package/dist/_internal/server/features/products/data.d.ts +12 -3
- package/dist/_internal/server/features/products/data.js +55 -4
- package/dist/_internal/server/features/products/index.d.ts +1 -1
- package/dist/_internal/server/features/products/index.js +1 -1
- package/dist/_internal/server/features/products/list-public.d.ts +19 -0
- package/dist/_internal/server/features/products/list-public.js +39 -6
- package/dist/_internal/server/features/reviews/data.d.ts +1 -1
- package/dist/_internal/server/features/reviews/data.js +3 -2
- package/dist/_internal/server/features/tester/visibility.js +3 -3
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.d.ts +1 -1
- package/dist/_internal/server/jobs/core/testerSandboxCleanup.js +4 -1
- package/dist/_internal/server/jobs/core/testerSandboxRefresh.js +2 -0
- package/dist/_internal/server/jobs/handlers/_helpers.js +4 -1
- package/dist/_internal/shared/features/promotions/schema.d.ts +0 -16
- package/dist/_internal/shared/features/promotions/schema.js +0 -2
- package/dist/_internal/shared/features/reviews/config.d.ts +8 -1
- package/dist/_internal/shared/features/reviews/config.js +8 -1
- package/dist/_internal/shared/listing-types/_registry.d.ts +88 -1
- package/dist/_internal/shared/listing-types/_registry.js +63 -1
- package/dist/_internal/shared/listing-types/art/config.d.ts +59 -0
- package/dist/_internal/shared/listing-types/art/config.js +12 -0
- package/dist/_internal/shared/listing-types/auction/config.d.ts +53 -0
- package/dist/_internal/shared/listing-types/auction/config.js +17 -0
- package/dist/_internal/shared/listing-types/classified/config.d.ts +59 -0
- package/dist/_internal/shared/listing-types/classified/config.js +14 -0
- package/dist/_internal/shared/listing-types/digital-code/config.d.ts +59 -0
- package/dist/_internal/shared/listing-types/digital-code/config.js +12 -0
- package/dist/_internal/shared/listing-types/live/config.d.ts +59 -0
- package/dist/_internal/shared/listing-types/live/config.js +14 -0
- package/dist/_internal/shared/listing-types/pre-order/config.d.ts +110 -0
- package/dist/_internal/shared/listing-types/pre-order/config.js +14 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +44 -0
- package/dist/_internal/shared/listing-types/prize-draw/config.js +10 -0
- package/dist/_internal/shared/listing-types/standard/config.d.ts +59 -0
- package/dist/_internal/shared/listing-types/standard/config.js +9 -0
- package/dist/_internal/shared/listing-types/stickers/config.d.ts +59 -0
- package/dist/_internal/shared/listing-types/stickers/config.js +12 -0
- package/dist/client.d.ts +5 -1
- package/dist/client.js +4 -1
- package/dist/constants/field-names.d.ts +12 -0
- package/dist/constants/field-names.js +12 -0
- package/dist/constants/table-keys.d.ts +2 -0
- package/dist/constants/table-keys.js +2 -0
- package/dist/features/account/components/UserOrdersView.js +27 -5
- package/dist/features/admin/components/AdminAddressesView.js +3 -1
- package/dist/features/admin/components/AdminCouponEditorView.js +1 -4
- package/dist/features/admin/components/AdminPaymentMethodsView.js +3 -1
- package/dist/features/admin/components/AdminProductsView.js +34 -18
- package/dist/features/admin/constants/filter-tabs.d.ts +26 -9
- package/dist/features/admin/constants/filter-tabs.js +22 -9
- package/dist/features/auctions/components/AuctionBidsTable.js +1 -1
- package/dist/features/auctions/components/AuctionBottomActions.js +1 -0
- package/dist/features/auctions/components/AuctionDetailPageView.js +13 -11
- package/dist/features/cart/schemas/firestore.d.ts +0 -2
- package/dist/features/categories/components/BrandDetailTabs.js +31 -11
- package/dist/features/categories/components/CategoryDetailTabs.js +27 -7
- package/dist/features/categories/components/CategoryProductsListing.d.ts +9 -1
- package/dist/features/categories/components/CategoryProductsListing.js +5 -2
- package/dist/features/classified/components/ClassifiedDetailPageView.js +2 -1
- package/dist/features/contact/email.js +5 -1
- package/dist/features/digital-codes/components/DigitalCodeDetailPageView.js +17 -16
- package/dist/features/homepage/components/FeaturedResultsSection.js +1 -1
- package/dist/features/homepage/components/SectionCarousel.js +1 -1
- package/dist/features/homepage/components/ShopByCategorySection.js +1 -1
- package/dist/features/layout/AppLayoutShell.js +9 -2
- package/dist/features/layout/BottomActions.js +26 -3
- package/dist/features/layout/BottomActionsContext.d.ts +14 -0
- package/dist/features/layout/BottomActionsContext.js +13 -1
- package/dist/features/layout/hooks/useBottomActions.d.ts +6 -1
- package/dist/features/layout/hooks/useBottomActions.js +4 -1
- package/dist/features/layout/index.d.ts +1 -1
- package/dist/features/live/components/LiveItemDetailPageView.js +7 -6
- package/dist/features/orders/repository/orders.repository.d.ts +4 -0
- package/dist/features/orders/repository/orders.repository.js +4 -0
- package/dist/features/orders/types/index.d.ts +16 -0
- package/dist/features/pre-orders/components/PreOrderBottomActions.d.ts +1 -1
- package/dist/features/pre-orders/components/PreOrderBottomActions.js +3 -19
- package/dist/features/products/components/AuctionsIndexListing.js +1 -2
- package/dist/features/products/components/ListingBottomActions.d.ts +28 -0
- package/dist/features/products/components/ListingBottomActions.js +25 -0
- package/dist/features/products/components/PrizeDrawBottomActions.d.ts +1 -1
- package/dist/features/products/components/PrizeDrawBottomActions.js +3 -21
- package/dist/features/products/components/ProductDetailActions.js +4 -0
- package/dist/features/products/components/ProductDetailPageView.js +7 -7
- package/dist/features/products/constants/listing-tabs.d.ts +43 -198
- package/dist/features/products/constants/listing-tabs.js +71 -58
- package/dist/features/products/constants/sieve.d.ts +104 -1
- package/dist/features/products/constants/sieve.js +27 -7
- package/dist/features/products/repository/products.repository.d.ts +45 -1
- package/dist/features/products/repository/products.repository.js +26 -3
- package/dist/features/products/utils/listing-badge-variant.d.ts +20 -1
- package/dist/features/products/utils/listing-badge-variant.js +26 -1
- package/dist/features/products/utils/listing-type.d.ts +24 -0
- package/dist/features/products/utils/listing-type.js +48 -0
- package/dist/features/promotions/actions/coupon-actions.d.ts +7 -0
- package/dist/features/promotions/actions/coupon-actions.js +12 -1
- package/dist/features/promotions/actions/coupon-stacking.d.ts +24 -0
- package/dist/features/promotions/actions/coupon-stacking.js +21 -0
- package/dist/features/promotions/actions/index.d.ts +1 -0
- package/dist/features/promotions/actions/index.js +1 -0
- package/dist/features/promotions/actions/seller-coupon-actions.d.ts +0 -1
- package/dist/features/promotions/actions/seller-coupon-actions.js +0 -1
- package/dist/features/promotions/components/CouponHelpDetails.d.ts +20 -0
- package/dist/features/promotions/components/CouponHelpDetails.js +17 -0
- package/dist/features/promotions/components/CouponsIndexListing.js +2 -1
- package/dist/features/promotions/components/index.d.ts +2 -0
- package/dist/features/promotions/components/index.js +1 -0
- package/dist/features/promotions/constants/coupon-help.d.ts +27 -0
- package/dist/features/promotions/constants/coupon-help.js +42 -0
- package/dist/features/promotions/repository/coupons.repository.d.ts +10 -1
- package/dist/features/promotions/repository/coupons.repository.js +22 -8
- package/dist/features/promotions/schemas/firestore.d.ts +0 -1
- package/dist/features/promotions/schemas/firestore.js +1 -1
- package/dist/features/promotions/schemas/index.d.ts +0 -20
- package/dist/features/promotions/schemas/index.js +0 -1
- package/dist/features/reviews/actions/review-actions.js +5 -1
- package/dist/features/reviews/components/ReviewDetailPageView.js +2 -1
- package/dist/features/reviews/components/ReviewFilters.d.ts +7 -1
- package/dist/features/reviews/components/ReviewFilters.js +4 -3
- package/dist/features/reviews/components/ReviewsIndexListing.d.ts +6 -0
- package/dist/features/reviews/components/ReviewsIndexListing.js +9 -109
- package/dist/features/reviews/components/ReviewsListingPanel.d.ts +41 -0
- package/dist/features/reviews/components/ReviewsListingPanel.js +226 -0
- package/dist/features/reviews/components/index.d.ts +2 -0
- package/dist/features/reviews/components/index.js +1 -0
- package/dist/features/reviews/hooks/useReviews.d.ts +8 -0
- package/dist/features/reviews/hooks/useReviews.js +14 -6
- package/dist/features/reviews/repository/reviews.repository.d.ts +29 -1
- package/dist/features/reviews/repository/reviews.repository.js +77 -41
- package/dist/features/reviews/types/index.d.ts +8 -1
- package/dist/features/seller/components/SellerProductsCards.js +2 -2
- package/dist/features/seller/components/SellerProductsView.js +21 -18
- package/dist/features/stores/actions/store-query-actions.js +1 -1
- package/dist/features/stores/components/StoreAuctionsListing.js +5 -11
- package/dist/features/stores/components/StorePreOrdersListing.js +6 -11
- package/dist/features/stores/components/StoreReviewsListing.d.ts +4 -0
- package/dist/features/stores/components/StoreReviewsListing.js +7 -108
- package/dist/features/stores/schemas/index.d.ts +2 -2
- package/dist/features/tester/seed-data/coupons-tester-seed-data.js +3 -3
- package/dist/features/tester/seed-data/index.d.ts +1 -0
- package/dist/features/tester/seed-data/index.js +1 -0
- package/dist/features/tester/seed-data/offers-tester-seed-data.d.ts +2 -0
- package/dist/features/tester/seed-data/offers-tester-seed-data.js +61 -0
- package/dist/features/tester/seed-data/tester-checklist-seed-data.js +337 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.js +11 -2
- package/dist/react/hooks/useListingTypeFlags.d.ts +12 -19
- package/dist/react/hooks/useListingTypeFlags.js +4 -9
- package/dist/schemas/registry.d.ts +0 -12
- package/dist/seed/claimed-coupons-seed-data.js +3 -3
- package/dist/seed/coupons-seed-data.js +29 -10
- package/dist/seed/faq-seed-data.js +2 -2
- package/dist/seed/manifest.js +2 -2
- package/dist/styles.css +44 -39
- package/dist/tailwind-utilities.css +1 -1
- package/dist/ui/components/FilterChipGroup.d.ts +21 -3
- package/dist/ui/components/FilterChipGroup.js +33 -4
- package/dist/ui/components/HorizontalScroller.d.ts +1 -2
- package/dist/ui/components/HorizontalScroller.js +9 -5
- package/dist/ui/components/HorizontalScroller.style.css +43 -38
- package/package.json +1 -1
|
@@ -376,7 +376,7 @@ export class CouponsRepository extends BaseRepository {
|
|
|
376
376
|
* `eligibleSubtotal` (the order slice the discount is based on)
|
|
377
377
|
* and `eligibleProductIds` (the `productId` values that qualify).
|
|
378
378
|
*/
|
|
379
|
-
async validateCouponForCart(code, userId, cartItems) {
|
|
379
|
+
async validateCouponForCart(code, userId, cartItems, opts) {
|
|
380
380
|
const check = await this._fetchAndValidateCoupon(code, userId);
|
|
381
381
|
if (!check.ok)
|
|
382
382
|
return check.result;
|
|
@@ -400,25 +400,39 @@ export class CouponsRepository extends BaseRepository {
|
|
|
400
400
|
eligible = eligible.filter((item) => item.listingType !== "auction");
|
|
401
401
|
}
|
|
402
402
|
// Apply product/category restrictions from the coupon itself
|
|
403
|
-
const { applicableProducts, applicableCategories, excludeProducts } = coupon.restrictions;
|
|
403
|
+
const { applicableProducts, applicableCategories, excludeProducts, excludeCategories, } = coupon.restrictions;
|
|
404
404
|
if (applicableProducts && applicableProducts.length > 0) {
|
|
405
405
|
eligible = eligible.filter((item) => applicableProducts.includes(item.productId));
|
|
406
406
|
}
|
|
407
407
|
if (excludeProducts && excludeProducts.length > 0) {
|
|
408
408
|
eligible = eligible.filter((item) => !excludeProducts.includes(item.productId));
|
|
409
409
|
}
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
|
|
410
|
+
// Category restrictions need product metadata the cart doesn't snapshot,
|
|
411
|
+
// so they are resolved through the injected callback. A product carries
|
|
412
|
+
// `categorySlugs[]` (1-to-many), so the test is set intersection.
|
|
413
|
+
const needsCategories = (applicableCategories?.length ?? 0) > 0 ||
|
|
414
|
+
(excludeCategories?.length ?? 0) > 0;
|
|
415
|
+
if (needsCategories && eligible.length > 0 && opts?.resolveCategorySlugs) {
|
|
416
|
+
const slugsByProduct = await opts.resolveCategorySlugs(eligible.map((item) => item.productId));
|
|
417
|
+
const slugsFor = (productId) => slugsByProduct.get(productId) ?? [];
|
|
418
|
+
if (applicableCategories && applicableCategories.length > 0) {
|
|
419
|
+
eligible = eligible.filter((item) => slugsFor(item.productId).some((slug) => applicableCategories.includes(slug)));
|
|
420
|
+
}
|
|
421
|
+
if (excludeCategories && excludeCategories.length > 0) {
|
|
422
|
+
eligible = eligible.filter((item) => !slugsFor(item.productId).some((slug) => excludeCategories.includes(slug)));
|
|
423
|
+
}
|
|
424
|
+
}
|
|
413
425
|
if (eligible.length === 0) {
|
|
414
426
|
return {
|
|
415
427
|
valid: false,
|
|
416
428
|
coupon,
|
|
417
429
|
message: coupon.applicableToAuctions === true
|
|
418
430
|
? "This coupon only applies to auction items"
|
|
419
|
-
:
|
|
420
|
-
? "This coupon
|
|
421
|
-
:
|
|
431
|
+
: needsCategories
|
|
432
|
+
? "This coupon does not apply to any of the categories in your cart"
|
|
433
|
+
: scope === "seller"
|
|
434
|
+
? "This coupon is not valid for the items in your cart from this seller"
|
|
435
|
+
: "No eligible items found for this coupon",
|
|
422
436
|
};
|
|
423
437
|
}
|
|
424
438
|
const eligibleSubtotal = eligible.reduce((sum, item) => sum + item.price * item.quantity, 0);
|
|
@@ -62,7 +62,7 @@ export const COUPON_TYPE_LABELS = {
|
|
|
62
62
|
export const DEFAULT_COUPON_DATA = {
|
|
63
63
|
usage: { currentUsage: 0 },
|
|
64
64
|
validity: { isActive: true, startDate: new Date(), endDate: undefined },
|
|
65
|
-
restrictions: { firstTimeUserOnly: false
|
|
65
|
+
restrictions: { firstTimeUserOnly: false },
|
|
66
66
|
stats: { totalUses: 0, totalRevenue: 0, totalDiscount: 0 },
|
|
67
67
|
};
|
|
68
68
|
export const COUPONS_PUBLIC_FIELDS = [
|
|
@@ -93,10 +93,8 @@ export declare const restrictionsConfigSchema: z.ZodObject<{
|
|
|
93
93
|
excludeProducts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
94
94
|
excludeCategories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
95
95
|
firstTimeUserOnly: z.ZodBoolean;
|
|
96
|
-
combineWithSellerCoupons: z.ZodBoolean;
|
|
97
96
|
}, "strip", z.ZodTypeAny, {
|
|
98
97
|
firstTimeUserOnly: boolean;
|
|
99
|
-
combineWithSellerCoupons: boolean;
|
|
100
98
|
applicableProducts?: string[] | undefined;
|
|
101
99
|
applicableCategories?: string[] | undefined;
|
|
102
100
|
applicableSellers?: string[] | undefined;
|
|
@@ -104,7 +102,6 @@ export declare const restrictionsConfigSchema: z.ZodObject<{
|
|
|
104
102
|
excludeCategories?: string[] | undefined;
|
|
105
103
|
}, {
|
|
106
104
|
firstTimeUserOnly: boolean;
|
|
107
|
-
combineWithSellerCoupons: boolean;
|
|
108
105
|
applicableProducts?: string[] | undefined;
|
|
109
106
|
applicableCategories?: string[] | undefined;
|
|
110
107
|
applicableSellers?: string[] | undefined;
|
|
@@ -239,10 +236,8 @@ export declare const couponFirestoreSchema: z.ZodObject<{
|
|
|
239
236
|
excludeProducts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
240
237
|
excludeCategories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
241
238
|
firstTimeUserOnly: z.ZodBoolean;
|
|
242
|
-
combineWithSellerCoupons: z.ZodBoolean;
|
|
243
239
|
}, "strip", z.ZodTypeAny, {
|
|
244
240
|
firstTimeUserOnly: boolean;
|
|
245
|
-
combineWithSellerCoupons: boolean;
|
|
246
241
|
applicableProducts?: string[] | undefined;
|
|
247
242
|
applicableCategories?: string[] | undefined;
|
|
248
243
|
applicableSellers?: string[] | undefined;
|
|
@@ -250,7 +245,6 @@ export declare const couponFirestoreSchema: z.ZodObject<{
|
|
|
250
245
|
excludeCategories?: string[] | undefined;
|
|
251
246
|
}, {
|
|
252
247
|
firstTimeUserOnly: boolean;
|
|
253
|
-
combineWithSellerCoupons: boolean;
|
|
254
248
|
applicableProducts?: string[] | undefined;
|
|
255
249
|
applicableCategories?: string[] | undefined;
|
|
256
250
|
applicableSellers?: string[] | undefined;
|
|
@@ -311,7 +305,6 @@ export declare const couponFirestoreSchema: z.ZodObject<{
|
|
|
311
305
|
};
|
|
312
306
|
restrictions: {
|
|
313
307
|
firstTimeUserOnly: boolean;
|
|
314
|
-
combineWithSellerCoupons: boolean;
|
|
315
308
|
applicableProducts?: string[] | undefined;
|
|
316
309
|
applicableCategories?: string[] | undefined;
|
|
317
310
|
applicableSellers?: string[] | undefined;
|
|
@@ -370,7 +363,6 @@ export declare const couponFirestoreSchema: z.ZodObject<{
|
|
|
370
363
|
};
|
|
371
364
|
restrictions: {
|
|
372
365
|
firstTimeUserOnly: boolean;
|
|
373
|
-
combineWithSellerCoupons: boolean;
|
|
374
366
|
applicableProducts?: string[] | undefined;
|
|
375
367
|
applicableCategories?: string[] | undefined;
|
|
376
368
|
applicableSellers?: string[] | undefined;
|
|
@@ -450,10 +442,8 @@ export declare const claimedCouponSnapshotSchema: z.ZodObject<{
|
|
|
450
442
|
excludeProducts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
451
443
|
excludeCategories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
452
444
|
firstTimeUserOnly: z.ZodBoolean;
|
|
453
|
-
combineWithSellerCoupons: z.ZodBoolean;
|
|
454
445
|
}, "strip", z.ZodTypeAny, {
|
|
455
446
|
firstTimeUserOnly: boolean;
|
|
456
|
-
combineWithSellerCoupons: boolean;
|
|
457
447
|
applicableProducts?: string[] | undefined;
|
|
458
448
|
applicableCategories?: string[] | undefined;
|
|
459
449
|
applicableSellers?: string[] | undefined;
|
|
@@ -461,7 +451,6 @@ export declare const claimedCouponSnapshotSchema: z.ZodObject<{
|
|
|
461
451
|
excludeCategories?: string[] | undefined;
|
|
462
452
|
}, {
|
|
463
453
|
firstTimeUserOnly: boolean;
|
|
464
|
-
combineWithSellerCoupons: boolean;
|
|
465
454
|
applicableProducts?: string[] | undefined;
|
|
466
455
|
applicableCategories?: string[] | undefined;
|
|
467
456
|
applicableSellers?: string[] | undefined;
|
|
@@ -479,7 +468,6 @@ export declare const claimedCouponSnapshotSchema: z.ZodObject<{
|
|
|
479
468
|
};
|
|
480
469
|
restrictions: {
|
|
481
470
|
firstTimeUserOnly: boolean;
|
|
482
|
-
combineWithSellerCoupons: boolean;
|
|
483
471
|
applicableProducts?: string[] | undefined;
|
|
484
472
|
applicableCategories?: string[] | undefined;
|
|
485
473
|
applicableSellers?: string[] | undefined;
|
|
@@ -499,7 +487,6 @@ export declare const claimedCouponSnapshotSchema: z.ZodObject<{
|
|
|
499
487
|
};
|
|
500
488
|
restrictions: {
|
|
501
489
|
firstTimeUserOnly: boolean;
|
|
502
|
-
combineWithSellerCoupons: boolean;
|
|
503
490
|
applicableProducts?: string[] | undefined;
|
|
504
491
|
applicableCategories?: string[] | undefined;
|
|
505
492
|
applicableSellers?: string[] | undefined;
|
|
@@ -541,10 +528,8 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
541
528
|
excludeProducts: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
542
529
|
excludeCategories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
543
530
|
firstTimeUserOnly: z.ZodBoolean;
|
|
544
|
-
combineWithSellerCoupons: z.ZodBoolean;
|
|
545
531
|
}, "strip", z.ZodTypeAny, {
|
|
546
532
|
firstTimeUserOnly: boolean;
|
|
547
|
-
combineWithSellerCoupons: boolean;
|
|
548
533
|
applicableProducts?: string[] | undefined;
|
|
549
534
|
applicableCategories?: string[] | undefined;
|
|
550
535
|
applicableSellers?: string[] | undefined;
|
|
@@ -552,7 +537,6 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
552
537
|
excludeCategories?: string[] | undefined;
|
|
553
538
|
}, {
|
|
554
539
|
firstTimeUserOnly: boolean;
|
|
555
|
-
combineWithSellerCoupons: boolean;
|
|
556
540
|
applicableProducts?: string[] | undefined;
|
|
557
541
|
applicableCategories?: string[] | undefined;
|
|
558
542
|
applicableSellers?: string[] | undefined;
|
|
@@ -570,7 +554,6 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
570
554
|
};
|
|
571
555
|
restrictions: {
|
|
572
556
|
firstTimeUserOnly: boolean;
|
|
573
|
-
combineWithSellerCoupons: boolean;
|
|
574
557
|
applicableProducts?: string[] | undefined;
|
|
575
558
|
applicableCategories?: string[] | undefined;
|
|
576
559
|
applicableSellers?: string[] | undefined;
|
|
@@ -590,7 +573,6 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
590
573
|
};
|
|
591
574
|
restrictions: {
|
|
592
575
|
firstTimeUserOnly: boolean;
|
|
593
|
-
combineWithSellerCoupons: boolean;
|
|
594
576
|
applicableProducts?: string[] | undefined;
|
|
595
577
|
applicableCategories?: string[] | undefined;
|
|
596
578
|
applicableSellers?: string[] | undefined;
|
|
@@ -651,7 +633,6 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
651
633
|
};
|
|
652
634
|
restrictions: {
|
|
653
635
|
firstTimeUserOnly: boolean;
|
|
654
|
-
combineWithSellerCoupons: boolean;
|
|
655
636
|
applicableProducts?: string[] | undefined;
|
|
656
637
|
applicableCategories?: string[] | undefined;
|
|
657
638
|
applicableSellers?: string[] | undefined;
|
|
@@ -692,7 +673,6 @@ export declare const claimedCouponFirestoreSchema: z.ZodObject<{
|
|
|
692
673
|
};
|
|
693
674
|
restrictions: {
|
|
694
675
|
firstTimeUserOnly: boolean;
|
|
695
|
-
combineWithSellerCoupons: boolean;
|
|
696
676
|
applicableProducts?: string[] | undefined;
|
|
697
677
|
applicableCategories?: string[] | undefined;
|
|
698
678
|
applicableSellers?: string[] | undefined;
|
|
@@ -45,7 +45,6 @@ export const restrictionsConfigSchema = z.object({
|
|
|
45
45
|
excludeProducts: z.array(z.string()).optional(),
|
|
46
46
|
excludeCategories: z.array(z.string()).optional(),
|
|
47
47
|
firstTimeUserOnly: z.boolean(),
|
|
48
|
-
combineWithSellerCoupons: z.boolean(),
|
|
49
48
|
});
|
|
50
49
|
export const couponStatsSchema = z.object({
|
|
51
50
|
totalUses: z.number().int().nonnegative(),
|
|
@@ -174,9 +174,13 @@ export async function listReviewsBySeller(sellerId) {
|
|
|
174
174
|
}
|
|
175
175
|
const reviewBatches = await Promise.all(productIds
|
|
176
176
|
.slice(0, 20)
|
|
177
|
-
.map((id) => reviewRepository.findApprovedByProduct(id).catch(() => [])));
|
|
177
|
+
.map((id) => reviewRepository.findApprovedByProduct(id, 50).catch(() => [])));
|
|
178
|
+
// Each per-product batch is newest-first, but concatenating them yields product order,
|
|
179
|
+
// not chronological order — the flattened list has to be re-sorted or callers get an
|
|
180
|
+
// arbitrarily interleaved "latest reviews" list.
|
|
178
181
|
return reviewBatches
|
|
179
182
|
.flat()
|
|
183
|
+
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
180
184
|
.map((item) => maskPublicReview(item));
|
|
181
185
|
}
|
|
182
186
|
export async function getHomepageReviews() {
|
|
@@ -14,7 +14,8 @@ export async function ReviewDetailPageView({ id }) {
|
|
|
14
14
|
return (_jsx(Main, { children: _jsx(Section, { padding: "y-6xl", children: _jsx(Container, { size: "sm", children: _jsxs(Div, { className: "text-left", children: [_jsx(Text, { className: "mb-4", size: "5xl", "aria-hidden": "true", children: "\uD83D\uDD0D" }), _jsx(Heading, { color: "inverse", level: 1, className: "text-[var(--appkit-color-text)] dark: mb-2", size: "2xl", weight: "bold", children: "Review not found" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] mb-6", size: "sm", children: "This review may have been removed or is no longer available." }), _jsx(Link, { href: String(ROUTES.PUBLIC.REVIEWS), className: "inline-flex items-center gap-[var(--appkit-space-2)] rounded-lg bg-primary px-[var(--appkit-space-5)] py-[var(--appkit-space-2-5)] text-[length:var(--appkit-text-sm)] font-medium text-white hover:bg-primary-600 transition-colors", children: "\u2190 Back to Reviews" })] }) }) }) }));
|
|
15
15
|
}
|
|
16
16
|
const [sameProductDocs, sameStoreDocs] = await Promise.all([
|
|
17
|
-
|
|
17
|
+
// 12 = the 6 rendered below, with headroom for the current review + any filtered out.
|
|
18
|
+
reviewRepository.findApprovedByProduct(review.productId, 12).catch(() => []),
|
|
18
19
|
storeSlug ? reviewRepository.findApprovedByStore(storeSlug).catch(() => []) : Promise.resolve([]),
|
|
19
20
|
]);
|
|
20
21
|
const sameProductReviews = sameProductDocs.filter((r) => r.id !== review.id).slice(0, 6);
|
|
@@ -70,5 +70,11 @@ export interface ReviewFiltersProps {
|
|
|
70
70
|
variant?: ReviewFilterVariant;
|
|
71
71
|
/** Optional brand options for filter by brand */
|
|
72
72
|
brandOptions?: FacetOption[];
|
|
73
|
+
/**
|
|
74
|
+
* Restrict which filter controls render. Callers that only *apply* a subset must pass
|
|
75
|
+
* it here — a rendered control whose value the caller never reads is a silent no-op,
|
|
76
|
+
* which reads to the user as a broken filter. Omit to keep the variant's full set.
|
|
77
|
+
*/
|
|
78
|
+
keys?: readonly string[];
|
|
73
79
|
}
|
|
74
|
-
export declare function ReviewFilters({ table, variant, brandOptions, }: ReviewFiltersProps): import("react").JSX.Element;
|
|
80
|
+
export declare function ReviewFilters({ table, variant, brandOptions, keys, }: ReviewFiltersProps): import("react").JSX.Element;
|
|
@@ -38,7 +38,8 @@ export function getReviewSortOptions(variant) {
|
|
|
38
38
|
return REVIEW_PUBLIC_SORT_OPTIONS;
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
export function ReviewFilters({ table, variant = "admin", brandOptions, }) {
|
|
41
|
+
export function ReviewFilters({ table, variant = "admin", brandOptions, keys, }) {
|
|
42
|
+
const shows = (key) => (keys ? keys.includes(key) : true);
|
|
42
43
|
const t = useTranslations("filters");
|
|
43
44
|
const statusOptions = [
|
|
44
45
|
{ value: "pending", label: t("reviewStatusPending") },
|
|
@@ -59,6 +60,6 @@ export function ReviewFilters({ table, variant = "admin", brandOptions, }) {
|
|
|
59
60
|
? table.get("rating").split("|").filter(Boolean)
|
|
60
61
|
: [];
|
|
61
62
|
const isAdmin = variant === "admin";
|
|
62
|
-
const showStatus = variant !== "public";
|
|
63
|
-
return (_jsxs(Div, { children: [showStatus && (_jsx(FilterFacetSection, { title: t("status"), options: statusOptions, selected: selectedStatus, onChange: (vals) => table.set("status", vals.join("|")), searchable: false, defaultCollapsed: false })), _jsx(FilterFacetSection, { title: t("rating"), options: ratingOptions, selected: selectedRating, onChange: (vals) => table.set("rating", vals.join("|")), searchable: false, defaultCollapsed: variant === "admin" }), brandOptions && brandOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: table.get("brand") ? [table.get("brand")] : [], onChange: (vals) => table.set("brand", vals[0] ?? ""), searchable: brandOptions.length > 6, selectionMode: "single", defaultCollapsed: true })), _jsx(RangeFilter, { title: t("dateRange"), type: "date", minValue: table.get("dateFrom"), maxValue: table.get("dateTo"), onMinChange: (v) => table.set("dateFrom", v), onMaxChange: (v) => table.set("dateTo", v), minPlaceholder: t("minDate"), maxPlaceholder: t("maxDate"), defaultCollapsed: true }), isAdmin && (_jsxs(_Fragment, { children: [_jsx(SwitchFilter, { title: t("verified"), label: t("showVerifiedOnly"), checked: table.get("verified") === "true", onChange: (v) => table.set("verified", v ? "true" : "") }), _jsx(SwitchFilter, { title: t("featured"), label: t("showFeaturedOnly"), checked: table.get("featured") === "true", onChange: (v) => table.set("featured", v ? "true" : "") })] })), _jsx(RangeFilter, { title: t("votesRange"), minValue: table.get("minVotes"), maxValue: table.get("maxVotes"), onMinChange: (v) => table.set("minVotes", v), onMaxChange: (v) => table.set("maxVotes", v), minBound: 0, maxBound: 10000, step: 1, minPlaceholder: t("minVotes"), maxPlaceholder: t("maxVotes"), defaultCollapsed: true }), _jsx(SwitchFilter, { title: "Media", label: "Show reviews with photos only", checked: table.get("hasImages") === "true", onChange: (v) => table.set("hasImages", v ? "true" : "") })] }));
|
|
63
|
+
const showStatus = variant !== "public" && shows(TABLE_KEYS.STATUS);
|
|
64
|
+
return (_jsxs(Div, { children: [showStatus && (_jsx(FilterFacetSection, { title: t("status"), options: statusOptions, selected: selectedStatus, onChange: (vals) => table.set("status", vals.join("|")), searchable: false, defaultCollapsed: false })), shows(TABLE_KEYS.RATING) && (_jsx(FilterFacetSection, { title: t("rating"), options: ratingOptions, selected: selectedRating, onChange: (vals) => table.set("rating", vals.join("|")), searchable: false, defaultCollapsed: variant === "admin" })), shows(TABLE_KEYS.BRAND) && brandOptions && brandOptions.length > 0 && (_jsx(FilterFacetSection, { title: t("brand"), options: brandOptions, selected: table.get("brand") ? [table.get("brand")] : [], onChange: (vals) => table.set("brand", vals[0] ?? ""), searchable: brandOptions.length > 6, selectionMode: "single", defaultCollapsed: true })), shows(TABLE_KEYS.DATE_FROM) && (_jsx(RangeFilter, { title: t("dateRange"), type: "date", minValue: table.get("dateFrom"), maxValue: table.get("dateTo"), onMinChange: (v) => table.set("dateFrom", v), onMaxChange: (v) => table.set("dateTo", v), minPlaceholder: t("minDate"), maxPlaceholder: t("maxDate"), defaultCollapsed: true })), isAdmin && (_jsxs(_Fragment, { children: [_jsx(SwitchFilter, { title: t("verified"), label: t("showVerifiedOnly"), checked: table.get("verified") === "true", onChange: (v) => table.set("verified", v ? "true" : "") }), _jsx(SwitchFilter, { title: t("featured"), label: t("showFeaturedOnly"), checked: table.get("featured") === "true", onChange: (v) => table.set("featured", v ? "true" : "") })] })), shows("minVotes") && (_jsx(RangeFilter, { title: t("votesRange"), minValue: table.get("minVotes"), maxValue: table.get("maxVotes"), onMinChange: (v) => table.set("minVotes", v), onMaxChange: (v) => table.set("maxVotes", v), minBound: 0, maxBound: 10000, step: 1, minPlaceholder: t("minVotes"), maxPlaceholder: t("maxVotes"), defaultCollapsed: true })), shows("hasImages") && (_jsx(SwitchFilter, { title: "Media", label: "Show reviews with photos only", checked: table.get("hasImages") === "true", onChange: (v) => table.set("hasImages", v ? "true" : "") }))] }));
|
|
64
65
|
}
|
|
@@ -4,4 +4,10 @@ export interface ReviewsIndexListingProps {
|
|
|
4
4
|
initialData?: ReviewListResponse;
|
|
5
5
|
variant?: "admin" | "seller" | "public";
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* The public `/reviews` index. Everything below the props is `ReviewsListingPanel` —
|
|
9
|
+
* this file and `StoreReviewsListing` used to be near-identical copies of the same
|
|
10
|
+
* toolbar + sticky pagination + filter drawer + card grid, and the detail-page reviews
|
|
11
|
+
* tab would have been a third. See the Duplication Decision Framework (Rule of Three).
|
|
12
|
+
*/
|
|
7
13
|
export declare function ReviewsIndexListing({ initialData, variant, }: ReviewsIndexListingProps): React.JSX.Element;
|
|
@@ -1,112 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import { sortBy } from "../../../constants/sort";
|
|
11
|
-
import { REVIEW_FIELDS } from "../../../constants/field-names";
|
|
12
|
-
const __P = {
|
|
13
|
-
p4: "p-[var(--appkit-space-4)]",
|
|
14
|
-
};
|
|
15
|
-
const __O = {
|
|
16
|
-
hidden: "overflow-hidden",
|
|
17
|
-
};
|
|
18
|
-
const PAGE_SIZE = 12;
|
|
19
|
-
const DEFAULT_SORT = sortBy(REVIEW_FIELDS.CREATED_AT);
|
|
20
|
-
const FILTER_KEYS = ["rating", TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO, "minVotes", "maxVotes", "hasImages"];
|
|
21
|
-
const SORT_OPTION_LABELS = {
|
|
22
|
-
sortNewest: "Newest First",
|
|
23
|
-
sortOldest: "Oldest First",
|
|
24
|
-
sortHighestRated: "Highest Rated",
|
|
25
|
-
sortLowestRated: "Lowest Rated",
|
|
26
|
-
};
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { ReviewsListingPanel } from "./ReviewsListingPanel";
|
|
4
|
+
/**
|
|
5
|
+
* The public `/reviews` index. Everything below the props is `ReviewsListingPanel` —
|
|
6
|
+
* this file and `StoreReviewsListing` used to be near-identical copies of the same
|
|
7
|
+
* toolbar + sticky pagination + filter drawer + card grid, and the detail-page reviews
|
|
8
|
+
* tab would have been a third. See the Duplication Decision Framework (Rule of Three).
|
|
9
|
+
*/
|
|
27
10
|
export function ReviewsIndexListing({ initialData, variant = "public", }) {
|
|
28
|
-
|
|
29
|
-
const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
|
|
30
|
-
const [filterOpen, setFilterOpen] = useState(false);
|
|
31
|
-
const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
|
|
32
|
-
const handleViewToggle = (next) => {
|
|
33
|
-
if (next === VIEW_MODE.TABLE)
|
|
34
|
-
return;
|
|
35
|
-
setView(next);
|
|
36
|
-
table.set(TABLE_KEYS.VIEW, next);
|
|
37
|
-
};
|
|
38
|
-
const sort = table.get(TABLE_KEYS.SORT) || DEFAULT_SORT;
|
|
39
|
-
const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
|
|
40
|
-
// Pending filter state — buffered until "Apply Filters" clicked
|
|
41
|
-
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
42
|
-
const pendingTable = useMemo(() => ({
|
|
43
|
-
get: (key) => pendingFilters[key] ?? "",
|
|
44
|
-
getNumber: (key, fallback = 0) => {
|
|
45
|
-
const v = pendingFilters[key];
|
|
46
|
-
if (!v)
|
|
47
|
-
return fallback;
|
|
48
|
-
const n = Number(v);
|
|
49
|
-
return isNaN(n) ? fallback : n;
|
|
50
|
-
},
|
|
51
|
-
set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
|
|
52
|
-
setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
|
|
53
|
-
clear: (keys) => {
|
|
54
|
-
const ks = keys ?? FILTER_KEYS;
|
|
55
|
-
setPendingFilters((p) => ({
|
|
56
|
-
...p,
|
|
57
|
-
...Object.fromEntries(ks.map((k) => [k, ""])),
|
|
58
|
-
}));
|
|
59
|
-
},
|
|
60
|
-
setPage: (_) => { },
|
|
61
|
-
setPageSize: (_) => { },
|
|
62
|
-
setSort: (_) => { },
|
|
63
|
-
buildSieveParams: () => "",
|
|
64
|
-
buildSearchParams: () => "",
|
|
65
|
-
params: new URLSearchParams(),
|
|
66
|
-
}), [pendingFilters]);
|
|
67
|
-
const openFilters = useCallback(() => {
|
|
68
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
69
|
-
setFilterOpen(true);
|
|
70
|
-
}, [table]);
|
|
71
|
-
const applyFilters = useCallback(() => {
|
|
72
|
-
const updates = { page: "1" };
|
|
73
|
-
for (const k of FILTER_KEYS)
|
|
74
|
-
updates[k] = pendingFilters[k] ?? "";
|
|
75
|
-
table.setMany(updates);
|
|
76
|
-
setFilterOpen(false);
|
|
77
|
-
}, [pendingFilters, table]);
|
|
78
|
-
const clearFilters = useCallback(() => {
|
|
79
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
80
|
-
}, []);
|
|
81
|
-
const resetAll = useCallback(() => {
|
|
82
|
-
const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" };
|
|
83
|
-
for (const k of FILTER_KEYS)
|
|
84
|
-
updates[k] = "";
|
|
85
|
-
table.setMany(updates);
|
|
86
|
-
setSearchInput("");
|
|
87
|
-
}, [table]);
|
|
88
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
89
|
-
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
90
|
-
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
91
|
-
activeFilterCount > 0;
|
|
92
|
-
const commitSearch = useCallback(() => {
|
|
93
|
-
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
94
|
-
}, [searchInput, table]);
|
|
95
|
-
const { reviews, totalPages, isLoading } = useReviews({
|
|
96
|
-
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
97
|
-
rating: table.get(TABLE_KEYS.RATING) || undefined,
|
|
98
|
-
dateFrom: table.get(TABLE_KEYS.DATE_FROM) || undefined,
|
|
99
|
-
dateTo: table.get(TABLE_KEYS.DATE_TO) || undefined,
|
|
100
|
-
minVotes: table.get("minVotes") ? Number(table.get("minVotes")) : undefined,
|
|
101
|
-
maxVotes: table.get("maxVotes") ? Number(table.get("maxVotes")) : undefined,
|
|
102
|
-
hasImages: table.get("hasImages") === "true" ? true : undefined,
|
|
103
|
-
sort,
|
|
104
|
-
page: currentPage,
|
|
105
|
-
perPage: PAGE_SIZE,
|
|
106
|
-
}, { initialData });
|
|
107
|
-
const sortOptions = REVIEW_PUBLIC_SORT_OPTIONS.map((opt) => ({
|
|
108
|
-
value: opt.value,
|
|
109
|
-
label: SORT_OPTION_LABELS[opt.key] ?? opt.key,
|
|
110
|
-
}));
|
|
111
|
-
return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews by product name...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: sort, sortOptions: sortOptions, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsx(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: _jsxs(Stack, { className: `${__P.p4}`, gap: "3", children: [_jsx(Div, { className: "h-4 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-2/3", surface: "subtle", rounded: "default" })] }) }, i))) })) : reviews.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No reviews found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) : (_jsx(Div, { layout: "grid", gap: "6", className: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3", children: reviews.map((review) => (_jsx(ReviewCard, { review: review }, review.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: variant }) })] }));
|
|
11
|
+
return (_jsx(ReviewsListingPanel, { source: { kind: "all" }, stateMode: "url", initialData: initialData, variant: variant, showSummary: false, className: "min-h-screen" }));
|
|
112
12
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { ReviewListResponse } from "../types";
|
|
3
|
+
import type { ReviewCardContext } from "./ReviewsList";
|
|
4
|
+
/**
|
|
5
|
+
* Which reviews this panel lists. `product` and `all` go through `/api/reviews`;
|
|
6
|
+
* `store` goes through `/api/stores/[slug]/reviews`, which is the only endpoint that
|
|
7
|
+
* can fan a store's reviews out across its products.
|
|
8
|
+
*/
|
|
9
|
+
export type ReviewsSource = {
|
|
10
|
+
kind: "product";
|
|
11
|
+
productId: string;
|
|
12
|
+
} | {
|
|
13
|
+
kind: "store";
|
|
14
|
+
storeSlug: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "all";
|
|
17
|
+
};
|
|
18
|
+
export interface ReviewsListingPanelProps {
|
|
19
|
+
source: ReviewsSource;
|
|
20
|
+
pageSize?: number;
|
|
21
|
+
context?: ReviewCardContext;
|
|
22
|
+
/**
|
|
23
|
+
* `"url"` writes page/sort/filters to the query string — right for the dedicated
|
|
24
|
+
* review pages (shareable, SEO route segments read them back).
|
|
25
|
+
*
|
|
26
|
+
* `"local"` keeps that state in the component. Detail pages must use this: `useUrlTable`
|
|
27
|
+
* calls `router.replace()` on every change, which re-runs the whole product/auction RSC
|
|
28
|
+
* tree just to turn a page of reviews.
|
|
29
|
+
*/
|
|
30
|
+
stateMode?: "url" | "local";
|
|
31
|
+
/** SSR-seeded page 1. Only applied while the panel is genuinely at its default query. */
|
|
32
|
+
initialData?: ReviewListResponse;
|
|
33
|
+
/** Rating + count line above the toolbar. */
|
|
34
|
+
showSummary?: boolean;
|
|
35
|
+
/** Free-text search over product titles — meaningless on a single-product list. */
|
|
36
|
+
showSearch?: boolean;
|
|
37
|
+
variant?: "admin" | "seller" | "public";
|
|
38
|
+
emptyLabel?: string;
|
|
39
|
+
className?: string;
|
|
40
|
+
}
|
|
41
|
+
export declare function ReviewsListingPanel({ source, pageSize, context, stateMode, initialData, showSummary, showSearch, variant, emptyLabel, className, }: ReviewsListingPanelProps): React.JSX.Element;
|