@mohasinac/appkit 2.7.53 → 2.7.55

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 (197) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/server/jobs/core/auctionSettlement.js +6 -3
  3. package/dist/_internal/server/jobs/core/bundleStockSync.js +9 -7
  4. package/dist/_internal/server/jobs/core/onProductStockChange.d.ts +12 -3
  5. package/dist/_internal/server/jobs/core/onProductStockChange.js +12 -13
  6. package/dist/_internal/server/jobs/core/onProductWrite.js +1 -3
  7. package/dist/_internal/server/jobs/handlers/onProductStockChange.js +11 -2
  8. package/dist/_internal/server/jobs/runtime/adapters/firebase.js +5 -14
  9. package/dist/_internal/shared/actions/action-registry.js +827 -4
  10. package/dist/_internal/shared/features/events/schema.d.ts +4 -4
  11. package/dist/client.d.ts +2 -0
  12. package/dist/client.js +1 -0
  13. package/dist/constants/api-endpoints.d.ts +3 -0
  14. package/dist/constants/api-endpoints.js +1 -0
  15. package/dist/constants/field-names.d.ts +4 -3
  16. package/dist/constants/field-names.js +8 -8
  17. package/dist/errors/error-codes.d.ts +3 -0
  18. package/dist/errors/error-codes.js +3 -0
  19. package/dist/errors/messages.d.ts +3 -0
  20. package/dist/errors/messages.js +3 -0
  21. package/dist/features/account/components/UserOffersPanel.js +2 -1
  22. package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -3
  23. package/dist/features/admin/components/AdminBidsView.js +5 -3
  24. package/dist/features/admin/components/AdminBlogView.js +3 -2
  25. package/dist/features/admin/components/AdminBrandsView.js +2 -1
  26. package/dist/features/admin/components/AdminBundleEditorView.js +4 -5
  27. package/dist/features/admin/components/AdminBundlesView.js +3 -3
  28. package/dist/features/admin/components/AdminCarouselView.d.ts +2 -1
  29. package/dist/features/admin/components/AdminCarouselView.js +14 -5
  30. package/dist/features/admin/components/AdminCategoriesView.js +2 -1
  31. package/dist/features/admin/components/AdminContactView.d.ts +4 -1
  32. package/dist/features/admin/components/AdminContactView.js +27 -7
  33. package/dist/features/admin/components/AdminCouponsView.d.ts +3 -1
  34. package/dist/features/admin/components/AdminCouponsView.js +17 -3
  35. package/dist/features/admin/components/AdminFaqsView.d.ts +3 -1
  36. package/dist/features/admin/components/AdminFaqsView.js +18 -4
  37. package/dist/features/admin/components/AdminFeaturesView.js +2 -1
  38. package/dist/features/admin/components/AdminMediaView.js +3 -2
  39. package/dist/features/admin/components/AdminNavigationView.js +3 -2
  40. package/dist/features/admin/components/AdminNewsletterView.d.ts +2 -1
  41. package/dist/features/admin/components/AdminNewsletterView.js +13 -4
  42. package/dist/features/admin/components/AdminNotificationsView.js +6 -4
  43. package/dist/features/admin/components/AdminOrdersView.js +4 -3
  44. package/dist/features/admin/components/AdminPayoutsView.js +2 -2
  45. package/dist/features/admin/components/AdminPrizeDrawsView.js +2 -1
  46. package/dist/features/admin/components/AdminProductsView.js +3 -3
  47. package/dist/features/admin/components/AdminReviewsView.js +4 -3
  48. package/dist/features/admin/components/AdminScammersView.js +2 -1
  49. package/dist/features/admin/components/AdminSessionsView.js +5 -3
  50. package/dist/features/admin/components/AdminStoresView.js +2 -2
  51. package/dist/features/admin/components/AdminSublistingCategoriesView.d.ts +5 -1
  52. package/dist/features/admin/components/AdminSublistingCategoriesView.js +12 -3
  53. package/dist/features/admin/components/AdminSupportTicketsView.js +2 -1
  54. package/dist/features/admin/components/AdminTeamView.d.ts +2 -1
  55. package/dist/features/admin/components/AdminTeamView.js +13 -4
  56. package/dist/features/admin/components/AdminUsersView.js +2 -2
  57. package/dist/features/admin/types/product.types.d.ts +1 -1
  58. package/dist/features/admin/types/product.types.js +2 -3
  59. package/dist/features/auctions/actions/bid-actions.d.ts +9 -0
  60. package/dist/features/auctions/actions/bid-actions.js +61 -0
  61. package/dist/features/auctions/components/AuctionDetailPageView.d.ts +2 -1
  62. package/dist/features/auctions/components/AuctionDetailPageView.js +5 -4
  63. package/dist/features/auctions/components/PlaceBidFormClient.d.ts +3 -1
  64. package/dist/features/auctions/components/PlaceBidFormClient.js +32 -2
  65. package/dist/features/auctions/types/index.d.ts +1 -1
  66. package/dist/features/layout/AppLayoutShell.js +1 -1
  67. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +2 -11
  68. package/dist/features/products/api/[id]/route.js +4 -8
  69. package/dist/features/products/api/route.js +1 -3
  70. package/dist/features/products/components/AuctionsIndexListing.js +3 -11
  71. package/dist/features/products/components/PrizeDrawsIndexListing.js +2 -10
  72. package/dist/features/products/components/ProductFilters.d.ts +33 -9
  73. package/dist/features/products/components/ProductFilters.js +6 -29
  74. package/dist/features/products/components/ProductForm.js +2 -3
  75. package/dist/features/products/constants/action-defs.d.ts +86 -7
  76. package/dist/features/products/constants/action-defs.js +99 -7
  77. package/dist/features/products/constants/sieve.d.ts +162 -0
  78. package/dist/features/products/constants/sieve.js +95 -0
  79. package/dist/features/products/repository/products.repository.d.ts +5 -0
  80. package/dist/features/products/repository/products.repository.js +23 -3
  81. package/dist/features/products/schemas/firestore.d.ts +4 -4
  82. package/dist/features/products/schemas/firestore.js +2 -3
  83. package/dist/features/products/schemas/index.d.ts +6 -6
  84. package/dist/features/products/schemas/index.js +2 -6
  85. package/dist/features/products/types/index.d.ts +1 -1
  86. package/dist/features/seller/actions/offer-actions.js +2 -2
  87. package/dist/features/seller/components/QuickProductForm.d.ts +13 -0
  88. package/dist/features/seller/components/QuickProductForm.js +50 -0
  89. package/dist/features/seller/components/SellerAuctionsView.d.ts +4 -1
  90. package/dist/features/seller/components/SellerAuctionsView.js +37 -3
  91. package/dist/features/seller/components/SellerBidsView.js +2 -1
  92. package/dist/features/seller/components/SellerBundlesView.d.ts +3 -1
  93. package/dist/features/seller/components/SellerBundlesView.js +38 -5
  94. package/dist/features/seller/components/SellerClassifiedView.d.ts +3 -1
  95. package/dist/features/seller/components/SellerClassifiedView.js +37 -4
  96. package/dist/features/seller/components/SellerDigitalCodesView.d.ts +3 -1
  97. package/dist/features/seller/components/SellerDigitalCodesView.js +37 -4
  98. package/dist/features/seller/components/SellerGoogleReviewsView.js +2 -1
  99. package/dist/features/seller/components/SellerGroupedListingsView.js +3 -2
  100. package/dist/features/seller/components/SellerLiveView.d.ts +3 -1
  101. package/dist/features/seller/components/SellerLiveView.js +37 -4
  102. package/dist/features/seller/components/SellerOffersPanel.js +2 -1
  103. package/dist/features/seller/components/SellerOffersView.d.ts +4 -1
  104. package/dist/features/seller/components/SellerOffersView.js +8 -3
  105. package/dist/features/seller/components/SellerPayoutsView.d.ts +3 -1
  106. package/dist/features/seller/components/SellerPayoutsView.js +7 -3
  107. package/dist/features/seller/components/SellerPreOrdersView.d.ts +2 -1
  108. package/dist/features/seller/components/SellerPreOrdersView.js +30 -7
  109. package/dist/features/seller/components/SellerPrizeDrawsView.d.ts +2 -1
  110. package/dist/features/seller/components/SellerPrizeDrawsView.js +30 -7
  111. package/dist/features/seller/components/SellerProductShell.js +12 -0
  112. package/dist/features/seller/components/SellerReviewsView.js +2 -1
  113. package/dist/features/seller/components/SellerShippingConfigsView.js +3 -2
  114. package/dist/features/seller/components/SellerStoreCategoriesView.js +6 -5
  115. package/dist/features/seller/components/SellerTemplatesView.js +4 -3
  116. package/dist/features/whatsapp-bot/components/SellerWhatsAppSettingsView.js +30 -16
  117. package/dist/features/wishlist/types/index.d.ts +1 -1
  118. package/dist/index.d.ts +6 -3
  119. package/dist/index.js +6 -4
  120. package/dist/seed/addresses-seed-data.d.ts +2 -18
  121. package/dist/seed/addresses-seed-data.js +137 -203
  122. package/dist/seed/bids-seed-data.d.ts +2 -8
  123. package/dist/seed/bids-seed-data.js +172 -648
  124. package/dist/seed/blog-posts-seed-data.d.ts +0 -5
  125. package/dist/seed/blog-posts-seed-data.js +341 -596
  126. package/dist/seed/carousel-slides-seed-data.d.ts +0 -5
  127. package/dist/seed/carousel-slides-seed-data.js +61 -192
  128. package/dist/seed/carousels-seed-data.js +18 -1
  129. package/dist/seed/cart-seed-data.d.ts +1 -18
  130. package/dist/seed/cart-seed-data.js +116 -293
  131. package/dist/seed/categories-seed-data.d.ts +0 -6
  132. package/dist/seed/categories-seed-data.js +1409 -1537
  133. package/dist/seed/claimed-coupons-seed-data.d.ts +0 -11
  134. package/dist/seed/claimed-coupons-seed-data.js +32 -29
  135. package/dist/seed/conversations-seed-data.d.ts +0 -6
  136. package/dist/seed/conversations-seed-data.js +132 -280
  137. package/dist/seed/coupon-usage-seed-data.d.ts +0 -16
  138. package/dist/seed/coupon-usage-seed-data.js +30 -44
  139. package/dist/seed/coupons-seed-data.d.ts +0 -6
  140. package/dist/seed/coupons-seed-data.js +141 -489
  141. package/dist/seed/events-seed-data.d.ts +0 -6
  142. package/dist/seed/events-seed-data.js +201 -379
  143. package/dist/seed/faq-seed-data.d.ts +0 -14
  144. package/dist/seed/faq-seed-data.js +609 -1184
  145. package/dist/seed/grouped-listings-seed-data.d.ts +119 -8
  146. package/dist/seed/grouped-listings-seed-data.js +148 -233
  147. package/dist/seed/history-seed-data.d.ts +0 -7
  148. package/dist/seed/history-seed-data.js +50 -54
  149. package/dist/seed/homepage-sections-seed-data.d.ts +0 -5
  150. package/dist/seed/homepage-sections-seed-data.js +83 -175
  151. package/dist/seed/index.d.ts +1 -1
  152. package/dist/seed/index.js +1 -1
  153. package/dist/seed/manifest.js +2 -2
  154. package/dist/seed/notifications-seed-data.d.ts +0 -6
  155. package/dist/seed/notifications-seed-data.js +309 -196
  156. package/dist/seed/offers-seed-data.d.ts +0 -8
  157. package/dist/seed/offers-seed-data.js +118 -197
  158. package/dist/seed/orders-seed-data.d.ts +2 -8
  159. package/dist/seed/orders-seed-data.js +248 -1510
  160. package/dist/seed/payouts-seed-data.d.ts +0 -6
  161. package/dist/seed/payouts-seed-data.js +102 -441
  162. package/dist/seed/products-auctions-seed-data.d.ts +1 -14
  163. package/dist/seed/products-auctions-seed-data.js +612 -1084
  164. package/dist/seed/products-preorders-seed-data.d.ts +119 -14
  165. package/dist/seed/products-preorders-seed-data.js +154 -455
  166. package/dist/seed/products-standard-seed-data.d.ts +1 -15
  167. package/dist/seed/products-standard-seed-data.js +1355 -4128
  168. package/dist/seed/reviews-seed-data.d.ts +2 -8
  169. package/dist/seed/reviews-seed-data.js +99 -1182
  170. package/dist/seed/scammers-seed-data.d.ts +0 -15
  171. package/dist/seed/scammers-seed-data.js +36 -37
  172. package/dist/seed/sessions-seed-data.d.ts +0 -13
  173. package/dist/seed/sessions-seed-data.js +58 -335
  174. package/dist/seed/site-settings-seed-data.d.ts +0 -4
  175. package/dist/seed/site-settings-seed-data.js +61 -52
  176. package/dist/seed/store-addresses-seed-data.js +27 -151
  177. package/dist/seed/store-extensions-seed-data.d.ts +0 -7
  178. package/dist/seed/store-extensions-seed-data.js +147 -261
  179. package/dist/seed/stores-seed-data.d.ts +0 -5
  180. package/dist/seed/stores-seed-data.js +222 -184
  181. package/dist/seed/sub-listings-seed-data.d.ts +119 -0
  182. package/dist/seed/sub-listings-seed-data.js +447 -0
  183. package/dist/seed/support-tickets-seed-data.d.ts +0 -7
  184. package/dist/seed/support-tickets-seed-data.js +82 -75
  185. package/dist/seed/users-seed-data.d.ts +0 -5
  186. package/dist/seed/users-seed-data.js +279 -634
  187. package/dist/seed/wishlists-seed-data.d.ts +0 -9
  188. package/dist/seed/wishlists-seed-data.js +36 -52
  189. package/dist/seo/json-ld.js +6 -4
  190. package/dist/styles.css +8052 -3
  191. package/dist/tailwind-utilities.css +1 -1
  192. package/dist/utils/index.d.ts +1 -0
  193. package/dist/utils/index.js +1 -0
  194. package/dist/utils/search-tokens.d.ts +2 -0
  195. package/dist/utils/search-tokens.js +21 -0
  196. package/package.json +2 -2
  197. package/scripts/seed-cli.mjs +2 -2
@@ -1,4 +1,5 @@
1
1
  import type { ListingViewShellProps } from "../../../ui";
2
2
  export interface AdminTeamViewProps extends ListingViewShellProps {
3
+ onBulkRemove?: (ids: string[]) => Promise<void>;
3
4
  }
4
- export declare function AdminTeamView({ children, ...props }: AdminTeamViewProps): import("react/jsx-runtime").JSX.Element;
5
+ export declare function AdminTeamView({ children, onBulkRemove, ...props }: AdminTeamViewProps): import("react/jsx-runtime").JSX.Element;
@@ -4,8 +4,9 @@ import React, { useState, useCallback } from "react";
4
4
  import { X, UserPlus } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, } from "../../../ui";
7
+ import { BulkActionBar, Button, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu, } from "../../../ui";
8
8
  import { ADMIN_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
10
  import { ALL_TAB } from "../constants/filter-tabs";
10
11
  import { toRecordArray, toRelativeDate, toStringValue, useAdminListingData, } from "../hooks/useAdminListingData";
11
12
  import { DataTable } from "./DataTable";
@@ -48,7 +49,7 @@ function TeamFilterDrawer({ filterOpen, setFilterOpen, activeFilterCount, clearF
48
49
  return null;
49
50
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: "fixed inset-0 z-40 bg-black/40", "aria-hidden": "true", onClick: () => setFilterOpen(false) }), _jsxs("div", { className: "fixed inset-y-0 left-0 z-50 flex w-80 flex-col bg-white dark:bg-slate-900 shadow-2xl", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: [_jsx("span", { className: "text-base font-semibold text-zinc-900 dark:text-zinc-100", children: "Filters" }), _jsxs("div", { className: "flex items-center gap-2", children: [activeFilterCount > 0 && (_jsx("button", { type: "button", onClick: clearFilters, className: "text-xs text-zinc-500 hover:text-rose-500 dark:text-zinc-400 transition-colors", children: "Clear all" })), _jsx("button", { type: "button", onClick: () => setFilterOpen(false), "aria-label": "Close", className: "rounded-lg p-1.5 text-zinc-500 hover:bg-zinc-100 dark:hover:bg-slate-800 transition-colors", children: _jsx(X, { className: "h-5 w-5" }) })] })] }), _jsx("div", { className: "flex-1 overflow-y-auto px-4 py-4", children: _jsx(FilterChipGroup, { label: "Permission Group", tabs: GROUP_TABS, value: pendingGroup, onChange: (id) => setPendingGroup(id) }) }), _jsx("div", { className: "border-t border-zinc-200 dark:border-slate-700 px-4 py-3.5", children: _jsxs("button", { type: "button", onClick: applyFilters, className: "w-full rounded-lg bg-primary py-2.5 text-sm font-semibold text-white hover:bg-primary-600 transition-colors active:scale-[0.98]", children: ["Apply Filters", activeFilterCount > 0 ? ` (${activeFilterCount})` : ""] }) })] })] }));
50
51
  }
51
- export function AdminTeamView({ children, ...props }) {
52
+ export function AdminTeamView({ children, onBulkRemove, ...props }) {
52
53
  const hasChildren = React.Children.count(children) > 0;
53
54
  const [view, setView] = useState("table");
54
55
  const table = useUrlTable({
@@ -130,14 +131,22 @@ export function AdminTeamView({ children, ...props }) {
130
131
  const currentPage = table.getNumber("page", 1);
131
132
  const totalPages = Math.ceil(total / PAGE_SIZE);
132
133
  const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
134
+ const bulkActions = [
135
+ ...(onBulkRemove ? [{
136
+ id: "bulk-remove",
137
+ label: ACTIONS.ADMIN["remove-team-member"].label,
138
+ variant: "danger",
139
+ onClick: async () => { await onBulkRemove(selection.selectedIds); selection.clearSelection(); },
140
+ }] : []),
141
+ ];
133
142
  if (hasChildren) {
134
143
  return (_jsx(ListingViewShell, { portal: "admin", ...props, children: children }));
135
144
  }
136
145
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by name or email", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => {
137
146
  table.set("sort", v);
138
- }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] }) }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No employees found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
147
+ }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { type: "button", variant: "primary", onClick: openInvite, className: "flex items-center gap-1.5 whitespace-nowrap", children: [_jsx(UserPlus, { className: "h-4 w-4" }), "Invite Employee"] }) }), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), _jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No employees found", renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
139
148
  {
140
- label: "Edit Permissions",
149
+ label: ACTIONS.ADMIN["edit-team-member"].label,
141
150
  onClick: () => openEdit(row),
142
151
  },
143
152
  ] })) })] }), _jsx(TeamFilterDrawer, { filterOpen: filterOpen, setFilterOpen: setFilterOpen, activeFilterCount: activeFilterCount, clearFilters: clearFilters, pendingGroup: pendingGroup, setPendingGroup: setPendingGroup, applyFilters: applyFilters })] }), _jsx(AdminEmployeeEditorView, { open: drawerOpen, onClose: () => setDrawerOpen(false), mode: inviteMode ? "invite" : "edit", userId: selectedRow?.id, displayName: selectedRow?.primary, currentPermissionGroup: toStringValue(selectedRow?._raw?.permissionGroup, "custom"), currentPermissions: Array.isArray(selectedRow?._raw?.permissions)
@@ -147,13 +147,13 @@ export function AdminUsersView({ children, ...props }) {
147
147
  return _jsx(ListingViewShell, { portal: "admin", ...props, children: children });
148
148
  }
149
149
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search users, email, or seller handles", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState }), _jsx(BulkActionBar, { selectedCount: selection.selectedCount, onClearSelection: selection.clearSelection, actions: [
150
- { id: "manage", label: "Manage Selected", variant: "primary", onClick: () => { setSelectedRow(rows.find(r => r.id === selection.selectedIds[0]) ?? null); setDrawerOpen(true); selection.clearSelection(); } },
150
+ { id: "manage", label: ACTIONS.ADMIN["manage-user"].label, variant: "primary", onClick: () => { setSelectedRow(rows.find(r => r.id === selection.selectedIds[0]) ?? null); setDrawerOpen(true); selection.clearSelection(); } },
151
151
  ] }), totalPages > 1 && (_jsx("div", { className: "sticky top-[calc(var(--header-height,0px)+44px)] z-10 flex justify-center bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700 px-3 py-1.5", children: _jsx(Pagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: (p) => table.setPage(p) }) })), _jsxs("div", { className: "py-4 px-3 sm:px-4", children: [errorMessage && (_jsx("div", { className: "mb-4 rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-200", children: errorMessage })), view === "table" ? (_jsx(DataTable, { rows: rows, isLoading: isLoading, emptyLabel: "No users found", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: (next) => next ? selection.setSelectedIds(rows.map(r => r.id)) : selection.clearSelection(), renderRowActions: (row) => {
152
152
  const ur = row;
153
153
  const isBanned = ur.status === "Hard banned";
154
154
  return (_jsx(RowActionMenu, { actions: [
155
155
  {
156
- label: "Manage",
156
+ label: ACTIONS.ADMIN["manage-user"].label,
157
157
  onClick: () => { setSelectedRow(ur); setDrawerOpen(true); },
158
158
  },
159
159
  {
@@ -4,7 +4,7 @@
4
4
  * These types extend/complement the core ProductItem with admin-only fields
5
5
  * for managing auctions, pre-orders, stock, pricing, and lifecycle.
6
6
  */
7
- export type AdminProductStatus = "draft" | "published" | "out_of_stock" | "discontinued" | "sold";
7
+ export type AdminProductStatus = "draft" | "published" | "in_review" | "archived";
8
8
  export type AdminProductDrawerMode = "create" | "edit" | "delete" | null;
9
9
  /**
10
10
  * Admin product form/management type.
@@ -7,7 +7,6 @@
7
7
  export const ADMIN_PRODUCT_STATUS_OPTIONS = [
8
8
  { value: "draft", label: "Draft" },
9
9
  { value: "published", label: "Published" },
10
- { value: "out_of_stock", label: "Out of Stock" },
11
- { value: "discontinued", label: "Discontinued" },
12
- { value: "sold", label: "Sold" },
10
+ { value: "in_review", label: "In Review" },
11
+ { value: "archived", label: "Archived" },
13
12
  ];
@@ -15,6 +15,15 @@ export interface PlaceBidResult {
15
15
  bid: BidDocument;
16
16
  }
17
17
  export declare function placeBid(userId: string, userEmail: string, input: PlaceBidInput): Promise<PlaceBidResult>;
18
+ export interface BuyNowAuctionInput {
19
+ productId: string;
20
+ }
21
+ export interface BuyNowAuctionResult {
22
+ orderId: string;
23
+ amount: number;
24
+ currency: string;
25
+ }
26
+ export declare function buyNowAuction(userId: string, userName: string, userEmail: string, input: BuyNowAuctionInput): Promise<BuyNowAuctionResult>;
18
27
  export declare function listBidsByProduct(productId: string, params?: {
19
28
  page?: number;
20
29
  pageSize?: number;
@@ -26,6 +26,9 @@ export async function placeBid(userId, userEmail, input) {
26
26
  throw new NotFoundError(ERROR_MESSAGES.BID.AUCTION_NOT_FOUND);
27
27
  if (!isAuctionListing(product))
28
28
  throw new ValidationError(ERROR_MESSAGES.BID.NOT_AN_AUCTION);
29
+ if (product.isSold) {
30
+ throw new ValidationError(ERROR_MESSAGES.BID.AUCTION_ENDED, { code: BID_ERROR_CODES.AUCTION_ENDED });
31
+ }
29
32
  if (product.auctionEndDate) {
30
33
  const endDate = resolveDate(product.auctionEndDate);
31
34
  if (endDate && endDate.getTime() < Date.now()) {
@@ -152,6 +155,64 @@ export async function placeBid(userId, userEmail, input) {
152
155
  });
153
156
  return { bid };
154
157
  }
158
+ export async function buyNowAuction(userId, userName, userEmail, input) {
159
+ const { productId } = input;
160
+ const product = await productRepository.findById(productId);
161
+ if (!product)
162
+ throw new NotFoundError(ERROR_MESSAGES.BID.AUCTION_NOT_FOUND);
163
+ if (!isAuctionListing(product))
164
+ throw new ValidationError(ERROR_MESSAGES.BID.NOT_AN_AUCTION);
165
+ if (product.isSold) {
166
+ throw new ValidationError(ERROR_MESSAGES.BID.AUCTION_ENDED, { code: BID_ERROR_CODES.AUCTION_ENDED });
167
+ }
168
+ if (product.auctionEndDate) {
169
+ const endDate = resolveDate(product.auctionEndDate);
170
+ if (endDate && endDate.getTime() < Date.now()) {
171
+ throw new ValidationError(ERROR_MESSAGES.BID.AUCTION_ENDED, { code: BID_ERROR_CODES.AUCTION_ENDED });
172
+ }
173
+ }
174
+ if (product.storeId) {
175
+ const store = await storeRepository.findById(product.storeId);
176
+ if (store?.ownerId === userId) {
177
+ throw new AuthorizationError(ERROR_MESSAGES.BID.OWN_AUCTION);
178
+ }
179
+ }
180
+ const buyNowPrice = typeof product.buyNowPrice === "number" ? product.buyNowPrice : null;
181
+ if (buyNowPrice === null || buyNowPrice <= 0) {
182
+ throw new ValidationError(ERROR_MESSAGES.BID.BUY_NOW_NO_PRICE, { code: BID_ERROR_CODES.BUY_NOW_NO_PRICE });
183
+ }
184
+ if (product.bidsHaveStarted === true) {
185
+ throw new ValidationError(ERROR_MESSAGES.BID.BUY_NOW_BIDS_STARTED, { code: BID_ERROR_CODES.BUY_NOW_BIDS_STARTED });
186
+ }
187
+ const currency = getDefaultCurrency();
188
+ let orderId = "";
189
+ await unitOfWork.runBatch((batch) => {
190
+ const orderRef = unitOfWork.orders.createFromAuction(batch, {
191
+ productId,
192
+ productTitle: product.title,
193
+ userId,
194
+ userName,
195
+ userEmail,
196
+ storeId: product.storeId,
197
+ amount: buyNowPrice,
198
+ currency,
199
+ auctionProductId: productId,
200
+ });
201
+ orderId = orderRef.id;
202
+ unitOfWork.products.updateInBatch(batch, productId, {
203
+ isSold: true,
204
+ availableQuantity: 0,
205
+ auctionEndDate: new Date(),
206
+ });
207
+ });
208
+ serverLogger.info("buyNowAuction", {
209
+ orderId,
210
+ productId,
211
+ userId,
212
+ amount: buyNowPrice,
213
+ });
214
+ return { orderId, amount: buyNowPrice, currency };
215
+ }
155
216
  export async function listBidsByProduct(productId, params) {
156
217
  const result = await bidRepository.list({
157
218
  filters: `productId==${productId}`,
@@ -10,7 +10,8 @@ export interface AuctionDetailPageViewProps {
10
10
  */
11
11
  initialAuction?: import("../../products/schemas/firestore").ProductDocument | null;
12
12
  onPlaceBid?: (input: PlaceBidInput) => Promise<unknown>;
13
+ onBuyNow?: () => Promise<unknown>;
13
14
  /** SSR-loaded productFeatures (platform + store-scope). See ProductDetailPageView for semantics. */
14
15
  productFeatures?: ProductFeatureDocument[];
15
16
  }
16
- export declare function AuctionDetailPageView({ id, initialAuction, onPlaceBid, productFeatures }: AuctionDetailPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
17
+ export declare function AuctionDetailPageView({ id, initialAuction, onPlaceBid, onBuyNow, productFeatures }: AuctionDetailPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
@@ -40,7 +40,7 @@ function renderAuctionStoreReviews(storeReviews) {
40
40
  const avg = storeReviews.reduce((s, r) => s + r.rating, 0) / storeReviews.length;
41
41
  return (_jsxs(Section, { className: "mt-10", children: [_jsx(Heading, { level: 2, className: "mb-2 text-xl font-semibold text-zinc-900 dark:text-zinc-50", children: "Store Reviews" }), _jsxs(Div, { className: "mb-4 flex items-center gap-3", children: [_jsx(Span, { className: "text-3xl font-bold text-zinc-900 dark:text-zinc-50", children: avg.toFixed(1) }), _jsxs(Div, { children: [_jsx(Row, { gap: "xs", children: [1, 2, 3, 4, 5].map((star) => _jsx(Span, { className: star <= Math.round(avg) ? "text-amber-400" : "text-zinc-200 dark:text-zinc-700", children: "\u2605" }, star)) }), _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: [storeReviews.length, " review", storeReviews.length !== 1 ? "s" : ""] })] })] }), _jsx(Stack, { gap: "sm", children: storeReviews.slice(0, 10).map((review) => (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-white dark:bg-zinc-900/60 p-4 space-y-1.5", children: [_jsx(Row, { justify: "between", align: "center", children: _jsxs(Row, { gap: "xs", align: "center", children: [_jsx(Span, { className: "text-sm font-medium text-zinc-800 dark:text-zinc-200", children: review.userName }), _jsx(Row, { gap: "xs", children: [1, 2, 3, 4, 5].map((star) => _jsx(Span, { className: `text-xs ${star <= review.rating ? "text-amber-400" : "text-zinc-200 dark:text-zinc-700"}`, children: "\u2605" }, star)) })] }) }), review.title && _jsx(Text, { className: "text-sm font-semibold text-zinc-800 dark:text-zinc-100", children: review.title }), _jsx(Text, { className: "text-sm text-zinc-600 dark:text-zinc-400 leading-relaxed", children: review.comment }), _jsx(Text, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: review.productTitle })] }, review.id))) })] }));
42
42
  }
43
- export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, productFeatures }) {
43
+ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, onBuyNow, productFeatures }) {
44
44
  const product = initialAuction !== undefined
45
45
  ? (initialAuction ?? undefined)
46
46
  : await productRepository.findByIdOrSlug(id).catch(() => undefined);
@@ -79,6 +79,7 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, pr
79
79
  const isEnded = endDate ? endDate < new Date() : false;
80
80
  const bidCount = typeof p.bidCount === "number" ? p.bidCount : 0;
81
81
  const buyNowPrice = typeof p.buyNowPrice === "number" ? p.buyNowPrice : null;
82
+ const bidsHaveStarted = p.bidsHaveStarted === true;
82
83
  const condition = typeof p.condition === "string" ? p.condition : null;
83
84
  const featured = p.featured === true;
84
85
  const shippingPaidBy = p.shippingPaidBy;
@@ -116,9 +117,9 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, pr
116
117
  price: typeof p.price === "number" ? p.price : undefined,
117
118
  storeId: typeof p.storeId === "string" ? p.storeId : undefined,
118
119
  storeName: typeof p.storeName === "string" ? p.storeName : undefined,
119
- } }), _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between flex-wrap gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "text-zinc-700 dark:text-zinc-300 truncate max-w-[200px]", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => renderAuctionInfoPanel({ title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Div, { id: "auction-bid-form", className: "space-y-3", children: _jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-3", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(Span, { className: "text-xl font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { className: "text-xs text-zinc-500", children: [bidCount, " ", bidCount === 1 ? "bid" : "bids", " \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, triggerClassName: "w-full" })] }) })) : (
120
+ } }), _jsxs(Container, { size: "xl", className: "px-4 py-6", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between flex-wrap gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-1.5 text-xs text-zinc-500 dark:text-zinc-400 flex-wrap", children: [_jsx(Link, { href: String(ROUTES.HOME), className: CLS_BREADCRUMB_LINK, children: "Home" }), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.AUCTIONS), className: CLS_BREADCRUMB_LINK, children: "Auctions" }), category && (_jsxs(_Fragment, { children: [_jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(category)), className: CLS_BREADCRUMB_LINK, children: categoryName || category })] })), _jsx(Span, { "aria-hidden": true, children: "/" }), _jsx(Span, { className: "text-zinc-700 dark:text-zinc-300 truncate max-w-[200px]", children: title })] }), _jsx(ShareButton, { title: title })] }), _jsx(AuctionDetailView, { renderGallery: () => (_jsx(ProductGalleryClient, { images: images, productName: title })), renderInfo: () => renderAuctionInfoPanel({ title, currentBid, currency, bidCount, isEnded, endDate, buyNowPrice, featured, freeShipping, condition, category, categoryName, brand, brandSlug, productFeatures, features, descriptionHtml, safeSeller, storeHref }), renderBidForm: () => onPlaceBid ? (_jsx(Div, { id: "auction-bid-form", className: "space-y-3", children: _jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-3", children: [_jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(Span, { className: "text-xl font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { className: "text-xs text-zinc-500", children: [bidCount, " ", bidCount === 1 ? "bid" : "bids", " \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] }) })) : (
120
121
  /* Read-only bid panel — shown when no bid action is wired (preview/demo) */
121
- _jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [_jsx(Div, { className: "space-y-1", children: _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Starting bid: ", formatCurrency(startingBid, currency), " \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(currentBid + minBidIncrement, currency)}`, min: currentBid + minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Div, { className: "lg:hidden rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-4 space-y-3", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { className: "text-base font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { className: "text-xs text-zinc-500", children: [bidCount, " bids"] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-4 lg:hidden", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { className: "text-base font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { className: "text-xs text-zinc-500", children: [bidCount, " bids"] })] }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
122
+ _jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [_jsx(Div, { className: "space-y-1", children: _jsxs(Text, { className: "text-xs text-zinc-500 dark:text-zinc-400", children: ["Starting bid: ", formatCurrency(startingBid, currency), " \u00B7 min increment ", formatCurrency(minBidIncrement, currency)] }) }), _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", placeholder: `At least ${formatCurrency(currentBid + minBidIncrement, currency)}`, min: currentBid + minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded, children: isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && !bidsHaveStarted && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", disabled: true, children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) }))] })), renderMobileBidForm: () => !isEnded && onPlaceBid ? (_jsxs(Div, { className: "lg:hidden rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-4 space-y-3", children: [_jsxs(Row, { align: "center", gap: "sm", children: [_jsx(Span, { className: "text-base font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { className: "text-xs text-zinc-500", children: [bidCount, " bids"] })] }), _jsx(PlaceBidModalButton, { productId: String(product.id), currentBid: currentBid, startingBid: startingBid, minBidIncrement: minBidIncrement, currency: currency, isEnded: isEnded, buyNowPrice: buyNowPrice, bidsHaveStarted: bidsHaveStarted, bidCount: bidCount, tags: tags, onPlaceBid: onPlaceBid, onBuyNow: onBuyNow, triggerClassName: "w-full" })] })) : !isEnded ? (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-4 lg:hidden", children: [_jsxs(Row, { align: "center", gap: "sm", className: "mb-3", children: [_jsx(Span, { className: "text-base font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsxs(Span, { className: "text-xs text-zinc-500", children: [bidCount, " bids"] })] }), _jsx(Button, { variant: "primary", size: "md", className: "w-full", children: "Place Bid" })] })) : null, renderSublistingSection: sublistingCategoryId
122
123
  ? () => (_jsx(SublistingCarouselSection, { sublistingCategoryId: sublistingCategoryId, currentListingId: String(product.id) }))
123
124
  : undefined, renderTabs: () => (_jsx(ProductTabsShell, { descriptionContent: descriptionHtml ? (_jsx(RichText, { html: descriptionHtml, proseClass: "prose prose-sm sm:prose max-w-none dark:prose-invert", className: "text-zinc-700 dark:text-zinc-300" })) : undefined, specsContent: specs.length > 0 ? (_jsx("dl", { className: "divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800 overflow-hidden text-sm", children: specs.map((s, i) => (_jsxs("div", { className: "flex gap-4 px-4 py-3 bg-white dark:bg-zinc-900 even:bg-zinc-50 dark:even:bg-zinc-800/50", children: [_jsx("dt", { className: "w-36 flex-shrink-0 font-medium text-zinc-700 dark:text-zinc-300", children: s.name }), _jsxs("dd", { className: "flex-1 text-zinc-600 dark:text-zinc-400", children: [s.value, s.unit ? ` ${s.unit}` : ""] })] }, i))) })) : undefined, customTabs: customSections.map((s) => ({
124
125
  id: s.id,
@@ -140,7 +141,7 @@ export async function AuctionDetailPageView({ id, initialAuction, onPlaceBid, pr
140
141
  if (r.id === product.id || r.listingType !== "auction")
141
142
  return false;
142
143
  const s = r.status;
143
- if (s && ["sold", "out_of_stock", "archived", "discontinued", "draft"].includes(s))
144
+ if (s && ["archived", "in_review", "draft"].includes(s))
144
145
  return false;
145
146
  if (r.isSold === true)
146
147
  return false;
@@ -11,11 +11,13 @@ export interface PlaceBidFormClientProps {
11
11
  currency: string;
12
12
  isEnded: boolean;
13
13
  buyNowPrice: number | null;
14
+ bidsHaveStarted?: boolean;
14
15
  bidCount: number;
15
16
  tags?: string[];
16
17
  onPlaceBid: (input: PlaceBidInput) => Promise<unknown>;
18
+ onBuyNow?: () => Promise<unknown>;
17
19
  }
18
- export declare function PlaceBidFormClient({ productId, currentBid, startingBid, minBidIncrement, currency, isEnded, buyNowPrice, bidCount, tags, onPlaceBid, }: PlaceBidFormClientProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function PlaceBidFormClient({ productId, currentBid, startingBid, minBidIncrement, currency, isEnded, buyNowPrice, bidsHaveStarted, bidCount, tags, onPlaceBid, onBuyNow, }: PlaceBidFormClientProps): import("react/jsx-runtime").JSX.Element;
19
21
  /**
20
22
  * `PlaceBidModalButton` — opens a modal that hosts the same PlaceBidFormClient.
21
23
  *
@@ -11,13 +11,43 @@ const BID_ERROR_DISPLAY = {
11
11
  BID_SELF_BID: "You cannot bid on your own listing.",
12
12
  BID_USER_BANNED: "Your account is currently restricted from bidding. Contact support.",
13
13
  };
14
- export function PlaceBidFormClient({ productId, currentBid, startingBid, minBidIncrement, currency, isEnded, buyNowPrice, bidCount, tags = [], onPlaceBid, }) {
14
+ export function PlaceBidFormClient({ productId, currentBid, startingBid, minBidIncrement, currency, isEnded, buyNowPrice, bidsHaveStarted = false, bidCount, tags = [], onPlaceBid, onBuyNow, }) {
15
15
  const minBid = currentBid + minBidIncrement;
16
16
  const [bidAmount, setBidAmount] = useState(String(minBid));
17
17
  const [isPending, startTransition] = useTransition();
18
+ const [isBuyNowPending, startBuyNowTransition] = useTransition();
18
19
  const [error, setError] = useState(null);
19
20
  const [success, setSuccess] = useState(false);
20
21
  const [showLoginModal, setShowLoginModal] = useState(false);
22
+ const buyNowAvailable = buyNowPrice !== null && !isEnded && !bidsHaveStarted && !!onBuyNow;
23
+ function handleBuyNow() {
24
+ if (!onBuyNow)
25
+ return;
26
+ setError(null);
27
+ setSuccess(false);
28
+ startBuyNowTransition(async () => {
29
+ try {
30
+ const result = await onBuyNow();
31
+ if (result &&
32
+ typeof result === "object" &&
33
+ "ok" in result &&
34
+ result.ok === false) {
35
+ const r = result;
36
+ setError(r.error ?? "Buy Now failed. Please try again.");
37
+ return;
38
+ }
39
+ setSuccess(true);
40
+ }
41
+ catch (err) {
42
+ if (isAuthError(err)) {
43
+ setShowLoginModal(true);
44
+ }
45
+ else {
46
+ setError(err instanceof Error ? err.message : "Buy Now failed. Please try again.");
47
+ }
48
+ }
49
+ });
50
+ }
21
51
  function handleSubmit(e) {
22
52
  e.preventDefault();
23
53
  const amount = Number(bidAmount);
@@ -52,7 +82,7 @@ export function PlaceBidFormClient({ productId, currentBid, startingBid, minBidI
52
82
  }
53
83
  });
54
84
  }
55
- return (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [_jsxs(Div, { className: "space-y-1", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-xs text-zinc-500", children: "Current bid" }), _jsx(Text, { className: "text-xs text-zinc-500", children: "Starting bid" })] }), _jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(Span, { className: "text-xl font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsx(Span, { className: "text-sm text-zinc-500", children: formatCurrency(startingBid, currency) })] }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: [bidCount, " ", bidCount === 1 ? "bid" : "bids", " \u00B7 min increment", " ", formatCurrency(minBidIncrement, currency)] })] }), _jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to place a bid. Please log in or create an account to continue." }), _jsx("form", { onSubmit: handleSubmit, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", value: bidAmount, onChange: (e) => setBidAmount(e.target.value), placeholder: `At least ${formatCurrency(minBid, currency)}`, min: minBid, step: minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded || isPending }), error && (_jsx(Text, { className: "text-xs text-red-600 dark:text-red-400", children: error })), success && (_jsx(Text, { className: "text-xs text-emerald-600 dark:text-emerald-400", children: "\u2713 Bid placed successfully!" })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded || isPending, type: "submit", children: isPending ? "Placing Bid…" : isEnded ? "Auction Ended" : "Place Bid" }), buyNowPrice !== null && !isEnded && (_jsxs(Button, { variant: "secondary", size: "md", className: "w-full", type: "button", children: ["Buy Now \u2014 ", formatCurrency(buyNowPrice, currency)] }))] }) }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) }))] }));
85
+ return (_jsxs(Div, { className: "rounded-xl border border-zinc-100 dark:border-zinc-800 bg-zinc-50 dark:bg-zinc-900/60 p-5 space-y-4", children: [_jsxs(Div, { className: "space-y-1", children: [_jsxs(Row, { justify: "between", align: "center", children: [_jsx(Text, { className: "text-xs text-zinc-500", children: "Current bid" }), _jsx(Text, { className: "text-xs text-zinc-500", children: "Starting bid" })] }), _jsxs(Row, { justify: "between", align: "baseline", children: [_jsx(Span, { className: "text-xl font-bold text-primary-600 dark:text-primary-400", children: formatCurrency(currentBid, currency) }), _jsx(Span, { className: "text-sm text-zinc-500", children: formatCurrency(startingBid, currency) })] }), _jsxs(Text, { className: "text-xs text-zinc-400 dark:text-zinc-500", children: [bidCount, " ", bidCount === 1 ? "bid" : "bids", " \u00B7 min increment", " ", formatCurrency(minBidIncrement, currency)] })] }), _jsx(LoginRequiredModal, { isOpen: showLoginModal, onClose: () => setShowLoginModal(false), message: "You need to be signed in to place a bid. Please log in or create an account to continue." }), _jsx("form", { onSubmit: handleSubmit, children: _jsxs(Stack, { gap: "sm", children: [_jsx(Input, { type: "number", value: bidAmount, onChange: (e) => setBidAmount(e.target.value), placeholder: `At least ${formatCurrency(minBid, currency)}`, min: minBid, step: minBidIncrement, "aria-label": "Your bid amount", disabled: isEnded || isPending }), error && (_jsx(Text, { className: "text-xs text-red-600 dark:text-red-400", children: error })), success && (_jsx(Text, { className: "text-xs text-emerald-600 dark:text-emerald-400", children: "\u2713 Bid placed successfully!" })), _jsx(Button, { variant: "primary", size: "md", className: "w-full", disabled: isEnded || isPending, type: "submit", children: isPending ? "Placing Bid…" : isEnded ? "Auction Ended" : "Place Bid" }), buyNowAvailable && (_jsx(Button, { variant: "secondary", size: "md", className: "w-full", type: "button", disabled: isBuyNowPending || isPending, onClick: handleBuyNow, children: isBuyNowPending ? "Processing…" : `Buy Now ${formatCurrency(buyNowPrice, currency)}` }))] }) }), tags.length > 0 && (_jsx(Div, { className: "border-t border-zinc-200 dark:border-zinc-700 pt-4", children: _jsx(Row, { wrap: true, gap: "xs", children: tags.map((tag) => (_jsx(Span, { className: "rounded-full bg-zinc-100 dark:bg-zinc-800 px-2.5 py-1 text-xs text-zinc-600 dark:text-zinc-300", children: tag }, tag))) }) }))] }));
56
86
  }
57
87
  /**
58
88
  * `PlaceBidModalButton` — opens a modal that hosts the same PlaceBidFormClient.
@@ -14,7 +14,7 @@ export interface AuctionItem {
14
14
  currentBid?: number;
15
15
  bidCount: number;
16
16
  auctionEndDate: string;
17
- status: "published" | "draft" | "archived" | "sold";
17
+ status: "published" | "draft" | "in_review" | "archived";
18
18
  sellerId: string;
19
19
  storeId?: string;
20
20
  storeSlug?: string;
@@ -147,5 +147,5 @@ export function AppLayoutShell({ children, navItems, sidebarItems = [], sidebarS
147
147
  opacity: darkBackground.overlay?.opacity ?? 0,
148
148
  },
149
149
  };
150
- return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(Div, { className: "flex min-h-screen w-full flex-col overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }), eventBannerSlot, _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { className: "relative flex w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 ${hasBottomActions ? "mb-28" : "mb-16"} md:mb-0`, children: _jsx(Div, { className: contentClassName ?? "w-full px-5 py-6 md:px-6 lg:px-8", children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomActions, {}), _jsx(BottomNavbar, { user: user, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar }), _jsx(UnsavedChangesModal, {})] }) }));
150
+ return (_jsx(QueryClientProvider, { client: queryClient, children: _jsxs(Div, { className: "flex min-h-screen w-full flex-col overflow-x-clip transition-colors duration-300", children: [_jsx(BackgroundRenderer, { mode: theme === "dark" ? "dark" : "light", lightMode: normalizedLightBackground, darkMode: normalizedDarkBackground }), _jsxs(Div, { ref: headerRef, className: "sticky top-0 z-50 w-full", children: [_jsx(TitleBar, { onToggleSidebar: handleTogglePublicSidebar, sidebarOpen: sidebarOpen, onSearchToggle: () => setSearchOpen((prev) => !prev), searchOpen: searchOpen, brandName: brandName, brandShortName: brandShortName, siteLogoUrl: siteLogoUrl, logoHref: logoHref, promotionsHref: promotionsHref, cartHref: cartHref, wishlistHref: wishlistHref, userId: userId, profileHref: profileHref, loginHref: loginHref, registerHref: registerHref, user: user, navSlot: titleBarNavSlot, notificationSlot: titleBarNotificationSlot, devSlot: titleBarDevSlot, promoStripText: titleBarPromoStripText, isDark: theme === "dark", onToggleTheme: showThemeToggle ? toggleTheme : undefined, onBeforeToggleDashboardNav: handleBeforeDashboardNavToggle, suppressDashboardNav: suppressDashboardNav, hideSidebarToggle: hideSidebarToggle }), _jsx(NavbarWithSettings, { navItems: navItems, hiddenNavItems: hiddenNavItems, permissions: authUser?.permissions }), searchOpen && (searchSlotRenderer ? searchSlotRenderer(() => setSearchOpen(false)) : searchSlot)] }), eventBannerSlot, _jsx(AutoBreadcrumbs, {}), _jsxs(Div, { className: "relative flex w-full flex-1 overflow-x-clip", children: [_jsx(SidebarLayout, { isOpen: sidebarOpen, ariaLabel: "Secondary navigation", header: user ? (_jsx(SidebarUserHeader, { user: user, onClose: () => setSidebarOpen(false) })) : (_jsx(SidebarGuestHeader, { sidebarTitle: sidebarTitle, onClose: () => setSidebarOpen(false) })), onClose: () => setSidebarOpen(false), children: sidebarContent }), _jsx(Main, { id: "main-content", className: `w-full flex-1 flex flex-col ${hasBottomActions ? "mb-28" : "mb-16"} md:mb-0`, children: _jsx(Div, { className: `flex-1 ${contentClassName ?? "w-full px-5 py-6 md:px-6 lg:px-8"}`, children: children }) })] }), _jsx(BackToTop, {}), _jsx(FooterLayout, { ...footer }), _jsx(BottomActions, {}), _jsx(BottomNavbar, { user: user, homeHref: homeHref, shopHref: shopHref, cartHref: cartHref, profileHref: profileHref, loginHref: loginHref, onSearchToggle: () => setSearchOpen((prev) => !prev), navItems: navItems, onMoreToggle: hasDashboardNav ? toggleDashboardNav : handleTogglePublicSidebar }), _jsx(UnsavedChangesModal, {})] }) }));
151
151
  }
@@ -19,17 +19,8 @@ import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hook
19
19
  import { useBrands } from "../../products/hooks/useBrands";
20
20
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
21
21
  import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
22
- import { sortBy } from "../../../constants/sort";
23
- import { PRODUCT_FIELDS } from "../../../constants/field-names";
24
- const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
25
- const PREORDER_SORT_OPTIONS = [
26
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
27
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
28
- { value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE, "ASC"), label: "Delivery: Soonest" },
29
- { value: sortBy(PRODUCT_FIELDS.PRE_ORDER_DELIVERY_DATE), label: "Delivery: Furthest" },
30
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Price: Low to High" },
31
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: "Price: High to Low" },
32
- ];
22
+ import { PREORDER_SORT_OPTIONS } from "../../products/constants/sieve";
23
+ const DEFAULT_SORT = PREORDER_SORT_OPTIONS[2].value;
33
24
  const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.BRAND, TABLE_KEYS.MIN_PRICE, TABLE_KEYS.MAX_PRICE, TABLE_KEYS.STORE_ID, TABLE_KEYS.PREORDER_STATUS, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
34
25
  export function PreOrdersIndexListing({ initialData, categorySlug, brandName }) {
35
26
  const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
@@ -21,9 +21,7 @@ import { sanitizeProductForPublic } from "../../utils/sanitize";
21
21
  const ERR_DB_NOT_REGISTERED = "Database provider not registered";
22
22
  const ERR_PRODUCT_NOT_FOUND = "Product not found";
23
23
  const UNAVAILABLE_PRODUCT_STATUSES = new Set([
24
- "sold",
25
- "out_of_stock",
26
- "discontinued",
24
+ "archived",
27
25
  ]);
28
26
  function getRepo() {
29
27
  const { db } = getProviders();
@@ -43,10 +41,8 @@ const productUpdateSchema = z
43
41
  .enum([
44
42
  "draft",
45
43
  "published",
44
+ "in_review",
46
45
  "archived",
47
- "sold",
48
- "discontinued",
49
- "out_of_stock",
50
46
  ])
51
47
  .optional(),
52
48
  mainImage: z.string().optional(),
@@ -145,7 +141,7 @@ export const PATCH = createRouteHandler({
145
141
  },
146
142
  });
147
143
  // --- DELETE /api/products/[id] ------------------------------------------------
148
- // Soft-delete (sets status to "discontinued"). Auth required.
144
+ // Soft-delete (sets status to "archived"). Auth required.
149
145
  export const DELETE = createRouteHandler({
150
146
  auth: true,
151
147
  handler: async ({ user, params }) => {
@@ -166,7 +162,7 @@ export const DELETE = createRouteHandler({
166
162
  return NextResponse.json({ success: false, error: "Not authorized to delete this product" }, { status: 403 });
167
163
  }
168
164
  await repo.update(id, {
169
- status: "discontinued",
165
+ status: "archived",
170
166
  updatedAt: new Date().toISOString(),
171
167
  });
172
168
  // SB-UNI-V — bundle stock-sync handled by onProductStockChange Function.
@@ -32,10 +32,8 @@ const productMutateSchema = z
32
32
  .enum([
33
33
  "draft",
34
34
  "published",
35
+ "in_review",
35
36
  "archived",
36
- "sold",
37
- "discontinued",
38
- "out_of_stock",
39
37
  ])
40
38
  .optional(),
41
39
  mainImage: z.string().optional(),
@@ -13,19 +13,11 @@ import { pushWishlistOp } from "../../cart/utils/pending-ops";
13
13
  import { useCategoryTree, categoriesToFacetOptions } from "../../categories/hooks/useCategoryTree";
14
14
  import { useBrands } from "../hooks/useBrands";
15
15
  import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
16
- import { sortBy } from "../../../constants/sort";
17
- import { PRODUCT_FIELDS } from "../../../constants/field-names";
18
16
  import { useAuthGate } from "../../../react/hooks/useAuthGate";
19
17
  import { ACTION_ID } from "../constants/action-defs";
20
- const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC");
21
- const AUCTION_SORT_OPTIONS = [
22
- { value: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE, "ASC"), label: "Ending Soonest" },
23
- { value: sortBy(PRODUCT_FIELDS.AUCTION_END_DATE), label: "Ending Latest" },
24
- { value: sortBy(PRODUCT_FIELDS.CURRENT_BID, "ASC"), label: "Bid: Low to High" },
25
- { value: sortBy(PRODUCT_FIELDS.CURRENT_BID), label: "Bid: High to Low" },
26
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newly Listed" },
27
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest Listed" },
28
- ];
18
+ import { AUCTION_PUBLIC_SORT_OPTIONS } from "../constants/sieve";
19
+ const DEFAULT_SORT = AUCTION_PUBLIC_SORT_OPTIONS[0].value;
20
+ const AUCTION_SORT_OPTIONS = AUCTION_PUBLIC_SORT_OPTIONS;
29
21
  const FILTER_KEYS = [TABLE_KEYS.CATEGORY, TABLE_KEYS.BRAND, TABLE_KEYS.MIN_BID, TABLE_KEYS.MAX_BID, TABLE_KEYS.STORE_ID, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO];
30
22
  export function AuctionsIndexListing({ initialData, categorySlug, brandName }) {
31
23
  const table = useUrlTable({ defaults: { pageSize: "24", sort: DEFAULT_SORT } });
@@ -10,17 +10,9 @@ import { useBrands } from "../hooks/useBrands";
10
10
  import { MarketplacePrizeDrawCard } from "./MarketplacePrizeDrawCard";
11
11
  import { ProductFilters } from "./ProductFilters";
12
12
  import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
13
- import { sortBy } from "../../../constants/sort";
13
+ import { PRIZE_DRAW_SORT_OPTIONS } from "../constants/sieve";
14
14
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
15
- const DEFAULT_SORT = sortBy(PRODUCT_FIELDS.CREATED_AT);
16
- const PRIZE_DRAW_SORT_OPTIONS = [
17
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT), label: "Newest First" },
18
- { value: sortBy(PRODUCT_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
19
- { value: sortBy(PRODUCT_FIELDS.PRIZE_REVEAL_WINDOW_START, "ASC"), label: "Reveal: Soonest" },
20
- { value: sortBy(PRODUCT_FIELDS.PRIZE_REVEAL_WINDOW_START), label: "Reveal: Furthest" },
21
- { value: sortBy(PRODUCT_FIELDS.PRICE, "ASC"), label: "Entry: Low to High" },
22
- { value: sortBy(PRODUCT_FIELDS.PRICE), label: "Entry: High to Low" },
23
- ];
15
+ const DEFAULT_SORT = PRIZE_DRAW_SORT_OPTIONS[0].value;
24
16
  const FILTER_KEYS = [
25
17
  TABLE_KEYS.CATEGORY,
26
18
  TABLE_KEYS.BRAND,
@@ -15,18 +15,27 @@ export declare const PRODUCT_ADMIN_SORT_OPTIONS: readonly [{
15
15
  }, {
16
16
  readonly value: string;
17
17
  readonly label: "Oldest First";
18
+ }, {
19
+ readonly value: string;
20
+ readonly label: "Price: Low to High";
18
21
  }, {
19
22
  readonly value: string;
20
23
  readonly label: "Price: High to Low";
21
24
  }, {
22
25
  readonly value: string;
23
- readonly label: "Price: Low to High";
26
+ readonly label: "Name: A–Z";
24
27
  }, {
25
28
  readonly value: string;
26
- readonly label: "Title A-Z";
29
+ readonly label: "Name: Z–A";
27
30
  }, {
28
31
  readonly value: string;
29
- readonly label: "Title Z-A";
32
+ readonly label: "Featured First";
33
+ }, {
34
+ readonly value: string;
35
+ readonly label: "Promoted First";
36
+ }, {
37
+ readonly value: string;
38
+ readonly label: "Recently Updated";
30
39
  }, {
31
40
  readonly value: string;
32
41
  readonly label: "Most Viewed";
@@ -37,15 +46,30 @@ export declare const PRODUCT_SELLER_SORT_OPTIONS: readonly [{
37
46
  }, {
38
47
  readonly value: string;
39
48
  readonly label: "Oldest First";
49
+ }, {
50
+ readonly value: string;
51
+ readonly label: "Price: Low to High";
40
52
  }, {
41
53
  readonly value: string;
42
54
  readonly label: "Price: High to Low";
43
55
  }, {
44
56
  readonly value: string;
45
- readonly label: "Price: Low to High";
57
+ readonly label: "Name: A–Z";
46
58
  }, {
47
59
  readonly value: string;
48
- readonly label: "Title A-Z";
60
+ readonly label: "Name: Z–A";
61
+ }, {
62
+ readonly value: string;
63
+ readonly label: "Featured First";
64
+ }, {
65
+ readonly value: string;
66
+ readonly label: "Promoted First";
67
+ }, {
68
+ readonly value: string;
69
+ readonly label: "Recently Updated";
70
+ }, {
71
+ readonly value: string;
72
+ readonly label: "Most Viewed";
49
73
  }];
50
74
  export declare const PRODUCT_PUBLIC_SORT_OPTIONS: readonly [{
51
75
  readonly value: string;
@@ -55,16 +79,16 @@ export declare const PRODUCT_PUBLIC_SORT_OPTIONS: readonly [{
55
79
  readonly label: "Oldest First";
56
80
  }, {
57
81
  readonly value: string;
58
- readonly label: "Price: High to Low";
82
+ readonly label: "Price: Low to High";
59
83
  }, {
60
84
  readonly value: string;
61
- readonly label: "Price: Low to High";
85
+ readonly label: "Price: High to Low";
62
86
  }, {
63
87
  readonly value: string;
64
- readonly label: "Title A–Z";
88
+ readonly label: "Name: A–Z";
65
89
  }, {
66
90
  readonly value: string;
67
- readonly label: "Title Z–A";
91
+ readonly label: "Name: Z–A";
68
92
  }, {
69
93
  readonly value: string;
70
94
  readonly label: "Most Viewed";