@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
@@ -2,7 +2,7 @@ import { increment, serverTimestamp } from "../../../contracts/field-ops";
2
2
  import { DatabaseError } from "../../../errors";
3
3
  import { BaseRepository, prepareForFirestore, } from "../../../providers/db-firebase";
4
4
  import { cacheManager } from "../../../core";
5
- import { generateUniqueId, slugify } from "../../../utils";
5
+ import { generateUniqueId, slugify, buildSearchTokens, tokenizeQuery } from "../../../utils";
6
6
  import { PRODUCT_COLLECTION, ProductStatusValues, } from "../schemas";
7
7
  import { PRODUCT_FIELDS } from "../../../constants/field-names";
8
8
  /**
@@ -35,6 +35,9 @@ function buildListingKindClause(kind, inverted) {
35
35
  const op = inverted ? "!=" : "==";
36
36
  return `${PRODUCT_FIELDS.LISTING_TYPE}${op}${canonical}`;
37
37
  }
38
+ function buildProductSearchTokens(p) {
39
+ return buildSearchTokens(p.title, p.description, p.brand, p.brandSlug, p.categoryNames, p.tags, p.features, p.condition, p.card?.setName, p.card?.cardNumber, p.grading?.service, p.specifications?.map((s) => `${s.name} ${s.value}`));
40
+ }
38
41
  export class ProductRepository extends BaseRepository {
39
42
  constructor() {
40
43
  super(PRODUCT_COLLECTION);
@@ -80,7 +83,12 @@ export class ProductRepository extends BaseRepository {
80
83
  }
81
84
  async update(id, data) {
82
85
  this.cacheInvalidateForId(id);
83
- const updated = await super.update(id, data);
86
+ const current = await super.findById(id);
87
+ const merged = { ...current, ...data };
88
+ const updated = await super.update(id, {
89
+ ...data,
90
+ searchTokens: buildProductSearchTokens(merged),
91
+ });
84
92
  this.cacheSet(updated);
85
93
  return updated;
86
94
  }
@@ -100,6 +108,7 @@ export class ProductRepository extends BaseRepository {
100
108
  ...input,
101
109
  slug: id,
102
110
  availableQuantity: input.stockQuantity,
111
+ searchTokens: buildProductSearchTokens(input),
103
112
  createdAt: new Date(),
104
113
  updatedAt: new Date(),
105
114
  };
@@ -320,7 +329,17 @@ export class ProductRepository extends BaseRepository {
320
329
  if (opts?.storeId) {
321
330
  baseQuery = baseQuery.where(PRODUCT_FIELDS.STORE_ID, "==", opts.storeId);
322
331
  }
323
- if (opts?.categoriesIn && opts.categoriesIn.length > 0) {
332
+ const searchTokens = tokenizeQuery(opts?.search ?? "");
333
+ if (searchTokens.length > 0 && opts?.categoriesIn && opts.categoriesIn.length > 0) {
334
+ // Firestore cannot combine two array operators in one query
335
+ }
336
+ else if (searchTokens.length === 1) {
337
+ baseQuery = baseQuery.where("searchTokens", "array-contains", searchTokens[0]);
338
+ }
339
+ else if (searchTokens.length > 1) {
340
+ baseQuery = baseQuery.where("searchTokens", "array-contains-any", searchTokens);
341
+ }
342
+ if (searchTokens.length === 0 && opts?.categoriesIn && opts.categoriesIn.length > 0) {
324
343
  baseQuery = baseQuery.where(PRODUCT_FIELDS.CATEGORY_SLUGS, "array-contains-any", opts.categoriesIn);
325
344
  }
326
345
  return this.sieveQuery(model, ProductRepository.SIEVE_FIELDS, {
@@ -464,6 +483,7 @@ ProductRepository.SIEVE_FIELDS = {
464
483
  insurance: { canFilter: true, canSort: false },
465
484
  currency: { canFilter: true, canSort: false },
466
485
  freeShipping: { canFilter: true, canSort: false },
486
+ searchTokens: { canFilter: true, canSort: false },
467
487
  };
468
488
  /**
469
489
  * Virtual filter aliases — callers can pass `f=listingType==auction` for
@@ -169,6 +169,7 @@ export interface ProductDocument {
169
169
  preOrderCurrentCount?: number;
170
170
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship";
171
171
  preOrderCancellable?: boolean;
172
+ preOrderClosed?: boolean;
172
173
  isPromoted?: boolean;
173
174
  isOnSale?: boolean;
174
175
  isSold?: boolean;
@@ -246,6 +247,7 @@ export interface ProductDocument {
246
247
  shelf: string;
247
248
  bin: string;
248
249
  };
250
+ searchTokens?: string[];
249
251
  createdAt: Date;
250
252
  updatedAt: Date;
251
253
  }
@@ -271,13 +273,11 @@ export interface PrizeDrawItem {
271
273
  export declare const ProductStatusValues: {
272
274
  readonly DRAFT: "draft";
273
275
  readonly PUBLISHED: "published";
276
+ readonly IN_REVIEW: "in_review";
274
277
  readonly ARCHIVED: "archived";
275
- readonly SOLD: "sold";
276
- readonly OUT_OF_STOCK: "out_of_stock";
277
- readonly DISCONTINUED: "discontinued";
278
278
  };
279
279
  export declare const PRODUCT_COLLECTION: "products";
280
- export declare const PRODUCT_INDEXED_FIELDS: readonly ["storeId", "status", "category", "featured", "listingType", "isPromoted", "isOnSale", "isSold", "createdAt"];
280
+ export declare const PRODUCT_INDEXED_FIELDS: readonly ["storeId", "status", "category", "featured", "listingType", "isPromoted", "isOnSale", "isSold", "searchTokens", "createdAt"];
281
281
  export declare const DEFAULT_PRODUCT_DATA: Partial<ProductDocument>;
282
282
  export declare const PRODUCT_PUBLIC_FIELDS: readonly ["id", "title", "description", "category", "subcategory", "brand", "price", "currency", "stockQuantity", "availableQuantity", "images", "status", "storeName", "featured", "tags", "specifications", "features", "shippingInfo", "returnPolicy", "listingType", "auctionEndDate", "startingBid", "currentBid", "bidCount", "reservePrice", "buyNowPrice", "minBidIncrement", "autoExtendable", "auctionExtensionMinutes", "auctionShippingPaidBy", "buyItNowPriceInPaise", "bidsHaveStarted", "grading", "card", "classified", "digitalCode", "liveItem", "catalogProductId", "preOrderDeliveryDate", "preOrderDepositPercent", "preOrderDepositAmount", "preOrderMaxQuantity", "preOrderCurrentCount", "preOrderProductionStatus", "preOrderCancellable", "condition", "insurance", "insuranceCost", "shippingPaidBy", "isPromoted", "isOnSale", "isSold", "slug", "seoTitle", "seoDescription", "seoKeywords", "viewCount", "customFields", "customSections", "sublistingCategoryId", "groupId", "isGroupParent", "groupParentSlug", "groupChildSlugs", "groupTitle", "createdAt"];
283
283
  export declare const PRODUCT_UPDATABLE_FIELDS: readonly ["title", "description", "category", "subcategory", "brand", "price", "stockQuantity", "images", "status", "tags", "specifications", "features", "shippingInfo", "returnPolicy", "pickupAddressId", "condition", "insurance", "shippingPaidBy", "autoExtendable", "auctionExtensionMinutes", "auctionShippingPaidBy", "reservePrice", "buyNowPrice", "minBidIncrement", "buyItNowPriceInPaise", "bidsHaveStarted", "grading", "card", "classified", "digitalCode", "liveItem", "catalogProductId", "listingType", "preOrderDeliveryDate", "preOrderDepositPercent", "preOrderDepositAmount", "preOrderMaxQuantity", "preOrderProductionStatus", "preOrderCancellable", "isOnSale", "isSold", "seoTitle", "seoDescription", "seoKeywords", "customFields", "customSections", "sublistingCategoryId", "groupId", "isGroupParent", "groupParentSlug", "groupChildSlugs", "groupTitle"];
@@ -8,10 +8,8 @@ export const MAX_CUSTOM_SECTIONS = 3;
8
8
  export const ProductStatusValues = {
9
9
  DRAFT: "draft",
10
10
  PUBLISHED: "published",
11
+ IN_REVIEW: "in_review",
11
12
  ARCHIVED: "archived",
12
- SOLD: "sold",
13
- OUT_OF_STOCK: "out_of_stock",
14
- DISCONTINUED: "discontinued",
15
13
  };
16
14
  export const PRODUCT_COLLECTION = "products";
17
15
  export const PRODUCT_INDEXED_FIELDS = [
@@ -23,6 +21,7 @@ export const PRODUCT_INDEXED_FIELDS = [
23
21
  "isPromoted",
24
22
  "isOnSale",
25
23
  "isSold",
24
+ "searchTokens",
26
25
  "createdAt",
27
26
  ];
28
27
  export const DEFAULT_PRODUCT_DATA = {
@@ -91,7 +91,7 @@ export declare const productItemSchema: z.ZodObject<{
91
91
  category: z.ZodOptional<z.ZodString>;
92
92
  categorySlug: z.ZodOptional<z.ZodString>;
93
93
  sellerAvatar: z.ZodOptional<z.ZodString>;
94
- status: z.ZodEnum<["draft", "published", "archived", "sold", "out_of_stock", "discontinued"]>;
94
+ status: z.ZodEnum<["draft", "published", "in_review", "archived"]>;
95
95
  condition: z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken"]>>;
96
96
  listingType: z.ZodOptional<z.ZodEnum<["standard", "auction", "pre-order", "prize-draw", "bundle"]>>;
97
97
  maxPerUser: z.ZodOptional<z.ZodNumber>;
@@ -284,7 +284,7 @@ export declare const productItemSchema: z.ZodObject<{
284
284
  }[] | undefined;
285
285
  }>, "many">>;
286
286
  }, "strip", z.ZodTypeAny, {
287
- status: "draft" | "published" | "archived" | "sold" | "out_of_stock" | "discontinued";
287
+ status: "draft" | "published" | "archived" | "in_review";
288
288
  id: string;
289
289
  title: string;
290
290
  price: number;
@@ -413,7 +413,7 @@ export declare const productItemSchema: z.ZodObject<{
413
413
  } | undefined;
414
414
  publishedAt?: string | undefined;
415
415
  }, {
416
- status: "draft" | "published" | "archived" | "sold" | "out_of_stock" | "discontinued";
416
+ status: "draft" | "published" | "archived" | "in_review";
417
417
  id: string;
418
418
  title: string;
419
419
  price: number;
@@ -546,7 +546,7 @@ export declare const productItemSchema: z.ZodObject<{
546
546
  export declare const productListParamsSchema: z.ZodObject<{
547
547
  q: z.ZodOptional<z.ZodString>;
548
548
  category: z.ZodOptional<z.ZodString>;
549
- status: z.ZodOptional<z.ZodEnum<["draft", "published", "archived", "sold", "out_of_stock", "discontinued"]>>;
549
+ status: z.ZodOptional<z.ZodEnum<["draft", "published", "in_review", "archived"]>>;
550
550
  condition: z.ZodOptional<z.ZodEnum<["new", "like_new", "good", "fair", "poor", "used", "refurbished", "broken"]>>;
551
551
  minPrice: z.ZodOptional<z.ZodNumber>;
552
552
  maxPrice: z.ZodOptional<z.ZodNumber>;
@@ -558,7 +558,7 @@ export declare const productListParamsSchema: z.ZodObject<{
558
558
  perPage: z.ZodOptional<z.ZodNumber>;
559
559
  featured: z.ZodOptional<z.ZodBoolean>;
560
560
  }, "strip", z.ZodTypeAny, {
561
- status?: "draft" | "published" | "archived" | "sold" | "out_of_stock" | "discontinued" | undefined;
561
+ status?: "draft" | "published" | "archived" | "in_review" | undefined;
562
562
  sort?: string | undefined;
563
563
  storeId?: string | undefined;
564
564
  category?: string | undefined;
@@ -572,7 +572,7 @@ export declare const productListParamsSchema: z.ZodObject<{
572
572
  maxPrice?: number | undefined;
573
573
  inStock?: boolean | undefined;
574
574
  }, {
575
- status?: "draft" | "published" | "archived" | "sold" | "out_of_stock" | "discontinued" | undefined;
575
+ status?: "draft" | "published" | "archived" | "in_review" | undefined;
576
576
  sort?: string | undefined;
577
577
  storeId?: string | undefined;
578
578
  category?: string | undefined;
@@ -51,10 +51,8 @@ export const productItemSchema = z.object({
51
51
  status: z.enum([
52
52
  "draft",
53
53
  "published",
54
+ "in_review",
54
55
  "archived",
55
- "sold",
56
- "out_of_stock",
57
- "discontinued",
58
56
  ]),
59
57
  condition: z
60
58
  .enum([
@@ -193,10 +191,8 @@ export const productListParamsSchema = z.object({
193
191
  .enum([
194
192
  "draft",
195
193
  "published",
194
+ "in_review",
196
195
  "archived",
197
- "sold",
198
- "out_of_stock",
199
- "discontinued",
200
196
  ])
201
197
  .optional(),
202
198
  condition: z
@@ -1,7 +1,7 @@
1
1
  import type { MediaField } from "../../media/types/index";
2
2
  import type { CustomField, CustomSection } from "../schemas/firestore";
3
3
  export type { CustomField, CustomSection } from "../schemas/firestore";
4
- export type ProductStatus = "draft" | "published" | "archived" | "sold" | "out_of_stock" | "discontinued";
4
+ export type ProductStatus = "draft" | "published" | "in_review" | "archived";
5
5
  export type ProductCondition = "new" | "like_new" | "good" | "fair" | "poor" | "used" | "refurbished" | "broken";
6
6
  /**
7
7
  * Canonical listing-kind discriminator (SB1-G Phase 4).
@@ -267,8 +267,8 @@ export async function checkoutOffer(userId, offerId) {
267
267
  if (!product)
268
268
  throw new NotFoundError(ERROR_MESSAGES.PRODUCT.NOT_FOUND);
269
269
  if ((product.availableQuantity ?? 0) <= 0 ||
270
- product.status === ProductStatusValues.DISCONTINUED ||
271
- product.status === ProductStatusValues.SOLD)
270
+ product.isSold === true ||
271
+ product.status === ProductStatusValues.ARCHIVED)
272
272
  throw new ValidationError(ERROR_MESSAGES.OFFER.PRODUCT_UNAVAILABLE);
273
273
  serverLogger.info("checkoutOffer — adding to cart", {
274
274
  offerId,
@@ -0,0 +1,13 @@
1
+ import type { SellerProductDraft, SellerProductShellProps } from "./SellerProductShell";
2
+ export interface QuickProductFormProps {
3
+ values: SellerProductDraft;
4
+ onChange: (partial: Partial<SellerProductDraft>) => void;
5
+ onPublish: () => void;
6
+ onSave: () => void;
7
+ onSwitchToFull: () => void;
8
+ isLoading: boolean;
9
+ storeSlug?: string;
10
+ renderCategorySelector?: SellerProductShellProps["renderCategorySelector"];
11
+ onUploadImage?: (file: File) => Promise<string>;
12
+ }
13
+ export declare function QuickProductForm({ values, onChange, onPublish, onSave, onSwitchToFull, isLoading, renderCategorySelector, onUploadImage, }: QuickProductFormProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { Alert, Button, Div, FormField, Stack, Text } from "../../../ui";
5
+ function toRupees(paise) {
6
+ return paise != null && paise > 0 ? String(Math.round(paise / 100)) : "";
7
+ }
8
+ function toPaise(rupeeStr) {
9
+ return Math.round((parseFloat(rupeeStr) || 0) * 100);
10
+ }
11
+ function validate(values) {
12
+ const errors = {};
13
+ if (!values.title?.trim() || (values.title.trim().length < 3)) {
14
+ errors.title = "Title must be at least 3 characters";
15
+ }
16
+ if (!values.price || values.price <= 0) {
17
+ errors.price = "Price is required";
18
+ }
19
+ if (!values.mainImage) {
20
+ errors.mainImage = "Product image is required";
21
+ }
22
+ if (!values.category?.trim()) {
23
+ errors.category = "Category is required";
24
+ }
25
+ return errors;
26
+ }
27
+ export function QuickProductForm({ values, onChange, onPublish, onSave, onSwitchToFull, isLoading, renderCategorySelector, onUploadImage, }) {
28
+ const [errors, setErrors] = useState({});
29
+ const [touched, setTouched] = useState(false);
30
+ const handlePublish = () => {
31
+ setTouched(true);
32
+ const errs = validate(values);
33
+ setErrors(errs);
34
+ if (Object.keys(errs).length === 0) {
35
+ onPublish();
36
+ }
37
+ };
38
+ const handleSave = () => {
39
+ if (!values.title?.trim()) {
40
+ setErrors({ title: "Title is required to save a draft" });
41
+ setTouched(true);
42
+ return;
43
+ }
44
+ onSave();
45
+ };
46
+ return (_jsxs(Stack, { gap: "lg", className: "p-5", children: [_jsx(Div, { children: _jsx(Text, { className: "text-sm text-zinc-500 dark:text-zinc-400", children: "Quick add \u2014 fill the essentials and publish. You can add more details later." }) }), touched && Object.keys(errors).length > 0 && (_jsx(Alert, { variant: "error", children: "Please fix the highlighted fields before publishing." })), _jsx(FormField, { name: "title", label: "Product Name", type: "text", value: values.title ?? "", onChange: (v) => onChange({ title: v }), placeholder: "e.g. Charizard Base Set PSA 9", required: true, error: touched ? errors.title : undefined }), renderCategorySelector ? (_jsxs(Div, { children: [_jsx(Text, { className: "text-sm font-medium text-zinc-700 dark:text-zinc-300 mb-1", children: "Category" }), renderCategorySelector({
47
+ value: values.category ?? "",
48
+ onChange: (v) => onChange({ category: v }),
49
+ }), touched && errors.category && (_jsx(Text, { className: "text-xs text-[var(--appkit-color-error)] mt-1", children: errors.category }))] })) : (_jsx(FormField, { name: "category", label: "Category", type: "text", value: values.category ?? "", onChange: (v) => onChange({ category: v }), placeholder: "e.g. Trading Cards", error: touched ? errors.category : undefined })), _jsx(FormField, { name: "price", label: "Price (\u20B9)", type: "number", value: toRupees(values.price), onChange: (v) => onChange({ price: toPaise(v) }), placeholder: "e.g. 499", required: true, error: touched ? errors.price : undefined }), _jsx(FormField, { name: "mainImage", label: "Product Image", type: "image", value: values.mainImage ?? "", onChange: (v) => onChange({ mainImage: v }), onUpload: onUploadImage, required: true, error: touched ? errors.mainImage : undefined }), _jsx(FormField, { name: "description", label: "Description", type: "textarea", value: values.description ?? "", onChange: (v) => onChange({ description: v }), placeholder: "Brief description (optional)", rows: 3 }), _jsx(FormField, { name: "stockQuantity", label: "Stock Quantity", type: "number", value: String(values.stockQuantity ?? 1), onChange: (v) => onChange({ stockQuantity: Math.max(0, parseInt(v, 10) || 0) }), placeholder: "1", hint: "How many units are available" }), _jsxs(Div, { className: "flex flex-col gap-3 pt-2", children: [_jsxs(Div, { className: "flex gap-3", children: [_jsx(Button, { variant: "primary", onClick: handlePublish, disabled: isLoading, className: "flex-1", children: isLoading ? "Publishing..." : "Publish" }), _jsx(Button, { variant: "secondary", onClick: handleSave, disabled: isLoading, children: "Save Draft" })] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: onSwitchToFull, className: "self-center text-xs", children: "Show all fields (advanced)" })] })] }));
50
+ }
@@ -2,5 +2,8 @@ import React from "react";
2
2
  import type { ListingViewShellProps } from "../../../ui";
3
3
  export interface SellerAuctionsViewProps extends ListingViewShellProps {
4
4
  renderHeader?: (onAdd: () => void) => React.ReactNode;
5
+ onEditClick?: (id: string) => void;
6
+ onDelete?: (id: string) => Promise<void>;
7
+ onBulkDelete?: (ids: string[]) => Promise<void>;
5
8
  }
6
- export declare function SellerAuctionsView({ renderHeader, children, ...props }: SellerAuctionsViewProps): import("react/jsx-runtime").JSX.Element;
9
+ export declare function SellerAuctionsView({ renderHeader, children, onEditClick, onDelete, onBulkDelete, ...props }: SellerAuctionsViewProps): import("react/jsx-runtime").JSX.Element;
@@ -5,9 +5,11 @@ import { X } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
7
  import { AdminViewCards } from "../../admin/components/AdminViewCards";
8
- import { FilterChipGroup, ListingToolbar, Pagination, ListingViewShell } from "../../../ui";
8
+ import { BulkActionBar, ConfirmDeleteModal, FilterChipGroup, ListingToolbar, Pagination, ListingViewShell, RowActionMenu } from "../../../ui";
9
9
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
10
10
  import { SELLER_AUCTION_STATUS_TABS } from "../../admin/constants/filter-tabs";
11
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
12
+ import { ROUTES } from "../../../constants";
11
13
  import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
12
14
  import { DataTable } from "../../admin/components/DataTable";
13
15
  const PAGE_SIZE = 25;
@@ -20,9 +22,11 @@ const SORT_OPTIONS = [
20
22
  { value: "createdAt", label: "Oldest" },
21
23
  ];
22
24
  const STATUS_OPTIONS = SELLER_AUCTION_STATUS_TABS;
23
- export function SellerAuctionsView({ renderHeader, children, ...props }) {
25
+ export function SellerAuctionsView({ renderHeader, children, onEditClick, onDelete, onBulkDelete, ...props }) {
24
26
  const hasChildren = React.Children.count(children) > 0;
25
27
  const [view, setView] = useState("table");
28
+ const [deleteTargetId, setDeleteTargetId] = useState(null);
29
+ const [deletingId, setDeletingId] = useState(null);
26
30
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
27
31
  const [searchInput, setSearchInput] = useState(table.get("q") || "");
28
32
  const [filterOpen, setFilterOpen] = useState(false);
@@ -78,8 +82,38 @@ export function SellerAuctionsView({ renderHeader, children, ...props }) {
78
82
  const currentPage = table.getNumber("page", 1);
79
83
  const totalPages = Math.ceil(total / PAGE_SIZE);
80
84
  const selection = useBulkSelection({ items: rows, keyExtractor: (r) => r.id });
85
+ const bulkActions = onBulkDelete
86
+ ? [{
87
+ id: "bulk-delete",
88
+ label: ACTIONS.STORE["delete-listing"].label,
89
+ variant: "danger",
90
+ onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
91
+ }]
92
+ : [];
93
+ const handleDelete = useCallback(async (id) => {
94
+ setDeletingId(id);
95
+ try {
96
+ if (onDelete)
97
+ await onDelete(id);
98
+ else
99
+ await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
100
+ }
101
+ finally {
102
+ setDeletingId(null);
103
+ setDeleteTargetId(null);
104
+ }
105
+ }, [onDelete]);
106
+ const handleEdit = useCallback((id) => {
107
+ if (onEditClick)
108
+ onEditClick(id);
109
+ else
110
+ window.location.href = String(ROUTES.STORE.PRODUCTS_EDIT(id));
111
+ }, [onEditClick]);
81
112
  if (hasChildren) {
82
113
  return _jsx(ListingViewShell, { portal: "seller", ...props, children: children });
83
114
  }
84
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions by product name", 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 }), 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 auctions found" })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No auctions found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), 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: "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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: 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})` : ""] }) })] })] }))] }));
115
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search auctions by product name", 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 }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _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 auctions found", selectedIds: selection.selectedIdSet, onToggleSelect: (id) => selection.toggle(id), onToggleSelectAll: () => selection.toggleAll(), renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
116
+ { label: ACTIONS.STORE["edit-listing"].label, onClick: () => handleEdit(row.id) },
117
+ { label: ACTIONS.STORE["delete-listing"].label, destructive: true, onClick: () => setDeleteTargetId(row.id), disabled: deletingId === row.id },
118
+ ] })) })) : (_jsx(AdminViewCards, { rows: rows, view: view, isLoading: isLoading, emptyLabel: "No auctions found", onRowClick: undefined, selectedIdSet: selection.selectedIdSet, onToggleSelect: selection.toggle }))] }), 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: "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 space-y-5", children: _jsx(FilterChipGroup, { label: "Status", tabs: STATUS_OPTIONS, value: pendingFilters.status ?? "", onChange: (id) => setPendingFilters((p) => ({ ...p, status: 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})` : ""] }) })] })] })), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Auction", message: "Are you sure you want to delete this auction? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
85
119
  }
@@ -6,6 +6,7 @@ import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
7
  import { Badge, BulkActionBar, Div, FilterChipGroup, ListingToolbar, Pagination, Text } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
10
  import { SELLER_BID_STATUS_TABS } from "../../admin/constants/filter-tabs";
10
11
  import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
11
12
  import { DataTable } from "../../admin/components/DataTable";
@@ -147,7 +148,7 @@ export function SellerBidsView({ endpoint = SELLER_ENDPOINTS.BIDS }) {
147
148
  selection.clearSelection();
148
149
  }, [selection]);
149
150
  const bulkActions = [
150
- { id: "cancel", label: "Cancel selected", onClick: () => void bulkCancel(), variant: "danger" },
151
+ { id: "cancel", label: ACTIONS.SELLER["cancel-bid"].label, onClick: () => void bulkCancel(), variant: "danger" },
151
152
  ];
152
153
  return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: activeFilterCount, onFiltersClick: openFilters, searchValue: searchInput, searchPlaceholder: "Search by bidder name", 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 }), 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) }) })), selection.selectedIds.length > 0 && (_jsx("div", { className: "sticky z-20 px-3 sm:px-4 py-2 bg-white/95 dark:bg-slate-900/95 backdrop-blur-sm border-b border-zinc-200 dark:border-slate-700", style: { top: "calc(var(--header-height, 0px) + 88px)" }, children: _jsx(BulkActionBar, { selectedCount: selection.selectedIds.length, onClearSelection: selection.clearSelection, actions: bulkActions }) })), _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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), table.get("grouped") === "0" ? (_jsx(DataTable, { rows: rows, columns: columns, isLoading: isLoading, emptyLabel: "No bids found for your auctions", selectedIds: selection.selectedIdSet, onToggleSelect: selection.toggle, onToggleSelectAll: () => selection.toggleAll() })) : (_jsxs("div", { className: "space-y-3", children: [groupedRows.length === 0 && !isLoading && (_jsx(Text, { className: "text-sm text-zinc-500", children: "No bids found for your auctions." })), groupedRows.map((group) => {
153
154
  const collapsed = collapsedGroups.has(group.id);
@@ -1,5 +1,7 @@
1
1
  export interface SellerBundlesViewProps {
2
2
  onCreateClick?: () => void;
3
+ onEditClick?: (id: string) => void;
4
+ onDelete?: (id: string) => Promise<void>;
3
5
  onBulkDelete?: (ids: string[]) => Promise<void>;
4
6
  }
5
- export declare function SellerBundlesView({ onCreateClick, onBulkDelete, }: SellerBundlesViewProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function SellerBundlesView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }: SellerBundlesViewProps): import("react/jsx-runtime").JSX.Element;
@@ -4,8 +4,9 @@ import { useState, useCallback } from "react";
4
4
  import { Plus } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { BulkActionBar, Button, DataTable, Div, ListingToolbar, Pagination, Text, } from "../../../ui";
7
+ import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
10
  import { ROUTES } from "../../..";
10
11
  import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
11
12
  import { TABLE_KEYS } from "../../../constants/table-keys";
@@ -48,10 +49,12 @@ const COLUMNS = [
48
49
  render: (row) => _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.createdAt }),
49
50
  },
50
51
  ];
51
- export function SellerBundlesView({ onCreateClick, onBulkDelete, }) {
52
+ export function SellerBundlesView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }) {
52
53
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
53
54
  const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
54
- const [view] = useState("table");
55
+ const [view, setView] = useState("table");
56
+ const [deleteTargetId, setDeleteTargetId] = useState(null);
57
+ const [deletingId, setDeletingId] = useState(null);
55
58
  const commitSearch = useCallback(() => {
56
59
  table.set(TABLE_KEYS.QUERY, searchInput.trim());
57
60
  }, [searchInput, table]);
@@ -87,11 +90,30 @@ export function SellerBundlesView({ onCreateClick, onBulkDelete, }) {
87
90
  const bulkActions = onBulkDelete
88
91
  ? [{
89
92
  id: "bulk-delete",
90
- label: "Delete selected",
93
+ label: ACTIONS.STORE["delete-listing"].label,
91
94
  variant: "danger",
92
95
  onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
93
96
  }]
94
97
  : [];
98
+ const handleDelete = useCallback(async (id) => {
99
+ setDeletingId(id);
100
+ try {
101
+ if (onDelete)
102
+ await onDelete(id);
103
+ else
104
+ await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
105
+ }
106
+ finally {
107
+ setDeletingId(null);
108
+ setDeleteTargetId(null);
109
+ }
110
+ }, [onDelete]);
111
+ const handleEdit = useCallback((id) => {
112
+ if (onEditClick)
113
+ onEditClick(id);
114
+ else
115
+ window.location.href = String(ROUTES.STORE.PRODUCTS_EDIT(id));
116
+ }, [onEditClick]);
95
117
  const handleCreate = useCallback(() => {
96
118
  if (onCreateClick) {
97
119
  onCreateClick();
@@ -100,5 +122,16 @@ export function SellerBundlesView({ onCreateClick, onBulkDelete, }) {
100
122
  window.location.href = String(ROUTES.STORE.PRODUCTS_NEW);
101
123
  }
102
124
  }, [onCreateClick]);
103
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search bundles...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: view, onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Bundle" })] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No bundles yet \u2014 create a bundle to group multiple products together" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids) }))] })] }));
125
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search bundles...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: view, onViewChange: (v) => setView(v), onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Bundle" })] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No bundles yet \u2014 create a bundle to group multiple products together" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids), actions: (row) => (_jsx(RowActionMenu, { actions: [
126
+ {
127
+ label: ACTIONS.STORE["edit-listing"].label,
128
+ onClick: () => handleEdit(row.id),
129
+ },
130
+ {
131
+ label: ACTIONS.STORE["delete-listing"].label,
132
+ destructive: true,
133
+ onClick: () => setDeleteTargetId(row.id),
134
+ disabled: deletingId === row.id,
135
+ },
136
+ ] })) }))] }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Bundle", message: "Are you sure you want to delete this bundle? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
104
137
  }
@@ -1,5 +1,7 @@
1
1
  export interface SellerClassifiedViewProps {
2
2
  onCreateClick?: () => void;
3
+ onEditClick?: (id: string) => void;
4
+ onDelete?: (id: string) => Promise<void>;
3
5
  onBulkDelete?: (ids: string[]) => Promise<void>;
4
6
  }
5
- export declare function SellerClassifiedView({ onCreateClick, onBulkDelete, }: SellerClassifiedViewProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function SellerClassifiedView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }: SellerClassifiedViewProps): import("react/jsx-runtime").JSX.Element;
@@ -4,8 +4,9 @@ import { useState, useCallback } from "react";
4
4
  import { Plus } from "lucide-react";
5
5
  import { useUrlTable } from "../../../react/hooks/useUrlTable";
6
6
  import { useBulkSelection } from "../../../react/hooks/useBulkSelection";
7
- import { BulkActionBar, Button, DataTable, Div, ListingToolbar, Pagination, Text, } from "../../../ui";
7
+ import { BulkActionBar, Button, ConfirmDeleteModal, DataTable, Div, ListingToolbar, Pagination, RowActionMenu, Text, } from "../../../ui";
8
8
  import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
9
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
9
10
  import { ROUTES } from "../../..";
10
11
  import { toRecordArray, toRelativeDate, toRupees, toStringValue, useSellerListingData, } from "../hooks/useSellerListingData";
11
12
  import { TABLE_KEYS } from "../../../constants/table-keys";
@@ -54,10 +55,12 @@ const COLUMNS = [
54
55
  render: (row) => _jsx(Text, { className: "text-sm text-[var(--appkit-color-text-muted)]", children: row.createdAt }),
55
56
  },
56
57
  ];
57
- export function SellerClassifiedView({ onCreateClick, onBulkDelete, }) {
58
+ export function SellerClassifiedView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }) {
58
59
  const table = useUrlTable({ defaults: { pageSize: String(PAGE_SIZE), sort: DEFAULT_SORT } });
59
60
  const [searchInput, setSearchInput] = useState(table.get(TABLE_KEYS.QUERY) || "");
60
61
  const [view] = useState("table");
62
+ const [deleteTargetId, setDeleteTargetId] = useState(null);
63
+ const [deletingId, setDeletingId] = useState(null);
61
64
  const commitSearch = useCallback(() => {
62
65
  table.set(TABLE_KEYS.QUERY, searchInput.trim());
63
66
  }, [searchInput, table]);
@@ -96,11 +99,30 @@ export function SellerClassifiedView({ onCreateClick, onBulkDelete, }) {
96
99
  const bulkActions = onBulkDelete
97
100
  ? [{
98
101
  id: "bulk-delete",
99
- label: "Delete selected",
102
+ label: ACTIONS.STORE["delete-listing"].label,
100
103
  variant: "danger",
101
104
  onClick: async () => { await onBulkDelete(selection.selectedIds); selection.clearSelection(); },
102
105
  }]
103
106
  : [];
107
+ const handleDelete = useCallback(async (id) => {
108
+ setDeletingId(id);
109
+ try {
110
+ if (onDelete)
111
+ await onDelete(id);
112
+ else
113
+ await fetch(`/api/store/products/${id}`, { method: "DELETE", credentials: "include" });
114
+ }
115
+ finally {
116
+ setDeletingId(null);
117
+ setDeleteTargetId(null);
118
+ }
119
+ }, [onDelete]);
120
+ const handleEdit = useCallback((id) => {
121
+ if (onEditClick)
122
+ onEditClick(id);
123
+ else
124
+ window.location.href = String(ROUTES.STORE.CLASSIFIED_EDIT(id));
125
+ }, [onEditClick]);
104
126
  const handleCreate = useCallback(() => {
105
127
  if (onCreateClick) {
106
128
  onCreateClick();
@@ -109,5 +131,16 @@ export function SellerClassifiedView({ onCreateClick, onBulkDelete, }) {
109
131
  window.location.href = String(ROUTES.STORE.CLASSIFIED_NEW);
110
132
  }
111
133
  }, [onCreateClick]);
112
- return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search classified listings...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: view, onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Classified" })] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No classified listings yet \u2014 post your first buy/sell/trade ad" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids) }))] })] }));
134
+ return (_jsxs("div", { className: "min-h-screen", children: [_jsx(ListingToolbar, { filterCount: 0, searchValue: searchInput, searchPlaceholder: "Search classified listings...", onSearchChange: setSearchInput, onSearchCommit: commitSearch, sortValue: table.get(TABLE_KEYS.SORT) || DEFAULT_SORT, sortOptions: SORT_OPTIONS, onSortChange: (v) => { table.set(TABLE_KEYS.SORT, v); }, showTableView: true, view: view, onViewChange: () => { }, onResetAll: resetAll, hasActiveState: hasActiveState, extra: _jsxs(Button, { size: "sm", onClick: handleCreate, className: "flex items-center gap-1.5", children: [_jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: "New Classified" })] }) }), 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) }) })), selection.selectedCount > 0 && bulkActions.length > 0 && (_jsx(BulkActionBar, { selectedCount: selection.selectedCount, actions: bulkActions, onClearSelection: selection.clearSelection })), _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 dark:bg-red-950/40 dark:border-red-900/60 px-4 py-3 text-sm text-red-700 dark:text-red-200", children: errorMessage })), isLoading ? (_jsx(Div, { className: "space-y-2", children: Array.from({ length: 5 }).map((_, i) => (_jsx(Div, { className: "h-14 animate-pulse rounded-xl border border-zinc-100 dark:border-slate-700 bg-zinc-50 dark:bg-slate-800" }, i))) })) : rows.length === 0 ? (_jsx(Div, { className: "py-16 text-center", children: _jsx(Text, { className: "text-zinc-400 dark:text-zinc-500", children: "No classified listings yet \u2014 post your first buy/sell/trade ad" }) })) : (_jsx(DataTable, { columns: COLUMNS, data: rows, keyExtractor: (r) => r.id, selectable: bulkActions.length > 0, selectedIds: selection.selectedIds, onSelectionChange: (ids) => selection.setSelectedIds(ids), actions: (row) => (_jsx(RowActionMenu, { actions: [
135
+ {
136
+ label: ACTIONS.STORE["edit-listing"].label,
137
+ onClick: () => handleEdit(row.id),
138
+ },
139
+ {
140
+ label: ACTIONS.STORE["delete-listing"].label,
141
+ destructive: true,
142
+ onClick: () => setDeleteTargetId(row.id),
143
+ disabled: deletingId === row.id,
144
+ },
145
+ ] })) }))] }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Classified Listing", message: "Are you sure you want to delete this classified listing? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
113
146
  }
@@ -1,5 +1,7 @@
1
1
  export interface SellerDigitalCodesViewProps {
2
2
  onCreateClick?: () => void;
3
+ onEditClick?: (id: string) => void;
4
+ onDelete?: (id: string) => Promise<void>;
3
5
  onBulkDelete?: (ids: string[]) => Promise<void>;
4
6
  }
5
- export declare function SellerDigitalCodesView({ onCreateClick, onBulkDelete, }: SellerDigitalCodesViewProps): import("react/jsx-runtime").JSX.Element;
7
+ export declare function SellerDigitalCodesView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }: SellerDigitalCodesViewProps): import("react/jsx-runtime").JSX.Element;