@mohasinac/appkit 4.62.0 → 4.62.1

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 (204) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
  3. package/dist/_internal/server/features/checkout/actions.js +542 -26
  4. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  5. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  6. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  7. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  8. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  9. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  10. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  11. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  12. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  13. package/dist/_internal/server/features/orders/actions.js +81 -8
  14. package/dist/_internal/server/features/orders/adapters.js +33 -0
  15. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  16. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  17. package/dist/_internal/server/features/points/award.js +46 -4
  18. package/dist/_internal/server/features/refunds/actions.js +26 -0
  19. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  20. package/dist/_internal/server/features/shipping/courier-block.js +239 -0
  21. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  22. package/dist/_internal/server/features/site-settings/adapters.js +15 -0
  23. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  24. package/dist/_internal/server/features/stores/adapters.js +51 -0
  25. package/dist/_internal/server/functions/scheduled.js +38 -1
  26. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
  27. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  28. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  29. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  30. package/dist/_internal/server/jobs/core/index.js +2 -1
  31. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  32. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  33. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  34. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
  35. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  36. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  37. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  38. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
  39. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  40. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  41. package/dist/_internal/shared/actions/action-registry.js +28 -0
  42. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  43. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  44. package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
  45. package/dist/_internal/shared/checkout/order-math.js +173 -0
  46. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  47. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  48. package/dist/_internal/shared/checkout/split.d.ts +87 -0
  49. package/dist/_internal/shared/checkout/split.js +100 -0
  50. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  51. package/dist/_internal/shared/content/disclaimers.js +217 -0
  52. package/dist/_internal/shared/content/published-values.d.ts +88 -0
  53. package/dist/_internal/shared/content/published-values.js +198 -0
  54. package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
  55. package/dist/_internal/shared/features/admin/integrations.js +253 -0
  56. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  57. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  58. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  59. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  60. package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
  61. package/dist/_internal/shared/features/points/accepts.js +43 -0
  62. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  63. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  64. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  65. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  66. package/dist/_internal/shared/fees/calculator.js +32 -2
  67. package/dist/client.d.ts +11 -0
  68. package/dist/client.js +30 -0
  69. package/dist/constants/api-endpoints.d.ts +42 -0
  70. package/dist/constants/api-endpoints.js +14 -0
  71. package/dist/constants/field-names.d.ts +1 -0
  72. package/dist/constants/field-names.js +5 -0
  73. package/dist/contracts/index.d.ts +1 -1
  74. package/dist/contracts/shipping.d.ts +45 -0
  75. package/dist/contracts/shipping.js +15 -0
  76. package/dist/features/about/components/AboutView.js +1 -1
  77. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  78. package/dist/features/account/components/UserOrdersView.js +19 -33
  79. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  80. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  81. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  82. package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
  83. package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
  84. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  85. package/dist/features/admin/components/AdminOrdersView.js +67 -5
  86. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  87. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  88. package/dist/features/admin/constants/filter-tabs.js +6 -0
  89. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  90. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  91. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  92. package/dist/features/admin/schemas/firestore.d.ts +30 -0
  93. package/dist/features/admin/schemas/firestore.js +5 -0
  94. package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
  95. package/dist/features/audit-log/schemas/firestore.js +15 -0
  96. package/dist/features/auth/permissions/constants.js +13 -0
  97. package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
  98. package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
  99. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  100. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  101. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  102. package/dist/features/cart/components/index.d.ts +2 -0
  103. package/dist/features/cart/components/index.js +1 -0
  104. package/dist/features/cart/repository/cart.repository.d.ts +19 -0
  105. package/dist/features/cart/repository/cart.repository.js +27 -0
  106. package/dist/features/cart/schemas/firestore.d.ts +92 -0
  107. package/dist/features/contact/email.d.ts +27 -10
  108. package/dist/features/contact/email.js +20 -3
  109. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
  110. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
  111. package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
  112. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  113. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  114. package/dist/features/faq/components/FAQAccordion.js +29 -4
  115. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  116. package/dist/features/invoicing/schemas/firestore.js +45 -0
  117. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  118. package/dist/features/invoicing/utils/financial-year.js +53 -0
  119. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  120. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  121. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  122. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  123. package/dist/features/jobs/schemas/firestore.js +37 -0
  124. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  125. package/dist/features/media/MediaImage.js +22 -1
  126. package/dist/features/media/upload/ImageUpload.js +1 -1
  127. package/dist/features/obligations/constants/policies.js +24 -1
  128. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  129. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  130. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  131. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  132. package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
  133. package/dist/features/orders/components/RefundRequestView.js +19 -1
  134. package/dist/features/orders/components/index.d.ts +4 -0
  135. package/dist/features/orders/components/index.js +2 -0
  136. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  137. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  138. package/dist/features/orders/repository/orders.repository.d.ts +57 -0
  139. package/dist/features/orders/repository/orders.repository.js +145 -0
  140. package/dist/features/orders/schemas/firestore.d.ts +196 -1
  141. package/dist/features/orders/schemas/index.d.ts +54 -6
  142. package/dist/features/orders/schemas/index.js +12 -0
  143. package/dist/features/orders/types/index.d.ts +26 -0
  144. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  145. package/dist/features/orders/utils/courier-block.js +23 -0
  146. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  147. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  148. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  149. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  150. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  151. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  152. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  153. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  154. package/dist/features/promotions/components/CouponCard.js +13 -0
  155. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  156. package/dist/features/promotions/constants/coupon-help.js +9 -1
  157. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  158. package/dist/features/promotions/schemas/firestore.js +4 -0
  159. package/dist/features/promotions/types/index.d.ts +19 -1
  160. package/dist/features/scams/components/ScamProfileView.js +2 -2
  161. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  162. package/dist/features/search/action-index/tab-entries.js +1 -1
  163. package/dist/features/seller/actions/seller-actions.js +33 -3
  164. package/dist/features/seller/components/SellerOrdersView.js +129 -27
  165. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  166. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  167. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
  168. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
  169. package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
  170. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  171. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  172. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  173. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  174. package/dist/features/stores/schemas/firestore.d.ts +71 -0
  175. package/dist/features/stores/types/index.d.ts +19 -0
  176. package/dist/features/support/schemas/index.d.ts +6 -6
  177. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  178. package/dist/index.d.ts +6 -1
  179. package/dist/index.js +14 -1
  180. package/dist/next/routing/route-map.d.ts +20 -0
  181. package/dist/next/routing/route-map.js +10 -0
  182. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  183. package/dist/providers/shipping-shiprocket/index.js +84 -1
  184. package/dist/schemas/registry.d.ts +47 -18
  185. package/dist/schemas/sieve.d.ts +2 -2
  186. package/dist/server-entry.d.ts +2 -2
  187. package/dist/server-entry.js +8 -1
  188. package/dist/server.d.ts +10 -1
  189. package/dist/server.js +39 -1
  190. package/dist/styles.css +64 -80
  191. package/dist/tailwind-utilities.css +1 -1
  192. package/dist/tokens/themes/default-dark.js +1 -0
  193. package/dist/tokens/themes/default-light.js +1 -0
  194. package/dist/tokens/themes/required.d.ts +1 -1
  195. package/dist/tokens/themes/required.js +1 -0
  196. package/dist/tokens/tokens.css +15 -0
  197. package/dist/ui/components/Disclaimer.d.ts +19 -0
  198. package/dist/ui/components/Disclaimer.js +74 -0
  199. package/dist/ui/components/tabs-overflow.js +64 -11
  200. package/dist/ui/index.d.ts +2 -0
  201. package/dist/ui/index.js +6 -0
  202. package/dist/ui/rich-text/RichText.style.css +48 -79
  203. package/package.json +1 -1
  204. package/scripts/seed-cli.mjs +1831 -1822
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Group-purchase contribution splits — the ONE owner of the invariant.
3
+ *
4
+ * A coordinator divides what a grouped line costs among its members. They may
5
+ * type absolute amounts or percentages, and the total may come out at or ABOVE
6
+ * the landed cost; the excess belongs to the seller.
7
+ *
8
+ * ## The invariant
9
+ *
10
+ * ```
11
+ * landedCost = goods + shipping + fees + add-ons (per order group)
12
+ *
13
+ * contribution(member) = percent × landedCost OR a typed amount
14
+ * require: Σ(contributions) ≥ landedCost
15
+ * surplus: Σ(contributions) − landedCost
16
+ * ```
17
+ *
18
+ * Percentages summing above 100% and typed amounts summing above the landed
19
+ * cost are **the same case**, so one rule covers both and there is no
20
+ * mode-specific branch to get wrong.
21
+ *
22
+ * ## Why the surplus becomes `lockedPrice`
23
+ *
24
+ * `unitPriceFor` already honours `lockedPrice` ahead of everything else — that
25
+ * is how an accepted offer BELOW list is charged correctly. An agreed total
26
+ * ABOVE list is the same mechanism in the other direction, so GST is charged on
27
+ * what was actually paid, the payout base rises by the surplus,
28
+ * `refundableBase` ceilings at the agreed amount, and the invoice needs no new
29
+ * line. No new money term anywhere.
30
+ */
31
+ import { roundRupees } from "../../../utils/number.formatter.js";
32
+ /**
33
+ * Resolve contributions against a landed cost.
34
+ *
35
+ * Pure. No I/O, no clock — the landed cost is computed by the caller from the
36
+ * order group it already has.
37
+ *
38
+ * 🛑 ROUNDS ONCE, at the end of each member's amount, and then puts the
39
+ * ROUNDING REMAINDER on the last member. Rounding each percentage
40
+ * independently and summing is how a 3-way 33.33% split charges a rupee less
41
+ * than the agreed total and the surplus silently goes negative — the same
42
+ * remainder discipline `allocateAcrossMembers` uses for bundle tax.
43
+ */
44
+ export function resolveSplit(contributions, landedCost) {
45
+ const resolved = [];
46
+ for (const c of contributions) {
47
+ const raw = c.splitMode === "percent" ? ((c.percent ?? 0) / 100) * landedCost : (c.amount ?? 0);
48
+ resolved.push({ productId: c.productId, amount: roundRupees(raw) });
49
+ }
50
+ /*
51
+ * The remainder goes on the LAST member rather than being spread, so the
52
+ * parts reconcile to the total exactly and the discrepancy is one member's
53
+ * single rupee rather than a fraction on everybody.
54
+ *
55
+ * Only for a pure PERCENT split: typed amounts are what the coordinator
56
+ * actually said, and silently adjusting one of them would charge somebody a
57
+ * figure they did not enter.
58
+ */
59
+ const allPercent = contributions.every((c) => c.splitMode === "percent");
60
+ if (allPercent && resolved.length > 0) {
61
+ const pctTotal = contributions.reduce((s, c) => s + (c.percent ?? 0), 0);
62
+ const target = roundRupees((pctTotal / 100) * landedCost);
63
+ const sum = resolved.reduce((s, r) => s + r.amount, 0);
64
+ const drift = roundRupees(target - sum);
65
+ if (drift !== 0) {
66
+ const last = resolved[resolved.length - 1];
67
+ resolved[resolved.length - 1] = { ...last, amount: roundRupees(last.amount + drift) };
68
+ }
69
+ }
70
+ const contributed = roundRupees(resolved.reduce((s, r) => s + r.amount, 0));
71
+ const diff = roundRupees(contributed - landedCost);
72
+ return {
73
+ resolved,
74
+ contributed,
75
+ surplus: diff > 0 ? diff : 0,
76
+ shortfall: diff < 0 ? -diff : 0,
77
+ valid: diff >= 0,
78
+ };
79
+ }
80
+ /**
81
+ * The price to lock on the line, given the goods subtotal and a surplus.
82
+ *
83
+ * ## 🛑 THE TRAP THIS FUNCTION EXISTS TO PREVENT
84
+ *
85
+ * The percentage BASE is the landed cost — goods **plus shipping plus fees** —
86
+ * because that is what the coordinator is really dividing. But `lockedPrice` is
87
+ * the GOODS price of the line: the checkout adds shipping, the platform fee,
88
+ * GST and the add-ons on top of it, independently.
89
+ *
90
+ * So locking the landed cost would charge shipping and every fee **TWICE** —
91
+ * once inside `lockedPrice` and once again as the checkout's own terms. The
92
+ * surplus rides on the goods price alone.
93
+ *
94
+ * That is the whole reason this is a named function rather than an expression
95
+ * at a call site: `goods + surplus` and `landedCost + surplus` are both
96
+ * plausible-looking one-liners and only one of them is a correct charge.
97
+ */
98
+ export function lockedPriceWithSurplus(goodsSubtotal, surplus) {
99
+ return roundRupees(goodsSubtotal + surplus);
100
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Disclaimers — one registry, rendered by `<Disclaimer id>` (Phase 12).
3
+ *
4
+ * ## 🛑 Why a registry and not a sentence per surface
5
+ *
6
+ * Same failure mode as the published numbers in Phase 9: if each surface
7
+ * writes its own, they drift, and **the legally material ones end up
8
+ * weakest** — because the surface that most needs a careful disclaimer is
9
+ * usually the one whose author was busiest. The scam registry is the example:
10
+ * naming a person a scammer is defamation exposure, and it had no disclaimer
11
+ * at all.
12
+ *
13
+ * ## The three rules every entry obeys
14
+ *
15
+ * 1. **Visible BEFORE the decision it qualifies**, not only on a policy page
16
+ * reachable from the footer. The protection disclaimer belongs beside the
17
+ * checkbox, not in `/refund-policy`.
18
+ * 2. **It names a LIMIT or an EXCLUSION.** A disclaimer that only says "we
19
+ * try our best" qualifies nothing and is worse than none — it reads as
20
+ * cover while providing none.
21
+ * 3. **It AGREES with the code.** Limits render from the constants that
22
+ * enforce them (`SALE_PROTECTION_MAX_VALUE`, the claim windows), through
23
+ * the same `{{token}}` mechanism as Phase 9, so a constant change updates
24
+ * the published terms rather than contradicting them.
25
+ *
26
+ * ## 🛑 NOT a cookie-banner blanket
27
+ *
28
+ * A disclaimer that appears everywhere is read nowhere. Each entry names the
29
+ * specific surfaces that need it, and `audit-disclaimer-coverage` checks those
30
+ * surfaces render one — not that every page does.
31
+ */
32
+ export type DisclaimerId =
33
+ /** Naming a person a scammer. The highest-exposure entry in this file. */
34
+ "scam-registry"
35
+ /** Prize draws and the lottery — state-specific gaming law. */
36
+ | "prize-draw"
37
+ /** Collectibles shown with historical prices. */
38
+ | "not-investment-advice"
39
+ /** The realised-price index. */
40
+ | "price-index"
41
+ /** Authenticity and grading. */
42
+ | "authenticity"
43
+ /** Buyer protection cover — OUR cover, not insurance. */
44
+ | "protection-cover"
45
+ /** P&L, trial balance, GSTR-1 draft. */
46
+ | "accounting-output"
47
+ /** Live animals and plants. */
48
+ | "live-items"
49
+ /** Pre-orders. */
50
+ | "pre-order"
51
+ /** Auctions. */
52
+ | "auction-binding"
53
+ /** EMI. */
54
+ | "emi"
55
+ /** Delivery estimates. */
56
+ | "delivery-estimate"
57
+ /** Reviews, catalogue entries, blog comments. */
58
+ | "user-content"
59
+ /** GST shown on an invoice. */
60
+ | "tax-computation"
61
+ /** An automated decision — a timer cancelled, approved or completed something. */
62
+ | "automated-decision";
63
+ export interface DisclaimerEntry {
64
+ /** Short heading, when the surface renders one. Omit for inline notes. */
65
+ heading?: string;
66
+ /**
67
+ * The text. May contain `{{token}}`s resolved by `resolvePublishedCopy`.
68
+ *
69
+ * 🛑 Must name a limit or an exclusion — see rule 2 in the header. An entry
70
+ * that does not is not a disclaimer, and `audit-disclaimer-coverage` checks
71
+ * for it rather than trusting the author.
72
+ */
73
+ body: string;
74
+ /**
75
+ * Tone, which decides the rendered variant.
76
+ *
77
+ * `legal` is the quiet grey note; `warning` is the one a user must not scroll
78
+ * past. 🛑 Reserved for the entries where ignoring it has a consequence the
79
+ * user cannot undo — not used to make a disclaimer feel important.
80
+ */
81
+ tone: "legal" | "warning";
82
+ /**
83
+ * Where this must appear, as a free-text note for the reviewer and the
84
+ * audit's own registry.
85
+ */
86
+ surfaces: readonly string[];
87
+ }
88
+ export declare const DISCLAIMERS: Record<DisclaimerId, DisclaimerEntry>;
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Disclaimers — one registry, rendered by `<Disclaimer id>` (Phase 12).
3
+ *
4
+ * ## 🛑 Why a registry and not a sentence per surface
5
+ *
6
+ * Same failure mode as the published numbers in Phase 9: if each surface
7
+ * writes its own, they drift, and **the legally material ones end up
8
+ * weakest** — because the surface that most needs a careful disclaimer is
9
+ * usually the one whose author was busiest. The scam registry is the example:
10
+ * naming a person a scammer is defamation exposure, and it had no disclaimer
11
+ * at all.
12
+ *
13
+ * ## The three rules every entry obeys
14
+ *
15
+ * 1. **Visible BEFORE the decision it qualifies**, not only on a policy page
16
+ * reachable from the footer. The protection disclaimer belongs beside the
17
+ * checkbox, not in `/refund-policy`.
18
+ * 2. **It names a LIMIT or an EXCLUSION.** A disclaimer that only says "we
19
+ * try our best" qualifies nothing and is worse than none — it reads as
20
+ * cover while providing none.
21
+ * 3. **It AGREES with the code.** Limits render from the constants that
22
+ * enforce them (`SALE_PROTECTION_MAX_VALUE`, the claim windows), through
23
+ * the same `{{token}}` mechanism as Phase 9, so a constant change updates
24
+ * the published terms rather than contradicting them.
25
+ *
26
+ * ## 🛑 NOT a cookie-banner blanket
27
+ *
28
+ * A disclaimer that appears everywhere is read nowhere. Each entry names the
29
+ * specific surfaces that need it, and `audit-disclaimer-coverage` checks those
30
+ * surfaces render one — not that every page does.
31
+ */
32
+ export const DISCLAIMERS = {
33
+ /*
34
+ * ── 🛑 THE DEFAMATION ONE. Read this before editing it ────────────────────
35
+ *
36
+ * A scam profile names a real person, with their payment handle and their
37
+ * phone number, on a public page. That is defamation exposure, and the
38
+ * registry had NO disclaimer of any kind.
39
+ *
40
+ * Four things this text must do, each for a specific reason:
41
+ *
42
+ * 1. **Distinguish an ALLEGATION from a verified finding.** The status badge
43
+ * already says `pending_review` vs `verified`, but a visitor reads the
44
+ * name and the amount, not the badge — so the distinction has to be in
45
+ * words.
46
+ * 2. **State the evidence standard**, because "we checked" is not a standard
47
+ * and invites the reader to assume a higher one than we applied.
48
+ * 3. **Name the removal route.** A person named here needs a way to contest
49
+ * it that does not begin with a lawyer, and publishing one is itself part
50
+ * of the defence.
51
+ * 4. 🛑 **Refuse the shared-guilt inference.** `relatedScammerIds` means
52
+ * SAME IDENTITY — the same person under another handle — and never
53
+ * "similar pattern". A reader who takes it as the latter concludes that
54
+ * everyone in a cluster is guilty of everyone else's conduct, which is
55
+ * the single most damaging misreading this page permits.
56
+ */
57
+ "scam-registry": {
58
+ heading: "About these reports",
59
+ body: "These are reports submitted by buyers and sellers, not findings of fact or a legal judgment. " +
60
+ "A profile marked **under review** is an unverified allegation. A profile marked **verified** means " +
61
+ "our team has seen supporting evidence — payment records, messages or delivery proof — supplied by the " +
62
+ "person reporting; it is not a court finding and we are not able to investigate beyond the documents " +
63
+ "we are shown. " +
64
+ "Linked profiles indicate the SAME person under another name or handle, and never that separate people " +
65
+ "are connected to each other's conduct. " +
66
+ "If you are named here and believe it is wrong, contact us and we will review the report and remove it " +
67
+ "if it is not supported.",
68
+ tone: "warning",
69
+ surfaces: ["ScamProfileView", "ScamRegistryView", "/report"],
70
+ },
71
+ /*
72
+ * Gaming law in India is STATE-SPECIFIC, and the distinction that matters
73
+ * legally is skill versus chance. The entry names the exclusions rather than
74
+ * claiming a position we cannot defend in every state.
75
+ */
76
+ "prize-draw": {
77
+ heading: "Prize draw terms",
78
+ body: "Entry requires a purchase and is void where prohibited by local law. Residents of states that " +
79
+ "restrict prize draws or games of chance are not eligible, and an entry from such a state will be " +
80
+ "refunded rather than drawn. Prizes are as described on the listing, are not transferable and cannot " +
81
+ "be exchanged for cash. The draw is run by LetItRip and is not affiliated with any government lottery.",
82
+ tone: "warning",
83
+ surfaces: ["PrizeDrawDetailPageView", "/prize-draws", "/lottery", "PrizeRevealModal"],
84
+ },
85
+ /*
86
+ * 🛑 Shown wherever a historical price appears. Collectibles displayed with
87
+ * past realised prices invite an appreciation inference, and the moment a
88
+ * buyer treats that as a projection we are giving investment advice we are
89
+ * not licensed to give.
90
+ */
91
+ "not-investment-advice": {
92
+ body: "Prices shown are past transactions, not a valuation, a projection or a guarantee. Collectibles can " +
93
+ "fall in value as easily as rise, may be difficult to sell at any price, and LetItRip does not provide " +
94
+ "investment advice. Nothing here should be relied on as a reason to buy.",
95
+ tone: "legal",
96
+ surfaces: ["PriceIndexView", "RealisedPriceStrip", "/franchise", "ProductDetailPageView"],
97
+ },
98
+ /*
99
+ * 🛑 Names the SAMPLE, because an index built from four sales reads exactly
100
+ * like one built from four hundred — and "asked ≠ sold" is the distinction a
101
+ * seller pricing against it most needs.
102
+ */
103
+ "price-index": {
104
+ body: "Each figure is drawn from a limited number of observed sales over the period shown, and a small " +
105
+ "sample can move sharply on one unusual transaction. Asking prices are not sale prices: an item " +
106
+ "listed at a figure may never have sold at it.",
107
+ tone: "legal",
108
+ surfaces: ["PriceIndexView", "PriceBandChart"],
109
+ },
110
+ "authenticity": {
111
+ body: "LetItRip is not an authentication or grading service. We do not inspect, test or re-grade items, " +
112
+ "and a grade shown on a listing is the one the seller states a third-party grader assigned. " +
113
+ "Guidance on spotting a counterfeit is general information, not a verification of any particular item.",
114
+ tone: "legal",
115
+ surfaces: ["/spot-a-fake", "ProductDetailPageView", "authenticityKnowledge"],
116
+ },
117
+ /*
118
+ * 🛑 **"Not insurance" is the load-bearing phrase.** There is no insurer —
119
+ * this is our own cover, funded by us, and calling it insurance would be a
120
+ * regulatory misstatement as well as a false one.
121
+ *
122
+ * Every limit renders from the constant that enforces it, so a change to
123
+ * `SALE_PROTECTION_MAX_VALUE` or a claim window updates this text rather
124
+ * than contradicting it.
125
+ */
126
+ "protection-cover": {
127
+ heading: "What protection covers",
128
+ body: "Buyer protection is cover provided by LetItRip. It is **not insurance** and there is no insurer. " +
129
+ "Shipment protection covers loss or damage in transit and must be claimed within " +
130
+ "{{shipmentClaimHours}} hours of delivery. Sale protection covers an item up to " +
131
+ "{{saleProtectionMaxValue}} and must be claimed within {{saleClaimHours}} hours of delivery. " +
132
+ "Neither covers a change of mind, an item sold as final sale, or a claim made after its window has " +
133
+ "closed.",
134
+ tone: "legal",
135
+ surfaces: ["StoreAddonsPicker", "/fees", "/refund-policy", "ProtectionClaimForm"],
136
+ },
137
+ /*
138
+ * 🛑 "Management figures, not audited accounts" — and the GSTR-1 export is
139
+ * the INPUT to a filing, never a filing. Implying a return had been filed
140
+ * when it had not is the most consequential thing this product could say.
141
+ */
142
+ "accounting-output": {
143
+ body: "These are management figures produced from this platform's own records. They are not audited " +
144
+ "accounts and have not been reviewed by an accountant. The GSTR-1 export is a draft for your " +
145
+ "accountant to check and use — nothing here is filed with any tax authority, and LetItRip has no " +
146
+ "connection to the GST portal.",
147
+ tone: "warning",
148
+ surfaces: ["AdminInvoicingView", "AdminAnalyticsView", "ProfitLossView", "TrialBalanceView"],
149
+ },
150
+ "live-items": {
151
+ heading: "Live item terms",
152
+ body: "Live animals and plants ship only where law permits, and some species cannot be sent across state " +
153
+ "or international borders at all — CITES-listed species in particular. The buyer is responsible for " +
154
+ "holding any licence their own jurisdiction requires. No guarantee of health, size, sex or lifespan " +
155
+ "is given beyond what the listing states, and a live item cannot be returned for a change of mind.",
156
+ tone: "warning",
157
+ surfaces: ["LiveItemDetailPageView", "/live"],
158
+ },
159
+ "pre-order": {
160
+ body: "A release date on a pre-order is the manufacturer's estimate and can move or be cancelled by them. " +
161
+ "Your deposit is refundable until the item ships; the remaining balance is charged when it is ready " +
162
+ "to dispatch, against the terms shown when you placed the pre-order.",
163
+ tone: "legal",
164
+ surfaces: ["PreOrderDetailPageView", "/pre-orders"],
165
+ },
166
+ "auction-binding": {
167
+ body: "A bid is a binding offer to buy at that price and cannot be withdrawn. If the reserve price is not " +
168
+ "met the item does not sell, even if yours was the highest bid. Winning starts a payment window — " +
169
+ "miss it and the win is forfeited.",
170
+ tone: "warning",
171
+ surfaces: ["PlaceBidForm", "AuctionDetailPageView", "/auctions"],
172
+ },
173
+ "emi": {
174
+ body: "EMI adds a surcharge to the order total, shown before you pay. The instalment plan is between you " +
175
+ "and LetItRip rather than a bank, and a missed instalment may suspend access to the order and is " +
176
+ "recoverable as a debt.",
177
+ tone: "legal",
178
+ surfaces: ["CheckoutRouteClient", "/fees", "EmiScheduleTable"],
179
+ },
180
+ "delivery-estimate": {
181
+ body: "Delivery dates are estimates from the courier, not guarantees, and can be affected by weather, " +
182
+ "strikes and customs. A late delivery is not by itself grounds for a refund.",
183
+ tone: "legal",
184
+ surfaces: ["CheckoutRouteClient", "ProductDetailPageView", "OrderTrackView"],
185
+ },
186
+ "user-content": {
187
+ body: "Reviews, catalogue entries and comments are the opinions of the people who wrote them, not " +
188
+ "LetItRip's. We remove content that breaks our rules when it is reported, but we do not verify every " +
189
+ "claim made in it.",
190
+ tone: "legal",
191
+ surfaces: ["ReviewsList", "ReviewDetailShell", "PublicProfileView", "BlogPostView"],
192
+ },
193
+ "tax-computation": {
194
+ body: "GST shown is computed from the information available to us at the time of supply — the item's tax " +
195
+ "code and the delivery address you gave. Your own tax position, including any input credit, is yours " +
196
+ "to determine with your accountant.",
197
+ tone: "legal",
198
+ surfaces: ["InvoicePageView", "/gst", "OrderSummary"],
199
+ },
200
+ /*
201
+ * 🛑 The Phase 5b obligations act on timers — cancelling orders, approving
202
+ * returns, completing orders. A buyer whose order was cancelled by a sweep
203
+ * must be TOLD it was automatic and how to contest it; otherwise the first
204
+ * they know is a refund they did not request, which reads as us losing their
205
+ * order.
206
+ */
207
+ "automated-decision": {
208
+ body: "This was done automatically, by a time limit rather than by a person. If you think it was wrong, " +
209
+ "reply to this notification or open a support ticket and a person will look at it.",
210
+ tone: "legal",
211
+ surfaces: [
212
+ "obligation notifications",
213
+ "OrderStatusTimeline",
214
+ "AdminObligationsView",
215
+ ],
216
+ },
217
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Published numbers — ONE definition, rendered everywhere (Phase 9).
3
+ *
4
+ * ## 🛑 The problem this solves, stated as a measurement
5
+ *
6
+ * The same fact was published in four places with different answers. The
7
+ * refund timeline appeared as "5–7 business days via Razorpay" (policy page,
8
+ * naming the wrong gateway), "5–7 business days" (`REFUND_COPY`) and "3–5
9
+ * business days" (the FAQ). The return window appeared as 7 days, 2 days and
10
+ * 48 hours. The seller's return-response window is 48 hours in the FAQ and 3
11
+ * days in `AdminOrdersGuideView`. The offer checkout deadline is 48h in code
12
+ * and 24h in both the FAQ and the seed.
13
+ *
14
+ * Four owners, nothing reconciling them, and one of the four — the FAQ — is
15
+ * **seeded into Firestore and then admin-editable**, so a number quoted there
16
+ * drifts *after* deployment with no audit able to see it.
17
+ *
18
+ * ## 🛑 So copy carries TOKENS, and the token resolves at render time
19
+ *
20
+ * `{{returnWindowDays}}` in an FAQ answer is a promise that cannot drift: the
21
+ * FAQ is still admin-editable, but the NUMBER inside it comes from here. An
22
+ * admin can rewrite the sentence and cannot contradict the code.
23
+ *
24
+ * ## 🛑 This is NOT a settings group, and must not become one
25
+ *
26
+ * Nothing here is admin-editable. These are the values the CODE enforces —
27
+ * `ORDER_RETURN_WINDOW_MS`, the obligation policies, `LANE_CHECKOUT_WINDOW_MS`
28
+ * — surfaced for rendering. An editable copy of an enforced constant is two
29
+ * sources of truth with a settings UI making the wrong one look
30
+ * authoritative, which is the 18-dead-settings pattern (D28/D44/D57/D60)
31
+ * arriving from the opposite direction: not a control that does nothing, but a
32
+ * control that appears to do something the code ignores.
33
+ *
34
+ * Where a value genuinely IS configurable, the entry derives it from the
35
+ * setting rather than restating it — see `resolvePublishedValues`.
36
+ */
37
+ /**
38
+ * Every token that may appear in published copy.
39
+ *
40
+ * 🛑 A `Record` over this union, so a token added to the type without a value
41
+ * is a COMPILE error — and a token used in copy that is not in the union is
42
+ * caught by `resolvePublishedCopy` leaving it visibly unresolved rather than
43
+ * silently blanking it. Both directions matter: a blanked token reads as a
44
+ * sentence with a word missing, which nobody reports, while `{{wrongKey}}` on
45
+ * a live page gets reported in minutes.
46
+ */
47
+ export type PublishedValueKey = "returnWindowDays" | "sellerAcceptDays" | "sellerDispatchBusinessDays" | "returnRespondHours" | "returnPostDays" | "offerCheckoutHours" | "paymentWindowMinutes" | "deliveryConfirmDays" | "orderCompleteDays" | "shipmentClaimHours" | "saleClaimHours" | "disputeTransitWindowDays" | "disputeNotReachedGraceDays" | "platformFeeMax" | "platformFeePercent" | "shipmentProtectionPercent" | "shipmentProtectionMin" | "saleProtectionPercent" | "saleProtectionMin" | "saleProtectionMaxValue" | "codHandlingPercent" | "codDepositPercent" | "giftWrapFee" | "pointsEarnPercent" | "pointsMaxRedemptionPercent" | "pointsExpiryMonths" | "refundBusinessDaysMin" | "refundBusinessDaysMax";
48
+ export type PublishedValues = Record<PublishedValueKey, string>;
49
+ /**
50
+ * The settings this registry reads, where a value genuinely is configurable.
51
+ *
52
+ * Deliberately a narrow shape rather than `SiteSettingsDocument`: this module
53
+ * is imported by client components to render copy, and widening it to the
54
+ * whole settings document would invite a caller to pass one in — which is Root
55
+ * Cause #70's leak (a raw document reaching a Client Component prop, where
56
+ * TypeScript's structural typing strips nothing at runtime).
57
+ */
58
+ export interface PublishedValueSettings {
59
+ platformFeeMax?: number;
60
+ platformFeePercent?: number;
61
+ shipmentProtectionFeePercent?: number;
62
+ shipmentProtectionFeeMin?: number;
63
+ saleProtectionFeePercent?: number;
64
+ saleProtectionFeeMin?: number;
65
+ codHandlingFeePercent?: number;
66
+ codDepositPercent?: number;
67
+ giftWrapFee?: number;
68
+ returnWindowDays?: number;
69
+ }
70
+ /**
71
+ * Resolve every published value.
72
+ *
73
+ * 🛑 Settings are read where they exist and the CODE is read where it does
74
+ * not. A value with no setting is not a gap to fill with a literal — it is a
75
+ * constant the code enforces, and restating it here is how the four
76
+ * contradictions above happened in the first place.
77
+ */
78
+ export declare function resolvePublishedValues(settings?: PublishedValueSettings): PublishedValues;
79
+ /**
80
+ * Replace every `{{token}}` in a string.
81
+ *
82
+ * 🛑 An UNKNOWN token is left verbatim, never blanked. A blanked token reads
83
+ * as a sentence with a word missing — which looks like clumsy copy and nobody
84
+ * reports — while a visible `{{returnWindowDayz}}` on a live page is reported
85
+ * within minutes. Failing loudly is the whole point of a token system whose
86
+ * copy is admin-editable.
87
+ */
88
+ export declare function resolvePublishedCopy(text: string, values: PublishedValues): string;