@mohasinac/appkit 4.62.1 → 4.63.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 (261) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/layout/DashboardLayoutClient.js +8 -29
  3. package/dist/_internal/client/features/layout/filterNavItems.d.ts +28 -0
  4. package/dist/_internal/client/features/layout/filterNavItems.js +35 -0
  5. package/dist/_internal/server/features/ads/booking.d.ts +118 -0
  6. package/dist/_internal/server/features/ads/booking.js +326 -0
  7. package/dist/_internal/server/features/checkout/actions.d.ts +51 -11
  8. package/dist/_internal/server/features/checkout/actions.js +335 -80
  9. package/dist/_internal/server/features/checkout/expand-order-items.d.ts +1 -2
  10. package/dist/_internal/server/features/checkout/expand-order-items.js +32 -0
  11. package/dist/_internal/server/features/checkout/freeze-parts.d.ts +50 -0
  12. package/dist/_internal/server/features/checkout/freeze-parts.js +94 -0
  13. package/dist/_internal/server/features/checkout/value-otp-gate.d.ts +55 -0
  14. package/dist/_internal/server/features/checkout/value-otp-gate.js +60 -0
  15. package/dist/_internal/server/features/dashboard/attention.d.ts +128 -0
  16. package/dist/_internal/server/features/dashboard/attention.js +183 -0
  17. package/dist/_internal/server/features/dashboard/data.d.ts +47 -0
  18. package/dist/_internal/server/features/dashboard/data.js +82 -0
  19. package/dist/_internal/server/features/inventory/reservations.d.ts +99 -0
  20. package/dist/_internal/server/features/inventory/reservations.js +273 -0
  21. package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
  22. package/dist/_internal/server/features/offers/destinations.js +65 -0
  23. package/dist/_internal/server/features/orders/adapters.js +21 -0
  24. package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
  25. package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
  26. package/dist/_internal/server/features/orders/balance.d.ts +89 -0
  27. package/dist/_internal/server/features/orders/balance.js +256 -0
  28. package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
  29. package/dist/_internal/server/features/pricing/actions.js +144 -0
  30. package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
  31. package/dist/_internal/server/features/shipping/book-split.js +130 -0
  32. package/dist/_internal/server/features/shipping/book.d.ts +61 -0
  33. package/dist/_internal/server/features/shipping/book.js +176 -0
  34. package/dist/_internal/server/features/shipping/courier-block.js +26 -1
  35. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  36. package/dist/_internal/server/features/site-settings/adapters.js +14 -0
  37. package/dist/_internal/server/features/stores/adapters.d.ts +1 -1
  38. package/dist/_internal/server/features/stores/adapters.js +14 -0
  39. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  40. package/dist/_internal/server/functions/scheduled.js +57 -1
  41. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +2 -0
  42. package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
  43. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +4 -1
  44. package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
  45. package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
  46. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +15 -0
  47. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  48. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  49. package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
  50. package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
  51. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  52. package/dist/_internal/shared/actions/action-registry.js +268 -0
  53. package/dist/_internal/shared/checkout/order-math.d.ts +31 -2
  54. package/dist/_internal/shared/checkout/order-math.js +22 -36
  55. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  56. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  57. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
  58. package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
  59. package/dist/_internal/shared/checkout/split.d.ts +0 -12
  60. package/dist/_internal/shared/checkout/split.js +22 -2
  61. package/dist/_internal/shared/content/disclaimers.js +7 -7
  62. package/dist/_internal/shared/content/published-values.d.ts +6 -1
  63. package/dist/_internal/shared/content/published-values.js +8 -0
  64. package/dist/_internal/shared/features/admin/integrations.d.ts +32 -2
  65. package/dist/_internal/shared/features/admin/integrations.js +19 -9
  66. package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
  67. package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
  68. package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
  69. package/dist/_internal/shared/features/emi/choice.js +69 -0
  70. package/dist/_internal/shared/features/emi/group.d.ts +84 -0
  71. package/dist/_internal/shared/features/emi/group.js +126 -0
  72. package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
  73. package/dist/_internal/shared/features/ledger/posting.js +165 -0
  74. package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
  75. package/dist/_internal/shared/features/ledger/reports.js +127 -0
  76. package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
  77. package/dist/_internal/shared/features/ledger/rules.js +221 -0
  78. package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
  79. package/dist/_internal/shared/features/offers/coordination.js +80 -0
  80. package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
  81. package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
  82. package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
  83. package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
  84. package/dist/_internal/shared/features/points/accepts.d.ts +1 -8
  85. package/dist/_internal/shared/features/points/accepts.js +12 -6
  86. package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
  87. package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
  88. package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
  89. package/dist/_internal/shared/features/shipping/parcel.js +139 -0
  90. package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
  91. package/dist/_internal/shared/features/site-settings/fees.js +12 -8
  92. package/dist/_internal/shared/fees/explain.d.ts +81 -0
  93. package/dist/_internal/shared/fees/explain.js +101 -0
  94. package/dist/_internal/shared/history/index.d.ts +1 -1
  95. package/dist/_internal/shared/history/index.js +1 -1
  96. package/dist/_internal/shared/history/types.d.ts +7 -1
  97. package/dist/_internal/shared/history/types.js +25 -0
  98. package/dist/client.d.ts +11 -1
  99. package/dist/client.js +13 -0
  100. package/dist/constants/api-endpoints.d.ts +72 -0
  101. package/dist/constants/api-endpoints.js +41 -0
  102. package/dist/constants/field-names.d.ts +41 -0
  103. package/dist/constants/field-names.js +47 -0
  104. package/dist/features/account/components/UserOrdersView.js +28 -2
  105. package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
  106. package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
  107. package/dist/features/admin/components/AdminOrderEditorView.d.ts +16 -1
  108. package/dist/features/admin/components/AdminOrderEditorView.js +72 -3
  109. package/dist/features/admin/components/AdminOrdersView.js +20 -1
  110. package/dist/features/admin/schemas/firestore.d.ts +32 -0
  111. package/dist/features/ads/schemas/booking-write.d.ts +120 -0
  112. package/dist/features/ads/schemas/booking-write.js +75 -0
  113. package/dist/features/ads/schemas/creative-form.d.ts +18 -0
  114. package/dist/features/ads/schemas/creative-form.js +51 -0
  115. package/dist/features/ads/schemas/firestore.d.ts +140 -0
  116. package/dist/features/ads/schemas/firestore.js +109 -0
  117. package/dist/features/auctions/repository/bid.repository.js +3 -1
  118. package/dist/features/audit-log/schemas/firestore.d.ts +110 -0
  119. package/dist/features/audit-log/schemas/firestore.js +74 -0
  120. package/dist/features/auth/actions/profile-actions.d.ts +69 -1
  121. package/dist/features/auth/actions/profile-actions.js +62 -1
  122. package/dist/features/auth/schemas/firestore.d.ts +16 -2
  123. package/dist/features/auth/schemas/firestore.js +2 -1
  124. package/dist/features/blog/actions/blog-actions.d.ts +6 -0
  125. package/dist/features/blog/actions/blog-actions.js +17 -2
  126. package/dist/features/blog/api/route.js +4 -1
  127. package/dist/features/blog/columns/index.js +5 -0
  128. package/dist/features/blog/components/BlogIndexPageView.js +2 -1
  129. package/dist/features/blog/repository/blog.repository.d.ts +4 -0
  130. package/dist/features/blog/repository/blog.repository.js +5 -0
  131. package/dist/features/blog/schemas/blog-form.d.ts +3 -0
  132. package/dist/features/blog/schemas/blog-form.js +1 -0
  133. package/dist/features/blog/schemas/firestore.d.ts +38 -1
  134. package/dist/features/blog/schemas/firestore.js +23 -0
  135. package/dist/features/blog/schemas/index.d.ts +6 -0
  136. package/dist/features/blog/schemas/index.js +2 -0
  137. package/dist/features/blog/types/index.d.ts +2 -0
  138. package/dist/features/cart/components/CartPriceBreakdown.d.ts +12 -1
  139. package/dist/features/cart/components/CartPriceBreakdown.js +24 -5
  140. package/dist/features/cart/repository/cart.repository.d.ts +14 -0
  141. package/dist/features/cart/repository/cart.repository.js +20 -0
  142. package/dist/features/cart/schemas/firestore.d.ts +24 -0
  143. package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
  144. package/dist/features/dashboard/components/DashboardHubView.js +56 -0
  145. package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
  146. package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
  147. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
  148. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
  149. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +24 -0
  150. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +28 -0
  151. package/dist/features/dispute-reasons/schemas/firestore.d.ts +3 -5
  152. package/dist/features/events/components/EventSaleBanner.js +1 -1
  153. package/dist/features/faq/components/FAQPageContent.js +2 -2
  154. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  155. package/dist/features/homepage/schemas/index.d.ts +8 -8
  156. package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
  157. package/dist/features/inventory/schemas/stock-reservation.js +103 -0
  158. package/dist/features/layout/AppLayoutShell.d.ts +9 -1
  159. package/dist/features/layout/AppLayoutShell.js +12 -4
  160. package/dist/features/layout/BottomActions.js +2 -1
  161. package/dist/features/layout/BottomNavbar.d.ts +10 -2
  162. package/dist/features/layout/SideActionDock.js +52 -2
  163. package/dist/features/layout/TitleBarLayout.js +13 -4
  164. package/dist/features/layout/TitleBarLayout.style.css +59 -0
  165. package/dist/features/layout/useCtaReveal.d.ts +16 -1
  166. package/dist/features/layout/useCtaReveal.js +17 -26
  167. package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
  168. package/dist/features/ledger/repository/journal.repository.js +312 -0
  169. package/dist/features/ledger/schemas/expenses.d.ts +87 -0
  170. package/dist/features/ledger/schemas/expenses.js +27 -0
  171. package/dist/features/ledger/schemas/firestore.d.ts +183 -0
  172. package/dist/features/ledger/schemas/firestore.js +69 -0
  173. package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
  174. package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
  175. package/dist/features/orders/components/RaiseDisputeForm.js +19 -11
  176. package/dist/features/orders/repository/orders.repository.d.ts +28 -1
  177. package/dist/features/orders/repository/orders.repository.js +60 -6
  178. package/dist/features/orders/schemas/balance.d.ts +40 -0
  179. package/dist/features/orders/schemas/balance.js +47 -0
  180. package/dist/features/orders/schemas/firestore.d.ts +202 -0
  181. package/dist/features/orders/schemas/firestore.js +43 -0
  182. package/dist/features/orders/schemas/index.d.ts +2 -2
  183. package/dist/features/orders/types/index.d.ts +21 -0
  184. package/dist/features/payments/repository/payout.repository.js +3 -2
  185. package/dist/features/payments/schemas/firestore.d.ts +92 -1
  186. package/dist/features/payments/schemas/firestore.js +23 -0
  187. package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
  188. package/dist/features/pricing/components/AdminPricingView.js +157 -0
  189. package/dist/features/products/schemas/firestore.d.ts +26 -1
  190. package/dist/features/products/schemas/firestore.js +2 -0
  191. package/dist/features/products/schemas/index.d.ts +2 -2
  192. package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
  193. package/dist/features/reviews/repository/reviews.repository.js +16 -0
  194. package/dist/features/scams/repository/scammer.repository.js +2 -1
  195. package/dist/features/scams/schemas/firestore.d.ts +2 -1
  196. package/dist/features/scams/schemas/firestore.js +2 -1
  197. package/dist/features/seller/actions/offer-actions.js +56 -0
  198. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  199. package/dist/features/seller/actions/seller-actions.js +4 -0
  200. package/dist/features/seller/components/DispatchForm.d.ts +11 -0
  201. package/dist/features/seller/components/DispatchForm.js +190 -0
  202. package/dist/features/seller/components/SellerOrdersView.js +94 -2
  203. package/dist/features/seller/components/index.d.ts +1 -0
  204. package/dist/features/seller/components/index.js +1 -0
  205. package/dist/features/seller/repository/offer.repository.js +4 -3
  206. package/dist/features/seller/schemas/firestore.d.ts +23 -2
  207. package/dist/features/seller/schemas/firestore.js +3 -2
  208. package/dist/features/seller/schemas/index.d.ts +4 -4
  209. package/dist/features/seller/schemas/order-forms.d.ts +61 -4
  210. package/dist/features/seller/schemas/order-forms.js +41 -8
  211. package/dist/features/shipments/schemas/firestore.d.ts +25 -0
  212. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +39 -0
  213. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +55 -0
  214. package/dist/features/shipping-boxes/schemas/firestore.d.ts +2 -4
  215. package/dist/features/stores/repository/store.repository.d.ts +18 -0
  216. package/dist/features/stores/repository/store.repository.js +27 -1
  217. package/dist/features/stores/schemas/firestore.d.ts +41 -0
  218. package/dist/features/stores/schemas/firestore.js +20 -0
  219. package/dist/features/stores/schemas/index.d.ts +6 -6
  220. package/dist/features/support/repository/support.repository.js +72 -6
  221. package/dist/features/support/schemas/firestore.d.ts +17 -0
  222. package/dist/features/support/schemas/index.d.ts +2 -2
  223. package/dist/index.d.ts +5 -0
  224. package/dist/index.js +19 -0
  225. package/dist/next/routing/route-map.d.ts +92 -0
  226. package/dist/next/routing/route-map.js +53 -0
  227. package/dist/react/contexts/SessionContext.d.ts +17 -1
  228. package/dist/schemas/registry.d.ts +22 -16
  229. package/dist/security/pii-schemas.d.ts +1 -1
  230. package/dist/security/pii-schemas.js +45 -0
  231. package/dist/server.d.ts +27 -1
  232. package/dist/server.js +144 -0
  233. package/dist/styles.css +349 -1
  234. package/dist/tailwind-utilities.css +1 -1
  235. package/dist/ui/components/Button.d.ts +10 -1
  236. package/dist/ui/components/Button.js +25 -3
  237. package/dist/ui/components/Chart.d.ts +63 -0
  238. package/dist/ui/components/Chart.js +169 -0
  239. package/dist/ui/components/Chart.style.css +214 -0
  240. package/dist/ui/components/Disclaimer.js +10 -1
  241. package/dist/ui/components/OverflowRow.js +54 -2
  242. package/dist/ui/components/OverflowRow.style.css +38 -0
  243. package/dist/ui/components/RichTextEditor.js +99 -8
  244. package/dist/ui/components/chart-spec.d.ts +68 -0
  245. package/dist/ui/components/chart-spec.js +140 -0
  246. package/dist/ui/components/index.style.css +1 -0
  247. package/dist/ui/components/surface-tokens.d.ts +1 -1
  248. package/dist/ui/components/surface-tokens.js +9 -0
  249. package/dist/ui/components/toolbar-collapse.d.ts +13 -0
  250. package/dist/ui/components/toolbar-collapse.js +10 -1
  251. package/dist/ui/index.d.ts +6 -0
  252. package/dist/ui/index.js +3 -0
  253. package/dist/ui/rich-text/RichText.d.ts +10 -1
  254. package/dist/ui/rich-text/RichText.js +33 -2
  255. package/dist/ui/rich-text/RichText.style.css +35 -0
  256. package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
  257. package/dist/ui/rich-text/RichTextRenderer.js +34 -2
  258. package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
  259. package/dist/ui/rich-text/chart-blocks.js +87 -0
  260. package/package.json +1 -1
  261. package/scripts/seed-cli.mjs +3 -0
@@ -64,6 +64,73 @@ export function actionLabel(def) {
64
64
  // just establishes the shape + a few "obvious" actions that other Phase 5
65
65
  // flows can reference today.
66
66
  export const ACTIONS = {
67
+ /*
68
+ * D56 — self-serve ad bookings.
69
+ *
70
+ * 🛑 `book-ad-slot` carries a confirmation because it is a PURCHASE of a
71
+ * named month, and cancelling it does not free that month for re-sale —
72
+ * the booking document IS the availability lock and keeping it is
73
+ * deliberate. A control that spends money and cannot be undone by the
74
+ * person who clicked it is exactly what Rule #7's confirmation is for.
75
+ */
76
+ ADS: {
77
+ "book-ad-slot": {
78
+ iconKey: "create",
79
+ id: "ads.book-ad-slot",
80
+ label: "Book this slot",
81
+ ariaLabel: "Book this ad slot for the selected month",
82
+ description: "Reserves one placement for one month at the listed flat fee. The creative is reviewed by a person before it appears anywhere.",
83
+ kind: "primary",
84
+ permissions: ["user"],
85
+ confirmation: {
86
+ title: "Book this slot?",
87
+ body: "This reserves the placement for the whole month at the listed price. Cancelling later does not release the month for someone else to buy.",
88
+ confirmLabel: "Yes, book it",
89
+ },
90
+ },
91
+ "cancel-ad-booking": {
92
+ iconKey: "cancel",
93
+ id: "ads.cancel-ad-booking",
94
+ label: "Cancel booking",
95
+ ariaLabel: "Cancel this ad booking",
96
+ description: "Stops the creative running. 🛑 Does NOT free the month — the slot stays yours for the period you booked; releasing it is a separate admin act with a refund behind it.",
97
+ kind: "danger",
98
+ permissions: ["user"],
99
+ confirmation: {
100
+ title: "Cancel this booking?",
101
+ body: "Your ad stops running. The month is NOT released for another advertiser, and this does not issue a refund on its own.",
102
+ confirmLabel: "Yes, cancel",
103
+ },
104
+ },
105
+ "approve-ad-creative": {
106
+ iconKey: "verify",
107
+ id: "ads.approve-ad-creative",
108
+ label: "Approve creative",
109
+ ariaLabel: "Approve this ad creative so it can render",
110
+ description: "Clears the creative to render for its booked month. Approval is of THIS creative — an edit returns it to review.",
111
+ kind: "primary",
112
+ permissions: ["admin", "moderator"],
113
+ confirmation: {
114
+ title: "Approve this creative?",
115
+ body: "It will render on the public site for its booked month once the booking is paid.",
116
+ confirmLabel: "Yes, approve",
117
+ },
118
+ },
119
+ "reject-ad-creative": {
120
+ iconKey: "reject",
121
+ id: "ads.reject-ad-creative",
122
+ label: "Reject creative",
123
+ ariaLabel: "Reject this ad creative and tell the advertiser why",
124
+ description: "Sends it back with your note. A note is REQUIRED — without one the advertiser resubmits the same creative and you review it twice.",
125
+ kind: "danger",
126
+ permissions: ["admin", "moderator"],
127
+ confirmation: {
128
+ title: "Reject this creative?",
129
+ body: "The advertiser sees your note and can submit a new creative against the same booking. The slot stays theirs.",
130
+ confirmLabel: "Yes, reject",
131
+ },
132
+ },
133
+ },
67
134
  PRODUCT: {
68
135
  /*
69
136
  * The buyer-facing verbs below shipped long ago; the four CRUD entries
@@ -929,6 +996,36 @@ export const ACTIONS = {
929
996
  kind: "primary",
930
997
  permissions: ["seller", "admin"],
931
998
  },
999
+ "dispatch-custom": {
1000
+ iconKey: "ship",
1001
+ id: "store.dispatch-custom",
1002
+ label: "Record dispatch",
1003
+ ariaLabel: "Record a parcel you posted yourself",
1004
+ description: "Record the carrier and tracking id for a parcel the seller posted themselves.",
1005
+ kind: "primary",
1006
+ permissions: ["seller", "admin"],
1007
+ },
1008
+ "dispatch-platform": {
1009
+ iconKey: "ship",
1010
+ id: "store.dispatch-platform",
1011
+ label: "Book through the platform",
1012
+ ariaLabel: "Book this parcel through the platform's courier account",
1013
+ description: "Book a pickup on the platform's own carrier account. The tracking id comes back from the courier.",
1014
+ kind: "primary",
1015
+ permissions: ["seller", "admin"],
1016
+ /*
1017
+ * 🛑 Confirmed because it SPENDS MONEY and cannot be undone from this
1018
+ * screen. A booking debits the platform's carrier wallet and schedules
1019
+ * a real pickup; `bookShipment` then refuses a second attempt on the
1020
+ * same order, so a mis-click is not a thing the seller can take back by
1021
+ * pressing the button again.
1022
+ */
1023
+ confirmation: {
1024
+ title: "Book this parcel through the platform?",
1025
+ body: "A courier pickup will be scheduled at the address you picked and the cost charged to the platform's carrier account. This cannot be undone from here.",
1026
+ confirmLabel: "Book pickup",
1027
+ },
1028
+ },
932
1029
  "mark-installment-paid": {
933
1030
  iconKey: "confirm",
934
1031
  id: "store.mark-installment-paid",
@@ -1727,6 +1824,137 @@ export const ACTIONS = {
1727
1824
  },
1728
1825
  },
1729
1826
  ADMIN: {
1827
+ /*
1828
+ * ── Integration switches (12b) ──────────────────────────────────
1829
+ *
1830
+ * 🛑 The confirmation BODY is deliberately empty on `disable-integration`,
1831
+ * and that is not an omission.
1832
+ *
1833
+ * `INTEGRATIONS[id].offConsequence` is the real body, and it differs per
1834
+ * integration by design — "payments off" abandons in-flight payments,
1835
+ * "support off" hides a contact route, "shipping off" selects a working
1836
+ * manual path and stops nothing. A generic "are you sure?" here would
1837
+ * flatten exactly the distinction that file exists to preserve, and a COPY
1838
+ * of each consequence string would be a second place for it to drift from
1839
+ * (Root Cause #42). `AdminIntegrationsView` composes the dialog body from
1840
+ * the descriptor at render time.
1841
+ *
1842
+ * The registry still owns the label, the aria-label, the kind, and the fact
1843
+ * that a confirmation is REQUIRED — which is what Rule #7 is about.
1844
+ */
1845
+ "enable-integration": {
1846
+ iconKey: "approve",
1847
+ id: "admin.enable-integration",
1848
+ label: "Enable",
1849
+ ariaLabel: "Enable this integration",
1850
+ description: "Turn an integration on. Writes the authoritative settings field for it.",
1851
+ kind: "primary",
1852
+ permissions: ["admin"],
1853
+ },
1854
+ "update-pricing": {
1855
+ iconKey: "save",
1856
+ id: "admin.update-pricing",
1857
+ label: "Save new rate",
1858
+ ariaLabel: "Save the new rate",
1859
+ description: "Change one platform rate from now on. Orders, invoices and payouts already recorded keep the rate they were charged at — a rate change never restates history.",
1860
+ kind: "primary",
1861
+ permissions: ["admin"],
1862
+ /*
1863
+ * 🛑 A confirmation even though `kind` is "primary" rather than "danger".
1864
+ * The registry requires one for destructive actions; this one is here
1865
+ * because the act is IRREVERSIBLE IN A WAY THAT DOES NOT LOOK IT — the
1866
+ * rate moves forward only, so "undo" means a second change with a second
1867
+ * audit row, and nothing restores what was charged in between.
1868
+ */
1869
+ confirmation: {
1870
+ title: "Change this rate?",
1871
+ body: "Every checkout from now on uses the new value. Past orders, invoices and payouts are untouched and keep the rate they were charged at.",
1872
+ confirmLabel: "Change the rate",
1873
+ },
1874
+ },
1875
+ "add-dispute-reason": {
1876
+ iconKey: "create",
1877
+ id: "admin.add-dispute-reason",
1878
+ label: "Add reason",
1879
+ ariaLabel: "Add a dispute reason",
1880
+ description: "Add a reason buyers can pick when reporting a problem. The reason's KIND decides which gate it is subject to; the wording is yours.",
1881
+ kind: "primary",
1882
+ permissions: ["admin"],
1883
+ },
1884
+ "retire-dispute-reason": {
1885
+ iconKey: "reject",
1886
+ id: "admin.retire-dispute-reason",
1887
+ label: "Retire",
1888
+ ariaLabel: "Retire this dispute reason",
1889
+ description: "Take a reason out of the buyer's picker. Never a delete — past disputes name it, and the order snapshots its label so history survives either way.",
1890
+ kind: "danger",
1891
+ permissions: ["admin"],
1892
+ confirmation: {
1893
+ title: "Retire this reason?",
1894
+ /*
1895
+ * Names what retiring does NOT do, because that is the part an agent
1896
+ * is most likely to be unsure about and the reason this is not a
1897
+ * delete. A vague "are you sure" would invite exactly the hesitation
1898
+ * the schema's soft-retire design exists to remove.
1899
+ */
1900
+ body: "Buyers will stop seeing it on the dispute form. Disputes already raised under it keep their wording, and you can put it back at any time.",
1901
+ confirmLabel: "Retire it",
1902
+ confirmKind: "danger",
1903
+ },
1904
+ },
1905
+ "reactivate-dispute-reason": {
1906
+ iconKey: "approve",
1907
+ id: "admin.reactivate-dispute-reason",
1908
+ label: "Reactivate",
1909
+ ariaLabel: "Put this dispute reason back in the picker",
1910
+ description: "Offer a retired reason to buyers again. The original author is kept.",
1911
+ kind: "primary",
1912
+ permissions: ["admin"],
1913
+ },
1914
+ "disable-integration": {
1915
+ iconKey: "reject",
1916
+ id: "admin.disable-integration",
1917
+ label: "Disable",
1918
+ ariaLabel: "Disable this integration",
1919
+ description: "Turn an integration off. The consequence differs per integration and is stated on the row.",
1920
+ kind: "danger",
1921
+ permissions: ["admin"],
1922
+ confirmation: {
1923
+ title: "Switch this integration off?",
1924
+ body: "",
1925
+ confirmLabel: "Switch it off",
1926
+ confirmKind: "danger",
1927
+ },
1928
+ },
1929
+ "enable-all-integrations": {
1930
+ iconKey: "approve",
1931
+ id: "admin.enable-all-integrations",
1932
+ label: "Enable all",
1933
+ ariaLabel: "Enable every integration",
1934
+ description: "Turn every integration on in a single write.",
1935
+ kind: "secondary",
1936
+ permissions: ["admin"],
1937
+ confirmation: {
1938
+ title: "Enable every integration?",
1939
+ body: "Online payments, customer email, courier booking and the support surface will all be switched on. One write, one audit-log entry.",
1940
+ confirmLabel: "Enable everything",
1941
+ },
1942
+ },
1943
+ "disable-all-integrations": {
1944
+ iconKey: "reject",
1945
+ id: "admin.disable-all-integrations",
1946
+ label: "Disable all",
1947
+ ariaLabel: "Disable every integration",
1948
+ description: "Turn every integration off in a single write.",
1949
+ kind: "danger",
1950
+ permissions: ["admin"],
1951
+ confirmation: {
1952
+ title: "Disable every integration?",
1953
+ body: "This stops online payments, customer email and courier booking, and hides the buyer support surface. 🛑 Payments already in progress are abandoned — a buyer part-way through the gateway may be charged with no order placed. Staff email and open tickets stay workable. One write, one audit-log entry.",
1954
+ confirmLabel: "Disable everything",
1955
+ confirmKind: "danger",
1956
+ },
1957
+ },
1730
1958
  // ── Product moderation ─────────────────────────────────────────────────
1731
1959
  "approve-product": {
1732
1960
  iconKey: "approve",
@@ -1883,6 +2111,46 @@ export const ACTIONS = {
1883
2111
  confirmKind: "danger",
1884
2112
  },
1885
2113
  },
2114
+ /*
2115
+ * D55 — the pre-order BALANCE's two review verdicts.
2116
+ *
2117
+ * 🛑 Deliberately separate from `verify-payment` / `reject-payment-fraud`
2118
+ * above, which review the DEPOSIT. A pre-order's two payments are months
2119
+ * apart, and the consequences do not match: rejecting a deposit proof
2120
+ * cancels the order, restores stock and bans the account for 7 days,
2121
+ * because the order had no legitimate basis. Rejecting a BALANCE proof on
2122
+ * goods already made and allocated must do none of that — it asks for a
2123
+ * better screenshot. Reusing the deposit's action would attach a 7-day
2124
+ * ban to a blurry photo.
2125
+ */
2126
+ "verify-balance": {
2127
+ iconKey: "verify",
2128
+ id: "admin.verify-balance",
2129
+ label: "Accept Balance",
2130
+ ariaLabel: "Accept the pre-order balance payment proof",
2131
+ description: "Confirms the pre-order balance was received. The order becomes shippable. Does NOT post a ledger entry — the sale was recognised in full at order creation.",
2132
+ kind: "primary",
2133
+ permissions: ["admin", "moderator"],
2134
+ confirmation: {
2135
+ title: "Accept the balance payment?",
2136
+ body: "This records the remaining balance as received and clears the order to ship. Check the reference against your bank first.",
2137
+ confirmLabel: "Yes, accept",
2138
+ },
2139
+ },
2140
+ "reject-balance": {
2141
+ iconKey: "reject",
2142
+ id: "admin.reject-balance",
2143
+ label: "Reject Balance Proof",
2144
+ ariaLabel: "Reject the pre-order balance proof and ask the buyer to resend",
2145
+ description: "Sends the proof back with your note. The buyer can submit again — no cancellation, no stock change, no ban: the goods are already made and allocated to them.",
2146
+ kind: "secondary",
2147
+ permissions: ["admin", "moderator"],
2148
+ confirmation: {
2149
+ title: "Reject this balance proof?",
2150
+ body: "The buyer is told why and can send another. The order stays open and the item stays theirs.",
2151
+ confirmLabel: "Reject and ask again",
2152
+ },
2153
+ },
1886
2154
  "verify-vendor": {
1887
2155
  iconKey: "verify",
1888
2156
  id: "admin.verify-vendor",
@@ -521,10 +521,39 @@ export declare function resolvePointsTender(input: {
521
521
  * shown and charged figures diverge on exactly the carts where the balance is
522
522
  * tight.
523
523
  */
524
- export declare function allocatePointsAcrossGroups(groups: readonly {
524
+ /** One order group's payable total, and whether its store takes points. */
525
+ export interface PointsBase {
525
526
  total: number;
526
527
  accepted: boolean;
527
- }[], balance: number, optedIn: boolean): {
528
+ }
529
+ /** Everything one group contributes to its own payable total. */
530
+ export interface PointsBaseParts {
531
+ /** Goods subtotal for the group, before any discount. */
532
+ goods: number;
533
+ couponDiscount: number;
534
+ shippingFee: number;
535
+ /** Gift wrap + shipment protection + any other per-store add-on. */
536
+ addonFee: number;
537
+ platformFee: number;
538
+ gstOnFee: number;
539
+ acceptsPoints: boolean;
540
+ }
541
+ /**
542
+ * The payable total a points allocation is measured against.
543
+ *
544
+ * 🛑 ONE implementation, because it was two. `POST /api/payment/create-order`
545
+ * and `verifyAndPlaceRazorpayOrderAction`'s amount guard each built this sum
546
+ * by hand, in the same shape, from the same six parts — and they are the
547
+ * *shown* figure and the *charged* figure for the same cart. Two hand-written
548
+ * copies of one money rule is Root Cause #75 exactly, and the failure mode is
549
+ * the worst kind: they only disagree on carts where the arithmetic differs, so
550
+ * nothing errors and the buyer is quoted one number and charged another.
551
+ *
552
+ * The coupon is floored at zero before the fees are added, never after — a
553
+ * discount larger than the goods must not eat the shipping and platform fee.
554
+ */
555
+ export declare function pointsBaseForGroup(parts: PointsBaseParts): PointsBase;
556
+ export declare function allocatePointsAcrossGroups(groups: readonly PointsBase[], balance: number, optedIn: boolean): {
528
557
  perGroup: number[];
529
558
  pointsApplied: number;
530
559
  };
@@ -715,43 +715,29 @@ export function resolvePointsTender(input) {
715
715
  return { pointsApplied, amountDue: roundRupees(total - pointsApplied) };
716
716
  }
717
717
  /**
718
- * Draw one balance down across a checkout's order groups, in group order.
719
- *
720
- * ## Why this exists rather than each caller looping
721
- *
722
- * The tender is applied PER GROUP (decision 3 — the accepting store absorbs
723
- * its own redemption), so "how many points does this checkout use" is a
724
- * sequential draw-down, not a single `min`. Four surfaces need the same answer
725
- * and must not each write the loop:
726
- *
727
- * | surface | why it needs the total |
728
- * |---|---|
729
- * | `previewCheckoutPricing` | the figure shown to the buyer |
730
- * | `POST /api/payment/create-order` | the figure CAPTURED |
731
- * | the post-capture guard | the figure compared |
732
- * | the two order paths | the per-group figure recorded |
733
- *
734
- * D37 is the record of what happens when one of those four is left out: the
735
- * coupon discount reached three of them and not the capture route, so the
736
- * buyer was shown one number, charged another, and the order recorded the
737
- * first — invisible in our own data because the guard was one-sided.
738
- *
739
- * 🛑 The order paths deliberately do NOT call this. They apply
740
- * `resolvePointsTender` to their own group as they create it, drawing down a
741
- * shared remainder — which is the same function applied in the same order, so
742
- * the sum is identical by construction. Making them call this instead would
743
- * mean computing every group's final total before creating any of them, which
744
- * is a restructure of two 200-line functions for an answer they already have.
745
- *
746
- * ## The cap is per group, and the ORDER of the groups therefore matters
747
- *
748
- * A balance smaller than the whole checkout's 50% allowance is consumed by the
749
- * earliest groups. That is arbitrary but it must be *consistently* arbitrary:
750
- * every caller iterates `orderGroups` in the order `splitCartIntoOrderGroups`
751
- * produced, so all four agree. Sorting differently anywhere would make the
752
- * shown and charged figures diverge on exactly the carts where the balance is
753
- * tight.
718
+ * The payable total a points allocation is measured against.
719
+ *
720
+ * 🛑 ONE implementation, because it was two. `POST /api/payment/create-order`
721
+ * and `verifyAndPlaceRazorpayOrderAction`'s amount guard each built this sum
722
+ * by hand, in the same shape, from the same six parts — and they are the
723
+ * *shown* figure and the *charged* figure for the same cart. Two hand-written
724
+ * copies of one money rule is Root Cause #75 exactly, and the failure mode is
725
+ * the worst kind: they only disagree on carts where the arithmetic differs, so
726
+ * nothing errors and the buyer is quoted one number and charged another.
727
+ *
728
+ * The coupon is floored at zero before the fees are added, never after — a
729
+ * discount larger than the goods must not eat the shipping and platform fee.
754
730
  */
731
+ export function pointsBaseForGroup(parts) {
732
+ return {
733
+ total: roundRupees(Math.max(0, parts.goods - parts.couponDiscount) +
734
+ parts.shippingFee +
735
+ parts.addonFee +
736
+ parts.platformFee +
737
+ parts.gstOnFee),
738
+ accepted: parts.acceptsPoints,
739
+ };
740
+ }
755
741
  export function allocatePointsAcrossGroups(groups, balance, optedIn) {
756
742
  const perGroup = groups.map(() => 0);
757
743
  if (!optedIn)
@@ -1,6 +1,6 @@
1
1
  export type { ListingCheckoutRule, CategoryCheckoutRule, CartItemProductPair, OrderItemInput, RefundPolicy, } from "./types";
2
2
  export { CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, getListingRule, getCategoryRule, pickOrderType, getSplitKey, runSyncPreflight, } from "./_registry";
3
- export { assertPaymentMethodAllowed, blockedMethodsForCart, type BlockedMethod, } from "./payment-gate";
3
+ export { assertPaymentMethodAllowed, assertEmiOptInAllowed, blockedMethodsForCart, type BlockedMethod, } from "./payment-gate";
4
4
  export { CART_MAX_ITEMS, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_limits";
5
5
  export { standardRule } from "./standard.rule";
6
6
  export { auctionRule } from "./auction.rule";
@@ -6,7 +6,7 @@ export { CHECKOUT_RULES, CATEGORY_CHECKOUT_RULES, getListingRule, getCategoryRul
6
6
  * apart is what stops someone folding it back into preflight, where it would
7
7
  * silently stop covering bundles and grouped lines.
8
8
  */
9
- export { assertPaymentMethodAllowed, blockedMethodsForCart, } from "./payment-gate.js";
9
+ export { assertPaymentMethodAllowed, assertEmiOptInAllowed, blockedMethodsForCart, } from "./payment-gate.js";
10
10
  export { CART_MAX_ITEMS, CHECKOUT_MAX_ORDERS_PER_TX, PRIZE_DRAW_MAX_REVEALS_PER_ORDER, PRIZE_DRAW_DURATION_DAYS_MIN, PRIZE_DRAW_DURATION_DAYS_MAX, BUNDLE_MAX_QTY_PER_TX, STANDARD_MAX_QTY_PER_LINE, } from "./_limits.js";
11
11
  // Individual rules — exported for tests and direct access.
12
12
  export { standardRule } from "./standard.rule.js";
@@ -24,3 +24,21 @@ export declare function blockedMethodsForCart(items: readonly CartItemDocument[]
24
24
  * decision.
25
25
  */
26
26
  export declare function assertPaymentMethodAllowed(items: readonly CartItemDocument[], productById: ReadonlyMap<string, ProductDocument>, paymentMethod: CheckoutPaymentMethod | undefined): void;
27
+ /**
28
+ * Throw when anything in the cart forbids an instalment plan.
29
+ *
30
+ * 🛑 Reads the SAME `blockedPaymentMethods` declaration, under the key
31
+ * `"emi"`, rather than introducing a parallel `blocksEmi` field. The listing
32
+ * rules already say "this listing type cannot be paid in instalments" — they
33
+ * said it by naming a payment method, because that is what EMI was. Once EMI
34
+ * became an order-level opt-in, that declaration needed a second reader, not
35
+ * a second declaration: a rule stated twice is a rule that drifts (Root Cause
36
+ * #61), and `prizeDrawRule`'s entry would otherwise have gone inert the day
37
+ * the tender disappeared.
38
+ *
39
+ * `blockedPaymentMessage` is reused verbatim for the same reason the gate
40
+ * above prefers it to a generated sentence: it names the REASON (winner
41
+ * assignment waits on confirmed payment), which is what lets a buyer choose
42
+ * differently instead of abandoning.
43
+ */
44
+ export declare function assertEmiOptInAllowed(items: readonly CartItemDocument[], productById: ReadonlyMap<string, ProductDocument>, emiOptIn: boolean): void;
@@ -131,3 +131,28 @@ export function assertPaymentMethodAllowed(items, productById, paymentMethod) {
131
131
  ? `${offender.message} Available instead: ${allowed.join(", ")}.`
132
132
  : `${offender.message} Nothing in this cart's combination can be paid for — remove the item and buy it on its own.`), { code: "PAYMENT_METHOD_BLOCKED" });
133
133
  }
134
+ /**
135
+ * Throw when anything in the cart forbids an instalment plan.
136
+ *
137
+ * 🛑 Reads the SAME `blockedPaymentMethods` declaration, under the key
138
+ * `"emi"`, rather than introducing a parallel `blocksEmi` field. The listing
139
+ * rules already say "this listing type cannot be paid in instalments" — they
140
+ * said it by naming a payment method, because that is what EMI was. Once EMI
141
+ * became an order-level opt-in, that declaration needed a second reader, not
142
+ * a second declaration: a rule stated twice is a rule that drifts (Root Cause
143
+ * #61), and `prizeDrawRule`'s entry would otherwise have gone inert the day
144
+ * the tender disappeared.
145
+ *
146
+ * `blockedPaymentMessage` is reused verbatim for the same reason the gate
147
+ * above prefers it to a generated sentence: it names the REASON (winner
148
+ * assignment waits on confirmed payment), which is what lets a buyer choose
149
+ * differently instead of abandoning.
150
+ */
151
+ export function assertEmiOptInAllowed(items, productById, emiOptIn) {
152
+ if (!emiOptIn)
153
+ return;
154
+ const offender = blockedMethodsForCart(items, productById).get("emi");
155
+ if (!offender)
156
+ return;
157
+ throw Object.assign(new ValidationError(`${offender.message} Pay for this order in full instead, or remove the item and buy it on its own.`), { code: "EMI_NOT_ALLOWED" });
158
+ }
@@ -53,18 +53,6 @@ export interface SplitResolution {
53
53
  /** The invariant holds. A caller must refuse to commit when false. */
54
54
  valid: boolean;
55
55
  }
56
- /**
57
- * Resolve contributions against a landed cost.
58
- *
59
- * Pure. No I/O, no clock — the landed cost is computed by the caller from the
60
- * order group it already has.
61
- *
62
- * 🛑 ROUNDS ONCE, at the end of each member's amount, and then puts the
63
- * ROUNDING REMAINDER on the last member. Rounding each percentage
64
- * independently and summing is how a 3-way 33.33% split charges a rupee less
65
- * than the agreed total and the surplus silently goes negative — the same
66
- * remainder discipline `allocateAcrossMembers` uses for bundle tax.
67
- */
68
56
  export declare function resolveSplit(contributions: readonly SplitContribution[], landedCost: number): SplitResolution;
69
57
  /**
70
58
  * The price to lock on the line, given the goods subtotal and a surplus.
@@ -41,10 +41,30 @@ import { roundRupees } from "../../../utils/number.formatter.js";
41
41
  * than the agreed total and the surplus silently goes negative — the same
42
42
  * remainder discipline `allocateAcrossMembers` uses for bundle tax.
43
43
  */
44
+ /**
45
+ * `percent` of `amount`, where `percent` is 0–100.
46
+ *
47
+ * 🛑 A NAMED HELPER rather than the division written inline, for legibility
48
+ * to a reader AND to `audit-money-units`. That audit flags a scale division
49
+ * beside a money identifier because it is the exact shape of the 2026-05
50
+ * minor-unit bug — a conversion left behind by a migration — and a
51
+ * percentage conversion is visually identical to it. Two such lines are two
52
+ * judgement calls; one helper is a single place to establish that this
53
+ * divisor is about PERCENTAGES and never about currency scale.
54
+ *
55
+ * 🛑 That audit reads COMMENTS as well as code, so this one cannot quote the
56
+ * expression it is about: a worked example of the shape is indistinguishable
57
+ * from the shape. Reading comments is correct of it — a stale "stored in
58
+ * minor units" remark in prose is one of the things it hunts, and Root Cause
59
+ * #87 is the opposite mistake, an audit that stripped comments and so
60
+ * counted a commented-out registration as live.
61
+ */
62
+ // audit-money-units-ok: a percent-to-fraction conversion, not a minor-unit conversion
63
+ const applyPercent = (percent, amount) => (percent / 100) * amount;
44
64
  export function resolveSplit(contributions, landedCost) {
45
65
  const resolved = [];
46
66
  for (const c of contributions) {
47
- const raw = c.splitMode === "percent" ? ((c.percent ?? 0) / 100) * landedCost : (c.amount ?? 0);
67
+ const raw = c.splitMode === "percent" ? applyPercent(c.percent ?? 0, landedCost) : (c.amount ?? 0);
48
68
  resolved.push({ productId: c.productId, amount: roundRupees(raw) });
49
69
  }
50
70
  /*
@@ -59,7 +79,7 @@ export function resolveSplit(contributions, landedCost) {
59
79
  const allPercent = contributions.every((c) => c.splitMode === "percent");
60
80
  if (allPercent && resolved.length > 0) {
61
81
  const pctTotal = contributions.reduce((s, c) => s + (c.percent ?? 0), 0);
62
- const target = roundRupees((pctTotal / 100) * landedCost);
82
+ const target = roundRupees(applyPercent(pctTotal, landedCost));
63
83
  const sum = resolved.reduce((s, r) => s + r.amount, 0);
64
84
  const drift = roundRupees(target - sum);
65
85
  if (drift !== 0) {
@@ -78,7 +78,7 @@ export const DISCLAIMERS = {
78
78
  body: "Entry requires a purchase and is void where prohibited by local law. Residents of states that " +
79
79
  "restrict prize draws or games of chance are not eligible, and an entry from such a state will be " +
80
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.",
81
+ "be exchanged for cash. The draw is run by {{siteName}} and is not affiliated with any government lottery.",
82
82
  tone: "warning",
83
83
  surfaces: ["PrizeDrawDetailPageView", "/prize-draws", "/lottery", "PrizeRevealModal"],
84
84
  },
@@ -90,7 +90,7 @@ export const DISCLAIMERS = {
90
90
  */
91
91
  "not-investment-advice": {
92
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 " +
93
+ "fall in value as easily as rise, may be difficult to sell at any price, and {{siteName}} does not provide " +
94
94
  "investment advice. Nothing here should be relied on as a reason to buy.",
95
95
  tone: "legal",
96
96
  surfaces: ["PriceIndexView", "RealisedPriceStrip", "/franchise", "ProductDetailPageView"],
@@ -108,7 +108,7 @@ export const DISCLAIMERS = {
108
108
  surfaces: ["PriceIndexView", "PriceBandChart"],
109
109
  },
110
110
  "authenticity": {
111
- body: "LetItRip is not an authentication or grading service. We do not inspect, test or re-grade items, " +
111
+ body: "{{siteName}} is not an authentication or grading service. We do not inspect, test or re-grade items, " +
112
112
  "and a grade shown on a listing is the one the seller states a third-party grader assigned. " +
113
113
  "Guidance on spotting a counterfeit is general information, not a verification of any particular item.",
114
114
  tone: "legal",
@@ -125,7 +125,7 @@ export const DISCLAIMERS = {
125
125
  */
126
126
  "protection-cover": {
127
127
  heading: "What protection covers",
128
- body: "Buyer protection is cover provided by LetItRip. It is **not insurance** and there is no insurer. " +
128
+ body: "Buyer protection is cover provided by {{siteName}}. It is **not insurance** and there is no insurer. " +
129
129
  "Shipment protection covers loss or damage in transit and must be claimed within " +
130
130
  "{{shipmentClaimHours}} hours of delivery. Sale protection covers an item up to " +
131
131
  "{{saleProtectionMaxValue}} and must be claimed within {{saleClaimHours}} hours of delivery. " +
@@ -142,7 +142,7 @@ export const DISCLAIMERS = {
142
142
  "accounting-output": {
143
143
  body: "These are management figures produced from this platform's own records. They are not audited " +
144
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 " +
145
+ "accountant to check and use — nothing here is filed with any tax authority, and {{siteName}} has no " +
146
146
  "connection to the GST portal.",
147
147
  tone: "warning",
148
148
  surfaces: ["AdminInvoicingView", "AdminAnalyticsView", "ProfitLossView", "TrialBalanceView"],
@@ -172,7 +172,7 @@ export const DISCLAIMERS = {
172
172
  },
173
173
  "emi": {
174
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 " +
175
+ "and {{siteName}} rather than a bank, and a missed instalment may suspend access to the order and is " +
176
176
  "recoverable as a debt.",
177
177
  tone: "legal",
178
178
  surfaces: ["CheckoutRouteClient", "/fees", "EmiScheduleTable"],
@@ -185,7 +185,7 @@ export const DISCLAIMERS = {
185
185
  },
186
186
  "user-content": {
187
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 " +
188
+ "{{siteName}}'s. We remove content that breaks our rules when it is reported, but we do not verify every " +
189
189
  "claim made in it.",
190
190
  tone: "legal",
191
191
  surfaces: ["ReviewsList", "ReviewDetailShell", "PublicProfileView", "BlogPostView"],
@@ -44,7 +44,7 @@
44
44
  * sentence with a word missing, which nobody reports, while `{{wrongKey}}` on
45
45
  * a live page gets reported in minutes.
46
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";
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" | "siteName";
48
48
  export type PublishedValues = Record<PublishedValueKey, string>;
49
49
  /**
50
50
  * The settings this registry reads, where a value genuinely is configurable.
@@ -56,6 +56,11 @@ export type PublishedValues = Record<PublishedValueKey, string>;
56
56
  * TypeScript's structural typing strips nothing at runtime).
57
57
  */
58
58
  export interface PublishedValueSettings {
59
+ /**
60
+ * The operator's display name, for the disclaimers that name who is making
61
+ * the statement. Falls back to `SITE_CONFIG.brand.name`.
62
+ */
63
+ siteName?: string;
59
64
  platformFeeMax?: number;
60
65
  platformFeePercent?: number;
61
66
  shipmentProtectionFeePercent?: number;
@@ -40,6 +40,7 @@
40
40
  * somebody has to remember — and it is why there is no literal for the offer
41
41
  * deadline or the claim windows even though both appear in published copy.
42
42
  */
43
+ import { SITE_CONFIG } from "../../../core/site-config.js";
43
44
  import { OBLIGATION_POLICIES } from "../../../features/obligations/constants/policies.js";
44
45
  import { POINTS_EARN_PERCENT, POINTS_EXPIRY_MONTHS, POINTS_MAX_REDEMPTION_PERCENT, } from "../../../features/points/schemas/firestore.js";
45
46
  import { PAYMENT_WINDOW_MINUTES } from "../../../features/orders/constants/payment-window.js";
@@ -171,6 +172,13 @@ export function resolvePublishedValues(settings) {
171
172
  */
172
173
  refundBusinessDaysMin: "5",
173
174
  refundBusinessDaysMax: "7",
175
+ /*
176
+ * `SITE_CONFIG.brand.name` is appkit's own neutral default ("App") until a
177
+ * consumer overrides it, which is exactly the right failure mode here: a
178
+ * fork that forgets to configure its brand publishes a visibly generic
179
+ * disclaimer rather than silently attributing its legal statements to us.
180
+ */
181
+ siteName: settings?.siteName ?? SITE_CONFIG.brand.name,
174
182
  };
175
183
  }
176
184
  /**