@mohasinac/appkit 3.2.19 → 3.3.0

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 (202) hide show
  1. package/dist/_internal/server/features/checkout/actions.d.ts +2 -0
  2. package/dist/_internal/server/features/checkout/actions.js +239 -190
  3. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  4. package/dist/_internal/server/functions/scheduled.js +9 -1
  5. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +8 -11
  6. package/dist/_internal/server/jobs/core/emiInstallmentReminder.d.ts +8 -0
  7. package/dist/_internal/server/jobs/core/emiInstallmentReminder.js +78 -0
  8. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.d.ts +2 -2
  9. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -10
  10. package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.d.ts +2 -0
  11. package/dist/_internal/server/jobs/handlers/emiInstallmentReminder.js +2 -0
  12. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  13. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  14. package/dist/_internal/server/providers/index.d.ts +4 -4
  15. package/dist/_internal/server/providers/index.js +4 -4
  16. package/dist/_internal/server/providers/payment/razorpay-mock.d.ts +3 -2
  17. package/dist/_internal/server/providers/payment/razorpay-mock.js +3 -1
  18. package/dist/_internal/server/providers/resolve.d.ts +10 -13
  19. package/dist/_internal/server/providers/resolve.js +9 -15
  20. package/dist/_internal/shared/actions/action-registry.js +28 -0
  21. package/dist/_internal/shared/checkout/rules/_defaults.js +1 -0
  22. package/dist/_internal/shared/checkout/rules/_registry.js +4 -0
  23. package/dist/_internal/shared/checkout/rules/art.rule.d.ts +9 -0
  24. package/dist/_internal/shared/checkout/rules/art.rule.js +5 -0
  25. package/dist/_internal/shared/checkout/rules/auction.rule.js +1 -0
  26. package/dist/_internal/shared/checkout/rules/classified.rule.js +1 -0
  27. package/dist/_internal/shared/checkout/rules/index.d.ts +2 -0
  28. package/dist/_internal/shared/checkout/rules/index.js +2 -0
  29. package/dist/_internal/shared/checkout/rules/stickers.rule.d.ts +9 -0
  30. package/dist/_internal/shared/checkout/rules/stickers.rule.js +5 -0
  31. package/dist/_internal/shared/checkout/rules/types.d.ts +6 -0
  32. package/dist/_internal/shared/features/checkout/config.d.ts +16 -1
  33. package/dist/_internal/shared/features/checkout/config.js +16 -1
  34. package/dist/_internal/shared/features/emi/schedule.d.ts +63 -0
  35. package/dist/_internal/shared/features/emi/schedule.js +62 -0
  36. package/dist/_internal/shared/fees/calculator.d.ts +53 -0
  37. package/dist/_internal/shared/fees/calculator.js +40 -0
  38. package/dist/_internal/shared/listing-types/_registry.d.ts +22 -66
  39. package/dist/_internal/shared/listing-types/_registry.js +19 -2
  40. package/dist/_internal/shared/listing-types/art/config.d.ts +15 -0
  41. package/dist/_internal/shared/listing-types/art/config.js +14 -0
  42. package/dist/_internal/shared/listing-types/art/ctas.d.ts +1 -0
  43. package/dist/_internal/shared/listing-types/art/ctas.js +3 -0
  44. package/dist/_internal/shared/listing-types/art/og.d.ts +1 -0
  45. package/dist/_internal/shared/listing-types/art/og.js +1 -0
  46. package/dist/_internal/shared/listing-types/art/schema.d.ts +2 -0
  47. package/dist/_internal/shared/listing-types/art/schema.js +3 -0
  48. package/dist/_internal/shared/listing-types/art/seed-factory.d.ts +1 -0
  49. package/dist/_internal/shared/listing-types/art/seed-factory.js +1 -0
  50. package/dist/_internal/shared/listing-types/auction/config.d.ts +8 -0
  51. package/dist/_internal/shared/listing-types/auction/config.js +6 -0
  52. package/dist/_internal/shared/listing-types/capabilities.js +17 -0
  53. package/dist/_internal/shared/listing-types/classified/config.d.ts +8 -0
  54. package/dist/_internal/shared/listing-types/classified/config.js +6 -0
  55. package/dist/_internal/shared/listing-types/digital-code/config.d.ts +8 -0
  56. package/dist/_internal/shared/listing-types/digital-code/config.js +6 -0
  57. package/dist/_internal/shared/listing-types/live/config.d.ts +8 -0
  58. package/dist/_internal/shared/listing-types/live/config.js +6 -0
  59. package/dist/_internal/shared/listing-types/pre-order/config.d.ts +8 -0
  60. package/dist/_internal/shared/listing-types/pre-order/config.js +6 -0
  61. package/dist/_internal/shared/listing-types/prize-draw/config.d.ts +8 -0
  62. package/dist/_internal/shared/listing-types/prize-draw/config.js +6 -0
  63. package/dist/_internal/shared/listing-types/standard/config.d.ts +8 -0
  64. package/dist/_internal/shared/listing-types/standard/config.js +6 -0
  65. package/dist/_internal/shared/listing-types/stickers/config.d.ts +15 -0
  66. package/dist/_internal/shared/listing-types/stickers/config.js +14 -0
  67. package/dist/_internal/shared/listing-types/stickers/ctas.d.ts +1 -0
  68. package/dist/_internal/shared/listing-types/stickers/ctas.js +3 -0
  69. package/dist/_internal/shared/listing-types/stickers/og.d.ts +1 -0
  70. package/dist/_internal/shared/listing-types/stickers/og.js +1 -0
  71. package/dist/_internal/shared/listing-types/stickers/schema.d.ts +2 -0
  72. package/dist/_internal/shared/listing-types/stickers/schema.js +3 -0
  73. package/dist/_internal/shared/listing-types/stickers/seed-factory.d.ts +1 -0
  74. package/dist/_internal/shared/listing-types/stickers/seed-factory.js +1 -0
  75. package/dist/client.d.ts +7 -2
  76. package/dist/client.js +4 -2
  77. package/dist/constants/field-names.d.ts +2 -6
  78. package/dist/constants/field-names.js +2 -6
  79. package/dist/contracts/index.d.ts +4 -2
  80. package/dist/contracts/index.js +6 -0
  81. package/dist/contracts/payment.d.ts +14 -9
  82. package/dist/contracts/payment.js +11 -1
  83. package/dist/contracts/shipping.d.ts +14 -9
  84. package/dist/contracts/shipping.js +11 -1
  85. package/dist/features/about/components/PublicProfileView.js +3 -6
  86. package/dist/features/account/components/UserOffersPanel.js +0 -1
  87. package/dist/features/admin/components/AdminAddressBookView.d.ts +11 -0
  88. package/dist/features/admin/components/AdminAddressBookView.js +30 -0
  89. package/dist/features/admin/components/AdminArtView.d.ts +3 -0
  90. package/dist/features/admin/components/AdminArtView.js +45 -0
  91. package/dist/features/admin/components/AdminCarouselEditorView.js +2 -3
  92. package/dist/features/admin/components/AdminFulfillmentView.js +0 -1
  93. package/dist/features/admin/components/AdminSiteConfigGuideView.js +1 -1
  94. package/dist/features/admin/components/AdminSiteSettingsView.js +14 -9
  95. package/dist/features/admin/components/AdminStickersView.d.ts +3 -0
  96. package/dist/features/admin/components/AdminStickersView.js +45 -0
  97. package/dist/features/admin/components/index.d.ts +6 -0
  98. package/dist/features/admin/components/index.js +4 -0
  99. package/dist/features/admin/hooks/useChat.js +0 -1
  100. package/dist/features/admin/schemas/firestore.d.ts +19 -11
  101. package/dist/features/admin/schemas/firestore.js +12 -2
  102. package/dist/features/auctions/components/MarketplaceAuctionCard.d.ts +3 -2
  103. package/dist/features/auth/hooks/useAuth.js +35 -10
  104. package/dist/features/auth/schemas/firestore.d.ts +2 -6
  105. package/dist/features/cart/schemas/firestore.d.ts +3 -2
  106. package/dist/features/consultation/schemas/index.d.ts +2 -2
  107. package/dist/features/history/hooks/useHistory.js +0 -1
  108. package/dist/features/loyalty/schemas/index.d.ts +6 -6
  109. package/dist/features/messages/hooks/useConversation.js +0 -1
  110. package/dist/features/messages/hooks/useConversations.js +0 -1
  111. package/dist/features/orders/repository/orders.repository.d.ts +20 -2
  112. package/dist/features/orders/repository/orders.repository.js +34 -5
  113. package/dist/features/orders/schemas/firestore.d.ts +33 -7
  114. package/dist/features/orders/schemas/firestore.js +1 -1
  115. package/dist/features/orders/schemas/index.d.ts +176 -31
  116. package/dist/features/orders/schemas/index.js +20 -6
  117. package/dist/features/orders/types/index.d.ts +3 -3
  118. package/dist/features/payments/repository/payout.repository.js +2 -2
  119. package/dist/features/payments/schemas/firestore.d.ts +3 -3
  120. package/dist/features/payments/schemas/firestore.js +2 -2
  121. package/dist/features/payments/schemas/index.d.ts +5 -5
  122. package/dist/features/payments/schemas/index.js +2 -2
  123. package/dist/features/products/api/[id]/route.js +4 -1
  124. package/dist/features/products/api/route.js +4 -1
  125. package/dist/features/products/components/CompareOverlay.js +7 -9
  126. package/dist/features/products/components/ProductDetailPageView.d.ts +3 -1
  127. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  128. package/dist/features/products/components/ProductForm.js +18 -7
  129. package/dist/features/products/components/ProductGrid.js +7 -12
  130. package/dist/features/products/components/ShowGroupSection.js +3 -5
  131. package/dist/features/products/components/SublistingCarouselSection.js +3 -6
  132. package/dist/features/products/constants/listing-tabs.d.ts +32 -0
  133. package/dist/features/products/constants/listing-tabs.js +8 -0
  134. package/dist/features/products/index.d.ts +1 -1
  135. package/dist/features/products/index.js +3 -1
  136. package/dist/features/products/schemas/firestore.d.ts +20 -1
  137. package/dist/features/products/schemas/firestore.js +2 -0
  138. package/dist/features/products/schemas/index.d.ts +48 -0
  139. package/dist/features/products/schemas/index.js +9 -0
  140. package/dist/features/products/schemas/product-features.d.ts +1 -1
  141. package/dist/features/products/types/index.d.ts +5 -3
  142. package/dist/features/products/utils/listing-type.d.ts +6 -0
  143. package/dist/features/products/utils/listing-type.js +3 -0
  144. package/dist/features/reviews/api/[id]/route.js +5 -4
  145. package/dist/features/seller/actions/seller-actions.d.ts +29 -14
  146. package/dist/features/seller/actions/seller-actions.js +101 -27
  147. package/dist/features/seller/components/FulfillmentView.js +0 -1
  148. package/dist/features/seller/components/SellerAddressesView.js +0 -1
  149. package/dist/features/seller/components/SellerArtView.d.ts +7 -0
  150. package/dist/features/seller/components/SellerArtView.js +114 -0
  151. package/dist/features/seller/components/SellerOffersPanel.js +0 -1
  152. package/dist/features/seller/components/SellerOrdersView.js +38 -1
  153. package/dist/features/seller/components/SellerProductShell.d.ts +5 -1
  154. package/dist/features/seller/components/SellerProductShell.js +69 -59
  155. package/dist/features/seller/components/SellerReviewsView.js +0 -1
  156. package/dist/features/seller/components/SellerShippingView.js +6 -25
  157. package/dist/features/seller/components/SellerStickersView.d.ts +7 -0
  158. package/dist/features/seller/components/SellerStickersView.js +114 -0
  159. package/dist/features/seller/components/index.d.ts +4 -0
  160. package/dist/features/seller/components/index.js +2 -0
  161. package/dist/features/seller/messages/en.json +1 -1
  162. package/dist/features/seller/schemas/index.d.ts +7 -7
  163. package/dist/features/seller/schemas/index.js +1 -1
  164. package/dist/features/seller/types/index.d.ts +1 -1
  165. package/dist/features/stores/components/StoreDetailLayoutView.js +18 -2
  166. package/dist/features/stores/schemas/firestore.d.ts +7 -1
  167. package/dist/features/stores/schemas/index.d.ts +19 -16
  168. package/dist/features/stores/schemas/index.js +1 -1
  169. package/dist/index.d.ts +21 -36
  170. package/dist/index.js +27 -39
  171. package/dist/next/routing/route-map.d.ts +22 -0
  172. package/dist/next/routing/route-map.js +10 -0
  173. package/dist/providers/payment-manual/index.d.ts +40 -0
  174. package/dist/providers/payment-manual/index.js +91 -0
  175. package/dist/providers/payment-razorpay/index.d.ts +4 -2
  176. package/dist/providers/payment-razorpay/index.js +4 -1
  177. package/dist/providers/shipping-manual/index.d.ts +22 -0
  178. package/dist/providers/shipping-manual/index.js +47 -0
  179. package/dist/providers.d.ts +2 -2
  180. package/dist/providers.js +5 -3
  181. package/dist/schemas/registry.d.ts +129 -70
  182. package/dist/schemas/registry.js +0 -4
  183. package/dist/security/pii-encrypt.js +0 -7
  184. package/dist/security/pii-redact.js +0 -1
  185. package/dist/seed/conversations-seed-data.js +2 -2
  186. package/dist/seed/faq-seed-data.js +7 -7
  187. package/dist/seed/grouped-listings-seed-data.d.ts +2 -0
  188. package/dist/seed/index.d.ts +2 -0
  189. package/dist/seed/index.js +2 -0
  190. package/dist/seed/manifest.js +20 -16
  191. package/dist/seed/payouts-seed-data.js +5 -5
  192. package/dist/seed/products-art-seed-data.d.ts +11 -0
  193. package/dist/seed/products-art-seed-data.js +125 -0
  194. package/dist/seed/products-preorders-seed-data.d.ts +2 -0
  195. package/dist/seed/products-stickers-seed-data.d.ts +11 -0
  196. package/dist/seed/products-stickers-seed-data.js +125 -0
  197. package/dist/seed/site-settings-seed-data.js +13 -4
  198. package/dist/seed/stores-seed-data.js +11 -11
  199. package/dist/server.d.ts +7 -15
  200. package/dist/server.js +17 -42
  201. package/dist/ui/rich-text/RichText.js +1 -1
  202. package/package.json +1 -1
@@ -5,7 +5,7 @@ import { generatePayoutId, } from "../../../utils/id-generators";
5
5
  export const PayoutStatusValues = {
6
6
  PENDING: "pending",
7
7
  PROCESSING: "processing",
8
- COMPLETED: "completed",
8
+ PAID: "paid",
9
9
  FAILED: "failed",
10
10
  };
11
11
  export const PAYOUT_COLLECTION = "payouts";
@@ -32,7 +32,7 @@ export const PAYOUT_FIELDS = {
32
32
  STATUS_VALUES: {
33
33
  PENDING: "pending",
34
34
  PROCESSING: "processing",
35
- COMPLETED: "completed",
35
+ PAID: "paid",
36
36
  FAILED: "failed",
37
37
  },
38
38
  };
@@ -1,7 +1,7 @@
1
1
  export * from "./firestore";
2
2
  export * from "./saved-methods-firestore";
3
3
  import { z } from "zod";
4
- export declare const payoutStatusEnumSchema: z.ZodEnum<["pending", "processing", "completed", "failed"]>;
4
+ export declare const payoutStatusEnumSchema: z.ZodEnum<["pending", "processing", "paid", "failed"]>;
5
5
  export declare const payoutPaymentMethodSchema: z.ZodEnum<["bank_transfer", "upi"]>;
6
6
  export declare const payoutBankAccountSchema: z.ZodObject<{
7
7
  accountHolderName: z.ZodString;
@@ -75,7 +75,7 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
75
75
  platformFee: z.ZodNumber;
76
76
  platformFeeRate: z.ZodNumber;
77
77
  currency: z.ZodString;
78
- status: z.ZodEnum<["pending", "processing", "completed", "failed"]>;
78
+ status: z.ZodEnum<["pending", "processing", "paid", "failed"]>;
79
79
  paymentMethod: z.ZodEnum<["bank_transfer", "upi"]>;
80
80
  bankAccount: z.ZodOptional<z.ZodObject<{
81
81
  accountHolderName: z.ZodString;
@@ -150,7 +150,7 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
150
150
  toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
151
151
  }, z.ZodTypeAny, "passthrough">>]>>;
152
152
  }, "strip", z.ZodTypeAny, {
153
- status: "pending" | "failed" | "processing" | "completed";
153
+ status: "pending" | "paid" | "failed" | "processing";
154
154
  currency: string;
155
155
  storeId: string;
156
156
  createdAt: string | Date | z.objectOutputType<{
@@ -200,7 +200,7 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
200
200
  toDate: z.ZodFunction<z.ZodTuple<[], z.ZodUnknown>, z.ZodDate>;
201
201
  }, z.ZodTypeAny, "passthrough"> | undefined;
202
202
  }, {
203
- status: "pending" | "failed" | "processing" | "completed";
203
+ status: "pending" | "paid" | "failed" | "processing";
204
204
  currency: string;
205
205
  storeId: string;
206
206
  createdAt: string | Date | z.objectInputType<{
@@ -252,7 +252,7 @@ export declare const payoutFirestoreSchema: z.ZodObject<{
252
252
  }>;
253
253
  export type PayoutFromSchema = z.infer<typeof payoutFirestoreSchema>;
254
254
  /** Zod schema for payout status — use instead of inline `z.enum(["pending",...])`. */
255
- export declare const payoutStatusSchema: z.ZodEnum<["pending", "processing", "completed", "failed"]>;
255
+ export declare const payoutStatusSchema: z.ZodEnum<["pending", "processing", "paid", "failed"]>;
256
256
  /**
257
257
  * Canonical payment gateway identifiers.
258
258
  * Use these constants instead of bare string literals.
@@ -6,7 +6,7 @@ import { auditTimestampsShape, firestoreDateSchema, paiseSchema } from "../../..
6
6
  // ─── Firestore document schema (W2) ───────────────────────────────────────────
7
7
  // Mirrors PayoutDocument + PayoutRefundDeduction + PayoutBankAccount in
8
8
  // ./firestore.ts. Registered into SCHEMAS.firestore.payouts.
9
- export const payoutStatusEnumSchema = z.enum(["pending", "processing", "completed", "failed"]);
9
+ export const payoutStatusEnumSchema = z.enum(["pending", "processing", "paid", "failed"]);
10
10
  export const payoutPaymentMethodSchema = z.enum(["bank_transfer", "upi"]);
11
11
  export const payoutBankAccountSchema = z.object({
12
12
  accountHolderName: z.string(),
@@ -54,7 +54,7 @@ export const payoutFirestoreSchema = z.object({
54
54
  export const payoutStatusSchema = z.enum([
55
55
  "pending",
56
56
  "processing",
57
- "completed",
57
+ "paid",
58
58
  "failed",
59
59
  ]);
60
60
  /**
@@ -18,6 +18,7 @@ import { storeRepository } from "../../../stores/repository/store.repository";
18
18
  // SB-UNI-V — bundlesRepository deleted; bundle stock-sync moves to the
19
19
  // `onProductStockChange` Firebase Function (see functions/src/bundle-stock-sync.ts).
20
20
  import { sanitizeProductForPublic } from "../../utils/sanitize";
21
+ import { printMetaSchema } from "../../schemas/index";
21
22
  import { isAdminUser, isModeratorUser } from "../../../auth/role-predicates";
22
23
  import { normalizeError } from "../../../../errors/normalize";
23
24
  const ERR_DB_NOT_REGISTERED = "Database provider not registered";
@@ -52,8 +53,10 @@ const productUpdateSchema = z
52
53
  tags: z.array(z.string()).optional(),
53
54
  featured: z.boolean().optional(),
54
55
  isPromoted: z.boolean().optional(),
56
+ allowShipBeforeEmiComplete: z.boolean().optional(),
57
+ printMeta: printMetaSchema.optional(),
55
58
  listingType: z
56
- .enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live"])
59
+ .enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live", "art", "stickers"])
57
60
  .optional(),
58
61
  media: z.array(mediaFieldSchema).optional(),
59
62
  })
@@ -17,6 +17,7 @@ import { z } from "zod";
17
17
  import { getProviders } from "../../../contracts";
18
18
  import { createRouteHandler } from "../../../next";
19
19
  import { mediaFieldSchema } from "../../media/types/index";
20
+ import { printMetaSchema } from "../schemas/index";
20
21
  import { sanitizeProductsForPublic } from "../utils/sanitize";
21
22
  import { normalizeError } from "../../../errors/normalize";
22
23
  // --- Mutation schemas ---------------------------------------------------------
@@ -42,12 +43,14 @@ const productMutateSchema = z
42
43
  tags: z.array(z.string()).optional(),
43
44
  featured: z.boolean().optional(),
44
45
  isPromoted: z.boolean().optional(),
46
+ allowShipBeforeEmiComplete: z.boolean().optional(),
47
+ printMeta: printMetaSchema.optional(),
45
48
  sellerId: z.string().optional(),
46
49
  sellerName: z.string().optional(),
47
50
  sellerEmail: z.string().email().optional(),
48
51
  slug: z.string().optional(),
49
52
  listingType: z
50
- .enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live"])
53
+ .enum(["standard", "auction", "pre-order", "prize-draw", "bundle", "classified", "digital-code", "live", "art", "stickers"])
51
54
  .optional(),
52
55
  media: z.array(mediaFieldSchema).optional(),
53
56
  })
@@ -14,10 +14,10 @@ import Link from "next/link";
14
14
  import { X } from "lucide-react";
15
15
  import { Button, Div, Heading, Row, Span, Stack, Text } from "../../../ui";
16
16
  import { useSwipe } from "../../../react/hooks/useSwipe";
17
- import { ROUTES } from "../../../next";
18
17
  import { formatCurrency } from "../../../utils/number.formatter";
19
18
  import { COMPARE_MAX_ITEMS } from "../constants/action-defs";
20
- import { isAuctionListing, isPreOrderListing } from "../utils/listing-type";
19
+ import { normalizeListingType } from "../utils/listing-type";
20
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
21
21
  const DEFAULT_LABELS = {
22
22
  title: "Compare items",
23
23
  empty: "Select up to 4 items from the listing to compare.",
@@ -40,13 +40,11 @@ const CHIP_CLASS = "inline-flex items-center rounded-full bg-[var(--appkit-color
40
40
  const COLUMN_CARD_CLASS = "flex flex-col gap-3 rounded-xl border border-[var(--appkit-color-border-subtle)] bg-[var(--appkit-color-surface)] p-3";
41
41
  function detailHref(item, type) {
42
42
  const id = item.slug ?? item.id;
43
- if (type === "auction" || isAuctionListing(item)) {
44
- return String(ROUTES.PUBLIC.AUCTION_DETAIL(id));
45
- }
46
- if (type === "preorder" || isPreOrderListing(item)) {
47
- return String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(id));
48
- }
49
- return String(ROUTES.PUBLIC.PRODUCT_DETAIL(id));
43
+ // The caller-supplied `type` hint takes precedence some compare contexts
44
+ // (e.g. an auction comparison page) feed items that don't carry their own
45
+ // `listingType` field, so the hint is the only signal available.
46
+ const listingType = type === "auction" ? "auction" : type === "preorder" ? "pre-order" : normalizeListingType(item);
47
+ return pluginFor(listingType).detailRoute(id);
50
48
  }
51
49
  function priceLabel(item) {
52
50
  if (typeof item.price !== "number")
@@ -42,5 +42,7 @@ export interface ProductDetailPageViewProps {
42
42
  * legacy bullets render unchanged.
43
43
  */
44
44
  productFeatures?: ProductFeatureDocument[];
45
+ /** Site-wide COD availability (`siteSettings.payment.codEnabled`), threaded from the page's data layer. Drives the "Cash on Delivery" feature badge. */
46
+ codEnabled?: boolean;
45
47
  }
46
- export declare function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, }: ProductDetailPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
48
+ export declare function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, }: ProductDetailPageViewProps): Promise<import("react/jsx-runtime").JSX.Element>;
@@ -106,7 +106,7 @@ function StarRating({ value }) {
106
106
  // ---------------------------------------------------------------------------
107
107
  // Component
108
108
  // ---------------------------------------------------------------------------
109
- export async function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, }) {
109
+ export async function ProductDetailPageView({ slug, initialProduct, renderOfferAction, renderPrimaryActions, productFeatures, codEnabled, }) {
110
110
  // Use pre-fetched data when available to avoid a redundant repository call.
111
111
  // The page layer wraps getProductForDetail in React.cache(), so both
112
112
  // generateMetadata() and this component share the same in-flight promise.
@@ -262,7 +262,7 @@ export async function ProductDetailPageView({ slug, initialProduct, renderOfferA
262
262
  ? ` — only ${effectiveStock} left`
263
263
  : ""] })] })) : (_jsxs(Span, { layout: "inline-flex", gap: "xs", className: `w-fit .5 ${inStock ? CLS_STOCK_IN : CLS_STOCK_OUT}`, rounded: "full", padding: "pill-md", size: "xs", weight: "medium", children: [inStock ? "✓ In Stock" : "✗ Out of Stock", inStock && effectiveStock !== null && effectiveStock <= 10
264
264
  ? ` — only ${effectiveStock} left`
265
- : ""] })), (categorySlugs.length > 0 || subcategory || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [categorySlugs.map((slug, i) => (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(slug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryNames[i] ?? slug }, slug))), subcategory && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", transform: "capitalize", children: subcategory })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand }))] })), bundleMemberships.length > 0 && (_jsx(Row, { gap: "sm", wrap: true, children: bundleMemberships.map((b) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.BUNDLE_DETAIL(b.id)), className: CLS_BUNDLE_PILL, children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), "In bundle: ", b.title] }, b.id))) })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, condition: condition ?? undefined, returnable: returnPolicy != null && returnPolicy.length > 0, labels: {
265
+ : ""] })), (categorySlugs.length > 0 || subcategory || brand) && (_jsxs(Row, { gap: "sm", wrap: true, children: [categorySlugs.map((slug, i) => (_jsx(Link, { href: String(ROUTES.PUBLIC.CATEGORY_DETAIL(slug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: categoryNames[i] ?? slug }, slug))), subcategory && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", transform: "capitalize", children: subcategory })), brand && brandSlug && (_jsx(Link, { href: String(ROUTES.PUBLIC.BRAND_DETAIL(brandSlug)), className: "inline-flex items-center rounded-full border border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] px-2.5 py-1 text-xs font-medium text-[var(--appkit-color-text-muted)] transition-colors hover:border-primary-300 hover:bg-primary-50 hover:text-primary-700 dark:hover:border-primary-700/60 dark:hover:bg-primary-900/20 dark:hover:text-primary-400", children: brand })), brand && !brandSlug && (_jsx(Span, { layout: "inline-flex", size: "xs", weight: "medium", border: "subtle", rounded: "full", padding: "pill-sm-tall", surface: "muted", color: "muted", children: brand }))] })), bundleMemberships.length > 0 && (_jsx(Row, { gap: "sm", wrap: true, children: bundleMemberships.map((b) => (_jsxs(Link, { href: String(ROUTES.PUBLIC.BUNDLE_DETAIL(b.id)), className: CLS_BUNDLE_PILL, children: [_jsx(Span, { "aria-hidden": "true", children: "\uD83D\uDCE6" }), "In bundle: ", b.title] }, b.id))) })), _jsx(ProductFeatureBadges, { featured: featured, freeShipping: freeShipping, codAvailable: codEnabled === true, condition: condition ?? undefined, returnable: returnPolicy != null && returnPolicy.length > 0, labels: {
266
266
  featured: "Featured",
267
267
  fasterDelivery: "Faster Delivery",
268
268
  ratedSeller: "Rated Seller",
@@ -12,8 +12,23 @@ import { useMediaUpload } from "../../media";
12
12
  import { resolveDate } from "../../../utils/date.formatter";
13
13
  import { normalizeRichTextHtml } from "../../../utils/string.formatter";
14
14
  import { useListingTypeFlags } from "../../../react/hooks/useListingTypeFlags";
15
- import { isAuctionListing, isPreOrderListing, isPrizeDrawListing, } from "../utils/listing-type";
15
+ import { isAuctionListing, isPreOrderListing, isPrizeDrawListing, normalizeListingType, } from "../utils/listing-type";
16
16
  import { PrizeDrawItemsEditor } from "./PrizeDrawItemsEditor";
17
+ // ListingType uses the canonical SB1-G spellings ("standard", "pre-order");
18
+ // ProductFeatureProductType predates it with legacy spellings for 2 values.
19
+ // Replaces the old 2-case ternary that silently mapped every other type
20
+ // (classified/digital-code/live/prize-draw/art/stickers) to "product".
21
+ const LISTING_TYPE_TO_FEATURE_PRODUCT_TYPE = {
22
+ standard: "product",
23
+ auction: "auction",
24
+ "pre-order": "preorder",
25
+ "prize-draw": "prize-draw",
26
+ classified: "classified",
27
+ "digital-code": "digital-code",
28
+ live: "live",
29
+ art: "art",
30
+ stickers: "stickers",
31
+ };
17
32
  export const PRODUCT_STATUS_OPTIONS = [
18
33
  { value: "draft", label: "Draft" },
19
34
  { value: "published", label: "Published" },
@@ -122,7 +137,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
122
137
  .split(",")
123
138
  .map((tag) => tag.trim())
124
139
  .filter(Boolean),
125
- }), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly })] }), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionConditionShipping") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "condition", label: t("formCondition"), type: "select", value: product.condition || "new", onChange: (value) => update({
140
+ }), disabled: isReadonly, placeholder: t("formTagsPlaceholder") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(Checkbox, { label: t("formFeatured"), checked: !!product.featured, onChange: (e) => update({ featured: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formIsPromoted"), checked: !!product.isPromoted, onChange: (e) => update({ isPromoted: e.target.checked }), disabled: isReadonly }), _jsx(Checkbox, { label: t("formAllowShipBeforeEmiComplete"), checked: !!product.allowShipBeforeEmiComplete, onChange: (e) => update({ allowShipBeforeEmiComplete: e.target.checked }), disabled: isReadonly })] }), _jsx(Heading, { level: 4, className: "mt-4", children: t("sectionConditionShipping") }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "condition", label: t("formCondition"), type: "select", value: product.condition || "new", onChange: (value) => update({
126
141
  condition: value,
127
142
  }), disabled: isReadonly, options: [
128
143
  { value: "new", label: t("formConditionNew") },
@@ -199,11 +214,7 @@ export function ProductForm({ product, onChange, isReadonly = false, renderDescr
199
214
  value: product.groupId,
200
215
  onChange: (id) => update({ groupId: id || undefined }),
201
216
  disabled: isReadonly,
202
- }), !isAuctionListing(product) && renderGroupSettings?.(product), _jsx(ProductFeaturesSelector, { value: product.features ?? [], onChange: (features) => update({ features }), productType: (isAuctionListing(product)
203
- ? "auction"
204
- : isPreOrderListing(product)
205
- ? "preorder"
206
- : "product"), storeId: product.storeId, disabled: isReadonly }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Custom Sections" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Add up to 3 custom tabs to your product page \u2014 e.g. \"Box Contents\", \"Compatibility\", \"Grading Details\"." }), _jsx(CustomSectionsEditor, { sections: product.customSections ?? [], onChange: (sections) => update({ customSections: sections }) })] }), _jsx(FormField, { name: "shippingInfo", label: t("formShipping"), type: "textarea", value: product.shippingInfo || "", onChange: (value) => update({ shippingInfo: value }), disabled: isReadonly, placeholder: "Shipping information..." }), _jsx(FormField, { name: "returnPolicy", label: t("formReturnPolicy"), type: "textarea", value: product.returnPolicy || "", onChange: (value) => update({ returnPolicy: value }), disabled: isReadonly, placeholder: "Return policy details..." }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Physical Inventory" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Assign a storage location for warehouse or shelf organisation. Used by the Print & Label Center." }), _jsxs(FormGroup, { columns: 3, children: [_jsx(FormField, { name: "physicalLocation.zone", label: "Zone", type: "text", placeholder: "e.g. A", value: product.physicalLocation?.zone ?? "", onChange: (v) => update({
217
+ }), !isAuctionListing(product) && renderGroupSettings?.(product), _jsx(ProductFeaturesSelector, { value: product.features ?? [], onChange: (features) => update({ features }), productType: LISTING_TYPE_TO_FEATURE_PRODUCT_TYPE[normalizeListingType(product)], storeId: product.storeId, disabled: isReadonly }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Custom Sections" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Add up to 3 custom tabs to your product page \u2014 e.g. \"Box Contents\", \"Compatibility\", \"Grading Details\"." }), _jsx(CustomSectionsEditor, { sections: product.customSections ?? [], onChange: (sections) => update({ customSections: sections }) })] }), _jsx(FormField, { name: "shippingInfo", label: t("formShipping"), type: "textarea", value: product.shippingInfo || "", onChange: (value) => update({ shippingInfo: value }), disabled: isReadonly, placeholder: "Shipping information..." }), _jsx(FormField, { name: "returnPolicy", label: t("formReturnPolicy"), type: "textarea", value: product.returnPolicy || "", onChange: (value) => update({ returnPolicy: value }), disabled: isReadonly, placeholder: "Return policy details..." }), _jsxs(Div, { children: [_jsx(Heading, { level: 3, className: "mb-1", color: "muted", size: "sm", weight: "semibold", children: "Physical Inventory" }), _jsx(Text, { className: "mb-3", color: "muted", size: "xs", children: "Assign a storage location for warehouse or shelf organisation. Used by the Print & Label Center." }), _jsxs(FormGroup, { columns: 3, children: [_jsx(FormField, { name: "physicalLocation.zone", label: "Zone", type: "text", placeholder: "e.g. A", value: product.physicalLocation?.zone ?? "", onChange: (v) => update({
207
218
  physicalLocation: {
208
219
  zone: v,
209
220
  shelf: product.physicalLocation?.shelf ?? "",
@@ -11,15 +11,14 @@ import { useLongPress } from "../../../react/hooks/useLongPress";
11
11
  import { FeatureBadgeList } from "./FeatureBadge";
12
12
  import { useProductFeatures } from "./ProductFeaturesContext";
13
13
  import { PRODUCT_FEATURE_CARD_MAX_VISIBLE } from "../constants/product-features.constants";
14
- import { isAuctionListing, isPreOrderListing } from "../utils/listing-type";
14
+ import { normalizeListingType } from "../utils/listing-type";
15
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
15
16
  const __P = {
16
17
  p3: "p-3",
17
18
  };
18
19
  const __O = {
19
20
  hidden: "overflow-hidden",
20
21
  };
21
- const CLS_BADGE_AUCTION = "bg-warning-surface text-white";
22
- const CLS_BADGE_PREORDER = "bg-violet-600 text-white";
23
22
  const CLS_BADGE_NEW = "rounded-full bg-error-surface px-2 py-0.5 text-[10px] text-white shadow-sm";
24
23
  const CLS_BADGE_SALE = "rounded-full bg-success-surface px-2 py-0.5 text-[10px] font-bold text-white shadow-sm";
25
24
  const CLS_BADGE_TRENDING = "rounded-full bg-indigo-600 px-2 py-0.5 text-[10px] font-bold text-white shadow-sm";
@@ -38,14 +37,10 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
38
37
  ? Math.round(((product.originalPrice - product.price) / product.originalPrice) *
39
38
  100)
40
39
  : null;
41
- // SB1-G canonical accessors that handle the legacy boolean fallback.
42
- const isAuction = isAuctionListing(product);
43
- const isPreOrder = isPreOrderListing(product);
44
- const typeBadge = isAuction
45
- ? { label: "Auction", cls: CLS_BADGE_AUCTION }
46
- : isPreOrder
47
- ? { label: "Pre-Order", cls: CLS_BADGE_PREORDER }
48
- : null;
40
+ const listingPlugin = pluginFor(normalizeListingType(product));
41
+ const typeBadge = listingPlugin.badge
42
+ ? { label: listingPlugin.badge.label, cls: listingPlugin.badge.className }
43
+ : null;
49
44
  const longPress = useLongPress(() => onSelect?.(product.id));
50
45
  const featuresList = useProductFeatures();
51
46
  const handleCardClick = selectionMode
@@ -97,7 +92,7 @@ export function ProductCard({ product, href, onClick, onAddToWishlist, isWishlis
97
92
  return seller ? (_jsxs(Text, { className: "mt-0.5 text-[11px]", color: "faint", children: ["by ", seller] })) : null;
98
93
  })(), product.rating !== undefined && (_jsxs(Row, { className: "mt-1", gap: "xs", children: [_jsx(Span, { className: CLS_STAR, children: "\u2605" }), _jsxs(Span, { className: "text-[11px]", color: "muted", children: [product.rating.toFixed(1), product.reviewCount ? ` (${product.reviewCount})` : ""] })] })), _jsxs(Div, { className: "mt-auto", padding: "t-xs", children: [_jsxs(Row, { align: "baseline", gap: "sm", children: [_jsx(Span, { size: "base", weight: "bold", className: "text-primary dark:text-primary-400", children: formatCurrency(product.price, getDefaultCurrency()) }), product.originalPrice && product.originalPrice > product.price && (_jsx(Span, { size: "xs", className: "line-through", color: "faint", children: formatCurrency(product.originalPrice, getDefaultCurrency()) }))] }), (() => {
99
94
  const stock = product.stockCount ?? product.stockQuantity ?? product.availableQuantity;
100
- if (stock === undefined || isAuction)
95
+ if (stock === undefined || !listingPlugin.showsStockQuantity)
101
96
  return null;
102
97
  if (stock <= 0) {
103
98
  return (_jsx(Text, { className: CLS_DISCOUNT_TEXT, children: "Out of stock" }));
@@ -2,20 +2,18 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useEffect, useState } from "react";
4
4
  import Link from "next/link";
5
- import { ROUTES } from "../../../next";
6
5
  import { Div, Row, Span, Table, Thead, Tbody, Tr, Th, Td, Text, Modal, SideDrawer, Button } from "../../../ui";
7
6
  import { MediaImage } from "../../media/MediaImage";
8
7
  import { formatCurrency } from "../../../utils/number.formatter";
9
- import { isPreOrderListing } from "../utils/listing-type";
8
+ import { normalizeListingType } from "../utils/listing-type";
9
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
10
10
  const __O = {
11
11
  hidden: "overflow-hidden",
12
12
  xAuto: "overflow-x-auto",
13
13
  };
14
14
  function memberHref(m) {
15
15
  const slug = m.slug ?? m.id;
16
- if (isPreOrderListing(m))
17
- return String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(slug));
18
- return String(ROUTES.PUBLIC.PRODUCT_DETAIL(slug));
16
+ return pluginFor(normalizeListingType(m)).detailRoute(slug);
19
17
  }
20
18
  function MemberThumb({ member, isCurrent }) {
21
19
  const image = member.images?.[0] ?? "";
@@ -5,18 +5,15 @@ import Link from "next/link";
5
5
  import { ROUTES } from "../../../next";
6
6
  import { Div, Row, Span, Text } from "../../../ui";
7
7
  import { formatCurrency } from "../../../utils/number.formatter";
8
- import { isAuctionListing, isPreOrderListing } from "../utils/listing-type";
8
+ import { normalizeListingType } from "../utils/listing-type";
9
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
9
10
  const __O = {
10
11
  hidden: "overflow-hidden",
11
12
  xAuto: "overflow-x-auto",
12
13
  };
13
14
  function getHref(listing) {
14
15
  const slug = listing.slug ?? listing.id;
15
- if (isAuctionListing(listing))
16
- return String(ROUTES.PUBLIC.AUCTION_DETAIL(slug));
17
- if (isPreOrderListing(listing))
18
- return String(ROUTES.PUBLIC.PRE_ORDER_DETAIL(slug));
19
- return String(ROUTES.PUBLIC.PRODUCT_DETAIL(slug));
16
+ return pluginFor(normalizeListingType(listing)).detailRoute(slug);
20
17
  }
21
18
  function ListingThumb({ listing, isCurrent, }) {
22
19
  const image = listing.images?.[0] ?? listing.mainImage ?? "";
@@ -52,6 +52,14 @@ export declare const CATEGORY_PAGE_TABS: readonly [{
52
52
  readonly id: "live";
53
53
  readonly label: "Live Items";
54
54
  readonly listingType: "live";
55
+ }, {
56
+ readonly id: "art";
57
+ readonly label: "Art";
58
+ readonly listingType: "art";
59
+ }, {
60
+ readonly id: "stickers";
61
+ readonly label: "Stickers";
62
+ readonly listingType: "stickers";
55
63
  }, {
56
64
  readonly id: "stores";
57
65
  readonly label: "Stores";
@@ -91,6 +99,14 @@ export declare const STORE_PAGE_TABS: readonly [{
91
99
  readonly id: "live";
92
100
  readonly label: "Live Items";
93
101
  readonly listingType: "live";
102
+ }, {
103
+ readonly id: "art";
104
+ readonly label: "Art";
105
+ readonly listingType: "art";
106
+ }, {
107
+ readonly id: "stickers";
108
+ readonly label: "Stickers";
109
+ readonly listingType: "stickers";
94
110
  }];
95
111
  export type StoreTabId = (typeof STORE_PAGE_TABS)[number]["id"];
96
112
  /** Tabs shown on the seller-dashboard listings view + admin products list. */
@@ -125,6 +141,14 @@ export declare const SELLER_LISTING_TABS: readonly [{
125
141
  readonly id: "live";
126
142
  readonly label: "Live Items";
127
143
  readonly listingType: "live";
144
+ }, {
145
+ readonly id: "art";
146
+ readonly label: "Art";
147
+ readonly listingType: "art";
148
+ }, {
149
+ readonly id: "stickers";
150
+ readonly label: "Stickers";
151
+ readonly listingType: "stickers";
128
152
  }];
129
153
  export type SellerListingTabId = (typeof SELLER_LISTING_TABS)[number]["id"];
130
154
  /** Tabs shown on `/search` results. */
@@ -163,5 +187,13 @@ export declare const SEARCH_RESULT_TABS: readonly [{
163
187
  readonly id: "live";
164
188
  readonly label: "Live Items";
165
189
  readonly listingType: "live";
190
+ }, {
191
+ readonly id: "art";
192
+ readonly label: "Art";
193
+ readonly listingType: "art";
194
+ }, {
195
+ readonly id: "stickers";
196
+ readonly label: "Stickers";
197
+ readonly listingType: "stickers";
166
198
  }];
167
199
  export type SearchTabId = (typeof SEARCH_RESULT_TABS)[number]["id"];
@@ -18,6 +18,8 @@ export const CATEGORY_PAGE_TABS = [
18
18
  { id: "classifieds", label: "Classifieds", listingType: "classified" },
19
19
  { id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
20
20
  { id: "live", label: "Live Items", listingType: "live" },
21
+ { id: "art", label: "Art", listingType: "art" },
22
+ { id: "stickers", label: "Stickers", listingType: "stickers" },
21
23
  { id: "stores", label: "Stores", entity: "stores" },
22
24
  ];
23
25
  /** Tabs shown on the public `/stores/[slug]` nav bar. */
@@ -30,6 +32,8 @@ export const STORE_PAGE_TABS = [
30
32
  { id: "classifieds", label: "Classifieds", listingType: "classified" },
31
33
  { id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
32
34
  { id: "live", label: "Live Items", listingType: "live" },
35
+ { id: "art", label: "Art", listingType: "art" },
36
+ { id: "stickers", label: "Stickers", listingType: "stickers" },
33
37
  ];
34
38
  /** Tabs shown on the seller-dashboard listings view + admin products list. */
35
39
  export const SELLER_LISTING_TABS = [
@@ -41,6 +45,8 @@ export const SELLER_LISTING_TABS = [
41
45
  { id: "classifieds", label: "Classifieds", listingType: "classified" },
42
46
  { id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
43
47
  { id: "live", label: "Live Items", listingType: "live" },
48
+ { id: "art", label: "Art", listingType: "art" },
49
+ { id: "stickers", label: "Stickers", listingType: "stickers" },
44
50
  ];
45
51
  /** Tabs shown on `/search` results. */
46
52
  export const SEARCH_RESULT_TABS = [
@@ -53,4 +59,6 @@ export const SEARCH_RESULT_TABS = [
53
59
  { id: "classifieds", label: "Classifieds", listingType: "classified" },
54
60
  { id: "digital-codes", label: "Digital Codes", listingType: "digital-code" },
55
61
  { id: "live", label: "Live Items", listingType: "live" },
62
+ { id: "art", label: "Art", listingType: "art" },
63
+ { id: "stickers", label: "Stickers", listingType: "stickers" },
56
64
  ];
@@ -9,5 +9,5 @@ export * from "./components";
9
9
  export * from "./schemas";
10
10
  export * from "./columns";
11
11
  export { manifest } from "./manifest";
12
- export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, } from "./utils/listing-type";
12
+ export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing, isClassifiedListing, isDigitalCodeListing, isLiveListing, isArtListing, isStickersListing, } from "./utils/listing-type";
13
13
  export { sanitizeProductForPublic, sanitizeProductsForPublic, } from "./utils/sanitize";
@@ -11,5 +11,7 @@ export * from "./columns";
11
11
  export { manifest } from "./manifest";
12
12
  export { normalizeListingType, isAuctionListing, isPreOrderListing, isStandardListing, isPrizeDrawListing,
13
13
  // SB-UNI-F 2026-05-13 — Phase 2 predicates.
14
- isClassifiedListing, isDigitalCodeListing, isLiveListing, } from "./utils/listing-type";
14
+ isClassifiedListing, isDigitalCodeListing, isLiveListing,
15
+ // Art/stickers session.
16
+ isArtListing, isStickersListing, } from "./utils/listing-type";
15
17
  export { sanitizeProductForPublic, sanitizeProductsForPublic, } from "./utils/sanitize";
@@ -105,6 +105,21 @@ export interface ProductLiveItemMeta {
105
105
  /** CITES permit number (Appendix I/II species). */
106
106
  cites?: string;
107
107
  }
108
+ /**
109
+ * Print-specific metadata shared by the "art" and "stickers" listing types
110
+ * (both are printed-only physical goods). Every field optional so partial
111
+ * seller input still saves.
112
+ */
113
+ export interface ProductPrintMeta {
114
+ /** e.g. "A4", "12x18 in", "5x5 cm sheet". */
115
+ size?: string;
116
+ /** e.g. "Matte photo paper", "Vinyl", "Canvas". */
117
+ material?: string;
118
+ /** e.g. "Glossy", "Matte", "Holographic". */
119
+ finish?: string;
120
+ /** Limited-edition print run size; omitted for open editions. */
121
+ editionSize?: number;
122
+ }
108
123
  export interface ProductDocument extends BaseDocument {
109
124
  title: string;
110
125
  description: string;
@@ -178,6 +193,10 @@ export interface ProductDocument extends BaseDocument {
178
193
  isPromoted?: boolean;
179
194
  isOnSale?: boolean;
180
195
  isSold?: boolean;
196
+ /** When true, an EMI order for this product ships as soon as it's confirmed instead of waiting for every installment to be paid. Default false. */
197
+ allowShipBeforeEmiComplete?: boolean;
198
+ /** Print-specific metadata for "art" / "stickers" listings — printed-only physical goods. Optional on every listing type; only populated for art/stickers. */
199
+ printMeta?: ProductPrintMeta;
181
200
  promotionEndDate?: Date;
182
201
  pickupAddressId?: string;
183
202
  viewCount?: number;
@@ -313,7 +332,7 @@ export declare const PRODUCT_COLLECTION: "products";
313
332
  export declare const PRODUCT_INDEXED_FIELDS: readonly ["storeId", "status", "category", "featured", "listingType", "isPromoted", "isOnSale", "isSold", "searchTokens", "createdAt", "barcodeId"];
314
333
  export declare const DEFAULT_PRODUCT_DATA: Partial<ProductDocument>;
315
334
  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"];
316
- 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", "barcodeId"];
335
+ 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", "allowShipBeforeEmiComplete", "printMeta", "seoTitle", "seoDescription", "seoKeywords", "customFields", "customSections", "sublistingCategoryId", "groupId", "isGroupParent", "groupParentSlug", "groupChildSlugs", "groupTitle", "barcodeId"];
317
336
  export type ProductCreateInput = Omit<ProductDocument, "id" | "createdAt" | "updatedAt" | "availableQuantity" | "bidCount" | "currentBid" | "auctionOriginalEndDate">;
318
337
  export type ProductUpdateInput = Partial<Pick<ProductDocument, (typeof PRODUCT_UPDATABLE_FIELDS)[number]>>;
319
338
  export type ProductAdminUpdateInput = Partial<Omit<ProductDocument, "id" | "createdAt">>;
@@ -163,6 +163,8 @@ export const PRODUCT_UPDATABLE_FIELDS = [
163
163
  "preOrderCancellable",
164
164
  "isOnSale",
165
165
  "isSold",
166
+ "allowShipBeforeEmiComplete",
167
+ "printMeta",
166
168
  "seoTitle",
167
169
  "seoDescription",
168
170
  "seoKeywords",
@@ -29,6 +29,23 @@ export declare const productSeoSchema: z.ZodObject<{
29
29
  description?: string | undefined;
30
30
  keywords?: string[] | undefined;
31
31
  }>;
32
+ /** Print-specific metadata for "art" / "stickers" listings. */
33
+ export declare const printMetaSchema: z.ZodObject<{
34
+ size: z.ZodOptional<z.ZodString>;
35
+ material: z.ZodOptional<z.ZodString>;
36
+ finish: z.ZodOptional<z.ZodString>;
37
+ editionSize: z.ZodOptional<z.ZodNumber>;
38
+ }, "strip", z.ZodTypeAny, {
39
+ size?: string | undefined;
40
+ material?: string | undefined;
41
+ finish?: string | undefined;
42
+ editionSize?: number | undefined;
43
+ }, {
44
+ size?: string | undefined;
45
+ material?: string | undefined;
46
+ finish?: string | undefined;
47
+ editionSize?: number | undefined;
48
+ }>;
32
49
  /**
33
50
  * Base Zod schema for a product item.
34
51
  * Apps can extend this to add their own fields:
@@ -86,6 +103,23 @@ export declare const productItemSchema: z.ZodObject<{
86
103
  }>>;
87
104
  featured: z.ZodOptional<z.ZodBoolean>;
88
105
  isPromoted: z.ZodOptional<z.ZodBoolean>;
106
+ allowShipBeforeEmiComplete: z.ZodOptional<z.ZodBoolean>;
107
+ printMeta: z.ZodOptional<z.ZodObject<{
108
+ size: z.ZodOptional<z.ZodString>;
109
+ material: z.ZodOptional<z.ZodString>;
110
+ finish: z.ZodOptional<z.ZodString>;
111
+ editionSize: z.ZodOptional<z.ZodNumber>;
112
+ }, "strip", z.ZodTypeAny, {
113
+ size?: string | undefined;
114
+ material?: string | undefined;
115
+ finish?: string | undefined;
116
+ editionSize?: number | undefined;
117
+ }, {
118
+ size?: string | undefined;
119
+ material?: string | undefined;
120
+ finish?: string | undefined;
121
+ editionSize?: number | undefined;
122
+ }>>;
89
123
  currentBid: z.ZodOptional<z.ZodNumber>;
90
124
  availableQuantity: z.ZodOptional<z.ZodNumber>;
91
125
  category: z.ZodOptional<z.ZodString>;
@@ -337,6 +371,13 @@ export declare const productItemSchema: z.ZodObject<{
337
371
  preOrderCurrentCount?: number | undefined;
338
372
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
339
373
  preOrderCancellable?: boolean | undefined;
374
+ allowShipBeforeEmiComplete?: boolean | undefined;
375
+ printMeta?: {
376
+ size?: string | undefined;
377
+ material?: string | undefined;
378
+ finish?: string | undefined;
379
+ editionSize?: number | undefined;
380
+ } | undefined;
340
381
  pickupAddressId?: string | undefined;
341
382
  viewCount?: number | undefined;
342
383
  avgRating?: number | undefined;
@@ -466,6 +507,13 @@ export declare const productItemSchema: z.ZodObject<{
466
507
  preOrderCurrentCount?: number | undefined;
467
508
  preOrderProductionStatus?: "upcoming" | "in_production" | "ready_to_ship" | undefined;
468
509
  preOrderCancellable?: boolean | undefined;
510
+ allowShipBeforeEmiComplete?: boolean | undefined;
511
+ printMeta?: {
512
+ size?: string | undefined;
513
+ material?: string | undefined;
514
+ finish?: string | undefined;
515
+ editionSize?: number | undefined;
516
+ } | undefined;
469
517
  pickupAddressId?: string | undefined;
470
518
  viewCount?: number | undefined;
471
519
  avgRating?: number | undefined;
@@ -13,6 +13,13 @@ export const productSeoSchema = z.object({
13
13
  description: z.string().optional(),
14
14
  keywords: z.array(z.string()).optional(),
15
15
  });
16
+ /** Print-specific metadata for "art" / "stickers" listings. */
17
+ export const printMetaSchema = z.object({
18
+ size: z.string().max(80).optional(),
19
+ material: z.string().max(80).optional(),
20
+ finish: z.string().max(80).optional(),
21
+ editionSize: z.number().int().positive().optional(),
22
+ });
16
23
  // --- Base item schema ---------------------------------------------------------
17
24
  /**
18
25
  * Base Zod schema for a product item.
@@ -43,6 +50,8 @@ export const productItemSchema = z.object({
43
50
  .optional(),
44
51
  featured: z.boolean().optional(),
45
52
  isPromoted: z.boolean().optional(),
53
+ allowShipBeforeEmiComplete: z.boolean().optional(),
54
+ printMeta: printMetaSchema.optional(),
46
55
  currentBid: z.number().optional(),
47
56
  availableQuantity: z.number().optional(),
48
57
  category: z.string().optional(),