@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
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- export type ProductListingMode = "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | "classified" | "digital-code" | "live";
2
+ export type ProductListingMode = "standard" | "auction" | "pre-order" | "prize-draw" | "bundle" | "classified" | "digital-code" | "live" | "art" | "stickers";
3
3
  export interface SellerProductDraft {
4
4
  title?: string;
5
5
  slug?: string;
@@ -61,6 +61,10 @@ export interface SellerProductDraft {
61
61
  liveCites?: boolean;
62
62
  /** Barcode ID of the physical sticker on this item. Auto-generated on save if blank. */
63
63
  barcodeId?: string;
64
+ printSize?: string;
65
+ printMaterial?: string;
66
+ printFinish?: string;
67
+ printEditionSize?: number;
64
68
  }
65
69
  export interface SellerProductShellProps {
66
70
  mode: "create" | "edit";
@@ -10,7 +10,21 @@ import { ImageUpload, MediaUploadField, MediaUploadList, useMediaUpload } from "
10
10
  import { StoreAddressSelectorCreate } from "../../stores/components/StoreAddressSelectorCreate";
11
11
  import { QuickProductForm } from "./QuickProductForm";
12
12
  import { BarcodeField } from "./BarcodeField";
13
+ import { pluginFor } from "../../../_internal/shared/listing-types/_registry";
13
14
  import { normalizeError } from "../../../errors/normalize";
15
+ /**
16
+ * "bundle" is a categoryType, not a ListingType, so it can't live in
17
+ * LISTING_TYPE_REGISTRY — this is the single, explicit escape hatch that
18
+ * lets the seller form's registry lookups also cover it.
19
+ */
20
+ const BUNDLE_PLUGIN_FALLBACK = {
21
+ priceLabel: "Price (₹)",
22
+ typeLabel: "Bundle",
23
+ showsStockQuantity: false,
24
+ };
25
+ function pluginForMode(mode) {
26
+ return mode === "bundle" ? BUNDLE_PLUGIN_FALLBACK : pluginFor(mode);
27
+ }
14
28
  const sellerProductSchema = z.object({
15
29
  title: z.string().min(1, "Product title is required").max(200),
16
30
  description: z.string().max(10000).optional().or(z.literal("")),
@@ -114,18 +128,59 @@ const LIVE_TRANSPORT_OPTIONS = [
114
128
  function StepLiveItemSettings({ values, onChange, }) {
115
129
  return (_jsxs(Stack, { gap: "md", children: [_jsx(Alert, { variant: "warning", children: "Live animal / plant listings must comply with all applicable laws, CITES regulations, and local jurisdiction rules. LetItRip is not responsible for legal compliance \u2014 this is solely the seller's responsibility." }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "liveSpecies", label: "Species / Common Name", type: "text", value: values.liveSpecies ?? "", onChange: (v) => onChange({ liveSpecies: v }), placeholder: "e.g. Axolotl (Ambystoma mexicanum)" }), _jsx(FormField, { name: "liveAgeMonths", label: "Age (months, optional)", type: "number", value: String(values.liveAgeMonths ?? ""), onChange: (v) => onChange({ liveAgeMonths: v ? Number(v) : undefined }), placeholder: "e.g. 6" })] }), _jsx(FormField, { name: "liveSex", label: "Sex", type: "select", value: values.liveSex ?? "unknown", onChange: (v) => onChange({ liveSex: v }), options: LIVE_SEX_OPTIONS }), _jsx(FormField, { name: "liveCareInfo", label: "Care Instructions (optional)", type: "textarea", value: values.liveCareInfo ?? "", onChange: (v) => onChange({ liveCareInfo: v }), placeholder: "Diet, temperature, habitat requirements\u2026" }), _jsx(FormField, { name: "liveTransportMethod", label: "Transport Method", type: "select", value: values.liveTransportMethod ?? "in-person", onChange: (v) => onChange({ liveTransportMethod: v }), options: LIVE_TRANSPORT_OPTIONS }), values.liveTransportMethod !== "in-person" && (_jsx(FormField, { name: "liveHandlingFee", label: "Handling / Transport Fee (\u20B9, optional)", type: "number", value: toRupees(values.liveHandlingFee), onChange: (v) => onChange({ liveHandlingFee: v ? toPaise(v) : undefined }), placeholder: "0" })), _jsx(FormField, { name: "liveJurisdictions", label: "Jurisdictions where sale is permitted (comma-separated)", type: "text", value: (values.liveJurisdictions ?? []).join(", "), onChange: (v) => onChange({ liveJurisdictions: v.split(",").map((s) => s.trim()).filter(Boolean) }), placeholder: "e.g. Maharashtra, Karnataka", helpText: "List all Indian states / UTs where you can legally sell" }), _jsx(Toggle, { checked: !!values.liveCites, onChange: (checked) => onChange({ liveCites: checked }), label: "This species requires CITES documentation" })] }));
116
130
  }
131
+ // ── Step: Print Meta (art / stickers) ───────────────────────────────────────
132
+ // Shared by both "art" and "stickers" — identical field set, only the intro
133
+ // copy differs (set by the caller via `intro`).
134
+ function StepPrintMetaSettings({ values, onChange, intro, }) {
135
+ return (_jsxs(Stack, { gap: "md", children: [_jsx(Alert, { variant: "info", children: intro }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "printSize", label: "Size", type: "text", value: values.printSize ?? "", onChange: (v) => onChange({ printSize: v }), placeholder: "e.g. A4, 12x18 in" }), _jsx(FormField, { name: "printMaterial", label: "Material", type: "text", value: values.printMaterial ?? "", onChange: (v) => onChange({ printMaterial: v }), placeholder: "e.g. Matte photo paper, Vinyl" })] }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "printFinish", label: "Finish (optional)", type: "text", value: values.printFinish ?? "", onChange: (v) => onChange({ printFinish: v }), placeholder: "e.g. Glossy, Matte, Holographic" }), _jsx(FormField, { name: "printEditionSize", label: "Edition Size (optional)", type: "number", value: String(values.printEditionSize ?? ""), onChange: (v) => onChange({ printEditionSize: v ? Number(v) : undefined }), placeholder: "Leave blank for an open edition" })] })] }));
136
+ }
137
+ const TYPE_SPECIFIC_STEPS = {
138
+ auction: {
139
+ id: "auction",
140
+ label: "Auction Settings",
141
+ render: ({ values, onChange }) => (_jsx(StepAuctionSettings, { values: values, onChange: onChange })),
142
+ validate: (v) => (!v.startingBid ? "Starting bid is required" : !v.auctionEndDate ? "Auction end date is required" : null),
143
+ },
144
+ "pre-order": {
145
+ id: "preorder",
146
+ label: "Pre-Order",
147
+ sectionHeading: "Pre-Order Settings",
148
+ render: ({ values, onChange }) => (_jsx(StepPreOrderSettings, { values: values, onChange: onChange })),
149
+ validate: (v) => (!v.preOrderDeliveryDate ? "Estimated delivery date is required" : null),
150
+ },
151
+ classified: {
152
+ id: "classified",
153
+ label: "Meetup Details",
154
+ render: ({ values, onChange }) => (_jsx(StepClassifiedSettings, { values: values, onChange: onChange })),
155
+ validate: (v) => (!v.classifiedCity?.trim() ? "City is required for classified listings" : null),
156
+ },
157
+ "digital-code": {
158
+ id: "digitalcode",
159
+ label: "Code Details",
160
+ render: ({ values, onChange }) => (_jsx(StepDigitalCodeSettings, { values: values, onChange: onChange })),
161
+ },
162
+ live: {
163
+ id: "live",
164
+ label: "Live Item Details",
165
+ render: ({ values, onChange }) => (_jsx(StepLiveItemSettings, { values: values, onChange: onChange })),
166
+ validate: (v) => (!v.liveSpecies?.trim() ? "Species name is required" : null),
167
+ },
168
+ art: {
169
+ id: "printmeta",
170
+ label: "Print Details",
171
+ render: ({ values, onChange }) => (_jsx(StepPrintMetaSettings, { values: values, onChange: onChange, intro: "Art prints are sold as physical, printed-only reproductions \u2014 no original artwork or digital files." })),
172
+ },
173
+ stickers: {
174
+ id: "printmeta",
175
+ label: "Print Details",
176
+ render: ({ values, onChange }) => (_jsx(StepPrintMetaSettings, { values: values, onChange: onChange, intro: "Sticker listings are sold as physical, printed sheets or packs \u2014 no digital files." })),
177
+ },
178
+ };
117
179
  // ── Step: Pricing ─────────────────────────────────────────────────────────
118
180
  function StepPricing({ values, onChange, listingType, }) {
119
- const priceLabel = listingType === "auction"
120
- ? "Suggested Retail Price (₹)"
121
- : listingType === "pre-order"
122
- ? "Pre-Order Price (₹)"
123
- : listingType === "classified"
124
- ? "Asking Price (₹)"
125
- : listingType === "digital-code"
126
- ? "Price per Code (₹)"
127
- : "Price (₹)";
128
- return (_jsxs(Stack, { gap: "md", children: [_jsx(Alert, { variant: "info", children: "All prices in Indian Rupees (\u20B9)." }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "price", label: priceLabel, type: "number", value: toRupees(values.price), onChange: (v) => onChange({ price: toPaise(v) }), placeholder: "999" }), _jsx(FormField, { name: "compareAtPrice", label: "Compare-at Price (\u20B9, optional)", type: "number", value: toRupees(values.compareAtPrice), onChange: (v) => onChange({ compareAtPrice: v ? toPaise(v) : undefined }), placeholder: "1299", helpText: "Original price shown as strikethrough" })] }), (listingType === "standard" || listingType === "classified" || listingType === "live") && (_jsx(FormField, { name: "stockQuantity", label: "Stock Quantity", type: "number", value: String(values.stockQuantity ?? ""), onChange: (v) => onChange({ stockQuantity: Number(v) }), placeholder: "1" })), _jsxs(FormGroup, { columns: 2, children: [_jsx(Toggle, { checked: !!values.featured, onChange: (checked) => onChange({ featured: checked }), label: "Mark as Featured" }), _jsx(Toggle, { checked: !!values.isNew, onChange: (checked) => onChange({ isNew: checked }), label: "Mark as New Arrival" })] })] }));
181
+ const listingPlugin = pluginForMode(listingType);
182
+ const priceLabel = listingPlugin.priceLabel;
183
+ return (_jsxs(Stack, { gap: "md", children: [_jsx(Alert, { variant: "info", children: "All prices in Indian Rupees (\u20B9)." }), _jsxs(FormGroup, { columns: 2, children: [_jsx(FormField, { name: "price", label: priceLabel, type: "number", value: toRupees(values.price), onChange: (v) => onChange({ price: toPaise(v) }), placeholder: "999" }), _jsx(FormField, { name: "compareAtPrice", label: "Compare-at Price (\u20B9, optional)", type: "number", value: toRupees(values.compareAtPrice), onChange: (v) => onChange({ compareAtPrice: v ? toPaise(v) : undefined }), placeholder: "1299", helpText: "Original price shown as strikethrough" })] }), listingPlugin.showsStockQuantity && (_jsx(FormField, { name: "stockQuantity", label: "Stock Quantity", type: "number", value: String(values.stockQuantity ?? ""), onChange: (v) => onChange({ stockQuantity: Number(v) }), placeholder: "1" })), _jsxs(FormGroup, { columns: 2, children: [_jsx(Toggle, { checked: !!values.featured, onChange: (checked) => onChange({ featured: checked }), label: "Mark as Featured" }), _jsx(Toggle, { checked: !!values.isNew, onChange: (checked) => onChange({ isNew: checked }), label: "Mark as New Arrival" })] })] }));
129
184
  }
130
185
  // ── Step: Shipping ────────────────────────────────────────────────────────
131
186
  function StepShipping({ values, onChange, renderAddressSelector, }) {
@@ -235,49 +290,8 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
235
290
  showToast(err instanceof Error ? err.message : "Failed to publish.", "error");
236
291
  }
237
292
  }, [draft, onPublish, markClean, showToast]);
238
- const listingTypeLabel = listingType === "auction"
239
- ? "Auction"
240
- : listingType === "pre-order"
241
- ? "Pre-Order"
242
- : listingType === "bundle"
243
- ? "Bundle"
244
- : listingType === "classified"
245
- ? "Classified"
246
- : listingType === "digital-code"
247
- ? "Digital Code"
248
- : listingType === "live"
249
- ? "Live Item"
250
- : "Product";
251
- const typeSpecificStep = listingType === "auction"
252
- ? {
253
- label: "Auction Settings",
254
- render: ({ values, onChange }) => (_jsx(StepAuctionSettings, { values: values, onChange: onChange })),
255
- validate: (v) => (!v.startingBid ? "Starting bid is required" : !v.auctionEndDate ? "Auction end date is required" : null),
256
- }
257
- : listingType === "pre-order"
258
- ? {
259
- label: "Pre-Order",
260
- render: ({ values, onChange }) => (_jsx(StepPreOrderSettings, { values: values, onChange: onChange })),
261
- validate: (v) => (!v.preOrderDeliveryDate ? "Estimated delivery date is required" : null),
262
- }
263
- : listingType === "classified"
264
- ? {
265
- label: "Meetup Details",
266
- render: ({ values, onChange }) => (_jsx(StepClassifiedSettings, { values: values, onChange: onChange })),
267
- validate: (v) => (!v.classifiedCity?.trim() ? "City is required for classified listings" : null),
268
- }
269
- : listingType === "digital-code"
270
- ? {
271
- label: "Code Details",
272
- render: ({ values, onChange }) => (_jsx(StepDigitalCodeSettings, { values: values, onChange: onChange })),
273
- }
274
- : listingType === "live"
275
- ? {
276
- label: "Live Item Details",
277
- render: ({ values, onChange }) => (_jsx(StepLiveItemSettings, { values: values, onChange: onChange })),
278
- validate: (v) => (!v.liveSpecies?.trim() ? "Species name is required" : null),
279
- }
280
- : null;
293
+ const listingTypeLabel = pluginForMode(listingType).typeLabel;
294
+ const typeSpecificStep = TYPE_SPECIFIC_STEPS[listingType] ?? null;
281
295
  const steps = [
282
296
  {
283
297
  label: "Basic",
@@ -349,11 +363,7 @@ export function SellerProductShell({ mode, listingType = "standard", initialValu
349
363
  // Edit mode — FormShell with section nav + full form
350
364
  const editSections = [
351
365
  ...EDIT_SECTIONS,
352
- ...(listingType === "auction" ? [{ id: "auction", label: "Auction" }] : []),
353
- ...(listingType === "pre-order" ? [{ id: "preorder", label: "Pre-Order" }] : []),
354
- ...(listingType === "classified" ? [{ id: "classified", label: "Meetup Details" }] : []),
355
- ...(listingType === "digital-code" ? [{ id: "digitalcode", label: "Code Details" }] : []),
356
- ...(listingType === "live" ? [{ id: "live", label: "Live Item" }] : []),
366
+ ...(typeSpecificStep ? [{ id: typeSpecificStep.id, label: typeSpecificStep.label }] : []),
357
367
  ];
358
- return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, sections: editSections, onSaveDraft: handleSave, onPublish: handlePublish, saveLabel: "Save Changes", publishLabel: "Update", previewSlot: previewSlot, children: _jsx(FormShellProvider, { isDirty: isDirty, values: draft, children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Section, { id: "basic", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Basic Info" }), _jsx(StepBasic, { values: draft, onChange: update, renderCategorySelector: renderCategorySelector, renderBrandSelector: renderBrandSelector, renderTemplateSelector: renderTemplateSelector })] }), _jsxs(Section, { id: "media", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Media" }), _jsx(StepMedia, { values: draft, onChange: update, storeSlug: storeSlug })] }), listingType === "auction" && (_jsxs(Section, { id: "auction", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Auction Settings" }), _jsx(StepAuctionSettings, { values: draft, onChange: update })] })), listingType === "pre-order" && (_jsxs(Section, { id: "preorder", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Pre-Order Settings" }), _jsx(StepPreOrderSettings, { values: draft, onChange: update })] })), listingType === "classified" && (_jsxs(Section, { id: "classified", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Meetup Details" }), _jsx(StepClassifiedSettings, { values: draft, onChange: update })] })), listingType === "digital-code" && (_jsxs(Section, { id: "digitalcode", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Code Details" }), _jsx(StepDigitalCodeSettings, { values: draft, onChange: update })] })), listingType === "live" && (_jsxs(Section, { id: "live", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Live Item Details" }), _jsx(StepLiveItemSettings, { values: draft, onChange: update })] })), _jsxs(Section, { id: "pricing", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Pricing" }), _jsx(StepPricing, { values: draft, onChange: update, listingType: listingType })] }), _jsxs(Section, { id: "shipping", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Shipping" }), _jsx(StepShipping, { values: draft, onChange: update, renderAddressSelector: renderAddressSelector })] }), _jsxs(Section, { id: "publish", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Publish" }), _jsx(StepPublish, { values: draft, onChange: update }), onSaveAsTemplate && (_jsxs(Div, { className: "mt-4 border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onSaveAsTemplate(draft), children: "Save as Template" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-secondary-text)]", size: "xs", children: "Save these settings as a reusable template for future listings." })] }))] })] }) }) }));
368
+ return (_jsx(FormShell, { isOpen: true, onClose: handleDiscard, title: title, breadcrumb: breadcrumb, isDirty: isDirty, isLoading: isLoading, sections: editSections, onSaveDraft: handleSave, onPublish: handlePublish, saveLabel: "Save Changes", publishLabel: "Update", previewSlot: previewSlot, children: _jsx(FormShellProvider, { isDirty: isDirty, values: draft, children: _jsxs(Stack, { gap: "lg", children: [_jsxs(Section, { id: "basic", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Basic Info" }), _jsx(StepBasic, { values: draft, onChange: update, renderCategorySelector: renderCategorySelector, renderBrandSelector: renderBrandSelector, renderTemplateSelector: renderTemplateSelector })] }), _jsxs(Section, { id: "media", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Media" }), _jsx(StepMedia, { values: draft, onChange: update, storeSlug: storeSlug })] }), typeSpecificStep && (_jsxs(Section, { id: typeSpecificStep.id, children: [_jsx(Heading, { level: 3, className: "mb-4", children: typeSpecificStep.sectionHeading ?? typeSpecificStep.label }), typeSpecificStep.render({ values: draft, onChange: update, errors: {} })] })), _jsxs(Section, { id: "pricing", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Pricing" }), _jsx(StepPricing, { values: draft, onChange: update, listingType: listingType })] }), _jsxs(Section, { id: "shipping", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Shipping" }), _jsx(StepShipping, { values: draft, onChange: update, renderAddressSelector: renderAddressSelector })] }), _jsxs(Section, { id: "publish", children: [_jsx(Heading, { level: 3, className: "mb-4", children: "Publish" }), _jsx(StepPublish, { values: draft, onChange: update }), onSaveAsTemplate && (_jsxs(Div, { className: "mt-4 border-t border-[var(--appkit-color-border)]", padding: "t-md", children: [_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => onSaveAsTemplate(draft), children: "Save as Template" }), _jsx(Text, { className: "mt-1 text-[var(--appkit-color-secondary-text)]", size: "xs", children: "Save these settings as a reusable template for future listings." })] }))] })] }) }) }));
359
369
  }
@@ -94,7 +94,6 @@ export function SellerReviewsView({ reviewsApiBase = "/api/store/reviews", reply
94
94
  }, [feedbackTarget, feedbackText]);
95
95
  const fetchReviewsRef = useMemo(() => ({ current: null }), []);
96
96
  const fetchReviews = useCallback(async () => {
97
- // toast-intentionally-silent: background data loader — error displayed in inline error state
98
97
  setLoading(true);
99
98
  setError(null);
100
99
  try {
@@ -7,11 +7,8 @@ import { StoreAddressSelectorCreate } from "../../stores/components/StoreAddress
7
7
  import { StepForm } from "../../shell";
8
8
  import { normalizeError } from "../../../errors/normalize";
9
9
  const DEFAULT_DRAFT = {
10
- method: "custom",
11
10
  customCarrierName: "",
12
11
  customShippingPrice: "",
13
- shiprocketEmail: "",
14
- shiprocketPassword: "",
15
12
  pickupAddressId: "",
16
13
  freeShippingThreshold: "",
17
14
  fragileSurcharge: "",
@@ -31,11 +28,8 @@ export function SellerShippingView({ apiBase = "/api/store/shipping" }) {
31
28
  const cfg = res?.data?.shippingConfig ?? { method: "custom", isConfigured: false };
32
29
  setCurrent(cfg);
33
30
  setDraft({
34
- method: cfg.method,
35
31
  customCarrierName: cfg.customCarrierName ?? "",
36
32
  customShippingPrice: cfg.customShippingPrice ? String(cfg.customShippingPrice / 100) : "",
37
- shiprocketEmail: cfg.shiprocketEmail ?? "",
38
- shiprocketPassword: "",
39
33
  pickupAddressId: "",
40
34
  freeShippingThreshold: (res?.data?.freeShippingThreshold ?? 0)
41
35
  ? String(res.data.freeShippingThreshold / 100)
@@ -57,20 +51,9 @@ export function SellerShippingView({ apiBase = "/api/store/shipping" }) {
57
51
  setSuccess(false);
58
52
  setSaving(true);
59
53
  try {
60
- const methodFields = draft.method === "custom"
61
- ? {
62
- method: "custom",
63
- customCarrierName: draft.customCarrierName.trim(),
64
- customShippingPrice: Math.round(parseFloat(draft.customShippingPrice || "0") * 100),
65
- }
66
- : {
67
- method: "shiprocket",
68
- ...(draft.shiprocketPassword
69
- ? { shiprocketCredentials: { email: draft.shiprocketEmail, password: draft.shiprocketPassword } }
70
- : {}),
71
- };
72
54
  const body = {
73
- ...methodFields,
55
+ customCarrierName: draft.customCarrierName.trim(),
56
+ customShippingPrice: Math.round(parseFloat(draft.customShippingPrice || "0") * 100),
74
57
  ...(draft.pickupAddressId ? { pickupAddressId: draft.pickupAddressId } : {}),
75
58
  freeShippingThreshold: Math.round(parseFloat(draft.freeShippingThreshold || "0") * 100),
76
59
  fragileSurcharge: Math.round(parseFloat(draft.fragileSurcharge || "0") * 100),
@@ -84,7 +67,7 @@ export function SellerShippingView({ apiBase = "/api/store/shipping" }) {
84
67
  if (!res.ok)
85
68
  throw new Error(json?.error ?? "Failed to save");
86
69
  setSuccess(true);
87
- const cfg = json?.data?.shippingConfig ?? { method: draft.method, isConfigured: false };
70
+ const cfg = json?.data?.shippingConfig ?? { method: "custom", isConfigured: false };
88
71
  setCurrent(cfg);
89
72
  }
90
73
  catch (err) {
@@ -98,14 +81,12 @@ export function SellerShippingView({ apiBase = "/api/store/shipping" }) {
98
81
  const busy = loading || saving;
99
82
  const steps = [
100
83
  {
101
- label: "Method",
102
- render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Shipping Method" }), current && (_jsxs(Row, { className: "mb-2", align: "center", gap: "sm", children: [_jsx(Badge, { variant: current.isConfigured ? "success" : "warning", children: current.isConfigured ? "Configured" : "Not configured" }), current.method === "shiprocket" && (_jsx(Badge, { variant: current.isTokenValid ? "success" : "danger", children: current.isTokenValid ? "Shiprocket connected" : "Shiprocket token expired" }))] })), _jsx(Stack, { gap: "sm", children: ["custom", "shiprocket"].map((m) => (_jsxs("label", { className: "flex items-center gap-3 p-3 rounded-lg border border-[var(--appkit-color-border)] cursor-pointer has-[:checked]:border-[var(--appkit-color-primary)] has-[:checked]:bg-[var(--appkit-color-primary)]/5", children: [_jsx("input", { type: "radio", name: "method", value: m, checked: values.method === m, onChange: () => onChange({ method: m }), className: "accent-[var(--appkit-color-primary)]", disabled: busy }), _jsxs(Div, { children: [_jsx(Text, { weight: "medium", children: m === "custom" ? "Custom / Manual" : "Shiprocket" }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: m === "custom"
103
- ? "Set a fixed shipping fee and carrier name for all orders."
104
- : "Automated shipping via Shiprocket — connect your account for label generation and tracking." })] })] }, m))) }), values.method === "custom" && (_jsxs(Stack, { gap: "md", className: "mt-2", children: [_jsx(FormField, { name: "customCarrierName", label: "Carrier Name", type: "text", value: values.customCarrierName, onChange: (v) => onChange({ customCarrierName: v }), placeholder: "e.g. India Post, DTDC, Delhivery", disabled: busy }), _jsx(FormField, { name: "customShippingPrice", label: "Shipping Price (\u20B9)", type: "number", value: values.customShippingPrice, onChange: (v) => onChange({ customShippingPrice: v }), placeholder: "0 for free shipping", helpText: "Charged to buyer at checkout. Enter 0 for free shipping.", disabled: busy })] })), values.method === "shiprocket" && (_jsxs(Stack, { gap: "md", className: "mt-2", children: [_jsx(FormField, { name: "shiprocketEmail", label: "Shiprocket Email", type: "email", value: values.shiprocketEmail, onChange: (v) => onChange({ shiprocketEmail: v }), placeholder: "your@email.com", disabled: busy }), _jsx(FormField, { name: "shiprocketPassword", label: current?.isTokenValid ? "Password (leave blank to keep existing token)" : "Password", type: "password", value: values.shiprocketPassword, onChange: (v) => onChange({ shiprocketPassword: v }), placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", helpText: current?.isTokenValid ? "Only fill this to re-authenticate." : "Required to connect your Shiprocket account.", disabled: busy })] }))] })),
84
+ label: "Carrier",
85
+ render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Shipping Carrier" }), current && (_jsx(Row, { className: "mb-2", align: "center", gap: "sm", children: _jsx(Badge, { variant: current.isConfigured ? "success" : "warning", children: current.isConfigured ? "Configured" : "Not configured" }) })), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: "Set a fixed shipping fee and carrier name for all orders. You enter the carrier and tracking number yourself when you ship each order." }), _jsxs(Stack, { gap: "md", className: "mt-2", children: [_jsx(FormField, { name: "customCarrierName", label: "Carrier Name", type: "text", value: values.customCarrierName, onChange: (v) => onChange({ customCarrierName: v }), placeholder: "e.g. India Post, DTDC, Delhivery", disabled: busy }), _jsx(FormField, { name: "customShippingPrice", label: "Shipping Price (\u20B9)", type: "number", value: values.customShippingPrice, onChange: (v) => onChange({ customShippingPrice: v }), placeholder: "0 for free shipping", helpText: "Charged to buyer at checkout. Enter 0 for free shipping.", disabled: busy })] })] })),
105
86
  },
106
87
  {
107
88
  label: "Pickup Address",
108
- render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Pickup Address" }), current?.pickupAddress && (_jsxs(Alert, { variant: "info", children: ["Current pickup: ", current.pickupAddress.locationName ?? "", current.pickupAddress.city ? `, ${current.pickupAddress.city}` : "", current.pickupAddress.isVerified ? " — ✓ Verified" : " — Pending OTP verification"] })), _jsx(StoreAddressSelectorCreate, { value: values.pickupAddressId, onChange: (id) => onChange({ pickupAddressId: id }), label: "Pickup Address", disabled: busy }), _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "xs", children: "Registering a pickup address sends an OTP to your phone for verification." })] })),
89
+ render: ({ values, onChange }) => (_jsxs(Stack, { gap: "md", children: [_jsx(Heading, { level: 3, className: "mb-2", children: "Pickup Address" }), current?.pickupAddress && (_jsxs(Alert, { variant: "info", children: ["Current pickup: ", current.pickupAddress.locationName ?? "", current.pickupAddress.city ? `, ${current.pickupAddress.city}` : ""] })), _jsx(StoreAddressSelectorCreate, { value: values.pickupAddressId, onChange: (id) => onChange({ pickupAddressId: id }), label: "Pickup Address", disabled: busy })] })),
109
90
  },
110
91
  {
111
92
  label: "Rules",
@@ -0,0 +1,7 @@
1
+ export interface SellerStickersViewProps {
2
+ onCreateClick?: () => void;
3
+ onEditClick?: (id: string) => void;
4
+ onDelete?: (id: string) => Promise<void>;
5
+ onBulkDelete?: (ids: string[]) => Promise<void>;
6
+ }
7
+ export declare function SellerStickersView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }: SellerStickersViewProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,114 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * SellerStickersView — seller browse/manage of sticker listings
5
+ * (EMI/art-stickers session). Mirrors SellerClassifiedView's pattern.
6
+ */
7
+ import { Badge, sortBy } from "@mohasinac/appkit";
8
+ import { useState, useCallback } from "react";
9
+ import { useEntityDelete } from "../../../react/hooks/useEntityDelete";
10
+ import { ConfirmDeleteModal, RowActionMenu, Text } from "../../../ui";
11
+ import { SELLER_ENDPOINTS } from "../../../constants/api-endpoints";
12
+ import { ACTIONS } from "../../../_internal/shared/actions/action-registry";
13
+ import { ROUTES } from "../../..";
14
+ import { toRecordArray, toRelativeDate, toRupees, toStringValue, } from "../../admin/hooks/useAdminListingData";
15
+ import { DataListingView } from "../../admin/components/DataListingView";
16
+ const COLUMNS = [
17
+ { key: "title", header: "Listing", render: (row) => _jsx(Text, { size: "sm", weight: "medium", children: row.title }) },
18
+ { key: "price", header: "Price", render: (row) => _jsx(Text, { className: "tabular-nums", size: "sm", children: row.price }) },
19
+ {
20
+ key: "size",
21
+ header: "Size",
22
+ render: (row) => _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: row.size || "—" }),
23
+ },
24
+ {
25
+ key: "status",
26
+ header: "Status",
27
+ render: (row) => (_jsx(Badge, { variant: row.status === "active" ? "active" : "inactive", size: "xs", className: "capitalize", children: row.status })),
28
+ },
29
+ {
30
+ key: "createdAt",
31
+ header: "Created",
32
+ render: (row) => _jsx(Text, { className: "text-[var(--appkit-color-text-muted)]", size: "sm", children: row.createdAt }),
33
+ },
34
+ ];
35
+ export function SellerStickersView({ onCreateClick, onEditClick, onDelete, onBulkDelete, }) {
36
+ const [deleteTargetId, setDeleteTargetId] = useState(null);
37
+ const { deletingId, handleDelete: performDelete } = useEntityDelete({
38
+ endpoint: (id) => `/api/store/products/${id}`,
39
+ deleteFn: onDelete,
40
+ successMessage: "Listing deleted.",
41
+ fetchOptions: { credentials: "include" },
42
+ });
43
+ const handleDelete = useCallback(async (id) => {
44
+ await performDelete(id);
45
+ setDeleteTargetId(null);
46
+ }, [performDelete]);
47
+ const handleEdit = useCallback((id) => {
48
+ if (onEditClick)
49
+ onEditClick(id);
50
+ else
51
+ window.location.href = String(ROUTES.STORE.STICKERS_EDIT(id));
52
+ }, [onEditClick]);
53
+ const handleCreate = useCallback(() => {
54
+ if (onCreateClick)
55
+ onCreateClick();
56
+ else
57
+ window.location.href = String(ROUTES.STORE.STICKERS_NEW);
58
+ }, [onCreateClick]);
59
+ const config = {
60
+ portal: "seller",
61
+ title: "Stickers",
62
+ searchPlaceholder: "Search sticker listings...",
63
+ emptyLabel: "No sticker listings yet — list your first pack",
64
+ filterKeys: [],
65
+ defaultSort: sortBy("createdAt", "DESC"),
66
+ queryKey: ["seller", "stickers"],
67
+ endpoint: SELLER_ENDPOINTS.PRODUCTS,
68
+ sortOptions: [
69
+ { value: sortBy("createdAt", "DESC"), label: "Newest" },
70
+ { value: sortBy("createdAt", "ASC"), label: "Oldest" },
71
+ { value: "productTitle", label: "Name A–Z" },
72
+ { value: sortBy("price", "ASC"), label: "Price: Low–High" },
73
+ { value: sortBy("price", "DESC"), label: "Price: High–Low" },
74
+ ],
75
+ columns: COLUMNS,
76
+ mapRows: (response) => toRecordArray(response.products).map((item, index) => {
77
+ const printMeta = (item.printMeta ?? {});
78
+ return {
79
+ id: toStringValue(item.id, `stickers-${index}`),
80
+ title: toStringValue(item.productTitle ?? item.title, "Untitled"),
81
+ price: toRupees(item.price),
82
+ size: toStringValue(printMeta.size, ""),
83
+ status: toStringValue(item.status, "draft"),
84
+ createdAt: toRelativeDate(item.createdAt),
85
+ };
86
+ }),
87
+ getTotal: (response, mappedRows) => typeof response.meta?.total === "number" ? response.meta.total : mappedRows.length,
88
+ buildFilters: () => "listingType==stickers",
89
+ primaryAction: { label: "New Sticker Listing", onClick: () => handleCreate() },
90
+ buildBulkActions: onBulkDelete
91
+ ? (selection) => [
92
+ {
93
+ id: "bulk-delete",
94
+ label: ACTIONS.STORE["delete-listing"].label,
95
+ variant: "danger",
96
+ onClick: async () => {
97
+ await onBulkDelete(selection.selectedIds);
98
+ selection.clearSelection();
99
+ },
100
+ },
101
+ ]
102
+ : undefined,
103
+ renderRowActions: (row) => (_jsx(RowActionMenu, { actions: [
104
+ { label: ACTIONS.STORE["edit-listing"].label, onClick: () => handleEdit(row.id) },
105
+ {
106
+ label: ACTIONS.STORE["delete-listing"].label,
107
+ destructive: true,
108
+ onClick: () => setDeleteTargetId(row.id),
109
+ disabled: deletingId === row.id,
110
+ },
111
+ ] })),
112
+ };
113
+ return (_jsxs(_Fragment, { children: [_jsx(DataListingView, { config: config }), deleteTargetId && (_jsx(ConfirmDeleteModal, { isOpen: true, title: "Delete Sticker Listing", message: "Are you sure you want to delete this sticker listing? This cannot be undone.", onConfirm: () => handleDelete(deleteTargetId), onClose: () => setDeleteTargetId(null), isDeleting: deletingId === deleteTargetId }))] }));
114
+ }
@@ -124,6 +124,10 @@ export { SellerBundlesView } from "./SellerBundlesView";
124
124
  export type { SellerBundlesViewProps } from "./SellerBundlesView";
125
125
  export { SellerClassifiedView } from "./SellerClassifiedView";
126
126
  export type { SellerClassifiedViewProps } from "./SellerClassifiedView";
127
+ export { SellerArtView } from "./SellerArtView";
128
+ export type { SellerArtViewProps } from "./SellerArtView";
129
+ export { SellerStickersView } from "./SellerStickersView";
130
+ export type { SellerStickersViewProps } from "./SellerStickersView";
127
131
  export { SellerDigitalCodesView } from "./SellerDigitalCodesView";
128
132
  export type { SellerDigitalCodesViewProps } from "./SellerDigitalCodesView";
129
133
  export { SellerLiveView } from "./SellerLiveView";
@@ -65,6 +65,8 @@ export { SellerShippingConfigsView } from "./SellerShippingConfigsView";
65
65
  export { SellerGoogleReviewsView } from "./SellerGoogleReviewsView";
66
66
  export { SellerBundlesView } from "./SellerBundlesView";
67
67
  export { SellerClassifiedView } from "./SellerClassifiedView";
68
+ export { SellerArtView } from "./SellerArtView";
69
+ export { SellerStickersView } from "./SellerStickersView";
68
70
  export { SellerDigitalCodesView } from "./SellerDigitalCodesView";
69
71
  export { SellerLiveView } from "./SellerLiveView";
70
72
  export { BarcodeField } from "./BarcodeField";
@@ -8,7 +8,7 @@
8
8
  "payoutStatus": {
9
9
  "pending": "Pending",
10
10
  "processing": "Processing",
11
- "completed": "Completed",
11
+ "paid": "Paid",
12
12
  "failed": "Failed"
13
13
  },
14
14
  "dashboard": {
@@ -1,7 +1,7 @@
1
1
  export * from "./firestore";
2
2
  import { z } from "zod";
3
3
  export declare const storeStatusSchema: z.ZodEnum<["pending", "active", "suspended", "rejected"]>;
4
- export declare const payoutStatusSchema: z.ZodEnum<["pending", "processing", "completed", "failed"]>;
4
+ export declare const payoutStatusSchema: z.ZodEnum<["pending", "processing", "paid", "failed"]>;
5
5
  export declare const payoutPaymentMethodSchema: z.ZodEnum<["bank_transfer", "upi"]>;
6
6
  export declare const socialLinksSchema: z.ZodObject<{
7
7
  twitter: z.ZodOptional<z.ZodString>;
@@ -159,7 +159,7 @@ export declare const payoutRecordSchema: z.ZodObject<{
159
159
  platformFee: z.ZodNumber;
160
160
  platformFeeRate: z.ZodNumber;
161
161
  currency: z.ZodString;
162
- status: z.ZodEnum<["pending", "processing", "completed", "failed"]>;
162
+ status: z.ZodEnum<["pending", "processing", "paid", "failed"]>;
163
163
  paymentMethod: z.ZodEnum<["bank_transfer", "upi"]>;
164
164
  orderIds: z.ZodArray<z.ZodString, "many">;
165
165
  requestedAt: z.ZodString;
@@ -167,7 +167,7 @@ export declare const payoutRecordSchema: z.ZodObject<{
167
167
  createdAt: z.ZodString;
168
168
  updatedAt: z.ZodString;
169
169
  }, "strip", z.ZodTypeAny, {
170
- status: "pending" | "failed" | "processing" | "completed";
170
+ status: "pending" | "paid" | "failed" | "processing";
171
171
  currency: string;
172
172
  createdAt: string;
173
173
  id: string;
@@ -184,7 +184,7 @@ export declare const payoutRecordSchema: z.ZodObject<{
184
184
  sellerId: string;
185
185
  processedAt?: string | undefined;
186
186
  }, {
187
- status: "pending" | "failed" | "processing" | "completed";
187
+ status: "pending" | "paid" | "failed" | "processing";
188
188
  currency: string;
189
189
  createdAt: string;
190
190
  id: string;
@@ -225,18 +225,18 @@ export declare const sellerListParamsSchema: z.ZodObject<{
225
225
  }>;
226
226
  export declare const payoutListParamsSchema: z.ZodObject<{
227
227
  sellerId: z.ZodOptional<z.ZodString>;
228
- status: z.ZodOptional<z.ZodEnum<["pending", "processing", "completed", "failed"]>>;
228
+ status: z.ZodOptional<z.ZodEnum<["pending", "processing", "paid", "failed"]>>;
229
229
  page: z.ZodOptional<z.ZodNumber>;
230
230
  perPage: z.ZodOptional<z.ZodNumber>;
231
231
  sort: z.ZodOptional<z.ZodString>;
232
232
  }, "strip", z.ZodTypeAny, {
233
- status?: "pending" | "failed" | "processing" | "completed" | undefined;
233
+ status?: "pending" | "paid" | "failed" | "processing" | undefined;
234
234
  sort?: string | undefined;
235
235
  page?: number | undefined;
236
236
  perPage?: number | undefined;
237
237
  sellerId?: string | undefined;
238
238
  }, {
239
- status?: "pending" | "failed" | "processing" | "completed" | undefined;
239
+ status?: "pending" | "paid" | "failed" | "processing" | undefined;
240
240
  sort?: string | undefined;
241
241
  page?: number | undefined;
242
242
  perPage?: number | undefined;
@@ -10,7 +10,7 @@ export const storeStatusSchema = z.enum([
10
10
  export const payoutStatusSchema = z.enum([
11
11
  "pending",
12
12
  "processing",
13
- "completed",
13
+ "paid",
14
14
  "failed",
15
15
  ]);
16
16
  export const payoutPaymentMethodSchema = z.enum(["bank_transfer", "upi"]);
@@ -32,7 +32,7 @@ export interface SellerStore {
32
32
  createdAt: string;
33
33
  updatedAt: string;
34
34
  }
35
- export type PayoutStatus = "pending" | "processing" | "completed" | "failed";
35
+ export type PayoutStatus = "pending" | "processing" | "paid" | "failed";
36
36
  export type PayoutPaymentMethod = "bank_transfer" | "upi";
37
37
  export interface PayoutBankAccount {
38
38
  accountHolderName: string;
@@ -17,6 +17,10 @@ const STORE_LISTING_HREF = {
17
17
  classifieds: (slug) => String(ROUTES.PUBLIC.STORE_CLASSIFIEDS(slug)),
18
18
  "digital-codes": (slug) => String(ROUTES.PUBLIC.STORE_DIGITAL_CODES(slug)),
19
19
  live: (slug) => String(ROUTES.PUBLIC.STORE_LIVE(slug)),
20
+ // Art / stickers have no dedicated storefront tab page — they're browsable
21
+ // (alongside everything else the store sells) on the "Products" tab.
22
+ art: (slug) => String(ROUTES.PUBLIC.STORE_PRODUCTS(slug)),
23
+ stickers: (slug) => String(ROUTES.PUBLIC.STORE_PRODUCTS(slug)),
20
24
  };
21
25
  export const getStoreBySlug = cache((slug) => storeRepository.findBySlug(slug).catch(() => undefined));
22
26
  function tabLabel(base, count) {
@@ -31,7 +35,7 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
31
35
  }
32
36
  const storeId = store?.id;
33
37
  const settings = await siteSettingsRepository.findById("global").catch(() => null);
34
- const [productsCount, auctionsCount, preOrdersCount, prizeDrawsCount, bundlesCount, classifiedsCount, digitalCodesCount, liveCount, couponsCount, reviewsCount] = storeId
38
+ const [productsCount, auctionsCount, preOrdersCount, prizeDrawsCount, bundlesCount, classifiedsCount, digitalCodesCount, liveCount, artCount, stickersCount, couponsCount, reviewsCount] = storeId
35
39
  ? await Promise.all([
36
40
  productRepository
37
41
  .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "standard")), page: 1, pageSize: 1 })
@@ -67,6 +71,14 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
67
71
  .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "live")), page: 1, pageSize: 1 })
68
72
  .then((r) => r.total)
69
73
  .catch(() => 0),
74
+ productRepository
75
+ .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "art")), page: 1, pageSize: 1 })
76
+ .then((r) => r.total)
77
+ .catch(() => 0),
78
+ productRepository
79
+ .list({ filters: sieveAnd(sieveFilter("storeId", SIEVE_OP.EQ, storeId), sieveFilter("status", SIEVE_OP.EQ, "published"), sieveFilter("listingType", SIEVE_OP.EQ, "stickers")), page: 1, pageSize: 1 })
80
+ .then((r) => r.total)
81
+ .catch(() => 0),
70
82
  couponsRepository
71
83
  .list({ filters: sieveAnd(sieveFilter("sellerId", SIEVE_OP.EQ, storeId), sieveFilter("validity.isActive", SIEVE_OP.EQ, "true")), page: 1, pageSize: 1 })
72
84
  .then((r) => r.total)
@@ -76,7 +88,7 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
76
88
  .then((r) => r.total)
77
89
  .catch(() => 0),
78
90
  ])
79
- : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
91
+ : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
80
92
  const listingCounts = {
81
93
  products: productsCount,
82
94
  auctions: auctionsCount,
@@ -86,6 +98,8 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
86
98
  classifieds: classifiedsCount,
87
99
  "digital-codes": digitalCodesCount,
88
100
  live: liveCount,
101
+ art: artCount,
102
+ stickers: stickersCount,
89
103
  };
90
104
  const TAB_LISTING_TYPE = {
91
105
  products: "standard",
@@ -95,6 +109,8 @@ export async function StoreDetailLayoutView({ storeSlug, activeTab, children, })
95
109
  classifieds: "classified",
96
110
  "digital-codes": "digital-code",
97
111
  live: "live",
112
+ art: "art",
113
+ stickers: "stickers",
98
114
  };
99
115
  const visibleStoreTabs = STORE_PAGE_TABS.filter((tab) => {
100
116
  if (tab.id === "bundles")
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import type { StoreCapability } from "../../auth/permissions/constants";
8
8
  import type { BaseDocument } from "../../../_internal/shared/types/base-document";
9
- export type ShippingProviderType = "shiprocket" | "self-courier" | "store-pickup" | "custom";
9
+ export type ShippingProviderType = "self-courier" | "store-pickup" | "custom";
10
10
  export interface ShippingProviderConfig {
11
11
  /** Stable slug — never changes after creation. */
12
12
  providerId: string;
@@ -101,6 +101,12 @@ export interface StoreDocument extends BaseDocument {
101
101
  * ShippingPicker; the chosen provider + fee lock onto each CartItem.
102
102
  */
103
103
  shippingConfig?: StoreShippingConfig;
104
+ /**
105
+ * Seller opt-in for EMI (installment) checkout. EMI is offered on this
106
+ * store's items only when this AND `siteSettings.emi.enabled` are both
107
+ * true. Default false — sellers must explicitly opt in.
108
+ */
109
+ emiEnabled?: boolean;
104
110
  whatsappConfig?: {
105
111
  /** Seller's WhatsApp Business phone number, digits-only with country code e.g. "919876543210" */
106
112
  phoneNumber?: string;