@mohasinac/appkit 4.30.16 → 4.30.17

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.
Files changed (97) hide show
  1. package/dist/_internal/shared/types/action-error-reporter.d.ts +40 -0
  2. package/dist/_internal/shared/types/action-error-reporter.js +27 -0
  3. package/dist/_internal/shared/types/action-result.d.ts +10 -0
  4. package/dist/_internal/shared/types/action-result.js +26 -0
  5. package/dist/client/api/surface-error.d.ts +6 -4
  6. package/dist/client/api/surface-error.js +19 -8
  7. package/dist/contracts/realtime-session.d.ts +27 -0
  8. package/dist/contracts/realtime-session.js +118 -0
  9. package/dist/core/unit-of-work.js +21 -13
  10. package/dist/errors/base-error.js +14 -1
  11. package/dist/errors/database-error.js +11 -1
  12. package/dist/errors/describe-cause.d.ts +5 -0
  13. package/dist/errors/describe-cause.js +76 -0
  14. package/dist/errors/error-display-map.d.ts +32 -1
  15. package/dist/errors/error-display-map.js +46 -0
  16. package/dist/errors/error-mapping.d.ts +47 -7
  17. package/dist/errors/error-mapping.js +46 -5
  18. package/dist/errors/index.d.ts +1 -0
  19. package/dist/errors/index.js +4 -0
  20. package/dist/features/about/components/PublicProfileView.js +4 -4
  21. package/dist/features/account/components/AddressBook.js +2 -2
  22. package/dist/features/account/components/AddressesIndexListing.js +2 -2
  23. package/dist/features/admin/components/AdminAnalyticsView.js +2 -2
  24. package/dist/features/admin/components/AdminCouponsView.js +2 -2
  25. package/dist/features/admin/components/AdminGuideHubView.js +2 -2
  26. package/dist/features/admin/components/AdminMediaView.js +2 -2
  27. package/dist/features/admin/components/AdminViewCards.js +2 -2
  28. package/dist/features/auctions/components/AuctionDetailPageView.js +1 -1
  29. package/dist/features/auctions/components/MarketplaceAuctionGrid.d.ts +1 -2
  30. package/dist/features/auctions/components/MarketplaceAuctionGrid.js +8 -7
  31. package/dist/features/auctions/components/PlaceBidFormClient.js +32 -15
  32. package/dist/features/before-after/components/BeforeAfterGallery.js +2 -2
  33. package/dist/features/blog/components/BlogIndexListing.js +3 -3
  34. package/dist/features/blog/components/BlogListView.js +3 -3
  35. package/dist/features/blog/components/BlogPostView.js +2 -2
  36. package/dist/features/categories/components/BundleCollage.js +2 -2
  37. package/dist/features/categories/components/CategoriesIndexListing.js +3 -3
  38. package/dist/features/categories/components/CategoryBundlesListing.js +2 -2
  39. package/dist/features/categories/components/CategoryGrid.js +2 -2
  40. package/dist/features/categories/components/CategoryProductsListing.js +2 -2
  41. package/dist/features/categories/components/CategoryStoresListing.js +2 -2
  42. package/dist/features/collections/components/CollectionCard.js +2 -2
  43. package/dist/features/events/components/EventRafflesSection.js +2 -2
  44. package/dist/features/events/components/EventsIndexListing.js +2 -2
  45. package/dist/features/events/components/EventsListView.js +3 -3
  46. package/dist/features/homepage/components/FeaturedBundlesSection.js +2 -2
  47. package/dist/features/homepage/components/GoogleReviewsSection.js +2 -2
  48. package/dist/features/homepage/components/HomepageSkeleton.js +1 -1
  49. package/dist/features/homepage/components/SecurityHighlightsSection.js +1 -1
  50. package/dist/features/homepage/components/SocialFeedSection.js +2 -2
  51. package/dist/features/homepage/components/StatsCounterSection.js +1 -1
  52. package/dist/features/homepage/components/TrustBadges.js +2 -2
  53. package/dist/features/homepage/components/TrustFeaturesSection.js +1 -1
  54. package/dist/features/messages/hooks/useConversation.js +39 -15
  55. package/dist/features/messages/hooks/useConversations.js +36 -14
  56. package/dist/features/messages/repository/conversations.repository.d.ts +23 -0
  57. package/dist/features/messages/repository/conversations.repository.js +53 -0
  58. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -3
  59. package/dist/features/products/components/AuctionsIndexListing.js +2 -3
  60. package/dist/features/products/components/PrizeDrawCollage.js +2 -2
  61. package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -3
  62. package/dist/features/products/components/PrizeDrawsSection.js +2 -2
  63. package/dist/features/products/components/ProductGrid.js +2 -7
  64. package/dist/features/products/components/ProductsIndexListing.js +3 -3
  65. package/dist/features/products/components/RelatedProducts.js +2 -2
  66. package/dist/features/promotions/components/CouponsIndexListing.js +1 -1
  67. package/dist/features/reviews/components/ReviewDetailPageView.js +2 -2
  68. package/dist/features/reviews/components/ReviewsListingPanel.js +2 -2
  69. package/dist/features/seller/components/SellerCouponsView.js +2 -2
  70. package/dist/features/seller/components/SellerDashboardView.js +3 -3
  71. package/dist/features/server-errors/schemas/firestore.d.ts +9 -0
  72. package/dist/features/stores/components/StoreAuctionsListing.js +2 -3
  73. package/dist/features/stores/components/StoreClassifiedsListing.js +2 -3
  74. package/dist/features/stores/components/StoreDigitalCodesListing.js +2 -3
  75. package/dist/features/stores/components/StoreGuideHubView.js +2 -2
  76. package/dist/features/stores/components/StoreLiveItemsListing.js +2 -3
  77. package/dist/features/stores/components/StorePreOrdersListing.js +2 -3
  78. package/dist/features/stores/components/StoreProductsListing.js +2 -2
  79. package/dist/features/stores/components/StoresIndexListing.js +3 -3
  80. package/dist/features/tester/seed-data/tester-checklist-seed-data.js +54 -1
  81. package/dist/next/api/routeHandler.js +27 -7
  82. package/dist/providers/db-firebase/base.repository.js +34 -18
  83. package/dist/react/hooks/useRealtimeEvent.js +22 -4
  84. package/dist/server.d.ts +2 -0
  85. package/dist/server.js +5 -0
  86. package/dist/styles.css +84 -20
  87. package/dist/tailwind-utilities.css +1 -1
  88. package/dist/tokens/tokens.css +7 -2
  89. package/dist/ui/components/Layout.d.ts +31 -18
  90. package/dist/ui/components/Layout.js +30 -19
  91. package/dist/ui/components/Layout.style.css +76 -17
  92. package/dist/ui/components/ListingLayout.d.ts +2 -0
  93. package/dist/ui/components/ListingLayout.js +16 -2
  94. package/dist/ui/components/StickyToolbar.js +3 -48
  95. package/dist/ui/components/toolbar-collapse.d.ts +44 -0
  96. package/dist/ui/components/toolbar-collapse.js +93 -0
  97. package/package.json +1 -1
@@ -133,6 +133,59 @@ export class ConversationsRepository {
133
133
  .get();
134
134
  return snap.docs.map((d) => normaliseDoc(d.id, d.data()));
135
135
  }
136
+ /**
137
+ * Conversation ids this user may subscribe to, for the RTDB `conversationIds`
138
+ * custom-token claim.
139
+ *
140
+ * 🛑 This claim previously had NO ISSUER ANYWHERE. The `chats/$conversationId`
141
+ * rule requires `auth.token.conversationIds[$conversationId] == true`, but
142
+ * `/api/realtime/token` only ever issued `chatIds`, built by
143
+ * `chatRepository.getChatIdsForUser()` over the **`chatRooms`** collection —
144
+ * a different collection with different document ids. So the buyer↔seller
145
+ * live-message channel was denied for every user, and both subscribing hooks
146
+ * swallowed the permission error (one into `setIsConnected(false)`, one into
147
+ * a literal `// ignore`). Messages only ever appeared for the sender, via an
148
+ * unrelated `refetch()` after send.
149
+ *
150
+ * `.select()` with no fields fetches ids only — the document bodies are not
151
+ * needed and this runs on every token issue.
152
+ *
153
+ * @param limit hard cap. Firebase custom claims are limited to 1000 BYTES
154
+ * total; each entry costs roughly `"<20-char id>":true,` ≈ 28 bytes, so a
155
+ * user in hundreds of conversations would make `createCustomToken` throw
156
+ * and take the whole route down (see the route's own comment).
157
+ */
158
+ async getConversationIdsForUser(userId, storeIds = [], limit = 25) {
159
+ const queries = [
160
+ this.collection()
161
+ .where(CONVERSATION_FIELDS.BUYER_ID, "==", userId)
162
+ .orderBy(CONVERSATION_FIELDS.LAST_MESSAGE_AT, "desc")
163
+ .limit(limit)
164
+ .select()
165
+ .get(),
166
+ // Firestore `in` caps at 30 values; a seller with more stores than that
167
+ // is not a shape this app produces.
168
+ ...(storeIds.length > 0
169
+ ? [
170
+ this.collection()
171
+ .where(CONVERSATION_FIELDS.STORE_ID, "in", storeIds.slice(0, 30))
172
+ .orderBy(CONVERSATION_FIELDS.LAST_MESSAGE_AT, "desc")
173
+ .limit(limit)
174
+ .select()
175
+ .get(),
176
+ ]
177
+ : []),
178
+ ];
179
+ const snaps = await Promise.all(queries);
180
+ const ids = new Set();
181
+ for (const snap of snaps) {
182
+ for (const doc of snap.docs)
183
+ ids.add(doc.id);
184
+ }
185
+ // Most-recent-first is already the sort order; truncating here keeps the
186
+ // claim inside the byte budget when a user is both buyer and seller.
187
+ return [...ids].slice(0, limit);
188
+ }
136
189
  /**
137
190
  * Append a message to a conversation in a transaction. Bumps the counterparty
138
191
  * unread counter; updates lastMessage / lastMessageAt / updatedAt.
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { Columns, Heart, ShoppingCart } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast, StickyToolbar } from "../../../ui";
7
+ import { BulkActionBar, Div, FilterDrawer, Grid, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, Text, useToast, StickyToolbar } from "../../../ui";
8
8
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
9
9
  import { useAuthGate } from "../../../react/hooks/useAuthGate";
10
10
  import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../../products/constants/action-defs";
@@ -133,7 +133,6 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
133
133
  showToast("Added to cart", "success");
134
134
  }, [localCart, showToast]);
135
135
  const selection = useBulkSelection({ items: preOrders, keyExtractor: (p) => p.id });
136
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
137
136
  const handleBulkAddToCart = useCallback(() => {
138
137
  const selected = preOrders.filter((p) => selection.selectedIdSet.has(p.id));
139
138
  selected.forEach((p) => {
@@ -227,7 +226,7 @@ export function PreOrdersIndexListing({ initialData, categorySlug, brandName })
227
226
  setCompareIds(ids);
228
227
  },
229
228
  },
230
- ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No pre-orders found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx(Div, { className: gridClass, children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
229
+ ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Grid, { cols: "cards", gap: "md", children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : preOrders.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No pre-orders found." })) : view === "list" ? (_jsx(Stack, { className: "divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)]", border: "subtle", rounded: "xl", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "list", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) : (_jsx(Grid, { cols: "cards", gap: "md", children: preOrders.map((product) => (_jsx(MarketplacePreorderCard, { product: product, variant: "grid", hrefBuilder: (p) => String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(p.id)), onAddToCart: handleAddToCart, wishlistActions: wishlistActions, selectable: selection.isSelecting, isSelected: selection.isSelected(product.id), onSelect: (id) => selection.toggle(id) }, product.id))) })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "preorder", onClose: () => {
231
230
  setCompareIds([]);
232
231
  selection.clearSelection();
233
232
  }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(PreOrderFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useCallback } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
5
  import { useProducts } from "../hooks/useProducts";
6
- import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
6
+ import { BulkActionBar, Div, FilterDrawer, Grid, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
7
7
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
8
8
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
9
9
  import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
@@ -112,7 +112,6 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
112
112
  },
113
113
  isWishlisted: (productId) => wishlistedIds.has(productId),
114
114
  };
115
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-3 gap-[var(--appkit-space-4)]";
116
115
  useBottomActions(selection.selectedCount > 0 ? { bulk: { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
117
116
  {
118
117
  id: ACTION_ID.WATCH_AUCTION,
@@ -156,5 +155,5 @@ export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
156
155
  selection.clearSelection();
157
156
  },
158
157
  },
159
- ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-[var(--appkit-space-4)]" : gridClass, wishlistActions: wishlistActions })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
158
+ ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Grid, { cols: "cards", gap: "md", children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", wishlistActions: wishlistActions })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9", categoryOptions: categoryOptions, brandOptions: brandOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
160
159
  }
@@ -16,7 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
16
16
  */
17
17
  import { Row } from "@mohasinac/appkit/client";
18
18
  import { useState } from "react";
19
- import { Button, Div, Scrim, Text } from "../../../ui";
19
+ import { Button, Div, Grid, Scrim, Text } from "../../../ui";
20
20
  import { MediaImage } from "../../media/MediaImage";
21
21
  import { ImageLightbox } from "../../../ui/components/ImageLightbox";
22
22
  const __P = {
@@ -51,7 +51,7 @@ export function PrizeDrawCollage({ items, highlightItemNumber, onItemClick, wonL
51
51
  return (_jsx(Div, { className: `border border-dashed border-[var(--appkit-color-border)] ${__P.p6} text-left`, rounded: "default", children: _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "No prizes configured yet." }) }));
52
52
  }
53
53
  const galleryImages = toGalleryImages(items);
54
- return (_jsxs(_Fragment, { children: [_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-2 sm:grid-cols-3 md:grid-cols-4", children: items.map((it, idx) => {
54
+ return (_jsxs(_Fragment, { children: [_jsx(Grid, { cols: "categoryCards", gap: "3", children: items.map((it, idx) => {
55
55
  const cover = it.images?.[0];
56
56
  const isHighlight = highlightItemNumber === it.itemNumber;
57
57
  return (_jsxs(Button, { variant: "ghost", type: "button", onClick: makePrizeItemClickHandler(it, idx, onItemClick, setLightboxIndex), rounded: "lg", paddingX: "none", paddingY: "none", className: [
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
5
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
6
6
  import { useProducts } from "../hooks/useProducts";
7
- import { Div, FilterDrawer, Label, ListingToolbar, Pagination, Row, Select, Stack, Text, StickyToolbar } from "../../../ui";
7
+ import { Div, FilterDrawer, Grid, Label, ListingToolbar, Pagination, Row, Select, Stack, Text, StickyToolbar } from "../../../ui";
8
8
  import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
9
9
  import { useBrands } from "../hooks/useBrands";
10
10
  import { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
@@ -82,7 +82,6 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
82
82
  if (e.key === "Enter")
83
83
  commitSearch();
84
84
  };
85
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
86
85
  return (_jsxs(Div, { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search prize draws...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, onSearchKeyDown: handleSearchKeyDown, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: PRIZE_DRAW_SORT_OPTIONS, onSortChange: (v) => {
87
86
  table.set(TABLE_KEYS.SORT, v);
88
87
  }, view: view, onViewChange: (v) => {
@@ -90,7 +89,7 @@ export function PrizeDrawsIndexListing({ initialData, categorySlug, brandName, s
90
89
  return;
91
90
  setView(v);
92
91
  table.set(TABLE_KEYS.VIEW, v);
93
- }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: PRIZE_DRAW_LISTING_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No prize draws found." })) : (_jsx(Div, { className: gridClass, children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsxs(Stack, { gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "prizeRevealStatusFilter", size: "xs", weight: "semibold", color: "muted", className: "block mb-1.5", children: "Reveal status" }), _jsx(Select, { id: "prizeRevealStatusFilter", options: [
92
+ }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: PRIZE_DRAW_LISTING_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Grid, { cols: "cards", gap: "md", children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : filteredDraws.length === 0 ? (_jsx(Text, { paddingY: "3xl", color: "muted", size: "sm", align: "start", children: "No prize draws found." })) : (_jsx(Grid, { cols: "cards", gap: "md", children: filteredDraws.map((product) => (_jsx(MarketplacePrizeDrawCard, { product: product, variant: view }, product.id))) })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsxs(Stack, { gap: "md", children: [_jsxs(_Fragment, { children: [_jsx(Label, { htmlFor: "prizeRevealStatusFilter", size: "xs", weight: "semibold", color: "muted", className: "block mb-1.5", children: "Reveal status" }), _jsx(Select, { id: "prizeRevealStatusFilter", options: [
94
93
  { value: "", label: "Any" },
95
94
  { value: "pending", label: "Reveal pending" },
96
95
  { value: "open", label: "Reveal open" },
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
4
4
  // a client-reference proxy and calling it throws during the server render
5
5
  // (Root Cause #18). That is exactly what took `section-prize-draws` down.
6
6
  import { sortBy } from "../../../constants/sort";
7
- import { Container, Div, Heading, Section, Stack, Text, } from "../../../ui";
7
+ import { Container, Grid, Heading, Section, Stack, Text } from "../../../ui";
8
8
  import { ROUTES } from "../../../next";
9
9
  import { listPublicProducts } from "../../../_internal/server/features/products/list-public";
10
10
  import { AVAILABILITY_VALUES, PRODUCT_FIELDS } from "../../../constants/field-names";
@@ -38,5 +38,5 @@ export async function PrizeDrawsSection({ config, }) {
38
38
  const draws = (result?.items ?? []);
39
39
  if (draws.length === 0)
40
40
  return null;
41
- return (_jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, size: "2xl", weight: "semibold", color: "primary", children: title }), subtitle ? (_jsx(Text, { size: "sm", color: "muted", children: subtitle })) : null] }), _jsx(Div, { gap: "3", className: "fluid-grid-card", children: draws.map((draw) => (_jsx(InteractiveProductCard, { product: draw, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(draw.slug ?? draw.id ?? "")) }, draw.id))) })] }) }) }));
41
+ return (_jsx(Section, { padding: "y-2xl", children: _jsx(Container, { size: "xl", children: _jsxs(Stack, { gap: "md", children: [_jsxs(Stack, { gap: "xs", children: [_jsx(Heading, { level: 2, size: "2xl", weight: "semibold", color: "primary", children: title }), subtitle ? (_jsx(Text, { size: "sm", color: "muted", children: subtitle })) : null] }), _jsx(Grid, { cols: "cardsWide", gap: "3", children: draws.map((draw) => (_jsx(InteractiveProductCard, { product: draw, href: String(ROUTES.PUBLIC.PRODUCT_DETAIL(draw.slug ?? draw.id ?? "")) }, draw.id))) })] }) }) }));
42
42
  }
@@ -123,10 +123,6 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
123
123
  }
124
124
  return cardBody;
125
125
  }
126
- // --- Grid class maps ---------------------------------------------------------
127
- const GRID_CLASSES = {
128
- card: "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-6)]",
129
- };
130
126
  function ProductListRow({ product, onClick, onAddToWishlist, isWishlisted, }) {
131
127
  const discount = product.originalPrice && product.originalPrice > product.price
132
128
  ? Math.round(((product.originalPrice - product.price) / product.originalPrice) *
@@ -196,7 +192,7 @@ export function ProductGrid({ products, renderCard, onProductClick, getProductHr
196
192
  return (_jsx(Stack, { className: `divide-y divide-zinc-100 divide-[var(--appkit-color-border-subtle)] ${className}`, border: "subtle", rounded: "xl", children: products.map((p) => (_jsx(ProductListRow, { product: p, onClick: onProductClick, onAddToWishlist: onWishlistToggle, isWishlisted: wishlistedIds?.has(p.id) ?? false }, p.id))) }));
197
193
  }
198
194
  if (view === "fluid") {
199
- return (_jsx(Grid, { cols: "productCardsCompact", className: className, children: products.map((p, i) => {
195
+ return (_jsx(Grid, { cols: "cards", className: className, children: products.map((p, i) => {
200
196
  const ctx = buildProductCardContext(p);
201
197
  const cardRenderer = renderCard ?? slots?.renderCard;
202
198
  return cardRenderer ? (_jsx(React.Fragment, { children: cardRenderer === renderCard
@@ -204,8 +200,7 @@ export function ProductGrid({ products, renderCard, onProductClick, getProductHr
204
200
  : slots.renderCard(p, i) }, p.id)) : (_jsx(ProductCard, { product: p, href: safeHref(getProductHref ? getProductHref(p) : undefined), onClick: onProductClick, onAddToWishlist: onWishlistToggle, onAddToCart: onAddToCart, onBuyNow: onBuyNow, isWishlisted: ctx.isWishlisted, selectionMode: selectionMode, isSelected: selectedIds?.has(p.id) ?? false, onSelect: onToggleSelect }, p.id));
205
201
  }) }));
206
202
  }
207
- const gridClass = GRID_CLASSES.card;
208
- return (_jsx(Div, { className: `${gridClass} ${className}`, children: products.map((p, i) => {
203
+ return (_jsx(Grid, { cols: "cards", gap: "lg", className: className, children: products.map((p, i) => {
209
204
  const ctx = buildProductCardContext(p);
210
205
  const cardRenderer = renderCard ?? slots?.renderCard;
211
206
  return cardRenderer ? (_jsx(React.Fragment, { children: cardRenderer === renderCard
@@ -5,7 +5,7 @@ import { ShoppingCart, Heart, Columns } from "lucide-react";
5
5
  import { useRouter } from "next/navigation";
6
6
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
7
7
  import { useProducts } from "../hooks/useProducts";
8
- import { BulkActionBar, Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
8
+ import { BulkActionBar, Div, FilterDrawer, Grid, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
9
9
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
10
10
  import { useAuthGate } from "../../../react/hooks/useAuthGate";
11
11
  import { ACTION_ID, ACTION_META, COMPARE_MAX_ITEMS } from "../constants/action-defs";
@@ -331,8 +331,8 @@ export function ProductsIndexListing({ initialData, listingTypes = GENERIC_PRODU
331
331
  setCompareIds(ids);
332
332
  },
333
333
  },
334
- ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4", children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => pluginFor(p.listingType ?? "standard").detailRoute(p.slug || p.id), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
334
+ ] }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Grid, { cols: "cards", gap: "md", children: Array.from({ length: 10 }).map((_, i) => (_jsxs(Div, { className: `${__O.hidden} animate-pulse`, border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/3", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(ProductGrid, { products: products, getProductHref: (p) => pluginFor(p.listingType ?? "standard").detailRoute(p.slug || p.id), view: view === "grid" ? "card" : "list", onWishlistToggle: handleWishlistToggle, wishlistedIds: wishlistedIds, onAddToCart: handleAddToCart, onBuyNow: handleBuyNow, selectionMode: selection.isSelecting, selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle })) }), _jsx(CompareOverlay, { isOpen: compareIds.length > 0, productIds: compareIds, productType: "product", onClose: () => {
335
335
  setCompareIds([]);
336
336
  selection.clearSelection();
337
- }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", listingTypeOptions: listingTypeOptions, listingTypeFooter: dedicatedPage ? (_jsx(Div, { padding: "x-md", className: "pb-[var(--appkit-space-3)]", children: _jsx(TextLink, { href: dedicatedPage.href, size: "sm", weight: "medium", children: dedicatedPage.label }) })) : null, categoryOptions: categoryOptions, brandOptions: brandOptions, featureOptions: featureOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
337
+ }, onRemove: (id) => setCompareIds((ids) => ids.filter((i) => i !== id)) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(ProductFilters, { table: pendingTable, currencyPrefix: "\u20B9", listingTypeOptions: listingTypeOptions, listingTypeFooter: dedicatedPage ? (_jsx(Div, { padding: "x-md", paddingY: "b-sm", children: _jsx(TextLink, { href: dedicatedPage.href, size: "sm", weight: "medium", children: dedicatedPage.label }) })) : null, categoryOptions: categoryOptions, brandOptions: brandOptions, featureOptions: featureOptions }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
338
338
  }
@@ -1,10 +1,10 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Div, Heading, Stack } from "../../../ui";
2
+ import { Div, Grid, Heading, Stack } from "../../../ui";
3
3
  export function RelatedProducts({ isLoading = false, isEmpty = false, renderGrid, renderSkeleton, renderTitle, labels = {}, className = "", }) {
4
4
  if (isLoading) {
5
5
  if (renderSkeleton)
6
6
  return _jsx(_Fragment, { children: renderSkeleton() });
7
- return (_jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 animate-pulse", children: Array.from({ length: 4 }).map((_, i) => (_jsx(Div, { className: "aspect-square", surface: "subtle", rounded: "xl" }, i))) }));
7
+ return (_jsx(Grid, { cols: "cards", gap: "md", className: "animate-pulse", children: Array.from({ length: 4 }).map((_, i) => (_jsx(Div, { className: "aspect-square", surface: "subtle", rounded: "xl" }, i))) }));
8
8
  }
9
9
  if (isEmpty)
10
10
  return null;
@@ -107,7 +107,7 @@ export function CouponsIndexListing({ initialCoupons, storeId, }) {
107
107
  const clearFilters = () => {
108
108
  table.setMany({ type: "", [TABLE_KEYS.DATE_FROM]: "", [TABLE_KEYS.DATE_TO]: "" });
109
109
  };
110
- return (_jsxs(Div, { className: "min-h-[40vh]", children: [_jsxs(StickyToolbar, { offset: "header", tone: "default", border: true, padding: "md", z: "above-toolbar", dismissible: true, id: "coupons-toolbar", children: [_jsxs(Row, { gap: "xs", className: "max-w-full", children: [_jsxs(Button, { type: "button", variant: hasActiveFilters ? "outline" : "ghost", onClick: openFilters, gap: "sm", rounded: "lg", paddingX: "md", paddingY: "sm", textSize: "sm", weight: "medium", className: hasActiveFilters ? "border-primary bg-primary-50 text-primary-700 dark:bg-primary-900/30 dark:text-primary-300 shrink-0" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] shrink-0", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsxs(Span, { className: "hidden sm:inline", children: ["Filters", hasActiveFilters ? " •" : ""] })] }), _jsxs(Row, { surface: "default", className: `flex-1 ${__O.hidden}`, border: "strong", rounded: "lg", children: [_jsx(Input, { bare: true, type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleKeyDown, placeholder: "Search by name or description\u2026", className: "min-w-0 flex-1 bg-transparent px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text)] placeholder-zinc-400 outline-none" }), searchInput && (_jsx(IconButton, { type: "button", onClick: () => { setSearchInput(""); table.set(TABLE_KEYS.QUERY, ""); }, "aria-label": "Clear search", variant: "ghost", size: "sm", children: _jsx(X, { className: "h-3.5 w-3.5" }) })), _jsx(IconButton, { type: "button", onClick: commitSearch, "aria-label": "Search", variant: "ghost", size: "sm", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs(Row, { color: "muted", textSize: "sm", gap: "xs", className: "shrink-0", children: [_jsx(Span, { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, onChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, options: COUPON_SORT_OPTIONS })] })] }), hasActiveFilters && (_jsxs(Row, { gap: "xs", wrap: true, className: "mt-2", children: [activeType && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: [COUPON_TYPES.find((t) => t.value === activeType)?.label ?? activeType, _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.TYPE, ""); }, "aria-label": "Remove type filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_FROM) && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: ["From: ", table.get(TABLE_KEYS.DATE_FROM), _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_FROM, ""); }, "aria-label": "Remove from-date filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_TO) && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: ["To: ", table.get(TABLE_KEYS.DATE_TO), _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_TO, ""); }, "aria-label": "Remove to-date filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), _jsx(Button, { variant: "ghost", type: "button", onClick: clearFilters, textSize: "xs", textColor: "muted", className: "underline", children: "Clear all" })] }))] }), _jsx(Div, { paddingY: "y-md", paddingX: "x-md", children: _jsx(CouponHelpDetails, {}) }), _jsxs(Div, { paddingY: "y-lg", paddingX: "x-md", children: [isLoading ? (_jsx(Grid, { gap: "sm", className: "md:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", gap: "sm", rounded: "xl", padding: "md", className: "animate-pulse", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : displayCoupons.length === 0 ? (_jsx(Div, { className: "text-left", padding: "y-4xl", children: _jsx(Text, { color: "faint", children: "No coupons match your search." }) })) : (_jsx(Grid, { gap: "sm", className: "md:grid-cols-2 lg:grid-cols-3", children: displayCoupons.map((coupon) => (_jsx(CouponCard, { coupon: coupon, labels: {
110
+ return (_jsxs(Div, { className: "min-h-[40vh]", children: [_jsxs(StickyToolbar, { offset: "header", tone: "default", border: true, padding: "md", z: "above-toolbar", dismissible: true, id: "coupons-toolbar", children: [_jsxs(Row, { gap: "xs", className: "max-w-full", children: [_jsxs(Button, { type: "button", variant: hasActiveFilters ? "outline" : "ghost", onClick: openFilters, gap: "sm", rounded: "lg", paddingX: "md", paddingY: "sm", textSize: "sm", weight: "medium", className: hasActiveFilters ? "border-primary bg-primary-50 text-primary-700 dark:bg-primary-900/30 dark:text-primary-300 shrink-0" : "border-[var(--appkit-color-border)] text-[var(--appkit-color-text-muted)] shrink-0", children: [_jsx(SlidersHorizontal, { className: "h-4 w-4" }), _jsxs(Span, { className: "hidden sm:inline", children: ["Filters", hasActiveFilters ? " •" : ""] })] }), _jsxs(Row, { surface: "default", className: `flex-1 ${__O.hidden}`, border: "strong", rounded: "lg", children: [_jsx(Input, { bare: true, type: "text", value: searchInput, onChange: (e) => setSearchInput(e.target.value), onKeyDown: handleKeyDown, placeholder: "Search by name or description\u2026", className: "min-w-0 flex-1 bg-transparent px-[var(--appkit-space-3)] py-[var(--appkit-space-2)] text-[length:var(--appkit-text-sm)] text-[var(--appkit-color-text)] placeholder-zinc-400 outline-none" }), searchInput && (_jsx(IconButton, { type: "button", onClick: () => { setSearchInput(""); table.set(TABLE_KEYS.QUERY, ""); }, "aria-label": "Clear search", variant: "ghost", size: "sm", children: _jsx(X, { className: "h-3.5 w-3.5" }) })), _jsx(IconButton, { type: "button", onClick: commitSearch, "aria-label": "Search", variant: "ghost", size: "sm", children: _jsx(Search, { className: "h-4 w-4" }) })] }), _jsxs(Row, { color: "muted", textSize: "sm", gap: "xs", className: "shrink-0", children: [_jsx(Span, { className: "hidden md:inline whitespace-nowrap", children: "Sort by" }), _jsx(SortDropdown, { value: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, onChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, options: COUPON_SORT_OPTIONS })] })] }), hasActiveFilters && (_jsxs(Row, { gap: "xs", wrap: true, className: "mt-2", children: [activeType && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: [COUPON_TYPES.find((t) => t.value === activeType)?.label ?? activeType, _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.TYPE, ""); }, "aria-label": "Remove type filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_FROM) && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: ["From: ", table.get(TABLE_KEYS.DATE_FROM), _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_FROM, ""); }, "aria-label": "Remove from-date filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), table.get(TABLE_KEYS.DATE_TO) && (_jsxs(Span, { layout: "flex", gap: "xs", size: "xs", weight: "medium", className: "bg-primary-50 dark:bg-primary-900/30 text-primary-700 dark:text-primary-300", rounded: "full", padding: "pill-sm-tall", children: ["To: ", table.get(TABLE_KEYS.DATE_TO), _jsx(Button, { variant: "ghost", type: "button", onClick: () => { table.set(TABLE_KEYS.DATE_TO, ""); }, "aria-label": "Remove to-date filter", className: CLS_CHIP_BTN, children: _jsx(X, { className: "h-3 w-3" }) })] })), _jsx(Button, { variant: "ghost", type: "button", onClick: clearFilters, textSize: "xs", textColor: "muted", className: "underline", children: "Clear all" })] }))] }), _jsx(Div, { paddingY: "y-md", paddingX: "x-md", children: _jsx(CouponHelpDetails, {}) }), _jsxs(Div, { paddingY: "y-lg", paddingX: "x-md", children: [isLoading ? (_jsx(Grid, { cols: "cardsWide", gap: "sm", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", gap: "sm", rounded: "xl", padding: "md", className: "animate-pulse", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : displayCoupons.length === 0 ? (_jsx(Div, { className: "text-left", padding: "y-4xl", children: _jsx(Text, { color: "faint", children: "No coupons match your search." }) })) : (_jsx(Grid, { cols: "cardsWide", gap: "sm", children: displayCoupons.map((coupon) => (_jsx(CouponCard, { coupon: coupon, labels: {
111
111
  copy: "Copy",
112
112
  copied: "Copied!",
113
113
  expires: "Expires",
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Link from "next/link";
3
3
  import { reviewRepository } from "../../../repositories";
4
- import { Container, Div, Heading, Main, Nav, Section, Span, Text } from "../../../ui";
4
+ import { Container, Div, Grid, Heading, Main, Nav, Section, Span, Text } from "../../../ui";
5
5
  import { ROUTES } from "../../../next";
6
6
  import { ReviewDetailShell } from "./ReviewDetailShell";
7
7
  import { ReviewCard } from "./ReviewsList";
@@ -20,5 +20,5 @@ export async function ReviewDetailPageView({ id }) {
20
20
  ]);
21
21
  const sameProductReviews = sameProductDocs.filter((r) => r.id !== review.id).slice(0, 6);
22
22
  const sameStoreReviews = sameStoreDocs.filter((r) => r.id !== review.id).slice(0, 6);
23
- return (_jsxs(Main, { children: [_jsx(Div, { paddingY: "y-xs-tall", className: "border-b border-neutral-100 bg-neutral-50", padding: "x-md", children: _jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "xs", color: "faint", className: "mx-auto max-w-3xl", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary transition-colors", children: "Home" }), _jsx(Span, { children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.REVIEWS), className: "hover:text-primary transition-colors", children: "Reviews" }), _jsx(Span, { children: "/" }), _jsx(Span, { className: "text-neutral-700 truncate max-w-[200px]", children: review.title ?? review.id })] }) }), _jsx(ReviewDetailShell, { review: review, storeHref: storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null }), sameProductReviews.length > 0 && (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsxs(Container, { size: "sm", children: [_jsxs(Heading, { level: 2, className: "mb-4", size: "lg", weight: "semibold", children: ["More reviews for ", review.productTitle ?? "this product"] }), _jsx(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: sameProductReviews.map((r) => (_jsx(ReviewCard, { review: r, context: "listing" }, r.id))) })] }) })), sameStoreReviews.length > 0 && (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsxs(Container, { size: "sm", children: [_jsxs(Heading, { level: 2, className: "mb-4", size: "lg", weight: "semibold", children: ["More reviews for ", review.storeName ?? "this store"] }), _jsx(Div, { layout: "grid", gap: "4", className: "sm:grid-cols-2", children: sameStoreReviews.map((r) => (_jsx(ReviewCard, { review: r, context: "store" }, r.id))) })] }) }))] }));
23
+ return (_jsxs(Main, { children: [_jsx(Div, { paddingY: "y-xs-tall", className: "border-b border-neutral-100 bg-neutral-50", padding: "x-md", children: _jsxs(Nav, { layout: "flex", gap: "2xs", textSize: "xs", color: "faint", className: "mx-auto max-w-3xl", "aria-label": "Breadcrumb", children: [_jsx(Link, { href: String(ROUTES.HOME), className: "hover:text-primary transition-colors", children: "Home" }), _jsx(Span, { children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.REVIEWS), className: "hover:text-primary transition-colors", children: "Reviews" }), _jsx(Span, { children: "/" }), _jsx(Span, { className: "text-neutral-700 truncate max-w-[200px]", children: review.title ?? review.id })] }) }), _jsx(ReviewDetailShell, { review: review, storeHref: storeSlug ? String(ROUTES.PUBLIC.STORE_DETAIL(storeSlug)) : null }), sameProductReviews.length > 0 && (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsxs(Container, { size: "sm", children: [_jsxs(Heading, { level: 2, className: "mb-4", size: "lg", weight: "semibold", children: ["More reviews for ", review.productTitle ?? "this product"] }), _jsx(Grid, { cols: "cardsWide", gap: "md", children: sameProductReviews.map((r) => (_jsx(ReviewCard, { review: r, context: "listing" }, r.id))) })] }) })), sameStoreReviews.length > 0 && (_jsx(Section, { padding: "y-lg", border: "subtle", className: "border-t", children: _jsxs(Container, { size: "sm", children: [_jsxs(Heading, { level: 2, className: "mb-4", size: "lg", weight: "semibold", children: ["More reviews for ", review.storeName ?? "this store"] }), _jsx(Grid, { cols: "cardsWide", gap: "md", children: sameStoreReviews.map((r) => (_jsx(ReviewCard, { review: r, context: "store" }, r.id))) })] }) }))] }));
24
24
  }
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useCallback, useMemo, useState } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
- import { Div, ListingFilterDrawer, ListingToolbar, Pagination, Row, Span, Stack, StickyToolbar, Text, } from "../../../ui";
5
+ import { Div, Grid, ListingFilterDrawer, ListingToolbar, Pagination, Row, Span, Stack, StickyToolbar, Text } from "../../../ui";
6
6
  import { ReviewCard } from "./ReviewsList";
7
7
  import { ReviewFilters, REVIEW_PUBLIC_SORT_OPTIONS } from "./ReviewFilters";
8
8
  import { useReviews } from "../hooks/useReviews";
@@ -222,5 +222,5 @@ export function ReviewsListingPanel({ source, pageSize, context = "general", sta
222
222
  const paginationBlock = totalPages > 1 ? (_jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })) : null;
223
223
  return (_jsxs(Div, { className: className, children: [showSummary && (totalReviews ?? 0) > 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 ?? 0).toFixed(1) }), _jsxs(Span, { size: "sm", color: "muted", children: ["/ 5 \u00B7 ", totalReviews, " review", totalReviews === 1 ? "" : "s"] })] })), _jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchEnabled ? searchInput : undefined, searchPlaceholder: "Search reviews by product name...", onSearchChange: searchEnabled ? setSearchInput : undefined, onSearchCommit: searchEnabled ? commitSearch : undefined, sortValue: effectiveSort, sortOptions: sortOptions, onSortChange: (v) => {
224
224
  table.set(TABLE_KEYS.SORT, v);
225
- }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), stateMode === "url" && paginationBlock && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: paginationBlock })), _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: emptyLabel })) : 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: context }, 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: context }, review.id))) })) }), stateMode === "local" && paginationBlock, _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: variant, keys: filterKeys }) })] }));
225
+ }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), stateMode === "url" && paginationBlock && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: paginationBlock })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Grid, { cols: "cards", gap: "lg", 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: emptyLabel })) : 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: context }, review.id))) })) : (_jsx(Grid, { cols: "cards", gap: "lg", children: reviews.map((review) => (_jsx(ReviewCard, { review: review, context: context }, review.id))) })) }), stateMode === "local" && paginationBlock, _jsx(ListingFilterDrawer, { open: filterOpen, onClose: () => setFilterOpen(false), onApply: applyFilters, onClear: clearFilters, activeCount: activeFilterCount, children: _jsx(ReviewFilters, { table: pendingTable, variant: variant, keys: filterKeys }) })] }));
226
226
  }
@@ -4,7 +4,7 @@ import { SIEVE_OP, Stack, sieveFilter } from "@mohasinac/appkit/client";
4
4
  import { sortBy } from "@mohasinac/appkit/client";
5
5
  import React, { useState, useCallback } from "react";
6
6
  import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
7
- import { Div, FilterChipGroup, ListingLayout, useToast } from "../../../ui";
7
+ import { Div, Grid, FilterChipGroup, ListingLayout, useToast } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
9
  import { toRecordArray, toStringValue, } from "../../admin/hooks/useAdminListingData";
10
10
  import { normalizeError } from "../../../errors/normalize";
@@ -83,7 +83,7 @@ export function SellerCouponsView({ onCreateClick, onEditClick, onToggle, onDele
83
83
  { id: "true", label: "Active" },
84
84
  { id: "false", label: "Inactive" },
85
85
  ], value: pendingFilters.isActive || "", onChange: (v) => setPendingFilters((p) => ({ ...p, isActive: v })) })),
86
- renderCards: (rows, _view, _selection, isLoading) => isLoading ? (_jsx(Div, { gap: "3", className: "fluid-grid-card", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", className: "animate-pulse", gap: "3", rounded: "xl", padding: "md", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : (_jsx(Div, { gap: "3", className: "fluid-grid-card", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: onEditClick ? handleEdit : undefined, onToggleActive: onToggle ? handleToggle : undefined, onDelete: onDelete ? handleDelete : undefined, className: togglingId === row.id || deletingId === row.id
86
+ renderCards: (rows, _view, _selection, isLoading) => isLoading ? (_jsx(Grid, { cols: "cardsWide", gap: "3", children: Array.from({ length: 6 }).map((_, i) => (_jsxs(Stack, { border: "skeleton", className: "animate-pulse", gap: "3", rounded: "xl", padding: "md", children: [_jsx(Div, { className: "h-6 w-2/3", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-full", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-3 w-1/2", surface: "subtle", rounded: "default" })] }, i))) })) : (_jsx(Grid, { cols: "cardsWide", gap: "3", children: rows.map((row) => (_jsx(CouponCard, { coupon: row.raw, onEdit: onEditClick ? handleEdit : undefined, onToggleActive: onToggle ? handleToggle : undefined, onDelete: onDelete ? handleDelete : undefined, className: togglingId === row.id || deletingId === row.id
87
87
  ? "pointer-events-none opacity-60"
88
88
  : undefined }, row.id))) })),
89
89
  };
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { StackedViewShell, Div, Text } from "../../../ui";
3
+ import { StackedViewShell, Div, Grid, Text } from "../../../ui";
4
4
  function DefaultStatsPlaceholder({ isLoading }) {
5
- return (_jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-2 sm:grid-cols-4", children: [1, 2, 3, 4].map((i) => (_jsx(Div, { padding: "5", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "xl", children: isLoading ? (_jsxs(_Fragment, { children: [_jsx(Div, { className: "h-3 w-16 animate-pulse bg-[var(--appkit-color-border)]", rounded: "default" }), _jsx(Div, { className: "mt-3 h-6 w-20 animate-pulse bg-[var(--appkit-color-border)]", rounded: "default" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { size: "xs", variant: "secondary", className: "tracking-widest", weight: "semibold", transform: "uppercase", children: ["Stat ", i] }), _jsx(Text, { size: "xl", className: "mt-2 tabular-nums", weight: "bold", children: "\u2014" })] })) }, i))) }));
5
+ return (_jsx(Grid, { cols: "statTiles", gap: "md", children: [1, 2, 3, 4].map((i) => (_jsx(Div, { padding: "5", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "xl", children: isLoading ? (_jsxs(_Fragment, { children: [_jsx(Div, { className: "h-3 w-16 animate-pulse bg-[var(--appkit-color-border)]", rounded: "default" }), _jsx(Div, { className: "mt-3 h-6 w-20 animate-pulse bg-[var(--appkit-color-border)]", rounded: "default" })] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { size: "xs", variant: "secondary", className: "tracking-widest", weight: "semibold", transform: "uppercase", children: ["Stat ", i] }), _jsx(Text, { size: "xl", className: "mt-2 tabular-nums", weight: "bold", children: "\u2014" })] })) }, i))) }));
6
6
  }
7
7
  function DefaultQuickActionsPlaceholder() {
8
- return (_jsx(Div, { layout: "grid", gap: "3", className: "grid-cols-2 sm:grid-cols-4", children: [1, 2, 3, 4].map((i) => (_jsx(Div, { className: "h-12 animate-pulse border border-[var(--appkit-color-border)] bg-[var(--appkit-color-border)]", rounded: "xl" }, i))) }));
8
+ return (_jsx(Grid, { cols: "statTiles", gap: "3", children: [1, 2, 3, 4].map((i) => (_jsx(Div, { className: "h-12 animate-pulse border border-[var(--appkit-color-border)] bg-[var(--appkit-color-border)]", rounded: "xl" }, i))) }));
9
9
  }
10
10
  export function SellerDashboardView({ labels = {}, renderStats, renderQuickActions, renderRecentListings, renderRevenueChart, renderTopProducts, isLoading = false, ...rest }) {
11
11
  const stats = (renderStats ?? ((busy) => _jsx(DefaultStatsPlaceholder, { isLoading: busy })))(isLoading);
@@ -16,6 +16,15 @@ export type ServerErrorDocument = {
16
16
  message: string;
17
17
  /** Truncated stack (≤4 KB) — never the raw request body. */
18
18
  stack?: string;
19
+ /**
20
+ * Flattened `Error.cause` chain (≤4 KB), one `caused by: …` block per link.
21
+ *
22
+ * Load-bearing, not a nicety: every AppError in this codebase is a WRAP, and
23
+ * `AppError` calls `Error.captureStackTrace(this, this.constructor)` — so
24
+ * `stack` above begins at the catch block that did the wrapping and the
25
+ * frames naming the real fault exist ONLY here.
26
+ */
27
+ cause?: string;
19
28
  /** React error boundary componentStack (client source only). */
20
29
  componentStack?: string;
21
30
  requestId: string;
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
5
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { Div, FilterDrawer, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
7
+ import { Div, FilterDrawer, Grid, ListingToolbar, LoginRequiredModal, Pagination, Row, Stack, useToast, StickyToolbar } from "../../../ui";
8
8
  import { useAuthGate } from "../../../react/hooks/useAuthGate";
9
9
  import { ACTION_ID } from "../../products/constants/action-defs";
10
10
  import { MarketplaceAuctionGrid } from "../../auctions/components/MarketplaceAuctionGrid";
@@ -113,7 +113,6 @@ export function StoreAuctionsListing({ storeId, initialData }) {
113
113
  },
114
114
  isWishlisted: (productId) => wishlistedIds.has(productId),
115
115
  };
116
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
117
116
  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) => {
118
117
  table.set("sort", v);
119
118
  }, view: view, onViewChange: (v) => {
@@ -121,7 +120,7 @@ export function StoreAuctionsListing({ storeId, initialData }) {
121
120
  return;
122
121
  setView(v);
123
122
  table.set("view", v);
124
- }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_AUCTION_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Div, { className: gridClass, children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", gridClassName: view === "list" ? "flex flex-col gap-[var(--appkit-space-4)]" : gridClass, wishlistActions: wishlistActions, labels: {
123
+ }, onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_AUCTION_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: isLoading ? (_jsx(Grid, { cols: "cards", gap: "md", children: Array.from({ length: 8 }).map((_, i) => (_jsxs(Div, { className: "overflow-hidden animate-pulse", border: "subtle", rounded: "xl", children: [_jsx(Div, { className: "aspect-square", surface: "subtle" }), _jsxs(Stack, { className: `${__P.p3}`, gap: "sm", children: [_jsx(Div, { className: "h-3 w-3/4", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-4 w-1/2", surface: "subtle", rounded: "default" }), _jsx(Div, { className: "h-8", surface: "subtle", rounded: "default" })] })] }, i))) })) : (_jsx(MarketplaceAuctionGrid, { auctions: auctions, variant: view === "list" ? "list" : "grid", wishlistActions: wishlistActions, labels: {
125
124
  emptyTitle: "No auctions yet",
126
125
  emptyDescription: "This store has no active auctions.",
127
126
  } })) }), _jsx(FilterDrawer, { open: filterOpen, onOpen: openFilters, onClose: () => setFilterOpen(false), onApply: applyFilters, onReset: onFilterClear, activeCount: filterActiveCount, hideTrigger: true, children: _jsx(AuctionFilters, { table: pendingTable, currencyPrefix: "\u20B9" }) }), _jsx(LoginRequiredModal, { isOpen: modalOpen, onClose: closeModal, message: modalMessage })] }));
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
5
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
7
+ import { Div, FilterDrawer, Grid, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
8
8
  import { ClassifiedFilters } from "../../classified/components/ClassifiedFilters";
9
9
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
10
10
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
@@ -56,8 +56,7 @@ export function StoreClassifiedsListing({ storeId, initialData }) {
56
56
  availability,
57
57
  };
58
58
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
59
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
60
- return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store classifieds\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_CLASSIFIED_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
59
+ return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search store classifieds\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_CLASSIFIED_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Grid, { cols: "cards", gap: "md", children: isLoading
61
60
  ? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
62
61
  : products.length === 0
63
62
  ? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No classified listings in this store yet." }) })
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
5
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
7
+ import { Div, FilterDrawer, Grid, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
8
8
  import { DigitalCodeFilters } from "../../digital-codes/components/DigitalCodeFilters";
9
9
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
10
10
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
@@ -54,8 +54,7 @@ export function StoreDigitalCodesListing({ storeId, initialData }) {
54
54
  availability,
55
55
  };
56
56
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
57
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
58
- return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_DIGITAL_CODE_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
57
+ return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search digital codes\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_DIGITAL_CODE_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Grid, { cols: "cards", gap: "md", children: isLoading
59
58
  ? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
60
59
  : products.length === 0
61
60
  ? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No digital codes in this store yet." }) })
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Package, ShoppingBag, Wallet, Settings, Shield, Headphones, CheckCircle2, Circle, BookOpen, MessageSquare, } from "lucide-react";
3
- import { Div, Heading, Progress, Row, Section, Stack, Text, TextLink } from "../../../ui";
3
+ import { Div, Grid, Heading, Progress, Row, Section, Stack, Text, TextLink } from "../../../ui";
4
4
  import { ROUTES } from "../../../next/routing/route-map";
5
5
  const CLS_CHECK_ICON = "w-5 h-5 flex-shrink-0 text-emerald-500";
6
6
  const CLS_SUCCESS_BOX = "rounded-xl border border-emerald-200 bg-emerald-50 dark:bg-emerald-950/30 dark:border-emerald-800 px-[var(--appkit-space-5)] py-[var(--appkit-space-4)]";
@@ -83,7 +83,7 @@ export function StoreGuideHubView({ store }) {
83
83
  const storeName = store?.storeName ?? "Your Store";
84
84
  const steps = buildChecklist(store);
85
85
  const completedCount = steps.filter((s) => s.done).length;
86
- return (_jsxs(Stack, { gap: "xl", padding: "b-2xl", children: [_jsx(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", shadow: "sm", children: _jsxs(Div, { paddingX: "x-md-xl", paddingY: "y-xl", className: "[background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_60%,var(--appkit-color-secondary-400)_100%)]", children: [_jsxs(Row, { className: "mb-3", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10", surface: "default", align: "center", justify: "center", rounded: "xl", children: _jsx(BookOpen, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { color: "inverse", className: "/80 tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Seller Guide" })] }), _jsx(Heading, { color: "inverse", level: 1, className: "mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: storeName }), _jsx(Text, { color: "inverse", className: "/80", size: "base", children: "Everything you need to know about selling on LetItRip." })] }) }), _jsxs(Div, { layout: "grid", gap: "8", className: "grid-cols-1 lg:grid-cols-3", children: [_jsxs(Stack, { className: "lg:col-span-2", gap: "md", children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)]", size: "lg", weight: "semibold", children: "Guides" }), _jsx(Div, { layout: "grid", gap: "4", className: "grid-cols-1 sm:grid-cols-2", children: GUIDE_CARDS.map(({ Icon, title, description, href }) => (_jsxs(TextLink, { variant: "bare", href: href, rounded: "xl", layout: "flex-col", gap: "md", paddingX: "lg", paddingY: "lg", shadow: "hover-md", className: "group border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] hover:border-[var(--appkit-color-primary)] transition-all", children: [_jsxs(Row, { align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-9 h-9 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "lg", children: _jsx(Icon, { className: "w-4 h-4 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text)] group-hover:text-[var(--appkit-color-primary)] transition-colors", weight: "semibold", children: title })] }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] leading-relaxed", size: "sm", children: description }), _jsx(Text, { className: "text-[var(--appkit-color-primary)] mt-auto", size: "xs", weight: "medium", children: "Read guide \u2192" })] }, title))) })] }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)]", size: "lg", weight: "semibold", children: "Getting started" }), _jsxs(Div, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "xl", children: [_jsxs(Div, { className: "px-[1.25rem] pt-[1.25rem] border-b border-[var(--appkit-color-border)]", padding: "b-md", children: [_jsxs(Row, { className: "mb-2", align: "center", justify: "between", children: [_jsxs(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "medium", children: [completedCount, " of ", steps.length, " complete"] }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: [Math.round((completedCount / steps.length) * 100), "%"] })] }), _jsx(Progress, { value: completedCount, max: steps.length, size: "sm", variant: "gradient" })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)]", children: steps.map(({ label, done, href }) => (_jsxs(TextLink, { variant: "bare", href: done ? "#" : href, "aria-disabled": done, layout: "flex", align: "center", gap: "sm", paddingX: "lg", paddingY: "sm-tall", size: "sm", className: `transition-colors ${done
86
+ return (_jsxs(Stack, { gap: "xl", padding: "b-2xl", children: [_jsx(Section, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "2xl", shadow: "sm", children: _jsxs(Div, { paddingX: "x-md-xl", paddingY: "y-xl", className: "[background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_60%,var(--appkit-color-secondary-400)_100%)]", children: [_jsxs(Row, { className: "mb-3", align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-10 h-10", surface: "default", align: "center", justify: "center", rounded: "xl", children: _jsx(BookOpen, { className: "w-5 h-5 text-white" }) }), _jsx(Text, { color: "inverse", className: "/80 tracking-widest", size: "sm", weight: "semibold", transform: "uppercase", children: "Seller Guide" })] }), _jsx(Heading, { color: "inverse", level: 1, className: "mb-2", mdSize: "3xl", size: "2xl", weight: "bold", children: storeName }), _jsx(Text, { color: "inverse", className: "/80", size: "base", children: "Everything you need to know about selling on LetItRip." })] }) }), _jsxs(Div, { layout: "grid", gap: "8", className: "grid-cols-1 lg:grid-cols-3", children: [_jsxs(Stack, { className: "lg:col-span-2", gap: "md", children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)]", size: "lg", weight: "semibold", children: "Guides" }), _jsx(Grid, { cols: "cardsWide", gap: "md", children: GUIDE_CARDS.map(({ Icon, title, description, href }) => (_jsxs(TextLink, { variant: "bare", href: href, rounded: "xl", layout: "flex-col", gap: "md", paddingX: "lg", paddingY: "lg", shadow: "hover-md", className: "group border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] hover:border-[var(--appkit-color-primary)] transition-all", children: [_jsxs(Row, { align: "center", gap: "3", children: [_jsx(Row, { className: "flex-shrink-0 w-9 h-9 [background:linear-gradient(135deg,var(--appkit-color-primary-700)_0%,var(--appkit-color-cobalt)_100%)]", align: "center", justify: "center", rounded: "lg", children: _jsx(Icon, { className: "w-4 h-4 text-white" }) }), _jsx(Text, { className: "text-[var(--appkit-color-text)] group-hover:text-[var(--appkit-color-primary)] transition-colors", weight: "semibold", children: title })] }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)] leading-relaxed", size: "sm", children: description }), _jsx(Text, { className: "text-[var(--appkit-color-primary)] mt-auto", size: "xs", weight: "medium", children: "Read guide \u2192" })] }, title))) })] }), _jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 2, className: "text-[var(--appkit-color-text)]", size: "lg", weight: "semibold", children: "Getting started" }), _jsxs(Div, { overflow: "hidden", className: "border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)]", rounded: "xl", children: [_jsxs(Div, { className: "px-[1.25rem] pt-[1.25rem] border-b border-[var(--appkit-color-border)]", padding: "b-md", children: [_jsxs(Row, { className: "mb-2", align: "center", justify: "between", children: [_jsxs(Text, { className: "text-[var(--appkit-color-text)]", size: "sm", weight: "medium", children: [completedCount, " of ", steps.length, " complete"] }), _jsxs(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: [Math.round((completedCount / steps.length) * 100), "%"] })] }), _jsx(Progress, { value: completedCount, max: steps.length, size: "sm", variant: "gradient" })] }), _jsx(Div, { className: "divide-y divide-[var(--appkit-color-border)]", children: steps.map(({ label, done, href }) => (_jsxs(TextLink, { variant: "bare", href: done ? "#" : href, "aria-disabled": done, layout: "flex", align: "center", gap: "sm", paddingX: "lg", paddingY: "sm-tall", size: "sm", className: `transition-colors ${done
87
87
  ? "cursor-default"
88
88
  : "hover:bg-[var(--appkit-color-border)]/30"}`, children: [done ? (_jsx(CheckCircle2, { className: CLS_CHECK_ICON })) : (_jsx(Circle, { className: "w-5 h-5 flex-shrink-0 text-[var(--appkit-color-text-muted)]" })), _jsx(Text, { className: `leading-snug ${done
89
89
  ? "line-through text-[var(--appkit-color-text-muted)]"
@@ -4,7 +4,7 @@ import { useState, useCallback } from "react";
4
4
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
5
5
  import { usePendingTable } from "../../../react/hooks/usePendingTable";
6
6
  import { useProducts } from "../../products/hooks/useProducts";
7
- import { Div, FilterDrawer, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
7
+ import { Div, FilterDrawer, Grid, ListingToolbar, Pagination, Row, Text, StickyToolbar } from "../../../ui";
8
8
  import { LiveItemFilters } from "../../live/components/LiveItemFilters";
9
9
  import { InteractiveProductCard } from "../../products/components/InteractiveProductCard";
10
10
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
@@ -56,8 +56,7 @@ export function StoreLiveItemsListing({ storeId, initialData }) {
56
56
  availability,
57
57
  };
58
58
  const { products, totalPages, page, isLoading } = useProducts(params, { initialData });
59
- const gridClass = "grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-[var(--appkit-space-4)]";
60
- return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live animals & plants\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_LIVE_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Div, { className: gridClass, children: isLoading
59
+ return (_jsxs(Div, { className: "min-h-[200px]", children: [_jsx(ListingToolbar, { filterCount: filterActiveCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search live animals & plants\u2026", onSearchChange: setSearchInput, onSearchCommit: () => table.set("q", searchInput.trim()), sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => table.set("sort", v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(Div, { padding: "y-sm", children: _jsx(AvailabilityTabs, { types: STORE_LIVE_TYPES }) }), totalPages > 1 && (_jsx(StickyToolbar, { offset: "header+pagination", tone: "translucent", border: true, padding: "toolbar", children: _jsx(Row, { justify: "center", children: _jsx(Pagination, { currentPage: page, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) }) })), _jsx(Div, { padding: "y-lg", children: _jsx(Grid, { cols: "cards", gap: "md", children: isLoading
61
60
  ? Array.from({ length: 8 }).map((_, i) => _jsx(Div, { surface: "muted", rounded: "xl", className: "aspect-square animate-pulse" }, i))
62
61
  : products.length === 0
63
62
  ? _jsx(Div, { paddingY: "y-4xl", className: "col-span-full text-left", children: _jsx(Text, { color: "muted", children: "No live items in this store yet." }) })