@mohasinac/appkit 4.62.0 → 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 (391) 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 +129 -9
  8. package/dist/_internal/server/features/checkout/actions.js +850 -79
  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/invoicing/allocate.d.ts +63 -0
  22. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  23. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  24. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  25. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  26. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  27. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  28. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  29. package/dist/_internal/server/features/offers/destinations.d.ts +40 -0
  30. package/dist/_internal/server/features/offers/destinations.js +65 -0
  31. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  32. package/dist/_internal/server/features/orders/actions.js +81 -8
  33. package/dist/_internal/server/features/orders/adapters.js +54 -0
  34. package/dist/_internal/server/features/orders/amend-delivery.d.ts +56 -0
  35. package/dist/_internal/server/features/orders/amend-delivery.js +124 -0
  36. package/dist/_internal/server/features/orders/balance.d.ts +89 -0
  37. package/dist/_internal/server/features/orders/balance.js +256 -0
  38. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  39. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  40. package/dist/_internal/server/features/points/award.js +46 -4
  41. package/dist/_internal/server/features/pricing/actions.d.ts +58 -0
  42. package/dist/_internal/server/features/pricing/actions.js +144 -0
  43. package/dist/_internal/server/features/refunds/actions.js +26 -0
  44. package/dist/_internal/server/features/shipping/book-split.d.ts +86 -0
  45. package/dist/_internal/server/features/shipping/book-split.js +130 -0
  46. package/dist/_internal/server/features/shipping/book.d.ts +61 -0
  47. package/dist/_internal/server/features/shipping/book.js +176 -0
  48. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  49. package/dist/_internal/server/features/shipping/courier-block.js +264 -0
  50. package/dist/_internal/server/features/site-settings/adapters.d.ts +2 -2
  51. package/dist/_internal/server/features/site-settings/adapters.js +29 -0
  52. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  53. package/dist/_internal/server/features/stores/adapters.js +65 -0
  54. package/dist/_internal/server/functions/scheduled.d.ts +2 -1
  55. package/dist/_internal/server/functions/scheduled.js +95 -2
  56. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +11 -2
  57. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  58. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  59. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  60. package/dist/_internal/server/jobs/core/index.js +2 -1
  61. package/dist/_internal/server/jobs/core/jobRunners.js +45 -0
  62. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  63. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  64. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  65. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +65 -0
  66. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  67. package/dist/_internal/server/jobs/core/reservationExpiry.d.ts +17 -0
  68. package/dist/_internal/server/jobs/core/reservationExpiry.js +40 -0
  69. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  70. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  71. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +25 -2
  72. package/dist/_internal/server/jobs/handlers/index.d.ts +1 -0
  73. package/dist/_internal/server/jobs/handlers/index.js +1 -0
  74. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  75. package/dist/_internal/server/jobs/handlers/reservationExpiry.d.ts +2 -0
  76. package/dist/_internal/server/jobs/handlers/reservationExpiry.js +2 -0
  77. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  78. package/dist/_internal/shared/actions/action-registry.d.ts +1 -1
  79. package/dist/_internal/shared/actions/action-registry.js +296 -0
  80. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  81. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  82. package/dist/_internal/shared/checkout/order-math.d.ts +174 -0
  83. package/dist/_internal/shared/checkout/order-math.js +159 -0
  84. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  85. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  86. package/dist/_internal/shared/checkout/rules/index.d.ts +1 -1
  87. package/dist/_internal/shared/checkout/rules/index.js +1 -1
  88. package/dist/_internal/shared/checkout/rules/payment-gate.d.ts +18 -0
  89. package/dist/_internal/shared/checkout/rules/payment-gate.js +25 -0
  90. package/dist/_internal/shared/checkout/split.d.ts +75 -0
  91. package/dist/_internal/shared/checkout/split.js +120 -0
  92. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  93. package/dist/_internal/shared/content/disclaimers.js +217 -0
  94. package/dist/_internal/shared/content/published-values.d.ts +93 -0
  95. package/dist/_internal/shared/content/published-values.js +206 -0
  96. package/dist/_internal/shared/features/admin/integrations.d.ts +139 -0
  97. package/dist/_internal/shared/features/admin/integrations.js +263 -0
  98. package/dist/_internal/shared/features/dashboard/hub-sections.d.ts +72 -0
  99. package/dist/_internal/shared/features/dashboard/hub-sections.js +75 -0
  100. package/dist/_internal/shared/features/emi/choice.d.ts +98 -0
  101. package/dist/_internal/shared/features/emi/choice.js +69 -0
  102. package/dist/_internal/shared/features/emi/group.d.ts +84 -0
  103. package/dist/_internal/shared/features/emi/group.js +126 -0
  104. package/dist/_internal/shared/features/ledger/posting.d.ts +107 -0
  105. package/dist/_internal/shared/features/ledger/posting.js +165 -0
  106. package/dist/_internal/shared/features/ledger/reports.d.ts +76 -0
  107. package/dist/_internal/shared/features/ledger/reports.js +127 -0
  108. package/dist/_internal/shared/features/ledger/rules.d.ts +170 -0
  109. package/dist/_internal/shared/features/ledger/rules.js +221 -0
  110. package/dist/_internal/shared/features/offers/coordination.d.ts +122 -0
  111. package/dist/_internal/shared/features/offers/coordination.js +80 -0
  112. package/dist/_internal/shared/features/orders/delivery-snapshot.d.ts +114 -0
  113. package/dist/_internal/shared/features/orders/delivery-snapshot.js +116 -0
  114. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  115. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  116. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  117. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  118. package/dist/_internal/shared/features/payouts/pay-period.d.ts +91 -0
  119. package/dist/_internal/shared/features/payouts/pay-period.js +104 -0
  120. package/dist/_internal/shared/features/points/accepts.d.ts +29 -0
  121. package/dist/_internal/shared/features/points/accepts.js +49 -0
  122. package/dist/_internal/shared/features/pricing/rate-registry.d.ts +150 -0
  123. package/dist/_internal/shared/features/pricing/rate-registry.js +246 -0
  124. package/dist/_internal/shared/features/shipping/parcel.d.ts +123 -0
  125. package/dist/_internal/shared/features/shipping/parcel.js +139 -0
  126. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  127. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  128. package/dist/_internal/shared/features/site-settings/fees.d.ts +16 -8
  129. package/dist/_internal/shared/features/site-settings/fees.js +12 -8
  130. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  131. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  132. package/dist/_internal/shared/fees/calculator.js +32 -2
  133. package/dist/_internal/shared/fees/explain.d.ts +81 -0
  134. package/dist/_internal/shared/fees/explain.js +101 -0
  135. package/dist/_internal/shared/history/index.d.ts +1 -1
  136. package/dist/_internal/shared/history/index.js +1 -1
  137. package/dist/_internal/shared/history/types.d.ts +7 -1
  138. package/dist/_internal/shared/history/types.js +25 -0
  139. package/dist/client.d.ts +21 -0
  140. package/dist/client.js +43 -0
  141. package/dist/constants/api-endpoints.d.ts +114 -0
  142. package/dist/constants/api-endpoints.js +55 -0
  143. package/dist/constants/field-names.d.ts +42 -0
  144. package/dist/constants/field-names.js +52 -0
  145. package/dist/contracts/index.d.ts +1 -1
  146. package/dist/contracts/shipping.d.ts +45 -0
  147. package/dist/contracts/shipping.js +15 -0
  148. package/dist/features/about/components/AboutView.js +1 -1
  149. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  150. package/dist/features/account/components/UserOrdersView.js +47 -35
  151. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  152. package/dist/features/admin/components/AdminIntegrationsView.d.ts +55 -0
  153. package/dist/features/admin/components/AdminIntegrationsView.js +74 -0
  154. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  155. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  156. package/dist/features/admin/components/AdminOrderEditorView.d.ts +33 -1
  157. package/dist/features/admin/components/AdminOrderEditorView.js +81 -3
  158. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  159. package/dist/features/admin/components/AdminOrdersView.js +86 -5
  160. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  161. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  162. package/dist/features/admin/constants/filter-tabs.js +6 -0
  163. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  164. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  165. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  166. package/dist/features/admin/schemas/firestore.d.ts +62 -0
  167. package/dist/features/admin/schemas/firestore.js +5 -0
  168. package/dist/features/ads/schemas/booking-write.d.ts +120 -0
  169. package/dist/features/ads/schemas/booking-write.js +75 -0
  170. package/dist/features/ads/schemas/creative-form.d.ts +18 -0
  171. package/dist/features/ads/schemas/creative-form.js +51 -0
  172. package/dist/features/ads/schemas/firestore.d.ts +140 -0
  173. package/dist/features/ads/schemas/firestore.js +109 -0
  174. package/dist/features/auctions/repository/bid.repository.js +3 -1
  175. package/dist/features/audit-log/schemas/firestore.d.ts +140 -0
  176. package/dist/features/audit-log/schemas/firestore.js +89 -0
  177. package/dist/features/auth/actions/profile-actions.d.ts +69 -1
  178. package/dist/features/auth/actions/profile-actions.js +62 -1
  179. package/dist/features/auth/permissions/constants.js +13 -0
  180. package/dist/features/auth/schemas/firestore.d.ts +16 -2
  181. package/dist/features/auth/schemas/firestore.js +2 -1
  182. package/dist/features/blog/actions/blog-actions.d.ts +6 -0
  183. package/dist/features/blog/actions/blog-actions.js +17 -2
  184. package/dist/features/blog/api/route.js +4 -1
  185. package/dist/features/blog/columns/index.js +5 -0
  186. package/dist/features/blog/components/BlogIndexPageView.js +2 -1
  187. package/dist/features/blog/repository/blog.repository.d.ts +4 -0
  188. package/dist/features/blog/repository/blog.repository.js +5 -0
  189. package/dist/features/blog/schemas/blog-form.d.ts +3 -0
  190. package/dist/features/blog/schemas/blog-form.js +1 -0
  191. package/dist/features/blog/schemas/firestore.d.ts +38 -1
  192. package/dist/features/blog/schemas/firestore.js +23 -0
  193. package/dist/features/blog/schemas/index.d.ts +6 -0
  194. package/dist/features/blog/schemas/index.js +2 -0
  195. package/dist/features/blog/types/index.d.ts +2 -0
  196. package/dist/features/cart/components/CartPriceBreakdown.d.ts +25 -1
  197. package/dist/features/cart/components/CartPriceBreakdown.js +29 -6
  198. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  199. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  200. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  201. package/dist/features/cart/components/index.d.ts +2 -0
  202. package/dist/features/cart/components/index.js +1 -0
  203. package/dist/features/cart/repository/cart.repository.d.ts +33 -0
  204. package/dist/features/cart/repository/cart.repository.js +47 -0
  205. package/dist/features/cart/schemas/firestore.d.ts +116 -0
  206. package/dist/features/contact/email.d.ts +27 -10
  207. package/dist/features/contact/email.js +20 -3
  208. package/dist/features/dashboard/components/DashboardHubView.d.ts +83 -0
  209. package/dist/features/dashboard/components/DashboardHubView.js +56 -0
  210. package/dist/features/dashboard/components/DashboardSectionsClient.d.ts +23 -0
  211. package/dist/features/dashboard/components/DashboardSectionsClient.js +66 -0
  212. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.d.ts +55 -0
  213. package/dist/features/dispute-reasons/components/AdminDisputeReasonsView.js +175 -0
  214. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +62 -0
  215. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +108 -0
  216. package/dist/features/dispute-reasons/schemas/firestore.d.ts +62 -0
  217. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  218. package/dist/features/events/components/EventSaleBanner.js +1 -1
  219. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  220. package/dist/features/faq/components/FAQAccordion.js +29 -4
  221. package/dist/features/faq/components/FAQPageContent.js +2 -2
  222. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  223. package/dist/features/homepage/schemas/index.d.ts +8 -8
  224. package/dist/features/inventory/schemas/stock-reservation.d.ts +114 -0
  225. package/dist/features/inventory/schemas/stock-reservation.js +103 -0
  226. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  227. package/dist/features/invoicing/schemas/firestore.js +45 -0
  228. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  229. package/dist/features/invoicing/utils/financial-year.js +53 -0
  230. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  231. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  232. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  233. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  234. package/dist/features/jobs/schemas/firestore.js +37 -0
  235. package/dist/features/layout/AppLayoutShell.d.ts +9 -1
  236. package/dist/features/layout/AppLayoutShell.js +12 -4
  237. package/dist/features/layout/BottomActions.js +2 -1
  238. package/dist/features/layout/BottomNavbar.d.ts +10 -2
  239. package/dist/features/layout/SideActionDock.js +52 -2
  240. package/dist/features/layout/TitleBarLayout.js +13 -4
  241. package/dist/features/layout/TitleBarLayout.style.css +59 -0
  242. package/dist/features/layout/useCtaReveal.d.ts +16 -1
  243. package/dist/features/layout/useCtaReveal.js +17 -26
  244. package/dist/features/ledger/repository/journal.repository.d.ts +110 -0
  245. package/dist/features/ledger/repository/journal.repository.js +312 -0
  246. package/dist/features/ledger/schemas/expenses.d.ts +87 -0
  247. package/dist/features/ledger/schemas/expenses.js +27 -0
  248. package/dist/features/ledger/schemas/firestore.d.ts +183 -0
  249. package/dist/features/ledger/schemas/firestore.js +69 -0
  250. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  251. package/dist/features/media/MediaImage.js +22 -1
  252. package/dist/features/media/upload/ImageUpload.js +1 -1
  253. package/dist/features/obligations/constants/policies.js +24 -1
  254. package/dist/features/orders/components/BalancePaymentPanel.d.ts +12 -0
  255. package/dist/features/orders/components/BalancePaymentPanel.js +94 -0
  256. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  257. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  258. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  259. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  260. package/dist/features/orders/components/RaiseDisputeForm.js +119 -0
  261. package/dist/features/orders/components/RefundRequestView.js +19 -1
  262. package/dist/features/orders/components/index.d.ts +4 -0
  263. package/dist/features/orders/components/index.js +2 -0
  264. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  265. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  266. package/dist/features/orders/repository/orders.repository.d.ts +85 -1
  267. package/dist/features/orders/repository/orders.repository.js +205 -6
  268. package/dist/features/orders/schemas/balance.d.ts +40 -0
  269. package/dist/features/orders/schemas/balance.js +47 -0
  270. package/dist/features/orders/schemas/firestore.d.ts +398 -1
  271. package/dist/features/orders/schemas/firestore.js +43 -0
  272. package/dist/features/orders/schemas/index.d.ts +56 -8
  273. package/dist/features/orders/schemas/index.js +12 -0
  274. package/dist/features/orders/types/index.d.ts +47 -0
  275. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  276. package/dist/features/orders/utils/courier-block.js +23 -0
  277. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  278. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  279. package/dist/features/payments/repository/payout.repository.js +3 -2
  280. package/dist/features/payments/schemas/firestore.d.ts +92 -1
  281. package/dist/features/payments/schemas/firestore.js +23 -0
  282. package/dist/features/pricing/components/AdminPricingView.d.ts +13 -0
  283. package/dist/features/pricing/components/AdminPricingView.js +157 -0
  284. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  285. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  286. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  287. package/dist/features/products/schemas/firestore.d.ts +26 -1
  288. package/dist/features/products/schemas/firestore.js +2 -0
  289. package/dist/features/products/schemas/index.d.ts +2 -2
  290. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  291. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  292. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  293. package/dist/features/promotions/components/CouponCard.js +13 -0
  294. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  295. package/dist/features/promotions/constants/coupon-help.js +9 -1
  296. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  297. package/dist/features/promotions/schemas/firestore.js +4 -0
  298. package/dist/features/promotions/types/index.d.ts +19 -1
  299. package/dist/features/reviews/repository/reviews.repository.d.ts +14 -0
  300. package/dist/features/reviews/repository/reviews.repository.js +16 -0
  301. package/dist/features/scams/components/ScamProfileView.js +2 -2
  302. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  303. package/dist/features/scams/repository/scammer.repository.js +2 -1
  304. package/dist/features/scams/schemas/firestore.d.ts +2 -1
  305. package/dist/features/scams/schemas/firestore.js +2 -1
  306. package/dist/features/search/action-index/tab-entries.js +1 -1
  307. package/dist/features/seller/actions/offer-actions.js +56 -0
  308. package/dist/features/seller/actions/seller-actions.d.ts +1 -1
  309. package/dist/features/seller/actions/seller-actions.js +37 -3
  310. package/dist/features/seller/components/DispatchForm.d.ts +11 -0
  311. package/dist/features/seller/components/DispatchForm.js +190 -0
  312. package/dist/features/seller/components/SellerOrdersView.js +221 -27
  313. package/dist/features/seller/components/index.d.ts +1 -0
  314. package/dist/features/seller/components/index.js +1 -0
  315. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  316. package/dist/features/seller/repository/offer.repository.js +4 -3
  317. package/dist/features/seller/schemas/firestore.d.ts +23 -2
  318. package/dist/features/seller/schemas/firestore.js +3 -2
  319. package/dist/features/seller/schemas/index.d.ts +4 -4
  320. package/dist/features/seller/schemas/order-forms.d.ts +61 -4
  321. package/dist/features/seller/schemas/order-forms.js +41 -8
  322. package/dist/features/shipments/schemas/firestore.d.ts +25 -0
  323. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  324. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +82 -0
  325. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +145 -0
  326. package/dist/features/shipping-boxes/schemas/firestore.d.ts +80 -0
  327. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  328. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  329. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  330. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  331. package/dist/features/stores/repository/store.repository.d.ts +18 -0
  332. package/dist/features/stores/repository/store.repository.js +27 -1
  333. package/dist/features/stores/schemas/firestore.d.ts +112 -0
  334. package/dist/features/stores/schemas/firestore.js +20 -0
  335. package/dist/features/stores/schemas/index.d.ts +6 -6
  336. package/dist/features/stores/types/index.d.ts +19 -0
  337. package/dist/features/support/repository/support.repository.js +72 -6
  338. package/dist/features/support/schemas/firestore.d.ts +17 -0
  339. package/dist/features/support/schemas/index.d.ts +8 -8
  340. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  341. package/dist/index.d.ts +11 -1
  342. package/dist/index.js +33 -1
  343. package/dist/next/routing/route-map.d.ts +112 -0
  344. package/dist/next/routing/route-map.js +63 -0
  345. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  346. package/dist/providers/shipping-shiprocket/index.js +84 -1
  347. package/dist/react/contexts/SessionContext.d.ts +17 -1
  348. package/dist/schemas/registry.d.ts +69 -34
  349. package/dist/schemas/sieve.d.ts +2 -2
  350. package/dist/security/pii-schemas.d.ts +1 -1
  351. package/dist/security/pii-schemas.js +45 -0
  352. package/dist/server-entry.d.ts +2 -2
  353. package/dist/server-entry.js +8 -1
  354. package/dist/server.d.ts +36 -1
  355. package/dist/server.js +183 -1
  356. package/dist/styles.css +412 -80
  357. package/dist/tailwind-utilities.css +1 -1
  358. package/dist/tokens/themes/default-dark.js +1 -0
  359. package/dist/tokens/themes/default-light.js +1 -0
  360. package/dist/tokens/themes/required.d.ts +1 -1
  361. package/dist/tokens/themes/required.js +1 -0
  362. package/dist/tokens/tokens.css +15 -0
  363. package/dist/ui/components/Button.d.ts +10 -1
  364. package/dist/ui/components/Button.js +25 -3
  365. package/dist/ui/components/Chart.d.ts +63 -0
  366. package/dist/ui/components/Chart.js +169 -0
  367. package/dist/ui/components/Chart.style.css +214 -0
  368. package/dist/ui/components/Disclaimer.d.ts +19 -0
  369. package/dist/ui/components/Disclaimer.js +83 -0
  370. package/dist/ui/components/OverflowRow.js +54 -2
  371. package/dist/ui/components/OverflowRow.style.css +38 -0
  372. package/dist/ui/components/RichTextEditor.js +99 -8
  373. package/dist/ui/components/chart-spec.d.ts +68 -0
  374. package/dist/ui/components/chart-spec.js +140 -0
  375. package/dist/ui/components/index.style.css +1 -0
  376. package/dist/ui/components/surface-tokens.d.ts +1 -1
  377. package/dist/ui/components/surface-tokens.js +9 -0
  378. package/dist/ui/components/tabs-overflow.js +64 -11
  379. package/dist/ui/components/toolbar-collapse.d.ts +13 -0
  380. package/dist/ui/components/toolbar-collapse.js +10 -1
  381. package/dist/ui/index.d.ts +8 -0
  382. package/dist/ui/index.js +9 -0
  383. package/dist/ui/rich-text/RichText.d.ts +10 -1
  384. package/dist/ui/rich-text/RichText.js +33 -2
  385. package/dist/ui/rich-text/RichText.style.css +83 -79
  386. package/dist/ui/rich-text/RichTextRenderer.d.ts +30 -1
  387. package/dist/ui/rich-text/RichTextRenderer.js +34 -2
  388. package/dist/ui/rich-text/chart-blocks.d.ts +62 -0
  389. package/dist/ui/rich-text/chart-blocks.js +87 -0
  390. package/package.json +1 -1
  391. package/scripts/seed-cli.mjs +1834 -1822
@@ -0,0 +1,83 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ /*
4
+ * Defining modules, not `../index` — Root Cause #18.
5
+ *
6
+ * The barrel re-exports THIS component, so importing three primitives back
7
+ * through it closed a two-module cycle (`audit-import-cycles` component 1).
8
+ * The deep paths below have one direction only.
9
+ */
10
+ import { Div } from "./Div.js";
11
+ import { Stack } from "./Layout.js";
12
+ import { Text } from "./Typography.js";
13
+ import { DISCLAIMERS, } from "../../_internal/shared/content/disclaimers.js";
14
+ import { resolvePublishedCopy, resolvePublishedValues, } from "../../_internal/shared/content/published-values.js";
15
+ /**
16
+ * The one way to render a disclaimer (Phase 12).
17
+ *
18
+ * ## 🛑 `id` only — there is no `text` prop, deliberately
19
+ *
20
+ * A `text` escape hatch would make this a styled box, and then the legally
21
+ * material disclaimers would be written at their call sites again, drift, and
22
+ * end up weakest exactly where they matter most. That is the failure this
23
+ * registry exists to close. If a surface needs a disclaimer that does not
24
+ * exist, the fix is a new registry entry — which is also what gets it
25
+ * reviewed.
26
+ *
27
+ * ## Tokens resolve here
28
+ *
29
+ * The protection entry quotes `{{saleProtectionMaxValue}}` and the claim
30
+ * windows, so the published limits come from the constants that ENFORCE them
31
+ * (Phase 9's mechanism). A change to `SALE_PROTECTION_MAX_VALUE` updates the
32
+ * terms rather than contradicting them.
33
+ *
34
+ * Code-derived values are the default; a caller with settings in hand passes
35
+ * better ones for the money figures.
36
+ *
37
+ * ## Safe in a Server Component
38
+ *
39
+ * No hooks, no state, and the registry is a plain object — so this renders on
40
+ * either side of the boundary. Do not add state here without checking every
41
+ * RSC call site (Root Cause #76).
42
+ */
43
+ const CODE_DERIVED_VALUES = resolvePublishedValues();
44
+ /**
45
+ * Renders `**bold**` and nothing else.
46
+ *
47
+ * 🛑 A deliberately tiny subset, not a markdown renderer. The emphasis is
48
+ * load-bearing in exactly the places it is used — "**not insurance**" in the
49
+ * protection entry, "**under review**" versus "**verified**" in the scam
50
+ * registry — and those are the phrases a reader skims past without it.
51
+ *
52
+ * 🛑 And it returns ELEMENTS, never HTML. The bodies are our own constants so
53
+ * injection is not the risk; the risk is that a `dangerouslySetInnerHTML`
54
+ * here would invite someone to pass admin-editable text through it later,
55
+ * which is how a disclaimer box becomes a stored-XSS surface.
56
+ */
57
+ function renderEmphasis(text) {
58
+ return text.split(/(\*\*[^*]+\*\*)/g).map((part, i) => part.startsWith("**") && part.endsWith("**") && part.length > 4 ? (_jsx(Text, { as: "span", weight: "semibold", children: part.slice(2, -2) }, i)) : (_jsx(React.Fragment, { children: part }, i)));
59
+ }
60
+ export function Disclaimer({ id, publishedValues = CODE_DERIVED_VALUES, inline = false, className = "", }) {
61
+ const entry = DISCLAIMERS[id];
62
+ /*
63
+ * A `Record<DisclaimerId, …>` means this cannot happen through the type, but
64
+ * an `as DisclaimerId` cast at a call site could — and rendering nothing is
65
+ * the wrong failure here. A missing disclaimer is invisible, which is the
66
+ * whole problem; so it renders nothing only when the registry genuinely has
67
+ * no entry, and the audit is what catches that case before it ships.
68
+ */
69
+ if (!entry)
70
+ return null;
71
+ const body = resolvePublishedCopy(entry.body, publishedValues);
72
+ const content = (_jsxs(Stack, { gap: "xs", children: [entry.heading && (_jsx(Text, { size: "xs", weight: "semibold", transform: "uppercase", color: "muted", children: entry.heading })), _jsx(Text, { size: "xs", color: "muted", className: "leading-relaxed", children: renderEmphasis(body) })] }));
73
+ if (inline)
74
+ return _jsx(Div, { className: className, children: content });
75
+ /*
76
+ * 🛑 `surface` + the matching ink, never a status background with literal
77
+ * white text. `-surface` is a near-white tint in light themes and a dark
78
+ * tint in dark ones, so a fixed `text-white` is invisible in exactly one
79
+ * theme — Root Cause #67, which shipped 26 invisible surfaces that way.
80
+ * `Text color="muted"` above pairs correctly with both.
81
+ */
82
+ return (_jsx(Div, { surface: entry.tone === "warning" ? "warning-surface" : "muted", rounded: "lg", padding: "sm", className: className, children: content }));
83
+ }
@@ -26,14 +26,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
26
26
  * @tag sideEffects:dom-measurement
27
27
  */
28
28
  import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
29
+ import { createPortal } from "react-dom";
29
30
  import NextLink from "next/link.js";
30
31
  import { Check, ChevronDown, Search } from "lucide-react";
31
32
  import { Avatar } from "./Avatar.js";
32
33
  import { Icon } from "./Icon.js";
33
34
  import { Input } from "./Input.js";
34
35
  import { useRowCollapse } from "./row-collapse.js";
36
+ import { MOBILE_QUERY } from "./toolbar-collapse.js";
37
+ import { useBottomChromeSlot } from "./bottom-chrome-slot.js";
38
+ import { useMediaQuery } from "../../react/hooks/useMediaQuery.js";
35
39
  const UI_OVERFLOW_ROW = {
36
40
  shell: "appkit-overflow-row",
41
+ /** The in-flow measuring box, once the control itself has been portalled away. */
42
+ shellDocked: "appkit-overflow-row--docked",
43
+ /** The portalled control, as its own row inside `BottomChrome`. */
44
+ dock: "appkit-overflow-row__dock",
37
45
  row: "appkit-overflow-row__row",
38
46
  pill: "appkit-overflow-row__pill",
39
47
  pillActive: "appkit-overflow-row__pill--active",
@@ -77,11 +85,22 @@ export function OverflowRow({ items, ariaLabel, activeId, variant = "tabs", plac
77
85
  const shellRef = useRef(null);
78
86
  const rowRef = useRef(null);
79
87
  const triggerRef = useRef(null);
88
+ /*
89
+ * The portalled control, when docked into the bottom bar. Needed only by the
90
+ * outside-click handler: a portal moves the node out of `shellRef`'s subtree
91
+ * in the DOM, so `shell.contains(target)` is FALSE for a click on the
92
+ * trigger itself — the dropdown would close on the very click that opened
93
+ * it, which reads as the control being dead.
94
+ */
95
+ const dockRef = useRef(null);
80
96
  const searchRef = useRef(null);
81
97
  const listboxId = useId();
82
98
  const [open, setOpen] = useState(false);
83
99
  const [query, setQuery] = useState("");
84
100
  const [activeIndex, setActiveIndex] = useState(0);
101
+ // Both before any conditional return — the collapsed branch is one.
102
+ const bottomChromeSlot = useBottomChromeSlot();
103
+ const isMobile = useMediaQuery(MOBILE_QUERY);
85
104
  const signature = useMemo(() => signatureOf(items), [items]);
86
105
  const collapsed = useRowCollapse(shellRef, rowRef, signature, {
87
106
  disabled: alwaysCollapsed,
@@ -92,7 +111,11 @@ export function OverflowRow({ items, ariaLabel, activeId, variant = "tabs", plac
92
111
  if (!open)
93
112
  return;
94
113
  const onDown = (event) => {
95
- if (!shellRef.current?.contains(event.target))
114
+ const target = event.target;
115
+ const inShell = shellRef.current?.contains(target) ?? false;
116
+ // See `dockRef` — when docked, the control lives outside the shell.
117
+ const inDock = dockRef.current?.contains(target) ?? false;
118
+ if (!inShell && !inDock)
96
119
  setOpen(false);
97
120
  };
98
121
  document.addEventListener("mousedown", onDown);
@@ -166,7 +189,7 @@ export function OverflowRow({ items, ariaLabel, activeId, variant = "tabs", plac
166
189
  const hasAnyVisual = items.some((item) => item.imageUrl || item.iconName || item.emoji || item.dotTone);
167
190
  // ── Collapsed: one searchable dropdown ──────────────────────────────────
168
191
  if (collapsed) {
169
- return (_jsxs("div", { ref: shellRef, className: [UI_OVERFLOW_ROW.shell, className].filter(Boolean).join(" "), "data-collapsed": "true", children: [_jsxs("button", { ref: triggerRef, type: "button", className: UI_OVERFLOW_ROW.trigger, "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-label": ariaLabel, onClick: () => setOpen((prev) => !prev), onKeyDown: (event) => {
192
+ const dropdown = (_jsxs(_Fragment, { children: [_jsxs("button", { ref: triggerRef, type: "button", className: UI_OVERFLOW_ROW.trigger, "aria-haspopup": "listbox", "aria-expanded": open, "aria-controls": open ? listboxId : undefined, "aria-label": ariaLabel, onClick: () => setOpen((prev) => !prev), onKeyDown: (event) => {
170
193
  if (!open && (event.key === "ArrowDown" || event.key === "Enter")) {
171
194
  event.preventDefault();
172
195
  setOpen(true);
@@ -186,6 +209,35 @@ export function OverflowRow({ items, ariaLabel, activeId, variant = "tabs", plac
186
209
  const body = (_jsxs(_Fragment, { children: [hasAnyVisual ? visualOf(item, "option") : null, _jsx("span", { className: UI_OVERFLOW_ROW.optionLabel, children: item.label }), typeof item.count === "number" && (_jsx("span", { className: UI_OVERFLOW_ROW.optionCount, children: item.count > 99 ? "99+" : item.count })), isSelected && (_jsx(Check, { className: `w-4 h-4 ${UI_OVERFLOW_ROW.check}`, "aria-hidden": "true" }))] }));
187
210
  return item.href ? (_jsx(Link, { ...linkProps, id: `${listboxId}-${item.id}`, role: "option", "aria-selected": isSelected, href: item.href, className: classes, onMouseEnter: () => setActiveIndex(index), children: body }, item.id)) : (_jsx("button", { id: `${listboxId}-${item.id}`, role: "option", type: "button", "aria-selected": isSelected, disabled: item.disabled, className: classes, onClick: () => commit(item), onMouseEnter: () => setActiveIndex(index), children: body }, item.id));
188
211
  })] })] }))] }));
212
+ /*
213
+ * ── Mobile: the collapsed row docks into the bottom bar ────────────────
214
+ *
215
+ * A row that has collapsed to a dropdown is no longer a row — it is one
216
+ * control, and on a phone the place for one control is the thumb zone,
217
+ * not a band competing with the page heading. It joins `BottomChrome` as
218
+ * its OWN row, so it stacks above a CTA bar instead of overlapping it,
219
+ * and the tier's single `ResizeObserver` adds its height to
220
+ * `--bottom-chrome-height` — which is what keeps page content from being
221
+ * hidden behind it on scroll, with no consumer doing anything.
222
+ *
223
+ * 🛑 `shellRef` STAYS IN PLACE, always. It is what `useRowCollapse`
224
+ * measures the available width against; portal it into the bottom bar and
225
+ * it would measure the VIEWPORT instead, read as "there is room now",
226
+ * expand, return to the narrow container, collapse, and oscillate forever.
227
+ * So the shell remains an empty in-flow box at the original position and
228
+ * only the CONTROL is portalled. The expanded branch below is unchanged
229
+ * and still renders the real row there.
230
+ */
231
+ const docked = isMobile && !!bottomChromeSlot;
232
+ return (_jsx("div", { ref: shellRef, className: [
233
+ UI_OVERFLOW_ROW.shell,
234
+ docked ? UI_OVERFLOW_ROW.shellDocked : "",
235
+ className,
236
+ ]
237
+ .filter(Boolean)
238
+ .join(" "), "data-collapsed": "true", "data-docked": docked ? "true" : "false", children: docked
239
+ ? createPortal(_jsx("div", { ref: dockRef, className: UI_OVERFLOW_ROW.dock, children: dropdown }), bottomChromeSlot)
240
+ : dropdown }));
189
241
  }
190
242
  // ── Expanded: the pill row ──────────────────────────────────────────────
191
243
  return (_jsx("div", { ref: shellRef, className: [UI_OVERFLOW_ROW.shell, className].filter(Boolean).join(" "), "data-collapsed": "false", children: _jsx("div", { ref: rowRef, className: UI_OVERFLOW_ROW.row, role: variant === "tabs" ? "tablist" : undefined, "aria-label": ariaLabel, children: items.map((item) => {
@@ -233,3 +233,41 @@
233
233
  font-size: var(--appkit-text-sm);
234
234
  color: var(--appkit-color-text-muted);
235
235
  }
236
+
237
+ /* ── Docked: the collapsed control lives in the bottom bar on mobile ────────
238
+ *
239
+ * The shell stays in flow as a zero-height measuring box — `useRowCollapse`
240
+ * compares the row's cached natural width against THIS element, so it must
241
+ * keep the original container's width. Portalling it instead would make it
242
+ * measure the viewport, read as "there is room", expand, and oscillate.
243
+ */
244
+ .appkit-overflow-row--docked {
245
+ height: 0;
246
+ /* Not `display:none`: a hidden box has no width, and width is the entire
247
+ input to the collapse decision. */
248
+ overflow: hidden;
249
+ }
250
+
251
+ .appkit-overflow-row__dock {
252
+ position: relative;
253
+ display: flex;
254
+ width: 100%;
255
+ pointer-events: auto;
256
+ padding: var(--appkit-space-2) var(--appkit-space-4);
257
+ background: var(--appkit-color-surface-elevated);
258
+ border-top: 1px solid var(--appkit-color-border);
259
+ }
260
+
261
+ .appkit-overflow-row__dock > .appkit-overflow-row__trigger {
262
+ width: 100%;
263
+ }
264
+
265
+ /* 🛑 Opens UPWARD when docked. The default popover is `top: 100%`, which from
266
+ a bar at the bottom edge renders off-screen — the control would look dead.
267
+ `BottomChrome` deliberately sets no `overflow`, so the panel may escape the
268
+ tier; that is the documented contract its own header relies on. */
269
+ .appkit-overflow-row__dock .appkit-overflow-row__popover {
270
+ top: auto;
271
+ bottom: calc(100% + var(--appkit-space-1));
272
+ width: 100%;
273
+ }
@@ -1,6 +1,44 @@
1
1
  "use client";
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useMemo, useRef } from "react";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { parseChartAttr, CHART_KINDS } from "./chart-spec.js";
5
+ import { chartFigureHtml } from "../rich-text/chart-blocks.js";
6
+ /**
7
+ * What the Chart button drops into the editor to be edited.
8
+ *
9
+ * A filled, working example rather than an empty skeleton: an author editing
10
+ * real numbers into a chart that already renders can see what each field does,
11
+ * where one facing `{"kind":"","categories":[],"series":[]}` has to guess the
12
+ * shape before anything appears at all.
13
+ */
14
+ const STARTER_CHART = JSON.stringify({
15
+ kind: "groupedBar",
16
+ title: "Untitled chart",
17
+ categories: ["First", "Second", "Third"],
18
+ series: [
19
+ { label: "Series A", values: [3, 5, 4] },
20
+ { label: "Series B", values: [4, 2, 6] },
21
+ ],
22
+ unit: "",
23
+ caption: "",
24
+ }, null, 2);
25
+ /** The chart JSON editor shown under the toolbar, with live validation. */
26
+ function ChartDraftPanel({ draft, onDraftChange, onInsert, onCancel, }) {
27
+ const result = useMemo(() => parseChartAttr(draft), [draft]);
28
+ /*
29
+ * 🛑 Generated, not a literal. Two RichTextEditors on one page — a category
30
+ * form with a description and an SEO blurb, say — would otherwise emit the
31
+ * SAME hardcoded id twice, so the second panel's `htmlFor` and
32
+ * `aria-describedby` resolve to the FIRST one's textarea and error. A screen
33
+ * reader then reads the wrong field's error and clicking the label focuses
34
+ * the wrong box, which is exactly the class of bug the `ariaLabelledBy` note
35
+ * at the top of this file exists about.
36
+ */
37
+ const panelId = useId();
38
+ const fieldId = `${panelId}-draft`;
39
+ const errorId = `${panelId}-error`;
40
+ return (_jsxs("div", { className: "appkit-rich-text-editor__chart border-b p-2 border-[var(--appkit-color-border)]", children: [_jsxs("label", { className: "mb-1 block text-xs font-medium text-[var(--appkit-color-text-muted)]", htmlFor: fieldId, children: ["Chart data \u2014 kinds: ", CHART_KINDS.join(", "), ". Each series needs one value per category."] }), _jsx("textarea", { id: fieldId, value: draft, onChange: (e) => onDraftChange(e.target.value), rows: 12, spellCheck: false, "aria-invalid": !result.ok, "aria-describedby": result.ok ? undefined : errorId, className: "w-full rounded border p-2 font-mono text-xs border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface-input)] text-[var(--appkit-color-text)]" }), result.ok ? null : (_jsx("p", { id: errorId, role: "alert", className: "mt-1 text-xs text-[var(--appkit-color-error)]", children: result.reason })), _jsxs("div", { className: "mt-2 flex gap-2", children: [_jsx("button", { type: "button", onClick: onInsert, disabled: !result.ok, className: "rounded px-2 py-1 text-xs font-medium transition disabled:cursor-not-allowed disabled:opacity-50 bg-[var(--appkit-color-primary)] text-[var(--appkit-color-text-on-primary)]", children: "Insert chart" }), _jsx("button", { type: "button", onClick: onCancel, className: "rounded px-2 py-1 text-xs font-medium transition hover:bg-surface-hover text-[var(--appkit-color-text-muted)]", children: "Cancel" })] })] }));
41
+ }
4
42
  // Matches exactly what this editor's own toolbar (bold/italic/underline/
5
43
  // lists/link/clear-formatting) and typing can legitimately produce.
6
44
  // Anything else — <script>, <img onerror=...>, <iframe>, <svg>, <style>,
@@ -10,6 +48,9 @@ import { useEffect, useMemo, useRef } from "react";
10
48
  const ALLOWED_TAGS = new Set([
11
49
  "B", "STRONG", "I", "EM", "U", "S", "STRIKE",
12
50
  "UL", "OL", "LI", "A", "BR", "DIV", "SPAN", "P",
51
+ // FIGURE is here only because the toolbar's "Chart" button produces it.
52
+ // It carries no text content — see sanitizeAttribute for the data-chart rule.
53
+ "FIGURE",
13
54
  ]);
14
55
  function isSafeHref(href) {
15
56
  const trimmed = href.trim();
@@ -18,14 +59,33 @@ function isSafeHref(href) {
18
59
  // relative), or "#" — reject everything else including javascript:.
19
60
  return /^(https?:\/\/|mailto:|\/(?!\/)|#)/i.test(trimmed);
20
61
  }
62
+ /**
63
+ * 🛑 This is the SAVE-time sanitiser, and it is a different, stricter one from
64
+ * `RichText.tsx`'s render-time `sanitiseHtml`. Both had to learn about
65
+ * `data-chart` — teaching only the renderer, as a first pass here did, means a
66
+ * chart inserted in the editor is silently deleted the moment it is saved,
67
+ * with the toolbar button appearing to work perfectly.
68
+ *
69
+ * The rule is stricter than the renderer's on purpose: the value is not merely
70
+ * allowed through, it must PARSE as a valid ChartSpec. Invalid JSON or a spec
71
+ * that fails validation loses the attribute, and the empty `<figure>` that
72
+ * remains is then dropped by `sanitizeElement`. So malformed chart data cannot
73
+ * be persisted at all, which is the right place to stop it — once stored, it
74
+ * is every future reader's problem.
75
+ */
21
76
  function sanitizeAttribute(el, attr) {
22
- const isSafeAnchorHref = el.tagName === "A" && attr.name.toLowerCase() === "href";
23
- if (!isSafeAnchorHref) {
24
- el.removeAttribute(attr.name);
77
+ const name = attr.name.toLowerCase();
78
+ if (el.tagName === "A" && name === "href") {
79
+ if (!isSafeHref(attr.value))
80
+ el.setAttribute("href", "#");
81
+ return;
82
+ }
83
+ if (el.tagName === "FIGURE" && name === "data-chart") {
84
+ if (!parseChartAttr(attr.value).ok)
85
+ el.removeAttribute(attr.name);
25
86
  return;
26
87
  }
27
- if (!isSafeHref(attr.value))
28
- el.setAttribute("href", "#");
88
+ el.removeAttribute(attr.name);
29
89
  }
30
90
  function sanitizeElement(root, el) {
31
91
  if (!ALLOWED_TAGS.has(el.tagName)) {
@@ -33,6 +93,14 @@ function sanitizeElement(root, el) {
33
93
  return;
34
94
  }
35
95
  Array.from(el.attributes).forEach((attr) => sanitizeAttribute(el, attr));
96
+ // A <figure> is only legal here as a chart carrier. If the attribute did not
97
+ // survive validation above, what is left is an empty element with no text and
98
+ // no meaning — drop it rather than persist a blank block the author cannot
99
+ // see, select, or delete.
100
+ if (el.tagName === "FIGURE" && !el.hasAttribute("data-chart")) {
101
+ root.removeChild(el);
102
+ return;
103
+ }
36
104
  sanitizeChildren(el);
37
105
  }
38
106
  function sanitizeChildren(root) {
@@ -55,6 +123,7 @@ function sanitizeRichTextHtml(html) {
55
123
  }
56
124
  export function RichTextEditor({ value, onChange, disabled = false, className = "", minHeightClassName = "min-h-[180px]", placeholder = "Write formatted content...", ariaLabelledBy, ariaLabel, }) {
57
125
  const editorRef = useRef(null);
126
+ const [chartDraft, setChartDraft] = useState(null);
58
127
  const emitChange = () => {
59
128
  if (!editorRef.current)
60
129
  return;
@@ -102,12 +171,34 @@ export function RichTextEditor({ value, onChange, disabled = false, className =
102
171
  exec("createLink", href);
103
172
  },
104
173
  },
174
+ {
175
+ label: "Chart",
176
+ title: "Insert chart",
177
+ run: () => setChartDraft(STARTER_CHART),
178
+ },
105
179
  {
106
180
  label: "Clear",
107
181
  title: "Clear formatting",
108
182
  run: () => exec("removeFormat"),
109
183
  },
110
184
  ], []);
185
+ /*
186
+ * `insertHTML` rather than building a node and splicing it in: the editor is
187
+ * a contenteditable and execCommand is already how every other toolbar action
188
+ * writes, so this keeps the caret and the undo stack behaving the same way.
189
+ * The inserted markup then goes straight through `emitChange` →
190
+ * `sanitizeRichTextHtml`, so the chart is validated on the way out exactly as
191
+ * a pasted one would be — the button gets no privileged path.
192
+ */
193
+ const insertChart = () => {
194
+ if (chartDraft === null)
195
+ return;
196
+ const parsed = parseChartAttr(chartDraft);
197
+ if (!parsed.ok)
198
+ return;
199
+ exec("insertHTML", chartFigureHtml(parsed.spec));
200
+ setChartDraft(null);
201
+ };
111
202
  useEffect(() => {
112
203
  const editor = editorRef.current;
113
204
  if (!editor)
@@ -120,7 +211,7 @@ export function RichTextEditor({ value, onChange, disabled = false, className =
120
211
  editor.innerHTML = safeValue;
121
212
  }
122
213
  }, [value]);
123
- return (_jsxs("div", { className: `appkit-rich-text-editor rounded-lg border border-zinc-200 bg-[var(--appkit-color-surface)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${className}`, "data-section": "richtexteditor-div-582", children: [_jsx("div", { className: "appkit-rich-text-editor__toolbar flex flex-wrap gap-1 border-b p-2 border-[var(--appkit-color-border)]", "data-section": "richtexteditor-div-583", children: toolbarActions.map((action) => (_jsx("button", { type: "button", title: action.title, onClick: action.run, disabled: disabled, className: "appkit-rich-text-editor__btn rounded px-2 py-1 text-xs font-medium transition hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50 text-[var(--appkit-color-text-muted)]", children: action.label }, action.title))) }), _jsx("div", { ref: editorRef,
214
+ return (_jsxs("div", { className: `appkit-rich-text-editor rounded-lg border border-zinc-200 bg-[var(--appkit-color-surface)] border-[var(--appkit-color-border)] bg-[var(--appkit-color-surface)] ${className}`, "data-section": "richtexteditor-div-582", children: [_jsx("div", { className: "appkit-rich-text-editor__toolbar flex flex-wrap gap-1 border-b p-2 border-[var(--appkit-color-border)]", "data-section": "richtexteditor-div-583", children: toolbarActions.map((action) => (_jsx("button", { type: "button", title: action.title, onClick: action.run, disabled: disabled, className: "appkit-rich-text-editor__btn rounded px-2 py-1 text-xs font-medium transition hover:bg-surface-hover disabled:cursor-not-allowed disabled:opacity-50 text-[var(--appkit-color-text-muted)]", children: action.label }, action.title))) }), chartDraft !== null ? (_jsx(ChartDraftPanel, { draft: chartDraft, onDraftChange: setChartDraft, onInsert: insertChart, onCancel: () => setChartDraft(null) })) : null, _jsx("div", { ref: editorRef,
124
215
  /*
125
216
  * 🛑 `role` and `aria-label` USED TO BE DECLARED HERE TOO, hardcoded
126
217
  * to "Rich text editor" — and when the labelling props were added
@@ -0,0 +1,68 @@
1
+ export declare const CHART_KINDS: readonly ["bar", "groupedBar", "line", "area"];
2
+ export type ChartKind = (typeof CHART_KINDS)[number];
3
+ /**
4
+ * 🛑 Eight, and a ninth is never a generated hue.
5
+ *
6
+ * The categorical palette is a fixed, validated ORDER of eight steps, checked
7
+ * for colourblind separation between adjacent pairs. A 9th colour generated at
8
+ * runtime cannot be validated, so a 9-series chart is REFUSED and the author is
9
+ * told to fold the tail into "Other" or split into small multiples.
10
+ */
11
+ export declare const CHART_MAX_SERIES = 8;
12
+ export interface ChartSeries {
13
+ label: string;
14
+ values: number[];
15
+ }
16
+ export interface ChartSpec {
17
+ kind: ChartKind;
18
+ /** X-axis categories. `series[].values` must be exactly this long. */
19
+ categories: string[];
20
+ series: ChartSeries[];
21
+ /** Appended to every value in tooltips and the table view. */
22
+ unit?: string;
23
+ /** Rendered under the plot. */
24
+ caption?: string;
25
+ /** Accessible name. Falls back to the caption. */
26
+ title?: string;
27
+ }
28
+ export type ChartSpecResult = {
29
+ ok: true;
30
+ spec: ChartSpec;
31
+ } | {
32
+ ok: false;
33
+ reason: string;
34
+ };
35
+ /**
36
+ * Validate an untrusted object into a `ChartSpec`.
37
+ *
38
+ * 🛑 Returns a REASON rather than throwing or silently returning null. This
39
+ * parses author-supplied JSON out of a `data-chart` attribute, and an author
40
+ * who mistyped needs to be told which rule they broke — a chart that quietly
41
+ * renders nothing is the failure mode this codebase keeps paying for.
42
+ *
43
+ * 🛑 There is deliberately no second y-axis in the shape. A dual-axis chart
44
+ * invites comparing two incommensurable scales on one plot; two measures mean
45
+ * two charts, small multiples, or indexing to a common base.
46
+ */
47
+ export declare function parseChartSpec(input: unknown): ChartSpecResult;
48
+ /**
49
+ * Parse the JSON string carried by a `data-chart` attribute.
50
+ *
51
+ * 🛑 The normalised error is deliberately NOT surfaced to the caller. The
52
+ * thrown value is a `SyntaxError` from `JSON.parse` carrying nothing an author
53
+ * can act on beyond "it is not JSON" — which the returned reason already says,
54
+ * in their language, at the place they will read it. Returning the engine's
55
+ * message instead is how server text reaches a user (Root Cause #86). Malformed
56
+ * authored content is an expected input here, not an incident to report.
57
+ *
58
+ * `normalizeError` is still called, because that is what `audit-catch-normalize`
59
+ * actually accepts: its header also lists "return immediately" as sufficient,
60
+ * but the implementation only tests for `normalizeError(var)` or `throw var`
61
+ * (audit-catch-normalize.mjs:210-213), so a bare early return is reported.
62
+ */
63
+ export declare function parseChartAttr(json: string): ChartSpecResult;
64
+ /**
65
+ * How many series the palette can colour. Exposed so a caller can decide to
66
+ * fold a tail into "Other" BEFORE validation rejects the whole chart.
67
+ */
68
+ export declare function exceedsSeriesLimit(count: number): boolean;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The chart DATA contract — pure, and deliberately separate from `<Chart>`.
3
+ *
4
+ * ## 🛑 Why this is not in Chart.tsx
5
+ *
6
+ * `Chart.tsx` is `"use client"` and statically reaches recharts. Anything that
7
+ * imports it inherits both. But the validator needs to be callable from places
8
+ * that are neither:
9
+ *
10
+ * - `chart-blocks.ts`, which must stay SSR-safe because `RichTextRenderer`
11
+ * is a Server Component;
12
+ * - a route handler or a seed script validating authored content before it
13
+ * is stored, where pulling in recharts would be absurd.
14
+ *
15
+ * Keeping the types and the parser here means a server-side caller gets the
16
+ * contract without the renderer — the same split as Root Cause #24's lesson
17
+ * about a file that mixed Node-only crypto with client-safe display helpers.
18
+ *
19
+ * No React, no DOM, no recharts.
20
+ */
21
+ import { normalizeError } from "../../errors/normalize.js";
22
+ export const CHART_KINDS = ["bar", "groupedBar", "line", "area"];
23
+ /**
24
+ * 🛑 Eight, and a ninth is never a generated hue.
25
+ *
26
+ * The categorical palette is a fixed, validated ORDER of eight steps, checked
27
+ * for colourblind separation between adjacent pairs. A 9th colour generated at
28
+ * runtime cannot be validated, so a 9-series chart is REFUSED and the author is
29
+ * told to fold the tail into "Other" or split into small multiples.
30
+ */
31
+ export const CHART_MAX_SERIES = 8;
32
+ /**
33
+ * Validate an untrusted object into a `ChartSpec`.
34
+ *
35
+ * 🛑 Returns a REASON rather than throwing or silently returning null. This
36
+ * parses author-supplied JSON out of a `data-chart` attribute, and an author
37
+ * who mistyped needs to be told which rule they broke — a chart that quietly
38
+ * renders nothing is the failure mode this codebase keeps paying for.
39
+ *
40
+ * 🛑 There is deliberately no second y-axis in the shape. A dual-axis chart
41
+ * invites comparing two incommensurable scales on one plot; two measures mean
42
+ * two charts, small multiples, or indexing to a common base.
43
+ */
44
+ export function parseChartSpec(input) {
45
+ if (typeof input !== "object" || input === null) {
46
+ return { ok: false, reason: "Chart data must be an object." };
47
+ }
48
+ const o = input;
49
+ const kind = o.kind;
50
+ if (typeof kind !== "string" || !CHART_KINDS.includes(kind)) {
51
+ return {
52
+ ok: false,
53
+ reason: `Chart "kind" must be one of ${CHART_KINDS.join(", ")}.`,
54
+ };
55
+ }
56
+ if (!Array.isArray(o.categories) ||
57
+ o.categories.length === 0 ||
58
+ !o.categories.every((c) => typeof c === "string" && c.length > 0)) {
59
+ return { ok: false, reason: 'Chart "categories" must be a non-empty array of labels.' };
60
+ }
61
+ const categories = o.categories;
62
+ if (!Array.isArray(o.series) || o.series.length === 0) {
63
+ return { ok: false, reason: 'Chart "series" must be a non-empty array.' };
64
+ }
65
+ if (o.series.length > CHART_MAX_SERIES) {
66
+ return {
67
+ ok: false,
68
+ reason: `A chart carries at most ${CHART_MAX_SERIES} series (got ${o.series.length}). ` +
69
+ `Fold the smallest into an "Other" series, or split into separate charts — ` +
70
+ `a 9th colour cannot be checked for colourblind separation.`,
71
+ };
72
+ }
73
+ const series = [];
74
+ for (const [i, raw] of o.series.entries()) {
75
+ if (typeof raw !== "object" || raw === null) {
76
+ return { ok: false, reason: `Series ${i + 1} must be an object.` };
77
+ }
78
+ const s = raw;
79
+ if (typeof s.label !== "string" || s.label.length === 0) {
80
+ return { ok: false, reason: `Series ${i + 1} needs a non-empty "label".` };
81
+ }
82
+ if (!Array.isArray(s.values) ||
83
+ !s.values.every((v) => typeof v === "number" && Number.isFinite(v))) {
84
+ return {
85
+ ok: false,
86
+ reason: `Series "${s.label}" — "values" must be an array of finite numbers.`,
87
+ };
88
+ }
89
+ if (s.values.length !== categories.length) {
90
+ return {
91
+ ok: false,
92
+ reason: `Series "${s.label}" has ${s.values.length} values but there are ` +
93
+ `${categories.length} categories. They must match one-to-one.`,
94
+ };
95
+ }
96
+ series.push({ label: s.label, values: s.values });
97
+ }
98
+ return {
99
+ ok: true,
100
+ spec: {
101
+ kind: kind,
102
+ categories,
103
+ series,
104
+ ...(typeof o.unit === "string" ? { unit: o.unit } : {}),
105
+ ...(typeof o.caption === "string" ? { caption: o.caption } : {}),
106
+ ...(typeof o.title === "string" ? { title: o.title } : {}),
107
+ },
108
+ };
109
+ }
110
+ /**
111
+ * Parse the JSON string carried by a `data-chart` attribute.
112
+ *
113
+ * 🛑 The normalised error is deliberately NOT surfaced to the caller. The
114
+ * thrown value is a `SyntaxError` from `JSON.parse` carrying nothing an author
115
+ * can act on beyond "it is not JSON" — which the returned reason already says,
116
+ * in their language, at the place they will read it. Returning the engine's
117
+ * message instead is how server text reaches a user (Root Cause #86). Malformed
118
+ * authored content is an expected input here, not an incident to report.
119
+ *
120
+ * `normalizeError` is still called, because that is what `audit-catch-normalize`
121
+ * actually accepts: its header also lists "return immediately" as sufficient,
122
+ * but the implementation only tests for `normalizeError(var)` or `throw var`
123
+ * (audit-catch-normalize.mjs:210-213), so a bare early return is reported.
124
+ */
125
+ export function parseChartAttr(json) {
126
+ try {
127
+ return parseChartSpec(JSON.parse(json));
128
+ }
129
+ catch (err) {
130
+ void normalizeError(err);
131
+ return { ok: false, reason: "Chart data is not valid JSON." };
132
+ }
133
+ }
134
+ /**
135
+ * How many series the palette can colour. Exposed so a caller can decide to
136
+ * fold a tail into "Other" BEFORE validation rejects the whole chart.
137
+ */
138
+ export function exceedsSeriesLimit(count) {
139
+ return count > CHART_MAX_SERIES;
140
+ }
@@ -51,6 +51,7 @@
51
51
  @import "./Toggle.style.css";
52
52
  @import "./QuantityStepper.style.css";
53
53
  @import "./Checkbox.style.css";
54
+ @import "./Chart.style.css";
54
55
  @import "./EmptyState.style.css";
55
56
  @import "./SkipToMain.style.css";
56
57
  @import "./Toast.style.css";
@@ -226,7 +226,7 @@ export declare const PADDING_PRESETS: {
226
226
  readonly "content-banner": "py-[var(--appkit-space-10)] md:py-[var(--appkit-space-12)] lg:py-[var(--appkit-space-16)]";
227
227
  };
228
228
  export type PaddingPresetKey = PaddingKey;
229
- export type XPaddingKey = "none" | "x-xs" | "x-sm" | "x-md" | "x-5" | "x-lg" | "x-xl" | "x-sm-md" | "x-sm-lg-md" | "x-md-lg" | "x-md-xl" | "x-md-2xl" | "x-page" | "l-xl";
229
+ export type XPaddingKey = "none" | "x-xs" | "x-sm" | "x-md" | "x-5" | "x-lg" | "x-xl" | "x-sm-md" | "x-sm-lg-md" | "x-md-lg" | "x-md-xl" | "x-md-2xl" | "x-page" | "l-md" | "l-xl";
230
230
  type YPaddingKey = "none" | "y-2xs" | "y-2xs-tall" | "y-xs" | "y-xs-tall" | "y-sm" | "y-sm-tall" | "y-md" | "y-md-lg" | "y-lg" | "y-xl" | "y-2xl" | "y-3xl" | "y-4xl" | "y-5xl" | "y-6xl" | "y-2-5xl" | "t-2xs" | "b-2xs" | "t-xs" | "b-xs" | "t-sm" | "b-sm" | "t-md" | "b-md" | "b-md-lg" | "b-lg" | "t-md-b-lg";
231
231
  export declare const X_ONLY_MAP: Record<XPaddingKey, string>;
232
232
  export type OverflowKey = "auto" | "hidden" | "scroll" | "visible" | "x-auto" | "x-hidden" | "y-auto" | "y-hidden";
@@ -232,6 +232,15 @@ export const X_ONLY_MAP = {
232
232
  // selection-checkbox gutter).
233
233
  /** Asymmetric: `pl-8` — reserves a left gutter (e.g. for an overlaid selection checkbox). */
234
234
  "l-xl": "pl-[var(--appkit-space-8)]",
235
+ /**
236
+ * Asymmetric: `pl-4` — indents a nested block under its own heading.
237
+ *
238
+ * The grouped order-item rows (admin and seller) are the motivating pair:
239
+ * a group header followed by its member lines, where the indent is the only
240
+ * thing expressing the nesting. `x-md` would pad the right edge too, which
241
+ * is not what an indent means.
242
+ */
243
+ "l-md": "pl-[var(--appkit-space-4)]",
235
244
  };
236
245
  const Y_ONLY_MAP = {
237
246
  none: "",