@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
|
@@ -17,9 +17,11 @@ import { ROUTES } from "../../../constants";
|
|
|
17
17
|
import { normalizeListingType } from "../../products/utils/listing-type";
|
|
18
18
|
import { toRecordArray, toRelativeDate, toStringValue } from "../hooks/useSellerListingData";
|
|
19
19
|
import { useListingTypeFlags } from "../../../react/hooks/useListingTypeFlags";
|
|
20
|
+
import { ALL_LISTING_TYPES } from "../../../_internal/shared/listing-types/feature-flags";
|
|
21
|
+
import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
|
|
20
22
|
import { SellerProductsCards } from "./SellerProductsCards";
|
|
21
23
|
import { SellerProductsFilterFields } from "./SellerProductsFilterDrawer";
|
|
22
|
-
import {
|
|
24
|
+
import { listingBadgeVariant } from "../../products/utils/listing-badge-variant";
|
|
23
25
|
import { DataListingView } from "../../admin/components/DataListingView";
|
|
24
26
|
import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
|
|
25
27
|
import { buildBulkAction } from "../../../_internal/shared/actions/bulk-helpers";
|
|
@@ -43,21 +45,24 @@ const SORT_OPTIONS = [
|
|
|
43
45
|
{ value: sortBy("title", "DESC"), label: "Title Z–A" },
|
|
44
46
|
{ value: sortBy("price", "DESC"), label: "Price High" },
|
|
45
47
|
{ value: sortBy("price", "ASC"), label: "Price Low" },
|
|
48
|
+
// Now genuinely sortable — see the note on AdminProductsView's sortOptions.
|
|
49
|
+
{ value: sortBy("featured", "DESC"), label: "Featured first" },
|
|
50
|
+
{ value: sortBy("isPromoted", "DESC"), label: "Promoted first" },
|
|
46
51
|
];
|
|
47
52
|
const STATUS_OPTIONS = SELLER_PRODUCT_STATUS_TABS;
|
|
48
53
|
function TypeDropdown({ active, onChange, }) {
|
|
49
54
|
const flags = useListingTypeFlags();
|
|
55
|
+
// Derived from ALL_LISTING_TYPES + the plugin registry (2026-08-21). This
|
|
56
|
+
// used to be a hand-written list that had no `art`/`stickers` entries and
|
|
57
|
+
// still offered `bundle` — which stopped being a listingType in SB-UNI-D, so
|
|
58
|
+
// picking it filtered on a value no product can hold and returned nothing.
|
|
50
59
|
const options = [
|
|
51
60
|
{ value: "all", label: "All listings" },
|
|
52
|
-
flags.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
flags.classified && { value: "classified", label: "Classified" },
|
|
58
|
-
flags["digital-code"] && { value: "digital-code", label: "Digital Code" },
|
|
59
|
-
flags.live && { value: "live", label: "Live" },
|
|
60
|
-
].filter(Boolean);
|
|
61
|
+
...ALL_LISTING_TYPES.filter(flags.isEnabled).map((type) => ({
|
|
62
|
+
value: type,
|
|
63
|
+
label: pluginFor(type).typeLabel,
|
|
64
|
+
})),
|
|
65
|
+
];
|
|
61
66
|
return (_jsxs(Row, { paddingX: "x-sm-lg-md", border: "bottom", padding: "y-xs", gap: "sm", children: [_jsx(Text, { className: "tracking-wide text-[var(--appkit-color-text-muted)]", size: "xs", weight: "semibold", transform: "uppercase", children: "Listing type" }), _jsx(Select, { options: options, value: active, onValueChange: (v) => onChange(v), wrapperClassName: "sm:max-w-xs", "aria-label": "Filter by listing type" })] }));
|
|
62
67
|
}
|
|
63
68
|
const PRODUCT_COLUMNS = [
|
|
@@ -70,7 +75,7 @@ const PRODUCT_COLUMNS = [
|
|
|
70
75
|
{
|
|
71
76
|
key: "primary",
|
|
72
77
|
header: "Product",
|
|
73
|
-
render: (row) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] line-clamp-1", weight: "medium", children: row.primary }), _jsxs(Row, { gap: "sm", children: [_jsx(Badge, { variant:
|
|
78
|
+
render: (row) => (_jsxs(Stack, { gap: "xs", children: [_jsx(Text, { className: "text-[var(--appkit-color-text)] line-clamp-1", weight: "medium", children: row.primary }), _jsxs(Row, { gap: "sm", children: [_jsx(Badge, { variant: listingBadgeVariant(row.listingKind), children: row.listingKind }), _jsx(Span, { size: "xs", color: "muted", children: row.secondary })] })] })),
|
|
74
79
|
},
|
|
75
80
|
{
|
|
76
81
|
key: "price",
|
|
@@ -206,13 +211,11 @@ export function SellerProductsView({ onDeleteProduct, onCreateClick, }) {
|
|
|
206
211
|
mapRows: (response) => {
|
|
207
212
|
const rows = toRecordArray(response.products).map((item, index) => {
|
|
208
213
|
const lt = normalizeListingType(item);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
? "prize-draw"
|
|
215
|
-
: "standard";
|
|
214
|
+
// The row's own type, verbatim. This used to be a 4-branch ternary
|
|
215
|
+
// that collapsed classified / digital-code / live / art / stickers all
|
|
216
|
+
// down to "standard", so five of nine types displayed the wrong badge
|
|
217
|
+
// in the seller table (2026-08-21).
|
|
218
|
+
const kind = lt;
|
|
216
219
|
const priceRaw = typeof item.price === "number" ? item.price : 0;
|
|
217
220
|
const auctionSecondary = kind === "auction"
|
|
218
221
|
? [
|
|
@@ -85,7 +85,7 @@ export async function getStoreReviews(storeSlug) {
|
|
|
85
85
|
const publishedProducts = allProducts
|
|
86
86
|
.filter((p) => p.status === ProductStatusValues.PUBLISHED)
|
|
87
87
|
.slice(0, 20);
|
|
88
|
-
const reviewArrays = await Promise.all(publishedProducts.map((p) => reviewRepository.findApprovedByProduct(p.id)));
|
|
88
|
+
const reviewArrays = await Promise.all(publishedProducts.map((p) => reviewRepository.findApprovedByProduct(p.id, 50)));
|
|
89
89
|
const allReviews = reviewArrays
|
|
90
90
|
.flat()
|
|
91
91
|
.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime())
|
|
@@ -12,19 +12,13 @@ import { AuctionFilters } from "../../auctions/components/AuctionFilters";
|
|
|
12
12
|
import { getDefaultCurrency } from "../../../core/baseline-resolver";
|
|
13
13
|
import { useGuestWishlist } from "../../wishlist/hooks/useGuestWishlist";
|
|
14
14
|
import { pushWishlistOp } from "../../cart/utils/pending-ops";
|
|
15
|
-
import {
|
|
16
|
-
import { sortBy } from "../../../constants/sort";
|
|
15
|
+
import { AUCTION_PUBLIC_SORT_OPTIONS } from "../../products/constants/sieve";
|
|
17
16
|
const __P = {
|
|
18
17
|
p3: "p-[var(--appkit-space-3)]",
|
|
19
18
|
};
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
{ value: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE), label: "Ending Latest" },
|
|
24
|
-
{ value: sortBy(PRODUCT_FIELDS.CURRENT_BID), label: "Highest Bid" },
|
|
25
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
|
|
26
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
|
|
27
|
-
];
|
|
19
|
+
// Shared with /auctions — a local copy here drifted ("Ending Soonest" vs
|
|
20
|
+
// "Ending Soon", and it silently omitted Most Bids / Buy It Now).
|
|
21
|
+
const DEFAULT_SORT = AUCTION_PUBLIC_SORT_OPTIONS[0].value;
|
|
28
22
|
const FILTER_KEYS = ["minBid", "maxBid", "dateFrom", "dateTo"];
|
|
29
23
|
export function StoreAuctionsListing({ storeId, initialData }) {
|
|
30
24
|
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
@@ -106,7 +100,7 @@ export function StoreAuctionsListing({ storeId, initialData }) {
|
|
|
106
100
|
isWishlisted: (productId) => wishlistedIds.has(productId),
|
|
107
101
|
};
|
|
108
102
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
|
|
109
|
-
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions:
|
|
103
|
+
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store auctions...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: AUCTION_PUBLIC_SORT_OPTIONS, onSortChange: (v) => {
|
|
110
104
|
table.set("sort", v);
|
|
111
105
|
}, view: view, onViewChange: (v) => {
|
|
112
106
|
if (v === "table")
|
|
@@ -8,19 +8,14 @@ import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Stack, Text, Sticky
|
|
|
8
8
|
import { MarketplacePreorderCard } from "../../pre-orders/components/MarketplacePreorderCard";
|
|
9
9
|
import { ProductFilters } from "../../products/components/ProductFilters";
|
|
10
10
|
import { ROUTES } from "../../../next";
|
|
11
|
-
import {
|
|
12
|
-
import { sortBy } from "../../../constants/sort";
|
|
11
|
+
import { PREORDER_PUBLIC_SORT_OPTIONS } from "../../products/constants/sieve";
|
|
13
12
|
const __P = {
|
|
14
13
|
p3: "p-[var(--appkit-space-3)]",
|
|
15
14
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{ value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE, "ASC"), label: "Delivery Soon" },
|
|
21
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
|
|
22
|
-
{ value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
|
|
23
|
-
];
|
|
15
|
+
// Shared with /pre-orders. The local copy also defaulted to -createdAt,
|
|
16
|
+
// so a store tab opened Newest-first while the public page opened
|
|
17
|
+
// Earliest-Delivery-first for the same data.
|
|
18
|
+
const DEFAULT_SORT = PREORDER_PUBLIC_SORT_OPTIONS[0].value;
|
|
24
19
|
const FILTER_KEYS = ["minPrice", "maxPrice"];
|
|
25
20
|
export function StorePreOrdersListing({ storeId, initialData }) {
|
|
26
21
|
const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
|
|
@@ -59,7 +54,7 @@ export function StorePreOrdersListing({ storeId, initialData }) {
|
|
|
59
54
|
table.set("q", searchInput.trim());
|
|
60
55
|
}, [searchInput, table]);
|
|
61
56
|
const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-[var(--appkit-space-4)]";
|
|
62
|
-
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions:
|
|
57
|
+
return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store pre-orders...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: PREORDER_PUBLIC_SORT_OPTIONS, onSortChange: (v) => {
|
|
63
58
|
table.set("sort", v);
|
|
64
59
|
}, view: view, onViewChange: (v) => {
|
|
65
60
|
if (v === "table")
|
|
@@ -2,4 +2,8 @@ import React from "react";
|
|
|
2
2
|
export interface StoreReviewsListingProps {
|
|
3
3
|
storeSlug: string;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* The `/stores/[slug]/reviews` tab. Delegates to the shared `ReviewsListingPanel` —
|
|
7
|
+
* see the note in `ReviewsIndexListing` for why the two were merged.
|
|
8
|
+
*/
|
|
5
9
|
export declare function StoreReviewsListing({ storeSlug }: StoreReviewsListingProps): React.JSX.Element;
|
|
@@ -1,111 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { useStoreReviews } from "../hooks/useStores";
|
|
9
|
-
import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
|
|
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, "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 "../../reviews/components/ReviewsListingPanel";
|
|
4
|
+
/**
|
|
5
|
+
* The `/stores/[slug]/reviews` tab. Delegates to the shared `ReviewsListingPanel` —
|
|
6
|
+
* see the note in `ReviewsIndexListing` for why the two were merged.
|
|
7
|
+
*/
|
|
27
8
|
export function StoreReviewsListing({ storeSlug }) {
|
|
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
|
-
const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
41
|
-
const pendingTable = useMemo(() => ({
|
|
42
|
-
get: (key) => pendingFilters[key] ?? "",
|
|
43
|
-
getNumber: (key, fallback = 0) => {
|
|
44
|
-
const v = pendingFilters[key];
|
|
45
|
-
if (!v)
|
|
46
|
-
return fallback;
|
|
47
|
-
const n = Number(v);
|
|
48
|
-
return isNaN(n) ? fallback : n;
|
|
49
|
-
},
|
|
50
|
-
set: (key, value) => setPendingFilters((p) => ({ ...p, [key]: value })),
|
|
51
|
-
setMany: (updates) => setPendingFilters((p) => ({ ...p, ...updates })),
|
|
52
|
-
clear: (keys) => {
|
|
53
|
-
const ks = keys ?? FILTER_KEYS;
|
|
54
|
-
setPendingFilters((p) => ({
|
|
55
|
-
...p,
|
|
56
|
-
...Object.fromEntries(ks.map((k) => [k, ""])),
|
|
57
|
-
}));
|
|
58
|
-
},
|
|
59
|
-
setPage: (_) => { },
|
|
60
|
-
setPageSize: (_) => { },
|
|
61
|
-
setSort: (_) => { },
|
|
62
|
-
buildSieveParams: () => "",
|
|
63
|
-
buildSearchParams: () => "",
|
|
64
|
-
params: new URLSearchParams(),
|
|
65
|
-
}), [pendingFilters]);
|
|
66
|
-
const openFilters = useCallback(() => {
|
|
67
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
|
|
68
|
-
setFilterOpen(true);
|
|
69
|
-
}, [table]);
|
|
70
|
-
const applyFilters = useCallback(() => {
|
|
71
|
-
const updates = { page: "1" };
|
|
72
|
-
for (const k of FILTER_KEYS)
|
|
73
|
-
updates[k] = pendingFilters[k] ?? "";
|
|
74
|
-
table.setMany(updates);
|
|
75
|
-
setFilterOpen(false);
|
|
76
|
-
}, [pendingFilters, table]);
|
|
77
|
-
const clearFilters = useCallback(() => {
|
|
78
|
-
setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
|
|
79
|
-
}, []);
|
|
80
|
-
const resetAll = useCallback(() => {
|
|
81
|
-
const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" };
|
|
82
|
-
for (const k of FILTER_KEYS)
|
|
83
|
-
updates[k] = "";
|
|
84
|
-
table.setMany(updates);
|
|
85
|
-
setSearchInput("");
|
|
86
|
-
}, [table]);
|
|
87
|
-
const commitSearch = useCallback(() => {
|
|
88
|
-
table.set(TABLE_KEYS.QUERY, searchInput.trim());
|
|
89
|
-
}, [searchInput, table]);
|
|
90
|
-
const ratingRaw = table.get("rating");
|
|
91
|
-
const ratingNum = ratingRaw ? Number(ratingRaw.split("|")[0]) : undefined;
|
|
92
|
-
const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
|
|
93
|
-
const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
|
|
94
|
-
table.get(TABLE_KEYS.SORT) !== DEFAULT_SORT ||
|
|
95
|
-
activeFilterCount > 0;
|
|
96
|
-
const { reviews, averageRating, totalReviews, totalPages, isLoading } = useStoreReviews(storeSlug, {
|
|
97
|
-
rating: ratingNum,
|
|
98
|
-
page: currentPage,
|
|
99
|
-
pageSize: PAGE_SIZE,
|
|
100
|
-
sort,
|
|
101
|
-
q: table.get(TABLE_KEYS.QUERY) || undefined,
|
|
102
|
-
dateFrom: table.get(TABLE_KEYS.DATE_FROM) || undefined,
|
|
103
|
-
dateTo: table.get(TABLE_KEYS.DATE_TO) || undefined,
|
|
104
|
-
hasImages: table.get("hasImages") === "true" ? true : undefined,
|
|
105
|
-
});
|
|
106
|
-
const sortOptions = REVIEW_PUBLIC_SORT_OPTIONS.map((opt) => ({
|
|
107
|
-
value: opt.value,
|
|
108
|
-
label: SORT_OPTION_LABELS[opt.key] ?? opt.key,
|
|
109
|
-
}));
|
|
110
|
-
return (_jsxs(Div, { className: "min-h-screen", children: [totalReviews > 0 && (_jsxs(Row, { border: "default", align: "center", gap: "sm", className: "border-b", padding: "inline", children: [_jsx(Span, { weight: "bold", size: "2xl", color: "primary", children: averageRating.toFixed(1) }), _jsxs(Span, { size: "sm", color: "muted", children: ["/ 5 \u00B7 ", totalReviews, " reviews"] })] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search reviews...", 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, { paddingY: "y-lg", paddingX: "x-md", 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, context: "store" }, 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, context: "store" }, review.id))) })) }), _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: "public" }) })] }));
|
|
9
|
+
return (_jsx(ReviewsListingPanel, { source: { kind: "store", storeSlug }, stateMode: "url", context: "store", className: "min-h-screen" }));
|
|
111
10
|
}
|
|
@@ -617,9 +617,9 @@ export declare const storeListItemSchema: z.ZodObject<{
|
|
|
617
617
|
storeLogoURL?: string | undefined;
|
|
618
618
|
storeBannerURL?: string | undefined;
|
|
619
619
|
itemsSold?: number | undefined;
|
|
620
|
+
averageRating?: number | undefined;
|
|
620
621
|
totalProducts?: number | undefined;
|
|
621
622
|
totalReviews?: number | undefined;
|
|
622
|
-
averageRating?: number | undefined;
|
|
623
623
|
}, {
|
|
624
624
|
status: string;
|
|
625
625
|
id: string;
|
|
@@ -633,9 +633,9 @@ export declare const storeListItemSchema: z.ZodObject<{
|
|
|
633
633
|
storeLogoURL?: string | undefined;
|
|
634
634
|
storeBannerURL?: string | undefined;
|
|
635
635
|
itemsSold?: number | undefined;
|
|
636
|
+
averageRating?: number | undefined;
|
|
636
637
|
totalProducts?: number | undefined;
|
|
637
638
|
totalReviews?: number | undefined;
|
|
638
|
-
averageRating?: number | undefined;
|
|
639
639
|
}>;
|
|
640
640
|
export declare const storeListParamsSchema: z.ZodObject<{
|
|
641
641
|
category: z.ZodOptional<z.ZodString>;
|
|
@@ -34,7 +34,7 @@ export const couponsTesterSeedData = [
|
|
|
34
34
|
discount: { value: 10, maxDiscount: 100, minPurchase: 100 },
|
|
35
35
|
usage: { totalLimit: undefined, perUserLimit: 1, currentUsage: 0 },
|
|
36
36
|
validity: { startDate: daysAgo(1), endDate: daysAhead(7), isActive: true },
|
|
37
|
-
restrictions: { firstTimeUserOnly: false
|
|
37
|
+
restrictions: { firstTimeUserOnly: false },
|
|
38
38
|
createdBy: "user-admin-letitrip",
|
|
39
39
|
createdAt: daysAgo(1),
|
|
40
40
|
updatedAt: daysAgo(1),
|
|
@@ -50,7 +50,7 @@ export const couponsTesterSeedData = [
|
|
|
50
50
|
discount: { value: 50, maxDiscount: 50, minPurchase: 100 },
|
|
51
51
|
usage: { totalLimit: undefined, perUserLimit: 5, currentUsage: 0 },
|
|
52
52
|
validity: { startDate: daysAgo(30), endDate: daysAgo(1), isActive: true },
|
|
53
|
-
restrictions: { firstTimeUserOnly: false
|
|
53
|
+
restrictions: { firstTimeUserOnly: false },
|
|
54
54
|
createdBy: "user-admin-letitrip",
|
|
55
55
|
createdAt: daysAgo(30),
|
|
56
56
|
updatedAt: daysAgo(30),
|
|
@@ -67,7 +67,7 @@ export const couponsTesterSeedData = [
|
|
|
67
67
|
discount: { value: 5, maxDiscount: 50, minPurchase: 0 },
|
|
68
68
|
usage: { totalLimit: undefined, perUserLimit: 3, currentUsage: 0 },
|
|
69
69
|
validity: { startDate: daysAgo(1), endDate: daysAhead(7), isActive: true },
|
|
70
|
-
restrictions: { firstTimeUserOnly: false
|
|
70
|
+
restrictions: { firstTimeUserOnly: false },
|
|
71
71
|
createdBy: "user-admin-letitrip",
|
|
72
72
|
createdAt: daysAgo(1),
|
|
73
73
|
updatedAt: daysAgo(1),
|
|
@@ -7,3 +7,4 @@ export { eventsTesterSeedData } from "./events-tester-seed-data";
|
|
|
7
7
|
export { couponsTesterSeedData } from "./coupons-tester-seed-data";
|
|
8
8
|
export { bidsTesterSeedData } from "./bids-tester-seed-data";
|
|
9
9
|
export { ordersTesterSeedData } from "./orders-tester-seed-data";
|
|
10
|
+
export { offersTesterSeedData } from "./offers-tester-seed-data";
|
|
@@ -7,3 +7,4 @@ export { eventsTesterSeedData } from "./events-tester-seed-data";
|
|
|
7
7
|
export { couponsTesterSeedData } from "./coupons-tester-seed-data";
|
|
8
8
|
export { bidsTesterSeedData } from "./bids-tester-seed-data";
|
|
9
9
|
export { ordersTesterSeedData } from "./orders-tester-seed-data";
|
|
10
|
+
export { offersTesterSeedData } from "./offers-tester-seed-data";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* WHY: Shared tester sandbox — a pre-ACCEPTED offer for the tester persona.
|
|
3
|
+
* Needed because the offer lifecycle spans two parties: a tester can make
|
|
4
|
+
* an offer (buyer side) but cannot accept it, since acceptance belongs to
|
|
5
|
+
* the store that owns the listing (`store-beyblade-arena`, not the
|
|
6
|
+
* tester's own sandbox store). Without a pre-accepted fixture, the
|
|
7
|
+
* "checkout at the agreed price" and "offer flips to paid" cases are not
|
|
8
|
+
* reachable by a single tester at all.
|
|
9
|
+
* WHAT: Exports offersTesterSeedData — 1 Partial<OfferDocument> already in the
|
|
10
|
+
* `accepted` state, with a live `checkoutDeadline`, so the tester can
|
|
11
|
+
* drive it straight through the Offers checkout lane.
|
|
12
|
+
*
|
|
13
|
+
* Every date is Date.now()-relative (per CLAUDE.md's tester-fixture standards)
|
|
14
|
+
* so the 4-hour testerSandboxRefresh re-arms it instead of letting it go stale.
|
|
15
|
+
*
|
|
16
|
+
* EXPORTS:
|
|
17
|
+
* offersTesterSeedData — Array of 1 Partial<OfferDocument> for the seed runner
|
|
18
|
+
*
|
|
19
|
+
* @tag domain:offers,seller,tester
|
|
20
|
+
* @tag layer:seed
|
|
21
|
+
* @tag pattern:none
|
|
22
|
+
* @tag access:server-only
|
|
23
|
+
* @tag consumers:seed/manifest.ts,seed-cli.mjs
|
|
24
|
+
* @tag sideEffects:none
|
|
25
|
+
*/
|
|
26
|
+
import { OfferStatusValues } from "../../seller/schemas/firestore";
|
|
27
|
+
import { testDataExpiresAt } from "./tester-ttl";
|
|
28
|
+
const NOW = new Date();
|
|
29
|
+
const hoursAgo = (n) => new Date(NOW.getTime() - n * 3600000);
|
|
30
|
+
const hoursFromNow = (n) => new Date(NOW.getTime() + n * 3600000);
|
|
31
|
+
export const offersTesterSeedData = [
|
|
32
|
+
{
|
|
33
|
+
id: "offer-tester-sandbox-accepted",
|
|
34
|
+
productId: "product-beyblade-burst-valkyrie",
|
|
35
|
+
productTitle: "Beyblade Burst Valkyrie",
|
|
36
|
+
productSlug: "product-beyblade-burst-valkyrie",
|
|
37
|
+
storeId: "store-beyblade-arena",
|
|
38
|
+
storeName: "Beyblade Arena",
|
|
39
|
+
buyerUid: "user-tester-qa",
|
|
40
|
+
buyerName: "QA Tester",
|
|
41
|
+
buyerEmail: "tester@letitrip.in",
|
|
42
|
+
listedPrice: 1899,
|
|
43
|
+
offerAmount: 1450,
|
|
44
|
+
// The agreed price. Checkout MUST bill this, not `listedPrice` — that
|
|
45
|
+
// divergence is exactly what the paired checklist case checks.
|
|
46
|
+
lockedPrice: 1450,
|
|
47
|
+
currency: "INR",
|
|
48
|
+
status: OfferStatusValues.ACCEPTED,
|
|
49
|
+
buyerNote: "Sandbox offer — already accepted so testers can drive checkout.",
|
|
50
|
+
sellerNote: "Accepted for QA sandbox testing.",
|
|
51
|
+
expiresAt: hoursFromNow(24),
|
|
52
|
+
acceptedAt: hoursAgo(1),
|
|
53
|
+
respondedAt: hoursAgo(1),
|
|
54
|
+
// Well inside the 48h window so the lane is live for a whole test session.
|
|
55
|
+
checkoutDeadline: hoursFromNow(36),
|
|
56
|
+
createdAt: hoursAgo(2),
|
|
57
|
+
updatedAt: hoursAgo(1),
|
|
58
|
+
isTestData: true,
|
|
59
|
+
testDataExpiresAt: testDataExpiresAt(),
|
|
60
|
+
},
|
|
61
|
+
];
|