@mohasinac/appkit 2.7.35 → 2.7.37

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 (186) hide show
  1. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +1 -1
  2. package/dist/_internal/client/features/seller/print-center/InventoryLabel.d.ts +12 -0
  3. package/dist/_internal/client/features/seller/print-center/InventoryLabel.js +49 -0
  4. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.d.ts +7 -0
  5. package/dist/_internal/client/features/seller/print-center/LabelDesignPicker.js +32 -0
  6. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.d.ts +28 -0
  7. package/dist/_internal/client/features/seller/print-center/OrderPackingLabel.js +33 -0
  8. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.d.ts +12 -0
  9. package/dist/_internal/client/features/seller/print-center/PhysicalLocationModal.js +30 -0
  10. package/dist/_internal/client/features/seller/print-center/PrintCenterView.d.ts +12 -0
  11. package/dist/_internal/client/features/seller/print-center/PrintCenterView.js +102 -0
  12. package/dist/_internal/client/features/seller/print-center/PrintGrid.d.ts +12 -0
  13. package/dist/_internal/client/features/seller/print-center/PrintGrid.js +13 -0
  14. package/dist/_internal/client/features/seller/print-center/StoreCard.d.ts +19 -0
  15. package/dist/_internal/client/features/seller/print-center/StoreCard.js +30 -0
  16. package/dist/_internal/client/features/seller/print-center/WebsiteCard.d.ts +11 -0
  17. package/dist/_internal/client/features/seller/print-center/WebsiteCard.js +25 -0
  18. package/dist/_internal/client/features/seller/print-center/index.d.ts +11 -0
  19. package/dist/_internal/client/features/seller/print-center/index.js +10 -0
  20. package/dist/_internal/client/features/seller/print-center/types.d.ts +20 -0
  21. package/dist/_internal/client/features/seller/print-center/types.js +20 -0
  22. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.d.ts +18 -0
  23. package/dist/_internal/client/features/seller/print-center/useInventoryPdf.js +76 -0
  24. package/dist/_internal/server/features/auctions/og.d.ts +1 -0
  25. package/dist/_internal/server/features/auctions/og.js +2 -1
  26. package/dist/_internal/server/features/blog/og.d.ts +1 -0
  27. package/dist/_internal/server/features/blog/og.js +3 -2
  28. package/dist/_internal/server/features/brands/og.d.ts +1 -0
  29. package/dist/_internal/server/features/brands/og.js +2 -1
  30. package/dist/_internal/server/features/bundles/og.d.ts +1 -0
  31. package/dist/_internal/server/features/bundles/og.js +2 -1
  32. package/dist/_internal/server/features/categories/og.d.ts +1 -0
  33. package/dist/_internal/server/features/categories/og.js +2 -1
  34. package/dist/_internal/server/features/checkout/actions.js +13 -5
  35. package/dist/_internal/server/features/classified/og.d.ts +2 -0
  36. package/dist/_internal/server/features/classified/og.js +2 -1
  37. package/dist/_internal/server/features/digital-code/og.d.ts +2 -0
  38. package/dist/_internal/server/features/digital-code/og.js +2 -1
  39. package/dist/_internal/server/features/events/og.d.ts +1 -0
  40. package/dist/_internal/server/features/events/og.js +3 -1
  41. package/dist/_internal/server/features/live/og.d.ts +2 -0
  42. package/dist/_internal/server/features/live/og.js +2 -1
  43. package/dist/_internal/server/features/pre-orders/og.d.ts +1 -0
  44. package/dist/_internal/server/features/pre-orders/og.js +2 -1
  45. package/dist/_internal/server/features/products/og.d.ts +3 -1
  46. package/dist/_internal/server/features/products/og.js +2 -1
  47. package/dist/_internal/server/features/seo/index.d.ts +1 -1
  48. package/dist/_internal/server/features/seo/index.js +1 -1
  49. package/dist/_internal/server/features/seo/og.d.ts +8 -1
  50. package/dist/_internal/server/features/seo/og.js +28 -8
  51. package/dist/_internal/server/features/stores/og.d.ts +1 -0
  52. package/dist/_internal/server/features/stores/og.js +3 -2
  53. package/dist/_internal/server/features/sublisting-categories/og.d.ts +1 -0
  54. package/dist/_internal/server/features/sublisting-categories/og.js +2 -1
  55. package/dist/_internal/server/jobs/core/draftPrune.d.ts +2 -0
  56. package/dist/_internal/server/jobs/core/draftPrune.js +15 -0
  57. package/dist/_internal/server/jobs/core/onBidPlaced.js +1 -1
  58. package/dist/_internal/server/jobs/core/onScamReportCreate.js +18 -14
  59. package/dist/_internal/server/jobs/core/onScamReportRejected.js +10 -7
  60. package/dist/_internal/server/jobs/core/onScamReportVerified.js +10 -7
  61. package/dist/_internal/server/jobs/core/promotions.d.ts +1 -0
  62. package/dist/_internal/server/jobs/core/promotions.js +21 -6
  63. package/dist/_internal/server/jobs/handlers/assignSpinPrize.js +18 -1
  64. package/dist/_internal/server/jobs/handlers/draftPrune.d.ts +2 -0
  65. package/dist/_internal/server/jobs/handlers/draftPrune.js +2 -0
  66. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  67. package/dist/_internal/server/jobs/handlers/index.js +2 -0
  68. package/dist/_internal/server/jobs/handlers/triggerEventRaffle.js +15 -1
  69. package/dist/_internal/shared/actions/action-registry.js +32 -0
  70. package/dist/_internal/shared/features/blog/schema.d.ts +8 -8
  71. package/dist/_internal/shared/features/categories/bundle-schemas.d.ts +10 -10
  72. package/dist/_internal/shared/features/orders/schema.d.ts +8 -8
  73. package/dist/_internal/shared/features/products/schema.d.ts +8 -8
  74. package/dist/_internal/shared/features/promotions/schema.d.ts +6 -6
  75. package/dist/_internal/shared/features/reviews/schema.d.ts +2 -2
  76. package/dist/client.d.ts +13 -1
  77. package/dist/client.js +7 -1
  78. package/dist/configs/next.js +1 -0
  79. package/dist/constants/api-endpoints.d.ts +15 -0
  80. package/dist/constants/api-endpoints.js +5 -0
  81. package/dist/core/server-action.d.ts +4 -0
  82. package/dist/errors/error-codes.d.ts +18 -0
  83. package/dist/errors/error-codes.js +18 -0
  84. package/dist/errors/index.d.ts +1 -1
  85. package/dist/errors/index.js +1 -1
  86. package/dist/errors/messages.d.ts +1 -0
  87. package/dist/errors/messages.js +1 -0
  88. package/dist/features/account/components/UserSidebar.js +1 -1
  89. package/dist/features/account/schemas/index.d.ts +12 -12
  90. package/dist/features/admin/components/AdminBundlesView.js +20 -2
  91. package/dist/features/admin/components/AdminPayoutsView.js +2 -1
  92. package/dist/features/admin/components/AdminProductsView.js +13 -0
  93. package/dist/features/admin/components/AdminSidebar.js +1 -1
  94. package/dist/features/admin/components/AdminStoresView.js +43 -5
  95. package/dist/features/admin/components/AdminUsersView.js +53 -6
  96. package/dist/features/admin/schemas/firestore.d.ts +1 -1
  97. package/dist/features/auctions/actions/bid-actions.js +13 -6
  98. package/dist/features/auctions/components/PlaceBidFormClient.js +10 -2
  99. package/dist/features/auctions/repository/bid.repository.d.ts +7 -0
  100. package/dist/features/auctions/repository/bid.repository.js +16 -0
  101. package/dist/features/auctions/schemas/index.d.ts +18 -18
  102. package/dist/features/auth/schemas/index.d.ts +2 -2
  103. package/dist/features/before-after/schemas/index.d.ts +2 -2
  104. package/dist/features/blog/actions/blog-actions.d.ts +32 -32
  105. package/dist/features/blog/components/BlogPostForm.js +2 -1
  106. package/dist/features/blog/schemas/index.d.ts +38 -38
  107. package/dist/features/categories/components/CategoryForm.js +1 -0
  108. package/dist/features/categories/schemas/index.d.ts +10 -10
  109. package/dist/features/collections/schemas/index.d.ts +4 -4
  110. package/dist/features/consultation/schemas/index.d.ts +9 -9
  111. package/dist/features/corporate/schemas/index.d.ts +4 -4
  112. package/dist/features/events/schemas/index.d.ts +50 -50
  113. package/dist/features/faq/actions/faq-actions.d.ts +6 -6
  114. package/dist/features/faq/schemas/index.d.ts +8 -8
  115. package/dist/features/homepage/actions/homepage-section-actions.d.ts +4 -4
  116. package/dist/features/homepage/components/SectionCarousel.js +1 -8
  117. package/dist/features/layout/AppLayoutShell.js +1 -1
  118. package/dist/features/layout/AutoBreadcrumbs.js +1 -1
  119. package/dist/features/media/AvatarUpload.js +1 -1
  120. package/dist/features/media/types/index.d.ts +4 -4
  121. package/dist/features/media/upload/ImageUpload.js +18 -9
  122. package/dist/features/media/upload/MediaUploadList.js +2 -2
  123. package/dist/features/orders/schemas/firestore.d.ts +6 -0
  124. package/dist/features/orders/schemas/index.d.ts +10 -10
  125. package/dist/features/orders/types/index.d.ts +5 -0
  126. package/dist/features/payments/schemas/index.d.ts +4 -4
  127. package/dist/features/pre-orders/schemas/index.d.ts +8 -8
  128. package/dist/features/products/components/PrizeDrawItemsEditor.js +1 -1
  129. package/dist/features/products/components/ProductForm.js +20 -2
  130. package/dist/features/products/repository/products.repository.d.ts +3 -1
  131. package/dist/features/products/repository/products.repository.js +12 -1
  132. package/dist/features/products/schemas/firestore.d.ts +7 -0
  133. package/dist/features/products/schemas/index.d.ts +34 -34
  134. package/dist/features/products/schemas/product-features.validators.d.ts +6 -6
  135. package/dist/features/products/types/index.d.ts +6 -0
  136. package/dist/features/promotions/schemas/index.d.ts +14 -14
  137. package/dist/features/reviews/schemas/index.d.ts +18 -18
  138. package/dist/features/seller/actions/offer-actions.js +10 -1
  139. package/dist/features/seller/components/PhysicalLocationModal.d.ts +12 -0
  140. package/dist/features/seller/components/PhysicalLocationModal.js +30 -0
  141. package/dist/features/seller/components/PrintCenterView.d.ts +40 -0
  142. package/dist/features/seller/components/PrintCenterView.js +6 -0
  143. package/dist/features/seller/components/SellerOrdersView.js +48 -3
  144. package/dist/features/seller/components/SellerProductShell.d.ts +1 -0
  145. package/dist/features/seller/components/SellerProductShell.js +26 -4
  146. package/dist/features/seller/components/SellerProductsView.js +54 -13
  147. package/dist/features/seller/components/SellerSidebar.js +1 -1
  148. package/dist/features/seller/components/index.d.ts +1 -0
  149. package/dist/features/seller/components/index.js +1 -0
  150. package/dist/features/seller/schemas/firestore.d.ts +2 -0
  151. package/dist/features/seller/schemas/firestore.js +1 -0
  152. package/dist/features/seller/schemas/index.d.ts +22 -22
  153. package/dist/features/shell/FormShell.js +1 -1
  154. package/dist/features/shell/StepForm.d.ts +3 -1
  155. package/dist/features/shell/StepForm.js +3 -3
  156. package/dist/features/stores/schemas/index.d.ts +4 -4
  157. package/dist/index.d.ts +9 -1
  158. package/dist/index.js +6 -1
  159. package/dist/next/routing/route-map.d.ts +8 -0
  160. package/dist/next/routing/route-map.js +3 -0
  161. package/dist/seed/homepage-sections-seed-data.js +10 -10
  162. package/dist/server.d.ts +1 -1
  163. package/dist/server.js +1 -1
  164. package/dist/tailwind-utilities.css +1 -1
  165. package/dist/ui/components/FormField.js +1 -0
  166. package/dist/ui/components/RowActionMenu.js +45 -10
  167. package/dist/ui/components/SideDrawer.style.css +2 -2
  168. package/dist/ui/forms/FieldCheckbox.d.ts +13 -0
  169. package/dist/ui/forms/FieldCheckbox.js +25 -0
  170. package/dist/ui/forms/FieldInput.d.ts +11 -0
  171. package/dist/ui/forms/FieldInput.js +25 -0
  172. package/dist/ui/forms/FieldSelect.d.ts +17 -0
  173. package/dist/ui/forms/FieldSelect.js +25 -0
  174. package/dist/ui/forms/FormShell.d.ts +48 -0
  175. package/dist/ui/forms/FormShell.js +176 -0
  176. package/dist/ui/forms/FormShell.style.css +287 -0
  177. package/dist/ui/forms/index.d.ts +8 -0
  178. package/dist/ui/forms/index.js +4 -0
  179. package/dist/ui/index.d.ts +9 -0
  180. package/dist/ui/index.js +5 -0
  181. package/dist/utils/action-response.d.ts +6 -0
  182. package/dist/utils/action-response.js +22 -0
  183. package/dist/utils/index.d.ts +1 -0
  184. package/dist/utils/index.js +1 -0
  185. package/dist/validation/schemas.d.ts +10 -10
  186. package/package.json +2 -2
@@ -0,0 +1,76 @@
1
+ "use client";
2
+ import { useState, useCallback } from "react";
3
+ const MM_TO_PT = 2.8346;
4
+ export function useInventoryPdf({ products = [], orders = [], storeCards = [], websiteCards = [], design, publicBaseUrl, brandName = "", }) {
5
+ const [isGenerating, setIsGenerating] = useState(false);
6
+ const downloadPdf = useCallback(async () => {
7
+ setIsGenerating(true);
8
+ try {
9
+ const [{ jsPDF }, qrcode, jsbarcodeModule] = await Promise.all([
10
+ import("jspdf"),
11
+ import("qrcode"),
12
+ import("jsbarcode"),
13
+ ]);
14
+ const JsBarcode = jsbarcodeModule.default;
15
+ const wPt = design.size.widthMm * MM_TO_PT;
16
+ const hPt = design.size.heightMm * MM_TO_PT;
17
+ const pdf = new jsPDF({
18
+ orientation: wPt > hPt ? "landscape" : "portrait",
19
+ unit: "pt",
20
+ format: [wPt, hPt],
21
+ });
22
+ let isFirst = true;
23
+ const genQr = (url) => qrcode.toDataURL(url, { width: 80, margin: 1 });
24
+ const genBarcode = (value) => {
25
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
26
+ JsBarcode(svg, value, { format: "CODE128", width: 1, height: 25, displayValue: false });
27
+ const xml = new XMLSerializer().serializeToString(svg);
28
+ return "data:image/svg+xml;base64," + btoa(xml);
29
+ };
30
+ const addPage = async (title, subtitle, qrUrl, barcodeValue, id) => {
31
+ if (!isFirst)
32
+ pdf.addPage([wPt, hPt]);
33
+ isFirst = false;
34
+ const qr = await genQr(qrUrl);
35
+ const barSvg = genBarcode(barcodeValue);
36
+ pdf.setFontSize(6);
37
+ pdf.setTextColor(150, 150, 150);
38
+ if (brandName)
39
+ pdf.text(brandName, 5, 9);
40
+ pdf.setFontSize(8);
41
+ pdf.setTextColor(20, 20, 20);
42
+ pdf.text(title.slice(0, 50), 5, 18);
43
+ if (subtitle) {
44
+ pdf.setFontSize(6);
45
+ pdf.setTextColor(100, 100, 100);
46
+ pdf.text(subtitle.slice(0, 60), 5, 25);
47
+ }
48
+ if (design.show.barcode) {
49
+ pdf.addImage(barSvg, "SVG", 5, hPt - 20, wPt * 0.6, 12);
50
+ }
51
+ pdf.addImage(qr, "PNG", wPt - 30, hPt - 32, 26, 26);
52
+ pdf.setFontSize(5);
53
+ pdf.setTextColor(180, 180, 180);
54
+ pdf.text(id, wPt / 2, hPt - 3, { align: "center" });
55
+ };
56
+ for (const p of products) {
57
+ const priceStr = p.price != null ? `Rs.${(p.price / 100).toFixed(2)}` : "";
58
+ await addPage(p.title, priceStr, `${publicBaseUrl}/products/${p.id}`, p.id, p.id);
59
+ }
60
+ for (const o of orders) {
61
+ await addPage(`Order: ${o.id}`, `${o.items.length} item(s)`, `${publicBaseUrl}/store/orders/${o.id}/view`, o.id, o.id);
62
+ }
63
+ for (const s of storeCards) {
64
+ await addPage(s.storeName, s.storeDescription?.slice(0, 60) ?? "", `${publicBaseUrl}/stores/${s.id}`, s.id, s.id);
65
+ }
66
+ for (const s of websiteCards) {
67
+ await addPage(brandName ? `${brandName} - ${s.storeName}` : s.storeName, "Scan to shop", `${publicBaseUrl}/stores/${s.id}`, s.id, s.id);
68
+ }
69
+ pdf.save(`labels-${Date.now()}.pdf`);
70
+ }
71
+ finally {
72
+ setIsGenerating(false);
73
+ }
74
+ }, [products, orders, storeCards, websiteCards, design, publicBaseUrl]);
75
+ return { downloadPdf, isGenerating };
76
+ }
@@ -14,6 +14,7 @@ interface AuctionDocLike {
14
14
  export declare function renderAuctionOg(doc: AuctionDocLike | null | undefined, opts: {
15
15
  siteName: string;
16
16
  locale?: string;
17
+ baseUrl?: string;
17
18
  }): ReactElement;
18
19
  export declare function renderAuctionOgImage(data: AuctionOgData, siteName: string): ReactElement;
19
20
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw auction document from `getAuctionForDetail`. */
3
4
  export function renderAuctionOg(doc, opts) {
4
5
  const locale = opts.locale ?? "en-IN";
@@ -13,7 +14,7 @@ export function renderAuctionOg(doc, opts) {
13
14
  return renderAuctionOgImage({
14
15
  title: doc?.title ?? "Live Auction",
15
16
  endsLabel,
16
- imageUrl: doc?.mainImage || doc?.images?.[0] || null,
17
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
17
18
  }, opts.siteName);
18
19
  }
19
20
  export function renderAuctionOgImage(data, siteName) {
@@ -18,6 +18,7 @@ interface BlogDocLike {
18
18
  /** High-level OG renderer — accepts the raw blog post document from `getBlogPostForDetail`. */
19
19
  export declare function renderBlogOg(doc: BlogDocLike | null | undefined, opts: {
20
20
  siteName: string;
21
+ baseUrl?: string;
21
22
  }): ReactElement;
22
23
  export declare function renderBlogOgImage(data: BlogOgData, siteName: string): ReactElement;
23
24
  export {};
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw blog post document from `getBlogPostForDetail`. */
3
4
  export function renderBlogOg(doc, opts) {
4
- const coverImage = typeof doc?.coverImage === "string"
5
+ const rawCoverImage = typeof doc?.coverImage === "string"
5
6
  ? doc.coverImage
6
7
  : doc?.coverImage?.url ?? null;
7
8
  return renderBlogOgImage({
@@ -9,7 +10,7 @@ export function renderBlogOg(doc, opts) {
9
10
  excerpt: doc?.excerpt?.slice(0, 120) ?? null,
10
11
  authorName: doc?.authorName ?? null,
11
12
  category: doc?.category ?? null,
12
- coverImage,
13
+ coverImage: resolveOgImageUrl(rawCoverImage, opts.baseUrl),
13
14
  }, opts.siteName);
14
15
  }
15
16
  export function renderBlogOgImage(data, siteName) {
@@ -12,6 +12,7 @@ interface BrandDocLike {
12
12
  /** High-level OG renderer — accepts the raw brand document from `getBrandForDetail`. */
13
13
  export declare function renderBrandOg(doc: BrandDocLike | null | undefined, opts: {
14
14
  siteName: string;
15
+ baseUrl?: string;
15
16
  }): ReactElement;
16
17
  export declare function renderBrandOgImage(data: BrandOgData, siteName: string): ReactElement;
17
18
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw brand document from `getBrandForDetail`. */
3
4
  export function renderBrandOg(doc, opts) {
4
5
  const name = doc?.name ?? "Brand";
@@ -6,7 +7,7 @@ export function renderBrandOg(doc, opts) {
6
7
  name,
7
8
  description: doc?.description?.slice(0, 120) ??
8
9
  `Shop authentic ${name} collectibles on ${opts.siteName}.`,
9
- logoUrl: doc?.logoURL ?? null,
10
+ logoUrl: resolveOgImageUrl(doc?.logoURL ?? null, opts.baseUrl),
10
11
  }, opts.siteName);
11
12
  }
12
13
  export function renderBrandOgImage(data, siteName) {
@@ -33,6 +33,7 @@ interface BundleDocLike {
33
33
  }
34
34
  export declare function renderBundleOg(doc: BundleDocLike | null | undefined, opts: {
35
35
  siteName: string;
36
+ baseUrl?: string;
36
37
  }): ReactElement;
37
38
  export declare function renderBundleOgImage(data: BundleOgData, siteName: string): ReactElement;
38
39
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  function formatPriceInr(paise) {
3
4
  const rupees = Math.round(paise / 100);
4
5
  return `₹${rupees.toLocaleString("en-IN")}`;
@@ -12,7 +13,7 @@ export function renderBundleOg(doc, opts) {
12
13
  name,
13
14
  description: doc?.description?.slice(0, 140) ??
14
15
  `Curated multi-product bundle on ${opts.siteName}.`,
15
- coverImageUrl: doc?.display?.coverImage ?? null,
16
+ coverImageUrl: resolveOgImageUrl(doc?.display?.coverImage ?? null, opts.baseUrl),
16
17
  priceLabel,
17
18
  itemCount: doc?.bundleProductIds?.length ?? null,
18
19
  stockStatus: doc?.bundleStockStatus ?? null,
@@ -28,6 +28,7 @@ interface CategoryDocLike {
28
28
  }
29
29
  export declare function renderCategoryOg(doc: CategoryDocLike | null | undefined, opts: {
30
30
  siteName: string;
31
+ baseUrl?: string;
31
32
  }): ReactElement;
32
33
  export declare function renderCategoryOgImage(data: CategoryOgData, siteName: string): ReactElement;
33
34
  export {};
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  export function renderCategoryOg(doc, opts) {
3
4
  const name = doc?.name ?? "Category";
4
5
  return renderCategoryOgImage({
5
6
  name,
6
7
  description: doc?.description?.slice(0, 140) ??
7
8
  `Browse ${name} on ${opts.siteName}.`,
8
- coverImageUrl: doc?.display?.coverImage ?? null,
9
+ coverImageUrl: resolveOgImageUrl(doc?.display?.coverImage ?? null, opts.baseUrl),
9
10
  productCount: doc?.metrics?.totalItemCount ?? null,
10
11
  }, opts.siteName);
11
12
  }
@@ -334,11 +334,18 @@ export async function createCheckoutOrderAction(input) {
334
334
  const orderIds = [];
335
335
  let total = 0;
336
336
  const emailsToSend = [];
337
- const cartSubtotal = orderGroups.reduce((s, { items: g }) => s + g.reduce((gs, { item }) => gs + item.price * item.quantity, 0), 0);
337
+ // SB-UNI-5 2026-05-13 bundle cart-lines use item.price (locked bundle
338
+ // price at add-time); regular lines use product.price (current Firestore).
339
+ // This prevents stale cart-cached prices from being charged on COD/UPI orders.
340
+ const unitPriceFor = (item, product) => item.bundleCategorySlug && item.bundleProductIds?.length
341
+ ? item.price
342
+ : product.price;
343
+ const cartSubtotal = orderGroups.reduce((s, { items: g }) => s + g.reduce((gs, { item, product }) => gs + unitPriceFor(item, product) * item.quantity, 0), 0);
338
344
  const couponUsageAccumulator = new Map();
339
345
  for (const { items: group, orderType } of orderGroups) {
340
346
  const firstItem = group[0].item;
341
- const groupTotal = group.reduce((sum, { item }) => sum + item.price * item.quantity, 0);
347
+ const firstProduct = group[0].product;
348
+ const groupTotal = group.reduce((sum, { item, product }) => sum + unitPriceFor(item, product) * item.quantity, 0);
342
349
  total += groupTotal;
343
350
  // S-SBUNI-RULES 2026-05-13 — order-item decoration via rule registry.
344
351
  const orderItems = group.map(({ item, product }) => {
@@ -352,12 +359,13 @@ export async function createCheckoutOrderAction(input) {
352
359
  bundleProductIds: item.bundleProductIds,
353
360
  }
354
361
  : {};
362
+ const unitPrice = unitPriceFor(item, product);
355
363
  const baseLine = {
356
364
  productId: item.productId,
357
365
  productTitle: item.productTitle,
358
366
  quantity: item.quantity,
359
- unitPrice: item.price,
360
- totalPrice: item.price * item.quantity,
367
+ unitPrice,
368
+ totalPrice: unitPrice * item.quantity,
361
369
  ...bundleFields,
362
370
  };
363
371
  return itemRule.decorateOrderItem(baseLine, product);
@@ -437,7 +445,7 @@ export async function createCheckoutOrderAction(input) {
437
445
  userName,
438
446
  userEmail,
439
447
  quantity: totalQuantity,
440
- unitPrice: firstItem.price,
448
+ unitPrice: unitPriceFor(firstItem, firstProduct),
441
449
  totalPrice: orderTotal,
442
450
  currency: firstItem.currency ?? getDefaultCurrency(),
443
451
  storeId: firstItem.storeId || undefined,
@@ -21,10 +21,12 @@ interface ClassifiedDocLike {
21
21
  }
22
22
  export declare function renderClassifiedOg(doc: ClassifiedDocLike | null | undefined, opts: {
23
23
  siteName: string;
24
+ baseUrl?: string;
24
25
  }): ReactElement;
25
26
  export declare function renderClassifiedOgImage(data: ClassifiedOgData, siteName: string): ReactElement;
26
27
  /** Type-safe overload that accepts the full ProductDocument. */
27
28
  export declare function renderClassifiedOgFromDoc(doc: ProductDocument | null | undefined, opts: {
28
29
  siteName: string;
30
+ baseUrl?: string;
29
31
  }): ReactElement;
30
32
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  export function renderClassifiedOg(doc, opts) {
3
4
  const meta = doc?.classified;
4
5
  const location = meta?.meetupArea
@@ -15,7 +16,7 @@ export function renderClassifiedOg(doc, opts) {
15
16
  title: doc?.title ?? "Classified Listing",
16
17
  priceLabel,
17
18
  location,
18
- imageUrl: doc?.mainImage || doc?.images?.[0] || null,
19
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
19
20
  }, opts.siteName);
20
21
  }
21
22
  export function renderClassifiedOgImage(data, siteName) {
@@ -18,10 +18,12 @@ interface DigitalCodeDocLike {
18
18
  }
19
19
  export declare function renderDigitalCodeOg(doc: DigitalCodeDocLike | null | undefined, opts: {
20
20
  siteName: string;
21
+ baseUrl?: string;
21
22
  }): ReactElement;
22
23
  export declare function renderDigitalCodeOgImage(data: DigitalCodeOgData, siteName: string): ReactElement;
23
24
  /** Type-safe overload that accepts the full ProductDocument. */
24
25
  export declare function renderDigitalCodeOgFromDoc(doc: ProductDocument | null | undefined, opts: {
25
26
  siteName: string;
27
+ baseUrl?: string;
26
28
  }): ReactElement;
27
29
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  export function renderDigitalCodeOg(doc, opts) {
3
4
  const priceLabel = doc?.price != null
4
5
  ? new Intl.NumberFormat("en-IN", {
@@ -16,7 +17,7 @@ export function renderDigitalCodeOg(doc, opts) {
16
17
  title: doc?.title ?? "Digital Code",
17
18
  priceLabel,
18
19
  deliveryMethod,
19
- imageUrl: doc?.mainImage || doc?.images?.[0] || null,
20
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
20
21
  }, opts.siteName);
21
22
  }
22
23
  export function renderDigitalCodeOgImage(data, siteName) {
@@ -21,6 +21,7 @@ export declare function renderEventOg(doc: EventDocLike | null | undefined, opts
21
21
  siteName: string;
22
22
  locale?: string;
23
23
  typeLabels?: Record<string, string>;
24
+ baseUrl?: string;
24
25
  }): ReactElement;
25
26
  export declare function renderEventOgImage(data: EventOgData, siteName: string): ReactElement;
26
27
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  const EVENT_TYPE_LABEL = {
3
4
  TOURNAMENT: "Tournament",
4
5
  CONVENTION: "Convention",
@@ -15,11 +16,12 @@ export function renderEventOg(doc, opts) {
15
16
  const locale = opts.locale ?? "en-IN";
16
17
  const labels = { ...EVENT_TYPE_LABEL, ...(opts.typeLabels ?? {}) };
17
18
  const eventType = doc?.type ?? null;
18
- const coverImageUrl = doc?.coverImageUrl ??
19
+ const rawCoverImageUrl = doc?.coverImageUrl ??
19
20
  (typeof doc?.coverImage === "string"
20
21
  ? doc.coverImage
21
22
  : doc?.coverImage?.url) ??
22
23
  null;
24
+ const coverImageUrl = resolveOgImageUrl(rawCoverImageUrl, opts.baseUrl);
23
25
  const startsAt = doc?.startsAt
24
26
  ? new Date(doc.startsAt).toLocaleDateString(locale, {
25
27
  day: "numeric",
@@ -18,10 +18,12 @@ interface LiveItemDocLike {
18
18
  }
19
19
  export declare function renderLiveItemOg(doc: LiveItemDocLike | null | undefined, opts: {
20
20
  siteName: string;
21
+ baseUrl?: string;
21
22
  }): ReactElement;
22
23
  export declare function renderLiveItemOgImage(data: LiveItemOgData, siteName: string): ReactElement;
23
24
  /** Type-safe overload that accepts the full ProductDocument. */
24
25
  export declare function renderLiveItemOgFromDoc(doc: ProductDocument | null | undefined, opts: {
25
26
  siteName: string;
27
+ baseUrl?: string;
26
28
  }): ReactElement;
27
29
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  export function renderLiveItemOg(doc, opts) {
3
4
  const priceLabel = doc?.price != null
4
5
  ? new Intl.NumberFormat("en-IN", {
@@ -11,7 +12,7 @@ export function renderLiveItemOg(doc, opts) {
11
12
  title: doc?.title ?? "Live Listing",
12
13
  species: doc?.liveItem?.species ?? null,
13
14
  priceLabel,
14
- imageUrl: doc?.mainImage || doc?.images?.[0] || null,
15
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
15
16
  }, opts.siteName);
16
17
  }
17
18
  export function renderLiveItemOgImage(data, siteName) {
@@ -14,6 +14,7 @@ interface PreOrderDocLike {
14
14
  export declare function renderPreOrderOg(doc: PreOrderDocLike | null | undefined, opts: {
15
15
  siteName: string;
16
16
  locale?: string;
17
+ baseUrl?: string;
17
18
  }): ReactElement;
18
19
  export declare function renderPreOrderOgImage(data: PreOrderOgData, siteName: string): ReactElement;
19
20
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw pre-order document from `getPreOrderForDetail`. */
3
4
  export function renderPreOrderOg(doc, opts) {
4
5
  const locale = opts.locale ?? "en-IN";
@@ -11,7 +12,7 @@ export function renderPreOrderOg(doc, opts) {
11
12
  return renderPreOrderOgImage({
12
13
  title: doc?.title ?? "Pre-Order",
13
14
  releaseDateLabel,
14
- imageUrl: doc?.mainImage || doc?.images?.[0] || null,
15
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
15
16
  }, opts.siteName);
16
17
  }
17
18
  export function renderPreOrderOgImage(data, siteName) {
@@ -14,6 +14,8 @@ interface ProductDocLike {
14
14
  images?: (string | null | undefined)[] | null;
15
15
  }
16
16
  /** High-level OG renderer — accepts the raw product document from `getProductForDetail`. */
17
- export declare function renderProductOg(doc: ProductDocLike | null | undefined, opts: OgOptions): ReactElement;
17
+ export declare function renderProductOg(doc: ProductDocLike | null | undefined, opts: OgOptions & {
18
+ baseUrl?: string;
19
+ }): ReactElement;
18
20
  export declare function renderProductOgImage(data: ProductOgData, siteName: string): ReactElement;
19
21
  export {};
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw product document from `getProductForDetail`. */
3
4
  export function renderProductOg(doc, opts) {
4
5
  return renderProductOgImage({
5
6
  title: doc?.title ?? "Product",
6
7
  price: doc?.price ?? null,
7
- imageUrl: doc?.mainImage || doc?.images?.[0] || null,
8
+ imageUrl: resolveOgImageUrl(doc?.mainImage || doc?.images?.[0] || null, opts.baseUrl),
8
9
  }, opts.siteName);
9
10
  }
10
11
  export function renderProductOgImage(data, siteName) {
@@ -4,5 +4,5 @@ export { buildManifest } from "./manifest";
4
4
  export type { ManifestOptions } from "./manifest";
5
5
  export { buildSitemap } from "./sitemap";
6
6
  export type { SitemapOptions } from "./sitemap";
7
- export { buildDefaultOgImage, DEFAULT_OG_SIZE } from "./og";
7
+ export { buildDefaultOgImage, DEFAULT_OG_SIZE, resolveOgImageUrl } from "./og";
8
8
  export type { DefaultOgOptions } from "./og";
@@ -1,4 +1,4 @@
1
1
  export { buildRobots } from "./robots";
2
2
  export { buildManifest } from "./manifest";
3
3
  export { buildSitemap } from "./sitemap";
4
- export { buildDefaultOgImage, DEFAULT_OG_SIZE } from "./og";
4
+ export { buildDefaultOgImage, DEFAULT_OG_SIZE, resolveOgImageUrl } from "./og";
@@ -3,9 +3,16 @@ export declare const DEFAULT_OG_SIZE: {
3
3
  width: number;
4
4
  height: number;
5
5
  };
6
+ /**
7
+ * Resolves a potentially relative image URL to an absolute URL for use in
8
+ * next/og (Satori). Satori cannot resolve relative paths; all <img src> values
9
+ * must be absolute URLs or data URIs.
10
+ */
11
+ export declare function resolveOgImageUrl(url: string | null | undefined, baseUrl?: string): string | null;
6
12
  export interface DefaultOgOptions {
7
13
  siteName: string;
8
14
  tagline?: string;
9
15
  domain?: string;
16
+ logoUrl?: string;
10
17
  }
11
- export declare function buildDefaultOgImage({ siteName, tagline, domain }: DefaultOgOptions): ImageResponse;
18
+ export declare function buildDefaultOgImage({ siteName, tagline, domain, logoUrl }: DefaultOgOptions): ImageResponse;
@@ -1,7 +1,24 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ImageResponse } from "next/og";
3
3
  export const DEFAULT_OG_SIZE = { width: 1200, height: 630 };
4
- export function buildDefaultOgImage({ siteName, tagline, domain }) {
4
+ /**
5
+ * Resolves a potentially relative image URL to an absolute URL for use in
6
+ * next/og (Satori). Satori cannot resolve relative paths; all <img src> values
7
+ * must be absolute URLs or data URIs.
8
+ */
9
+ export function resolveOgImageUrl(url, baseUrl) {
10
+ if (!url)
11
+ return null;
12
+ if (!baseUrl)
13
+ return url;
14
+ if (url.startsWith("http://") ||
15
+ url.startsWith("https://") ||
16
+ url.startsWith("data:")) {
17
+ return url;
18
+ }
19
+ return `${baseUrl.replace(/\/$/, "")}${url.startsWith("/") ? "" : "/"}${url}`;
20
+ }
21
+ export function buildDefaultOgImage({ siteName, tagline, domain, logoUrl }) {
5
22
  const subtitle = tagline ?? "Shop, Bid & Sell — India's Multi-Seller Marketplace";
6
23
  return new ImageResponse(_jsxs("div", { style: {
7
24
  width: "100%",
@@ -24,29 +41,32 @@ export function buildDefaultOgImage({ siteName, tagline, domain }) {
24
41
  display: "flex",
25
42
  alignItems: "center",
26
43
  justifyContent: "center",
27
- width: 120,
28
- height: 120,
29
- borderRadius: 24,
30
- background: "rgba(255,255,255,0.15)",
31
- marginBottom: 32,
32
- fontSize: 64,
33
- }, children: "\uD83D\uDECD\uFE0F" }), _jsx("div", { style: {
44
+ width: 140,
45
+ height: 140,
46
+ borderRadius: 28,
47
+ background: "rgba(255,255,255,0.18)",
48
+ marginBottom: 36,
49
+ overflow: "hidden",
50
+ }, children: logoUrl ? (_jsx("img", { src: logoUrl, alt: siteName, style: { width: 100, height: 100, objectFit: "contain" } })) : (_jsx("div", { style: { fontSize: 64, display: "flex" }, children: "\uD83D\uDECD\uFE0F" })) }), _jsx("div", { style: {
34
51
  fontSize: 80,
35
52
  fontWeight: 800,
36
53
  color: "white",
37
54
  letterSpacing: "-2px",
38
55
  marginBottom: 16,
39
56
  textShadow: "0 4px 24px rgba(0,0,0,0.3)",
57
+ display: "flex",
40
58
  }, children: siteName }), _jsx("div", { style: {
41
59
  fontSize: 32,
42
60
  color: "rgba(255,255,255,0.85)",
43
61
  fontWeight: 400,
44
62
  letterSpacing: "0.5px",
63
+ display: "flex",
45
64
  }, children: subtitle }), domain && (_jsx("div", { style: {
46
65
  position: "absolute",
47
66
  bottom: 36,
48
67
  fontSize: 22,
49
68
  color: "rgba(255,255,255,0.6)",
50
69
  letterSpacing: "1px",
70
+ display: "flex",
51
71
  }, children: domain }))] }), { ...DEFAULT_OG_SIZE });
52
72
  }
@@ -14,6 +14,7 @@ interface StoreDocLike {
14
14
  /** High-level OG renderer — accepts the raw store document from `getStoreForDetail`. */
15
15
  export declare function renderStoreOg(doc: StoreDocLike | null | undefined, opts: {
16
16
  siteName: string;
17
+ baseUrl?: string;
17
18
  }): ReactElement;
18
19
  export declare function renderStoreOgImage(data: StoreOgData, siteName: string): ReactElement;
19
20
  export {};
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw store document from `getStoreForDetail`. */
3
4
  export function renderStoreOg(doc, opts) {
4
5
  return renderStoreOgImage({
5
6
  name: doc?.storeName ?? `${opts.siteName} Store`,
6
7
  description: doc?.storeDescription?.slice(0, 120) ?? null,
7
- logoUrl: doc?.storeLogoURL ?? null,
8
- bannerUrl: doc?.storeBannerURL ?? null,
8
+ logoUrl: resolveOgImageUrl(doc?.storeLogoURL ?? null, opts.baseUrl),
9
+ bannerUrl: resolveOgImageUrl(doc?.storeBannerURL ?? null, opts.baseUrl),
9
10
  }, opts.siteName);
10
11
  }
11
12
  export function renderStoreOgImage(data, siteName) {
@@ -15,6 +15,7 @@ interface SublistingCategoryDocLike {
15
15
  /** High-level OG renderer — accepts the raw sublisting-category doc from the repository. */
16
16
  export declare function renderSublistingCategoryOg(doc: SublistingCategoryDocLike | null | undefined, opts: {
17
17
  siteName: string;
18
+ baseUrl?: string;
18
19
  }): ReactElement;
19
20
  export declare function renderSublistingCategoryOgImage(data: SublistingCategoryOgData, siteName: string): ReactElement;
20
21
  export {};
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { resolveOgImageUrl } from "../seo/og";
2
3
  /** High-level OG renderer — accepts the raw sublisting-category doc from the repository. */
3
4
  export function renderSublistingCategoryOg(doc, opts) {
4
5
  const baseName = doc?.name ?? "Sub-listing";
@@ -7,7 +8,7 @@ export function renderSublistingCategoryOg(doc, opts) {
7
8
  name,
8
9
  description: doc?.description?.slice(0, 120) ?? null,
9
10
  productCount: doc?.productCount ?? null,
10
- coverImage: doc?.coverImage ?? null,
11
+ coverImage: resolveOgImageUrl(doc?.coverImage ?? null, opts.baseUrl),
11
12
  }, opts.siteName);
12
13
  }
13
14
  export function renderSublistingCategoryOgImage(data, siteName) {
@@ -0,0 +1,2 @@
1
+ import type { JobContext } from "../runtime/types";
2
+ export declare function runDraftPrune(ctx: JobContext): Promise<void>;
@@ -0,0 +1,15 @@
1
+ import { productRepository } from "../../../../repositories";
2
+ import { batchDelete } from "../handlers/_helpers";
3
+ const DRAFT_TTL_DAYS = 30;
4
+ export async function runDraftPrune(ctx) {
5
+ ctx.logger.info(`Pruning product drafts older than ${DRAFT_TTL_DAYS} days`);
6
+ const cutoff = new Date();
7
+ cutoff.setDate(cutoff.getDate() - DRAFT_TTL_DAYS);
8
+ const refs = await productRepository.getStaleDraftRefs(cutoff);
9
+ if (refs.length === 0) {
10
+ ctx.logger.info("No stale drafts found");
11
+ return;
12
+ }
13
+ const deleted = await batchDelete(ctx, refs);
14
+ ctx.logger.info("Draft prune complete", { deleted });
15
+ }
@@ -17,7 +17,7 @@ export async function handleBidPlaced(input, ctx) {
17
17
  outbidUserId = prevWinning.data.userId;
18
18
  bidRepository.markOutbid(batch, prevWinning.ref);
19
19
  }
20
- productRepository.incrementBidCountInBatch(batch, newBid.productId, newBid.bidAmount);
20
+ productRepository.incrementBidCountInBatch(batch, newBid.productId, newBid.bidAmount, newBid.userId);
21
21
  await batch.commit();
22
22
  if (outbidUserId) {
23
23
  const outbidMessage = BID_MESSAGES.OUTBID_MESSAGE(newBid.productTitle, newBid.currency, newBid.bidAmount);