@mohasinac/appkit 2.7.11 → 2.7.12

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 (141) hide show
  1. package/dist/_internal/server/jobs/core/assignSpinPrize.js +8 -7
  2. package/dist/_internal/server/jobs/core/bundleStockSync.js +11 -6
  3. package/dist/_internal/server/jobs/core/countersReconcile.js +3 -2
  4. package/dist/_internal/server/jobs/core/onProductStockChange.js +12 -7
  5. package/dist/_internal/server/jobs/core/onProductWrite.js +3 -2
  6. package/dist/_internal/server/jobs/core/prizeRevealClose.js +7 -5
  7. package/dist/_internal/server/jobs/core/prizeRevealExpiry.js +11 -6
  8. package/dist/_internal/server/jobs/core/prizeRevealOpen.js +13 -7
  9. package/dist/_internal/server/jobs/core/prizeRevealReminder.js +9 -4
  10. package/dist/_internal/server/jobs/core/triggerEventRaffle.js +3 -2
  11. package/dist/_internal/shared/features/blog/schema.d.ts +4 -4
  12. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +32 -32
  13. package/dist/_internal/shared/features/events/schema.d.ts +4 -4
  14. package/dist/_internal/shared/features/products/schema.d.ts +20 -20
  15. package/dist/_internal/shared/features/promotions/schema.d.ts +2 -2
  16. package/dist/_internal/shared/features/reviews/schema.d.ts +1 -1
  17. package/dist/constants/field-names.d.ts +813 -0
  18. package/dist/constants/field-names.js +918 -0
  19. package/dist/constants/sort.d.ts +7 -0
  20. package/dist/constants/sort.js +5 -0
  21. package/dist/constants/table-keys.d.ts +48 -0
  22. package/dist/constants/table-keys.js +46 -0
  23. package/dist/features/account/components/AddressesIndexListing.js +9 -6
  24. package/dist/features/admin/components/AdminAllEventEntriesView.js +4 -1
  25. package/dist/features/admin/components/AdminBidsView.js +4 -1
  26. package/dist/features/admin/components/AdminBlogView.js +9 -2
  27. package/dist/features/admin/components/AdminBrandsView.js +9 -2
  28. package/dist/features/admin/components/AdminCarouselView.js +4 -1
  29. package/dist/features/admin/components/AdminCartsView.js +4 -1
  30. package/dist/features/admin/components/AdminCategoriesView.js +9 -2
  31. package/dist/features/admin/components/AdminContactView.js +4 -1
  32. package/dist/features/admin/components/AdminCouponsView.js +4 -1
  33. package/dist/features/admin/components/AdminFaqsView.js +5 -1
  34. package/dist/features/admin/components/AdminFeaturesView.js +5 -2
  35. package/dist/features/admin/components/AdminHistoryView.js +5 -1
  36. package/dist/features/admin/components/AdminNewsletterView.js +4 -1
  37. package/dist/features/admin/components/AdminNotificationsView.js +4 -1
  38. package/dist/features/admin/components/AdminOrdersView.js +14 -12
  39. package/dist/features/admin/components/AdminPayoutsView.js +4 -1
  40. package/dist/features/admin/components/AdminPrizeDrawsView.js +4 -1
  41. package/dist/features/admin/components/AdminProductsView.js +14 -3
  42. package/dist/features/admin/components/AdminReturnRequestsView.js +4 -1
  43. package/dist/features/admin/components/AdminReviewsView.js +4 -1
  44. package/dist/features/admin/components/AdminScammersView.js +4 -1
  45. package/dist/features/admin/components/AdminSessionsView.js +4 -1
  46. package/dist/features/admin/components/AdminStoreAddressesView.js +5 -1
  47. package/dist/features/admin/components/AdminStoresView.js +10 -3
  48. package/dist/features/admin/components/AdminSublistingCategoriesView.js +4 -1
  49. package/dist/features/admin/components/AdminSupportTicketsView.js +4 -1
  50. package/dist/features/admin/components/AdminTeamView.js +4 -1
  51. package/dist/features/admin/components/AdminUsersView.js +9 -12
  52. package/dist/features/admin/components/AdminViewCards.d.ts +12 -0
  53. package/dist/features/admin/components/AdminViewCards.js +53 -0
  54. package/dist/features/admin/components/AdminWishlistsView.js +4 -1
  55. package/dist/features/auctions/repository/bid.repository.js +1 -6
  56. package/dist/features/auctions/schemas/index.d.ts +4 -4
  57. package/dist/features/blog/actions/blog-actions.d.ts +16 -16
  58. package/dist/features/blog/components/BlogFilters.d.ts +28 -28
  59. package/dist/features/blog/components/BlogFilters.js +18 -15
  60. package/dist/features/blog/components/BlogIndexListing.js +20 -16
  61. package/dist/features/blog/schemas/index.d.ts +10 -10
  62. package/dist/features/categories/components/CategoriesIndexListing.js +25 -21
  63. package/dist/features/categories/schemas/index.d.ts +2 -2
  64. package/dist/features/events/components/AdminEventsView.js +5 -1
  65. package/dist/features/events/components/EventFilters.d.ts +24 -24
  66. package/dist/features/events/components/EventFilters.js +16 -13
  67. package/dist/features/events/components/EventStatusBadge.js +1 -0
  68. package/dist/features/events/components/EventsIndexListing.js +31 -26
  69. package/dist/features/events/components/EventsListPageView.js +13 -10
  70. package/dist/features/events/schemas/firestore.d.ts +1 -0
  71. package/dist/features/events/schemas/firestore.js +1 -0
  72. package/dist/features/events/schemas/index.d.ts +1 -1
  73. package/dist/features/events/types/index.d.ts +1 -1
  74. package/dist/features/faq/actions/faq-actions.d.ts +10 -10
  75. package/dist/features/faq/schemas/index.d.ts +4 -4
  76. package/dist/features/orders/components/OrderFilters.d.ts +20 -20
  77. package/dist/features/orders/components/OrderFilters.js +33 -30
  78. package/dist/features/orders/repository/orders.repository.js +1 -15
  79. package/dist/features/orders/schemas/index.d.ts +2 -2
  80. package/dist/features/pre-orders/components/PreOrdersIndexListing.js +35 -31
  81. package/dist/features/pre-orders/schemas/index.d.ts +3 -3
  82. package/dist/features/products/components/AuctionsIndexListing.js +34 -30
  83. package/dist/features/products/components/BidHistory.d.ts +0 -1
  84. package/dist/features/products/components/BidHistory.js +1 -1
  85. package/dist/features/products/components/PrizeDrawsIndexListing.js +44 -40
  86. package/dist/features/products/components/ProductDetailPageView.js +1 -1
  87. package/dist/features/products/components/ProductFilters.d.ts +20 -20
  88. package/dist/features/products/components/ProductFilters.js +62 -59
  89. package/dist/features/products/components/ProductsIndexListing.js +28 -24
  90. package/dist/features/products/components/ProductsIndexPageView.js +18 -10
  91. package/dist/features/products/hooks/useProducts.js +2 -0
  92. package/dist/features/products/repository/products.repository.js +2 -22
  93. package/dist/features/products/schemas/index.d.ts +28 -28
  94. package/dist/features/products/schemas/product-features.validators.d.ts +12 -12
  95. package/dist/features/products/types/index.d.ts +2 -0
  96. package/dist/features/promotions/components/CouponsIndexListing.js +21 -17
  97. package/dist/features/promotions/schemas/index.d.ts +4 -4
  98. package/dist/features/reviews/components/ReviewDetailShell.js +6 -5
  99. package/dist/features/reviews/components/ReviewFilters.d.ts +16 -16
  100. package/dist/features/reviews/components/ReviewFilters.js +10 -7
  101. package/dist/features/reviews/components/ReviewsIndexListing.js +21 -17
  102. package/dist/features/reviews/components/ReviewsList.d.ts +5 -2
  103. package/dist/features/reviews/components/ReviewsList.js +11 -5
  104. package/dist/features/reviews/schemas/firestore.d.ts +1 -0
  105. package/dist/features/reviews/schemas/index.d.ts +8 -8
  106. package/dist/features/reviews/types/index.d.ts +1 -0
  107. package/dist/features/search/schemas/index.d.ts +2 -2
  108. package/dist/features/seller/components/SellerAuctionsView.js +5 -1
  109. package/dist/features/seller/components/SellerBidsView.js +4 -1
  110. package/dist/features/seller/components/SellerCouponsView.js +4 -1
  111. package/dist/features/seller/components/SellerOffersView.js +5 -1
  112. package/dist/features/seller/components/SellerOrdersView.js +4 -1
  113. package/dist/features/seller/components/SellerPayoutsView.js +5 -1
  114. package/dist/features/seller/components/SellerPreOrdersView.js +4 -1
  115. package/dist/features/seller/components/SellerPrizeDrawsView.js +4 -1
  116. package/dist/features/seller/components/SellerProductsView.js +4 -1
  117. package/dist/features/seller/schemas/index.d.ts +10 -10
  118. package/dist/features/stores/api/route.js +10 -1
  119. package/dist/features/stores/components/StoreReviewsListing.js +1 -1
  120. package/dist/features/stores/components/StoresIndexListing.js +24 -20
  121. package/dist/features/stores/schemas/firestore.d.ts +1 -0
  122. package/dist/features/stores/schemas/index.d.ts +4 -4
  123. package/dist/index.d.ts +8 -1
  124. package/dist/index.js +12 -1
  125. package/dist/react/hooks/useUrlTable.js +2 -1
  126. package/dist/seed/bids-seed-data.js +45 -44
  127. package/dist/seed/categories-seed-data.js +41 -40
  128. package/dist/seed/coupons-seed-data.js +41 -40
  129. package/dist/seed/products-auctions-seed-data.js +54 -53
  130. package/dist/seed/products-preorders-seed-data.js +25 -24
  131. package/dist/seed/products-prize-draws-seed-data.js +24 -23
  132. package/dist/seed/products-standard-seed-data.js +294 -293
  133. package/dist/seed/reviews-seed-data.js +61 -60
  134. package/dist/seed/scammers-seed-data.js +3 -2
  135. package/dist/seed/stores-seed-data.js +9 -8
  136. package/dist/seed/support-tickets-seed-data.js +13 -12
  137. package/dist/seed/users-seed-data.js +28 -27
  138. package/dist/tailwind-utilities.css +1 -1
  139. package/dist/utils/sieve-builder.d.ts +41 -0
  140. package/dist/utils/sieve-builder.js +66 -0
  141. package/package.json +1 -1
@@ -7,18 +7,22 @@ import { useEvents } from "../hooks/useEvents";
7
7
  import { Pagination, ListingToolbar } from "../../../ui";
8
8
  import { EventCard } from "./EventCard";
9
9
  import { EventFilters, EVENT_PUBLIC_SORT_OPTIONS } from "./EventFilters";
10
+ import { EVENT_FIELDS } from "../../../constants/field-names";
11
+ import { sieveFilter, sieveMultiEq, sieveAnd, SIEVE_OP } from "../../../utils/sieve-builder";
12
+ import { TABLE_KEYS, VIEW_MODE } from "../../../constants/table-keys";
13
+ import { sortBy } from "../../../constants/sort";
10
14
  const PAGE_SIZE = 24;
11
- const FILTER_KEYS = ["type", "status", "dateFrom", "dateTo", "showExpired"];
15
+ const FILTER_KEYS = [TABLE_KEYS.TYPE, TABLE_KEYS.STATUS, TABLE_KEYS.DATE_FROM, TABLE_KEYS.DATE_TO, TABLE_KEYS.SHOW_EXPIRED];
12
16
  export function EventsIndexListing({ initialData }) {
13
- const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: "startsAt" } });
14
- const [searchInput, setSearchInput] = useState(table.get("q") || "");
17
+ const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: sortBy(EVENT_FIELDS.STARTS_AT, "ASC") } });
18
+ const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
15
19
  const [filterOpen, setFilterOpen] = useState(false);
16
- const [view, setView] = useState(table.get("view") || "grid");
20
+ const [view, setView] = useState(table.get(TABLE_KEYS.VIEW) || VIEW_MODE.GRID);
17
21
  const handleViewToggle = (next) => {
18
22
  if (next === "table")
19
23
  return;
20
24
  setView(next);
21
- table.set("view", next);
25
+ table.set(TABLE_KEYS.VIEW, next);
22
26
  };
23
27
  // Pending filter state — buffered until "Apply Filters" clicked
24
28
  const [pendingFilters, setPendingFilters] = useState(() => Object.fromEntries(FILTER_KEYS.map((k) => [k, table.get(k)])));
@@ -62,51 +66,52 @@ export function EventsIndexListing({ initialData }) {
62
66
  setPendingFilters(Object.fromEntries(FILTER_KEYS.map((k) => [k, ""])));
63
67
  }, []);
64
68
  const resetAll = useCallback(() => {
65
- const updates = { q: "", sort: "" };
69
+ const updates = { [TABLE_KEYS.QUERY]: "", [TABLE_KEYS.SORT]: "" };
66
70
  for (const k of FILTER_KEYS)
67
71
  updates[k] = "";
68
72
  table.setMany(updates);
69
73
  setSearchInput("");
70
74
  }, [table]);
71
75
  const activeFilterCount = FILTER_KEYS.filter((k) => !!table.get(k)).length;
72
- const hasActiveState = !!table.get("q") ||
73
- table.get("sort") !== "startsAt" ||
76
+ const hasActiveState = !!table.get(TABLE_KEYS.QUERY) ||
77
+ table.get(TABLE_KEYS.SORT) !== sortBy(EVENT_FIELDS.STARTS_AT, "ASC") ||
74
78
  activeFilterCount > 0;
75
79
  // Build client-side filter string from URL params
76
- const typeRaw = table.get("type");
77
- const statusRaw = table.get("status");
78
- const dateFrom = table.get("dateFrom");
79
- const dateTo = table.get("dateTo");
80
+ const typeRaw = table.get(TABLE_KEYS.TYPE);
81
+ const statusRaw = table.get(TABLE_KEYS.STATUS);
82
+ const dateFrom = table.get(TABLE_KEYS.DATE_FROM);
83
+ const dateTo = table.get(TABLE_KEYS.DATE_TO);
80
84
  const filterParts = [];
81
85
  if (typeRaw) {
82
86
  const types = typeRaw.split("|").filter(Boolean);
83
87
  if (types.length === 1)
84
- filterParts.push(`type==${types[0]}`);
88
+ filterParts.push(sieveFilter(EVENT_FIELDS.TYPE, SIEVE_OP.EQ, types[0]));
89
+ // BUG FIX: pipe is invalid for ==; expand to multiple AND clauses
85
90
  else if (types.length > 1)
86
- filterParts.push(`type==${types.join("|")}`);
91
+ filterParts.push(sieveMultiEq(EVENT_FIELDS.TYPE, types));
87
92
  }
88
93
  if (statusRaw) {
89
94
  const statuses = statusRaw.split("|").filter(Boolean);
90
95
  if (statuses.length === 1)
91
- filterParts.push(`status==${statuses[0]}`);
96
+ filterParts.push(sieveFilter(EVENT_FIELDS.STATUS, SIEVE_OP.EQ, statuses[0]));
92
97
  else if (statuses.length > 1)
93
- filterParts.push(`status==${statuses.join("|")}`);
98
+ filterParts.push(sieveMultiEq(EVENT_FIELDS.STATUS, statuses));
94
99
  }
95
100
  if (dateFrom)
96
- filterParts.push(`startsAt>=${dateFrom}`);
101
+ filterParts.push(sieveFilter(EVENT_FIELDS.STARTS_AT, SIEVE_OP.GTE, dateFrom));
97
102
  if (dateTo)
98
- filterParts.push(`endsAt<=${dateTo}`);
103
+ filterParts.push(sieveFilter(EVENT_FIELDS.ENDS_AT, SIEVE_OP.LTE, dateTo));
99
104
  const params = {
100
- q: table.get("q") || undefined,
101
- page: table.getNumber("page", 1),
102
- pageSize: table.getNumber("pageSize", PAGE_SIZE),
103
- sort: table.get("sort") || "startsAt",
104
- filters: filterParts.length > 0 ? filterParts.join(",") : undefined,
105
+ q: table.get(TABLE_KEYS.QUERY) || undefined,
106
+ page: table.getNumber(TABLE_KEYS.PAGE, 1),
107
+ pageSize: table.getNumber(TABLE_KEYS.PAGE_SIZE, PAGE_SIZE),
108
+ sort: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"),
109
+ filters: filterParts.length > 0 ? sieveAnd(...filterParts) : undefined,
105
110
  };
106
111
  const { events, total, totalPages, isLoading } = useEvents(params, { initialData });
107
- const currentPage = table.getNumber("page", 1);
112
+ const currentPage = table.getNumber(TABLE_KEYS.PAGE, 1);
108
113
  const commitSearch = useCallback(() => {
109
- table.set("q", searchInput.trim());
114
+ table.set(TABLE_KEYS.QUERY, searchInput.trim());
110
115
  }, [searchInput, table]);
111
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get("sort") || "startsAt", sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set("sort", v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), 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) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }), filterOpen && (_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: "flex items-center gap-2 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 filters", 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(EventFilters, { table: pendingTable, variant: "public" }) }), _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})` : ""] }) })] })] }))] }));
116
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search events...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || sortBy(EVENT_FIELDS.STARTS_AT, "ASC"), sortOptions: EVENT_PUBLIC_SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, view: view, onViewChange: handleViewToggle, onResetAll: resetAll, hasActiveState: hasActiveState }), 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) }) })), _jsx("div", { className: "py-6", children: isLoading ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: Array.from({ length: 6 }).map((_, i) => (_jsxs("div", { className: "rounded-xl border border-zinc-100 dark:border-slate-700 overflow-hidden animate-pulse", children: [_jsx("div", { className: "aspect-video bg-zinc-200 dark:bg-slate-700" }), _jsxs("div", { className: "p-4 space-y-2", children: [_jsx("div", { className: "h-4 bg-zinc-200 dark:bg-slate-700 rounded w-3/4" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-full" }), _jsx("div", { className: "h-3 bg-zinc-200 dark:bg-slate-700 rounded w-2/3" }), _jsx("div", { className: "h-8 bg-zinc-200 dark:bg-slate-700 rounded mt-2" })] })] }, i))) })) : events.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-zinc-500 dark:text-zinc-400", children: "No events found." })) : view === "list" ? (_jsx("div", { className: "flex flex-col divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl border border-zinc-100 dark:border-zinc-800", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) : (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6", children: events.map((event) => (_jsx(EventCard, { event: event }, event.id))) })) }), filterOpen && (_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: "flex items-center gap-2 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 filters", 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(EventFilters, { table: pendingTable, variant: "public" }) }), _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})` : ""] }) })] })] }))] }));
112
117
  }
@@ -3,42 +3,45 @@ import { eventRepository } from "../../../repositories";
3
3
  import { AdSlot } from "../../homepage/components/AdSlot";
4
4
  import { Container, Heading, Main, Section } from "../../../ui";
5
5
  import { EventsIndexListing } from "./EventsIndexListing";
6
+ import { EVENT_FIELDS } from "../../../constants/field-names";
7
+ import { sieveFilter, sieveMultiEq, sieveAnd, SIEVE_OP } from "../../../utils/sieve-builder";
8
+ import { sortBy } from "../../../constants/sort";
6
9
  function sp(params, key) {
7
10
  const v = params[key];
8
11
  return Array.isArray(v) ? v[0] ?? "" : v ?? "";
9
12
  }
10
13
  function buildEventFilters(params) {
11
- // Public page defaults to published status; allow overriding to active/ended
12
14
  const statusRaw = sp(params, "status");
13
15
  const parts = [];
14
16
  if (statusRaw) {
15
17
  const values = statusRaw.split("|").filter(Boolean);
16
18
  if (values.length === 1)
17
- parts.push(`status==${values[0]}`);
19
+ parts.push(sieveFilter(EVENT_FIELDS.STATUS, SIEVE_OP.EQ, values[0]));
20
+ // BUG FIX: pipe is invalid for ==; expand to multiple AND clauses
18
21
  else if (values.length > 1)
19
- parts.push(`status==${values.join("|")}`);
22
+ parts.push(sieveMultiEq(EVENT_FIELDS.STATUS, values));
20
23
  }
21
24
  else {
22
- parts.push("status==active");
25
+ parts.push(sieveFilter(EVENT_FIELDS.STATUS, SIEVE_OP.EQ, EVENT_FIELDS.STATUS_VALUES.ACTIVE));
23
26
  }
24
27
  const type = sp(params, "type");
25
28
  if (type) {
26
29
  const values = type.split("|").filter(Boolean);
27
30
  if (values.length === 1)
28
- parts.push(`type==${values[0]}`);
31
+ parts.push(sieveFilter(EVENT_FIELDS.TYPE, SIEVE_OP.EQ, values[0]));
29
32
  else if (values.length > 1)
30
- parts.push(`type==${values.join("|")}`);
33
+ parts.push(sieveMultiEq(EVENT_FIELDS.TYPE, values));
31
34
  }
32
35
  const dateFrom = sp(params, "dateFrom");
33
36
  const dateTo = sp(params, "dateTo");
34
37
  if (dateFrom)
35
- parts.push(`startsAt>=${dateFrom}`);
38
+ parts.push(sieveFilter(EVENT_FIELDS.STARTS_AT, SIEVE_OP.GTE, dateFrom));
36
39
  if (dateTo)
37
- parts.push(`endsAt<=${dateTo}`);
38
- return parts.join(",");
40
+ parts.push(sieveFilter(EVENT_FIELDS.ENDS_AT, SIEVE_OP.LTE, dateTo));
41
+ return sieveAnd(...parts);
39
42
  }
40
43
  export async function EventsListPageView({ searchParams = {} }) {
41
- const sort = sp(searchParams, "sort") || "startsAt";
44
+ const sort = sp(searchParams, "sort") || sortBy(EVENT_FIELDS.STARTS_AT, "ASC");
42
45
  const page = Number(sp(searchParams, "page")) || 1;
43
46
  const pageSize = Number(sp(searchParams, "pageSize")) || 24;
44
47
  const filters = buildEventFilters(searchParams);
@@ -95,6 +95,7 @@ export declare const EVENT_FIELDS: {
95
95
  readonly ACTIVE: EventStatus;
96
96
  readonly PAUSED: EventStatus;
97
97
  readonly ENDED: EventStatus;
98
+ readonly CANCELLED: EventStatus;
98
99
  };
99
100
  readonly TYPE_VALUES: {
100
101
  readonly SALE: EventType;
@@ -39,6 +39,7 @@ export const EVENT_FIELDS = {
39
39
  ACTIVE: "active",
40
40
  PAUSED: "paused",
41
41
  ENDED: "ended",
42
+ CANCELLED: "cancelled",
42
43
  },
43
44
  TYPE_VALUES: {
44
45
  SALE: "sale",
@@ -288,6 +288,7 @@ export declare const eventItemSchema: z.ZodObject<{
288
288
  type: "image";
289
289
  } | null;
290
290
  startsAt: string;
291
+ endsAt: string;
291
292
  additionalImages: {
292
293
  type: "video" | "image" | "file";
293
294
  url: string;
@@ -296,7 +297,6 @@ export declare const eventItemSchema: z.ZodObject<{
296
297
  thumbnailUrl?: string | undefined;
297
298
  youtubeId?: string | undefined;
298
299
  }[];
299
- endsAt: string;
300
300
  eventImages: {
301
301
  type: "video" | "image" | "file";
302
302
  url: string;
@@ -8,7 +8,7 @@ export interface SpinPrize {
8
8
  weight: number;
9
9
  isActive: boolean;
10
10
  }
11
- export type EventStatus = "draft" | "active" | "paused" | "ended";
11
+ export type EventStatus = "draft" | "active" | "paused" | "ended" | "cancelled";
12
12
  export type EntryReviewStatus = "pending" | "approved" | "flagged";
13
13
  export type PollResultsVisibility = "always" | "after_vote" | "after_end";
14
14
  export type FormFieldType = "text" | "textarea" | "email" | "phone" | "number" | "select" | "multiselect" | "checkbox" | "radio" | "date" | "rating" | "file";
@@ -33,15 +33,15 @@ export declare const faqCreateSchema: z.ZodEffects<z.ZodObject<{
33
33
  question: string;
34
34
  showInFooter: boolean;
35
35
  showOnHomepage: boolean;
36
- isPinned: boolean;
37
36
  answer: {
38
37
  text: string;
39
38
  format: "html" | "plain" | "markdown";
40
39
  };
40
+ isPinned: boolean;
41
41
  useSiteSettings: boolean;
42
42
  template?: string | undefined;
43
- variables?: Record<string, string> | undefined;
44
43
  tags?: string[] | undefined;
44
+ variables?: Record<string, string> | undefined;
45
45
  relatedFAQs?: string[] | undefined;
46
46
  }, {
47
47
  category: string;
@@ -54,8 +54,8 @@ export declare const faqCreateSchema: z.ZodEffects<z.ZodObject<{
54
54
  order?: number | undefined;
55
55
  priority?: number | undefined;
56
56
  isActive?: boolean | undefined;
57
- variables?: Record<string, string> | undefined;
58
57
  tags?: string[] | undefined;
58
+ variables?: Record<string, string> | undefined;
59
59
  showInFooter?: boolean | undefined;
60
60
  showOnHomepage?: boolean | undefined;
61
61
  isPinned?: boolean | undefined;
@@ -69,15 +69,15 @@ export declare const faqCreateSchema: z.ZodEffects<z.ZodObject<{
69
69
  question: string;
70
70
  showInFooter: boolean;
71
71
  showOnHomepage: boolean;
72
- isPinned: boolean;
73
72
  answer: {
74
73
  text: string;
75
74
  format: "html" | "plain" | "markdown";
76
75
  };
76
+ isPinned: boolean;
77
77
  useSiteSettings: boolean;
78
78
  template?: string | undefined;
79
- variables?: Record<string, string> | undefined;
80
79
  tags?: string[] | undefined;
80
+ variables?: Record<string, string> | undefined;
81
81
  relatedFAQs?: string[] | undefined;
82
82
  }, {
83
83
  category: string;
@@ -90,8 +90,8 @@ export declare const faqCreateSchema: z.ZodEffects<z.ZodObject<{
90
90
  order?: number | undefined;
91
91
  priority?: number | undefined;
92
92
  isActive?: boolean | undefined;
93
- variables?: Record<string, string> | undefined;
94
93
  tags?: string[] | undefined;
94
+ variables?: Record<string, string> | undefined;
95
95
  showInFooter?: boolean | undefined;
96
96
  showOnHomepage?: boolean | undefined;
97
97
  isPinned?: boolean | undefined;
@@ -128,16 +128,16 @@ export declare const faqUpdateSchema: z.ZodObject<{
128
128
  priority?: number | undefined;
129
129
  isActive?: boolean | undefined;
130
130
  category?: string | undefined;
131
- variables?: Record<string, string> | undefined;
132
131
  tags?: string[] | undefined;
132
+ variables?: Record<string, string> | undefined;
133
133
  question?: string | undefined;
134
134
  showInFooter?: boolean | undefined;
135
135
  showOnHomepage?: boolean | undefined;
136
- isPinned?: boolean | undefined;
137
136
  answer?: {
138
137
  text: string;
139
138
  format: "html" | "plain" | "markdown";
140
139
  } | undefined;
140
+ isPinned?: boolean | undefined;
141
141
  relatedFAQs?: string[] | undefined;
142
142
  useSiteSettings?: boolean | undefined;
143
143
  }, {
@@ -146,16 +146,16 @@ export declare const faqUpdateSchema: z.ZodObject<{
146
146
  priority?: number | undefined;
147
147
  isActive?: boolean | undefined;
148
148
  category?: string | undefined;
149
- variables?: Record<string, string> | undefined;
150
149
  tags?: string[] | undefined;
150
+ variables?: Record<string, string> | undefined;
151
151
  question?: string | undefined;
152
152
  showInFooter?: boolean | undefined;
153
153
  showOnHomepage?: boolean | undefined;
154
- isPinned?: boolean | undefined;
155
154
  answer?: {
156
155
  text: string;
157
156
  format?: "html" | "plain" | "markdown" | undefined;
158
157
  } | undefined;
158
+ isPinned?: boolean | undefined;
159
159
  relatedFAQs?: string[] | undefined;
160
160
  useSiteSettings?: boolean | undefined;
161
161
  }>;
@@ -83,7 +83,7 @@ export declare const faqSchema: z.ZodObject<{
83
83
  updatedAt: z.ZodOptional<z.ZodString>;
84
84
  }, "strip", z.ZodTypeAny, {
85
85
  id: string;
86
- category: "general" | "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support";
86
+ category: "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | "general";
87
87
  question: string;
88
88
  answer: {
89
89
  text: string;
@@ -109,7 +109,7 @@ export declare const faqSchema: z.ZodObject<{
109
109
  relatedFAQs?: string[] | undefined;
110
110
  }, {
111
111
  id: string;
112
- category: "general" | "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support";
112
+ category: "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | "general";
113
113
  question: string;
114
114
  answer: {
115
115
  text: string;
@@ -145,13 +145,13 @@ export declare const faqListParamsSchema: z.ZodObject<{
145
145
  perPage?: number | undefined;
146
146
  q?: string | undefined;
147
147
  page?: number | undefined;
148
- category?: "general" | "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | undefined;
148
+ category?: "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | "general" | undefined;
149
149
  homepage?: boolean | undefined;
150
150
  }, {
151
151
  perPage?: number | undefined;
152
152
  q?: string | undefined;
153
153
  page?: number | undefined;
154
- category?: "general" | "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | undefined;
154
+ category?: "orders_payment" | "shipping_delivery" | "returns_refunds" | "product_information" | "account_security" | "technical_support" | "general" | undefined;
155
155
  homepage?: boolean | undefined;
156
156
  }>;
157
157
  export * from "./firestore";
@@ -5,66 +5,66 @@ export declare const ORDER_FILTER_KEYS: {
5
5
  readonly user: readonly ["status", "paymentStatus", "minAmount", "maxAmount", "dateFrom", "dateTo"];
6
6
  };
7
7
  export declare const ORDER_ADMIN_SORT_OPTIONS: readonly [{
8
- readonly value: "-createdAt";
8
+ readonly value: string;
9
9
  readonly label: "Newest First";
10
10
  }, {
11
- readonly value: "createdAt";
11
+ readonly value: string;
12
12
  readonly label: "Oldest First";
13
13
  }, {
14
- readonly value: "-totalPrice";
14
+ readonly value: string;
15
15
  readonly label: "Amount: High to Low";
16
16
  }, {
17
- readonly value: "totalPrice";
17
+ readonly value: string;
18
18
  readonly label: "Amount: Low to High";
19
19
  }, {
20
- readonly value: "-orderDate";
20
+ readonly value: string;
21
21
  readonly label: "Order Date: Newest";
22
22
  }, {
23
- readonly value: "orderDate";
23
+ readonly value: string;
24
24
  readonly label: "Order Date: Oldest";
25
25
  }, {
26
- readonly value: "userName";
26
+ readonly value: string;
27
27
  readonly label: "Customer A–Z";
28
28
  }, {
29
- readonly value: "productTitle";
29
+ readonly value: string;
30
30
  readonly label: "Product A–Z";
31
31
  }];
32
32
  export declare const ORDER_SELLER_SORT_OPTIONS: readonly [{
33
- readonly value: "-createdAt";
33
+ readonly value: string;
34
34
  readonly label: "Newest First";
35
35
  }, {
36
- readonly value: "createdAt";
36
+ readonly value: string;
37
37
  readonly label: "Oldest First";
38
38
  }, {
39
- readonly value: "-totalPrice";
39
+ readonly value: string;
40
40
  readonly label: "Amount: High to Low";
41
41
  }, {
42
- readonly value: "totalPrice";
42
+ readonly value: string;
43
43
  readonly label: "Amount: Low to High";
44
44
  }, {
45
- readonly value: "-orderDate";
45
+ readonly value: string;
46
46
  readonly label: "Order Date: Newest";
47
47
  }, {
48
- readonly value: "userName";
48
+ readonly value: string;
49
49
  readonly label: "Customer A–Z";
50
50
  }];
51
51
  export declare const ORDER_USER_SORT_OPTIONS: readonly [{
52
- readonly value: "-createdAt";
52
+ readonly value: string;
53
53
  readonly label: "Newest First";
54
54
  }, {
55
- readonly value: "createdAt";
55
+ readonly value: string;
56
56
  readonly label: "Oldest First";
57
57
  }, {
58
- readonly value: "-totalPrice";
58
+ readonly value: string;
59
59
  readonly label: "Amount: High to Low";
60
60
  }, {
61
- readonly value: "totalPrice";
61
+ readonly value: string;
62
62
  readonly label: "Amount: Low to High";
63
63
  }, {
64
- readonly value: "-orderDate";
64
+ readonly value: string;
65
65
  readonly label: "Order Date: Newest";
66
66
  }, {
67
- readonly value: "orderDate";
67
+ readonly value: string;
68
68
  readonly label: "Order Date: Oldest";
69
69
  }];
70
70
  /** Controls which Sieve field set to expose in the filter UI */
@@ -4,58 +4,61 @@ import { useTranslations } from "next-intl";
4
4
  import { FilterFacetSection } from "../../filters/FilterFacetSection";
5
5
  import { RangeFilter } from "../../filters/RangeFilter";
6
6
  import { Div } from "../../../ui";
7
+ import { TABLE_KEYS } from "../../../constants/table-keys";
8
+ import { ORDER_FIELDS } from "../../../constants/field-names";
9
+ import { sortBy } from "../../../constants/sort";
7
10
  export const ORDER_FILTER_KEYS = {
8
11
  admin: [
9
- "status",
12
+ TABLE_KEYS.STATUS,
10
13
  "paymentStatus",
11
- "payoutStatus",
14
+ TABLE_KEYS.PAYOUT_STATUS,
12
15
  "minAmount",
13
16
  "maxAmount",
14
- "dateFrom",
15
- "dateTo",
17
+ TABLE_KEYS.DATE_FROM,
18
+ TABLE_KEYS.DATE_TO,
16
19
  ],
17
20
  seller: [
18
- "status",
21
+ TABLE_KEYS.STATUS,
19
22
  "paymentStatus",
20
23
  "minAmount",
21
24
  "maxAmount",
22
- "dateFrom",
23
- "dateTo",
25
+ TABLE_KEYS.DATE_FROM,
26
+ TABLE_KEYS.DATE_TO,
24
27
  ],
25
28
  user: [
26
- "status",
29
+ TABLE_KEYS.STATUS,
27
30
  "paymentStatus",
28
31
  "minAmount",
29
32
  "maxAmount",
30
- "dateFrom",
31
- "dateTo",
33
+ TABLE_KEYS.DATE_FROM,
34
+ TABLE_KEYS.DATE_TO,
32
35
  ],
33
36
  };
34
37
  export const ORDER_ADMIN_SORT_OPTIONS = [
35
- { value: "-createdAt", label: "Newest First" },
36
- { value: "createdAt", label: "Oldest First" },
37
- { value: "-totalPrice", label: "Amount: High to Low" },
38
- { value: "totalPrice", label: "Amount: Low to High" },
39
- { value: "-orderDate", label: "Order Date: Newest" },
40
- { value: "orderDate", label: "Order Date: Oldest" },
41
- { value: "userName", label: "Customer A–Z" },
42
- { value: "productTitle", label: "Product A–Z" },
38
+ { value: sortBy(ORDER_FIELDS.CREATED_AT), label: "Newest First" },
39
+ { value: sortBy(ORDER_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
40
+ { value: sortBy(ORDER_FIELDS.TOTAL_PRICE), label: "Amount: High to Low" },
41
+ { value: sortBy(ORDER_FIELDS.TOTAL_PRICE, "ASC"), label: "Amount: Low to High" },
42
+ { value: sortBy(ORDER_FIELDS.ORDER_DATE), label: "Order Date: Newest" },
43
+ { value: sortBy(ORDER_FIELDS.ORDER_DATE, "ASC"), label: "Order Date: Oldest" },
44
+ { value: sortBy(ORDER_FIELDS.USER_NAME, "ASC"), label: "Customer A–Z" },
45
+ { value: sortBy(ORDER_FIELDS.PRODUCT_TITLE, "ASC"), label: "Product A–Z" },
43
46
  ];
44
47
  export const ORDER_SELLER_SORT_OPTIONS = [
45
- { value: "-createdAt", label: "Newest First" },
46
- { value: "createdAt", label: "Oldest First" },
47
- { value: "-totalPrice", label: "Amount: High to Low" },
48
- { value: "totalPrice", label: "Amount: Low to High" },
49
- { value: "-orderDate", label: "Order Date: Newest" },
50
- { value: "userName", label: "Customer A–Z" },
48
+ { value: sortBy(ORDER_FIELDS.CREATED_AT), label: "Newest First" },
49
+ { value: sortBy(ORDER_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
50
+ { value: sortBy(ORDER_FIELDS.TOTAL_PRICE), label: "Amount: High to Low" },
51
+ { value: sortBy(ORDER_FIELDS.TOTAL_PRICE, "ASC"), label: "Amount: Low to High" },
52
+ { value: sortBy(ORDER_FIELDS.ORDER_DATE), label: "Order Date: Newest" },
53
+ { value: sortBy(ORDER_FIELDS.USER_NAME, "ASC"), label: "Customer A–Z" },
51
54
  ];
52
55
  export const ORDER_USER_SORT_OPTIONS = [
53
- { value: "-createdAt", label: "Newest First" },
54
- { value: "createdAt", label: "Oldest First" },
55
- { value: "-totalPrice", label: "Amount: High to Low" },
56
- { value: "totalPrice", label: "Amount: Low to High" },
57
- { value: "-orderDate", label: "Order Date: Newest" },
58
- { value: "orderDate", label: "Order Date: Oldest" },
56
+ { value: sortBy(ORDER_FIELDS.CREATED_AT), label: "Newest First" },
57
+ { value: sortBy(ORDER_FIELDS.CREATED_AT, "ASC"), label: "Oldest First" },
58
+ { value: sortBy(ORDER_FIELDS.TOTAL_PRICE), label: "Amount: High to Low" },
59
+ { value: sortBy(ORDER_FIELDS.TOTAL_PRICE, "ASC"), label: "Amount: Low to High" },
60
+ { value: sortBy(ORDER_FIELDS.ORDER_DATE), label: "Order Date: Newest" },
61
+ { value: sortBy(ORDER_FIELDS.ORDER_DATE, "ASC"), label: "Order Date: Oldest" },
59
62
  ];
60
63
  export function getOrderFilterKeys(variant) {
61
64
  return ORDER_FILTER_KEYS[variant];
@@ -3,21 +3,7 @@ import { BaseRepository, prepareForFirestore, } from "../../../providers/db-fire
3
3
  import { decryptPiiFields, encryptPiiFields, ORDER_PII_FIELDS, } from "../../../security";
4
4
  import { serverTimestamp } from "../../../contracts/field-ops";
5
5
  import { createOrderId, ORDER_COLLECTION, OrderStatusValues, } from "../schemas";
6
- const ORDER_FIELDS = {
7
- USER_ID: "userId",
8
- PRODUCT_ID: "productId",
9
- BUNDLE_ID: "bundleId",
10
- STATUS: "status",
11
- STATUS_VALUES: {
12
- PENDING: "pending",
13
- CONFIRMED: "confirmed",
14
- PROCESSING: "processing",
15
- SHIPPED: "shipped",
16
- DELIVERED: "delivered",
17
- CANCELLED: "cancelled",
18
- REFUNDED: "refunded",
19
- },
20
- };
6
+ import { ORDER_FIELDS } from "../../../constants/field-names";
21
7
  /**
22
8
  * Statuses that count toward a user's per-product / per-bundle purchase
23
9
  * allowance (SB6 maxPerUser enforcement). Cancelled and refunded orders are
@@ -147,9 +147,9 @@ export declare const orderSchema: z.ZodObject<{
147
147
  couponCode?: string | undefined;
148
148
  shippingCarrier?: string | undefined;
149
149
  tax?: number | undefined;
150
+ discount?: number | undefined;
150
151
  paymentGateway?: string | undefined;
151
152
  shippingCost?: number | undefined;
152
- discount?: number | undefined;
153
153
  timeline?: {
154
154
  status: "pending" | "processing" | "refunded" | "confirmed" | "shipped" | "delivered" | "cancelled" | "return_requested" | "returned";
155
155
  timestamp: string;
@@ -182,9 +182,9 @@ export declare const orderSchema: z.ZodObject<{
182
182
  couponCode?: string | undefined;
183
183
  shippingCarrier?: string | undefined;
184
184
  tax?: number | undefined;
185
+ discount?: number | undefined;
185
186
  paymentGateway?: string | undefined;
186
187
  shippingCost?: number | undefined;
187
- discount?: number | undefined;
188
188
  timeline?: {
189
189
  status: "pending" | "processing" | "refunded" | "confirmed" | "shipped" | "delivered" | "cancelled" | "return_requested" | "returned";
190
190
  timestamp: string;