@mohasinac/appkit 4.61.0 → 4.62.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/dist/.build-stamp.json +3 -3
  2. package/dist/_internal/client/features/lottery/LotteryAdminEditView.js +19 -3
  3. package/dist/_internal/client/features/maintenance/views/AnalysisRunnerView.js +1 -1
  4. package/dist/_internal/client/features/maintenance/views/CloudLogsListView.js +1 -1
  5. package/dist/_internal/server/features/checkout/actions.d.ts +80 -0
  6. package/dist/_internal/server/features/checkout/actions.js +542 -26
  7. package/dist/_internal/server/features/invoicing/allocate.d.ts +63 -0
  8. package/dist/_internal/server/features/invoicing/allocate.js +162 -0
  9. package/dist/_internal/server/features/invoicing/credit-note.d.ts +57 -0
  10. package/dist/_internal/server/features/invoicing/credit-note.js +126 -0
  11. package/dist/_internal/server/features/invoicing/gstr1.d.ts +74 -0
  12. package/dist/_internal/server/features/invoicing/gstr1.js +251 -0
  13. package/dist/_internal/server/features/invoicing/issue.d.ts +96 -0
  14. package/dist/_internal/server/features/invoicing/issue.js +202 -0
  15. package/dist/_internal/server/features/orders/actions.d.ts +8 -1
  16. package/dist/_internal/server/features/orders/actions.js +81 -8
  17. package/dist/_internal/server/features/orders/adapters.js +33 -0
  18. package/dist/_internal/server/features/orders/cancellation-refund.d.ts +15 -9
  19. package/dist/_internal/server/features/orders/cancellation-refund.js +15 -9
  20. package/dist/_internal/server/features/points/award.js +46 -4
  21. package/dist/_internal/server/features/refunds/actions.js +26 -0
  22. package/dist/_internal/server/features/shipping/courier-block.d.ts +69 -0
  23. package/dist/_internal/server/features/shipping/courier-block.js +239 -0
  24. package/dist/_internal/server/features/site-settings/adapters.d.ts +1 -1
  25. package/dist/_internal/server/features/site-settings/adapters.js +15 -0
  26. package/dist/_internal/server/features/stores/adapters.d.ts +2 -2
  27. package/dist/_internal/server/features/stores/adapters.js +51 -0
  28. package/dist/_internal/server/functions/scheduled.js +38 -1
  29. package/dist/_internal/server/jobs/core/autoPayoutEligibility.js +9 -2
  30. package/dist/_internal/server/jobs/core/dailyStatusDigest.d.ts +4 -0
  31. package/dist/_internal/server/jobs/core/dailyStatusDigest.js +19 -2
  32. package/dist/_internal/server/jobs/core/index.d.ts +2 -1
  33. package/dist/_internal/server/jobs/core/index.js +2 -1
  34. package/dist/_internal/server/jobs/core/onJobCreated.d.ts +30 -7
  35. package/dist/_internal/server/jobs/core/onJobCreated.js +78 -45
  36. package/dist/_internal/server/jobs/core/onOrderStatusChange.d.ts +7 -0
  37. package/dist/_internal/server/jobs/core/onOrderStatusChange.js +62 -0
  38. package/dist/_internal/server/jobs/core/paymentReconcile.js +87 -97
  39. package/dist/_internal/server/jobs/core/paymentWindowTimeout.js +33 -0
  40. package/dist/_internal/server/jobs/core/runJobOnce.d.ts +48 -0
  41. package/dist/_internal/server/jobs/core/runJobOnce.js +195 -0
  42. package/dist/_internal/server/jobs/core/weeklyPayoutEligibility.js +10 -2
  43. package/dist/_internal/server/jobs/handlers/onOrderStatusChange.js +17 -0
  44. package/dist/_internal/server/notifications/channel-health.d.ts +26 -3
  45. package/dist/_internal/shared/actions/action-registry.js +177 -9
  46. package/dist/_internal/shared/checkout/coupon-discount.d.ts +43 -1
  47. package/dist/_internal/shared/checkout/coupon-discount.js +26 -1
  48. package/dist/_internal/shared/checkout/order-math.d.ts +145 -0
  49. package/dist/_internal/shared/checkout/order-math.js +173 -0
  50. package/dist/_internal/shared/checkout/refundable-base.d.ts +25 -0
  51. package/dist/_internal/shared/checkout/refundable-base.js +13 -1
  52. package/dist/_internal/shared/checkout/split.d.ts +87 -0
  53. package/dist/_internal/shared/checkout/split.js +100 -0
  54. package/dist/_internal/shared/content/disclaimers.d.ts +88 -0
  55. package/dist/_internal/shared/content/disclaimers.js +217 -0
  56. package/dist/_internal/shared/content/published-values.d.ts +88 -0
  57. package/dist/_internal/shared/content/published-values.js +198 -0
  58. package/dist/_internal/shared/features/admin/integrations.d.ts +109 -0
  59. package/dist/_internal/shared/features/admin/integrations.js +253 -0
  60. package/dist/_internal/shared/features/orders/dispute-reasons.d.ts +113 -0
  61. package/dist/_internal/shared/features/orders/dispute-reasons.js +181 -0
  62. package/dist/_internal/shared/features/orders/refund-copy.d.ts +1 -1
  63. package/dist/_internal/shared/features/orders/refund-copy.js +18 -1
  64. package/dist/_internal/shared/features/points/accepts.d.ts +36 -0
  65. package/dist/_internal/shared/features/points/accepts.js +43 -0
  66. package/dist/_internal/shared/features/shipping/shared-purchase.d.ts +91 -0
  67. package/dist/_internal/shared/features/shipping/shared-purchase.js +108 -0
  68. package/dist/_internal/shared/features/site-settings/types.d.ts +11 -0
  69. package/dist/_internal/shared/fees/calculator.d.ts +33 -1
  70. package/dist/_internal/shared/fees/calculator.js +32 -2
  71. package/dist/_internal/shared/media/limits.d.ts +10 -0
  72. package/dist/_internal/shared/media/limits.js +10 -0
  73. package/dist/_internal/shared/types/action-result.js +15 -1
  74. package/dist/client/api/surface-error.js +16 -2
  75. package/dist/client/api/useApiMutation.js +17 -1
  76. package/dist/client.d.ts +12 -1
  77. package/dist/client.js +31 -1
  78. package/dist/constants/api-endpoints.d.ts +45 -0
  79. package/dist/constants/api-endpoints.js +15 -0
  80. package/dist/constants/field-names.d.ts +18 -0
  81. package/dist/constants/field-names.js +28 -0
  82. package/dist/contracts/index.d.ts +1 -1
  83. package/dist/contracts/shipping.d.ts +45 -0
  84. package/dist/contracts/shipping.js +15 -0
  85. package/dist/core/server-action.js +19 -3
  86. package/dist/errors/error-mapping.js +26 -1
  87. package/dist/features/about/components/AboutView.js +1 -1
  88. package/dist/features/account/components/BuyerAuctionsGuideView.js +4 -1
  89. package/dist/features/account/components/UserOffersPanel.js +5 -3
  90. package/dist/features/account/components/UserOrdersView.js +19 -33
  91. package/dist/features/admin/components/AdminActionIndexView.js +100 -5
  92. package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
  93. package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
  94. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  95. package/dist/features/admin/components/AdminCarouselView.js +96 -4
  96. package/dist/features/admin/components/AdminCouponEditorView.js +17 -0
  97. package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
  98. package/dist/features/admin/components/AdminInvoicingView.d.ts +2 -0
  99. package/dist/features/admin/components/AdminInvoicingView.js +65 -0
  100. package/dist/features/admin/components/AdminMediaView.js +36 -3
  101. package/dist/features/admin/components/AdminNavigationView.js +4 -1
  102. package/dist/features/admin/components/AdminOffersView.js +2 -1
  103. package/dist/features/admin/components/AdminOrderEditorView.d.ts +18 -1
  104. package/dist/features/admin/components/AdminOrderEditorView.js +11 -2
  105. package/dist/features/admin/components/AdminOrdersGuideView.js +23 -2
  106. package/dist/features/admin/components/AdminOrdersView.js +67 -5
  107. package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
  108. package/dist/features/admin/components/AdminProductEditorView.js +2 -2
  109. package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
  110. package/dist/features/admin/components/AdminSectionsView.js +486 -224
  111. package/dist/features/admin/components/OwnerQuickActions.js +24 -2
  112. package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
  113. package/dist/features/admin/constants/filter-tabs.d.ts +6 -0
  114. package/dist/features/admin/constants/filter-tabs.js +6 -0
  115. package/dist/features/admin/constants/site-settings-tabs.d.ts +1 -1
  116. package/dist/features/admin/hooks/useAdminListingData.js +16 -0
  117. package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
  118. package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
  119. package/dist/features/admin/repository/notification.repository.d.ts +15 -0
  120. package/dist/features/admin/repository/notification.repository.js +32 -0
  121. package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
  122. package/dist/features/admin/schemas/action-index-form.js +115 -0
  123. package/dist/features/admin/schemas/admin-user-form.d.ts +2 -2
  124. package/dist/features/admin/schemas/firestore.d.ts +30 -0
  125. package/dist/features/admin/schemas/firestore.js +5 -0
  126. package/dist/features/audit-log/schemas/firestore.d.ts +30 -0
  127. package/dist/features/audit-log/schemas/firestore.js +15 -0
  128. package/dist/features/auth/components/RegisterForm.js +2 -1
  129. package/dist/features/auth/permissions/constants.js +13 -0
  130. package/dist/features/cart/components/CartPriceBreakdown.d.ts +13 -0
  131. package/dist/features/cart/components/CartPriceBreakdown.js +6 -2
  132. package/dist/features/cart/components/PointsTenderPicker.d.ts +61 -0
  133. package/dist/features/cart/components/PointsTenderPicker.js +15 -0
  134. package/dist/features/cart/components/StoreAddonsPicker.js +2 -1
  135. package/dist/features/cart/components/index.d.ts +2 -0
  136. package/dist/features/cart/components/index.js +1 -0
  137. package/dist/features/cart/repository/cart.repository.d.ts +19 -0
  138. package/dist/features/cart/repository/cart.repository.js +27 -0
  139. package/dist/features/cart/schemas/firestore.d.ts +92 -0
  140. package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
  141. package/dist/features/contact/email.d.ts +27 -10
  142. package/dist/features/contact/email.js +20 -3
  143. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.d.ts +38 -0
  144. package/dist/features/dispute-reasons/repository/dispute-reasons.repository.js +80 -0
  145. package/dist/features/dispute-reasons/schemas/firestore.d.ts +64 -0
  146. package/dist/features/dispute-reasons/schemas/firestore.js +40 -0
  147. package/dist/features/events/components/AdminEventEditorView.js +8 -6
  148. package/dist/features/faq/components/FAQAccordion.d.ts +13 -1
  149. package/dist/features/faq/components/FAQAccordion.js +29 -4
  150. package/dist/features/invoicing/schemas/firestore.d.ts +112 -0
  151. package/dist/features/invoicing/schemas/firestore.js +45 -0
  152. package/dist/features/invoicing/utils/financial-year.d.ts +43 -0
  153. package/dist/features/invoicing/utils/financial-year.js +53 -0
  154. package/dist/features/jobs/actions/enqueue-job.js +18 -1
  155. package/dist/features/jobs/repository/jobs.repository.d.ts +62 -1
  156. package/dist/features/jobs/repository/jobs.repository.js +84 -1
  157. package/dist/features/jobs/schemas/firestore.d.ts +80 -0
  158. package/dist/features/jobs/schemas/firestore.js +37 -0
  159. package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
  160. package/dist/features/lottery/schemas/zod.d.ts +6 -6
  161. package/dist/features/media/MediaImage.js +22 -1
  162. package/dist/features/media/upload/ImageUpload.js +1 -1
  163. package/dist/features/obligations/constants/policies.js +24 -1
  164. package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
  165. package/dist/features/orders/components/OrderPaymentSummary.js +10 -1
  166. package/dist/features/orders/components/OrderProvenanceCard.d.ts +8 -0
  167. package/dist/features/orders/components/OrderProvenanceCard.js +185 -0
  168. package/dist/features/orders/components/RaiseDisputeForm.d.ts +14 -0
  169. package/dist/features/orders/components/RaiseDisputeForm.js +111 -0
  170. package/dist/features/orders/components/RefundRequestView.js +22 -4
  171. package/dist/features/orders/components/index.d.ts +4 -0
  172. package/dist/features/orders/components/index.js +2 -0
  173. package/dist/features/orders/constants/order-lane-tabs.d.ts +69 -0
  174. package/dist/features/orders/constants/order-lane-tabs.js +88 -0
  175. package/dist/features/orders/repository/orders.repository.d.ts +57 -0
  176. package/dist/features/orders/repository/orders.repository.js +145 -0
  177. package/dist/features/orders/schemas/firestore.d.ts +196 -1
  178. package/dist/features/orders/schemas/index.d.ts +60 -12
  179. package/dist/features/orders/schemas/index.js +12 -0
  180. package/dist/features/orders/types/index.d.ts +26 -0
  181. package/dist/features/orders/utils/courier-block.d.ts +25 -0
  182. package/dist/features/orders/utils/courier-block.js +23 -0
  183. package/dist/features/payment-cases/schemas/firestore.d.ts +26 -1
  184. package/dist/features/payment-cases/schemas/firestore.js +1 -0
  185. package/dist/features/products/components/GroupMemberPicker.js +4 -1
  186. package/dist/features/products/components/PrizeDrawDetailPageView.js +2 -2
  187. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  188. package/dist/features/products/components/ProductDetailActions.js +2 -1
  189. package/dist/features/products/components/ProductDetailPageView.d.ts +17 -1
  190. package/dist/features/products/components/ProductDetailPageView.js +2 -2
  191. package/dist/features/products/schemas/product-features.validators.d.ts +10 -10
  192. package/dist/features/promotions/actions/coupon-stacking.d.ts +30 -3
  193. package/dist/features/promotions/actions/coupon-stacking.js +21 -2
  194. package/dist/features/promotions/components/CouponCard.js +13 -0
  195. package/dist/features/promotions/constants/coupon-help.d.ts +2 -2
  196. package/dist/features/promotions/constants/coupon-help.js +9 -1
  197. package/dist/features/promotions/schemas/firestore.d.ts +24 -0
  198. package/dist/features/promotions/schemas/firestore.js +4 -0
  199. package/dist/features/promotions/types/index.d.ts +19 -1
  200. package/dist/features/scams/components/ScamProfileView.js +2 -2
  201. package/dist/features/scams/components/ScamRegistryView.js +2 -2
  202. package/dist/features/search/action-index/tab-entries.js +1 -1
  203. package/dist/features/seller/actions/seller-actions.js +33 -3
  204. package/dist/features/seller/components/FulfillmentView.js +3 -2
  205. package/dist/features/seller/components/SellerAddressesView.js +32 -4
  206. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
  207. package/dist/features/seller/components/SellerBundlesView.js +53 -14
  208. package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
  209. package/dist/features/seller/components/SellerListingTypeView.js +56 -10
  210. package/dist/features/seller/components/SellerOrdersView.js +129 -27
  211. package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
  212. package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
  213. package/dist/features/seller/components/SellerProductsView.js +5 -1
  214. package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
  215. package/dist/features/seller/components/SellerShippingView.js +2 -1
  216. package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
  217. package/dist/features/seller/components/SellerSupportView.js +69 -2
  218. package/dist/features/seller/hooks/useSellerListingData.js +25 -0
  219. package/dist/features/shell/SectionForm.js +20 -16
  220. package/dist/features/shell/build-sections.js +14 -1
  221. package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
  222. package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
  223. package/dist/features/shipments/schemas/validation.d.ts +4 -4
  224. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.d.ts +43 -0
  225. package/dist/features/shipping-boxes/repository/shipping-boxes.repository.js +90 -0
  226. package/dist/features/shipping-boxes/schemas/firestore.d.ts +82 -0
  227. package/dist/features/shipping-boxes/schemas/firestore.js +53 -0
  228. package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
  229. package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
  230. package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
  231. package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
  232. package/dist/features/store-extensions/schemas/report-create-form.d.ts +2 -2
  233. package/dist/features/store-extensions/schemas/shipping-config-form.d.ts +2 -2
  234. package/dist/features/stores/components/StoreOrdersGuideView.js +6 -1
  235. package/dist/features/stores/schemas/firestore.d.ts +71 -0
  236. package/dist/features/stores/types/index.d.ts +19 -0
  237. package/dist/features/support/schemas/index.d.ts +6 -6
  238. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
  239. package/dist/features/wishlist/hooks/useWishlistWithGuest.d.ts +1 -1
  240. package/dist/index.d.ts +11 -2
  241. package/dist/index.js +17 -2
  242. package/dist/next/routing/route-map.d.ts +24 -0
  243. package/dist/next/routing/route-map.js +19 -0
  244. package/dist/providers/shipping-shiprocket/index.d.ts +27 -1
  245. package/dist/providers/shipping-shiprocket/index.js +84 -1
  246. package/dist/schemas/registry.d.ts +51 -22
  247. package/dist/schemas/sieve.d.ts +5 -5
  248. package/dist/server-entry.d.ts +2 -2
  249. package/dist/server-entry.js +8 -1
  250. package/dist/server.d.ts +11 -2
  251. package/dist/server.js +40 -2
  252. package/dist/styles.css +64 -80
  253. package/dist/tailwind-utilities.css +1 -1
  254. package/dist/tokens/themes/default-dark.js +1 -0
  255. package/dist/tokens/themes/default-light.js +1 -0
  256. package/dist/tokens/themes/required.d.ts +1 -1
  257. package/dist/tokens/themes/required.js +1 -0
  258. package/dist/tokens/tokens.css +15 -0
  259. package/dist/ui/components/Checkbox.js +10 -2
  260. package/dist/ui/components/DateInput.js +8 -1
  261. package/dist/ui/components/Disclaimer.d.ts +19 -0
  262. package/dist/ui/components/Disclaimer.js +74 -0
  263. package/dist/ui/components/Input.js +22 -2
  264. package/dist/ui/components/PaginatedSelect.d.ts +8 -19
  265. package/dist/ui/components/PaginatedSelect.js +43 -9
  266. package/dist/ui/components/Radio.js +8 -1
  267. package/dist/ui/components/Select.js +10 -2
  268. package/dist/ui/components/TagInput.d.ts +8 -1
  269. package/dist/ui/components/TagInput.js +4 -3
  270. package/dist/ui/components/Textarea.js +10 -2
  271. package/dist/ui/components/tabs-overflow.js +64 -11
  272. package/dist/ui/forms/FieldCheckbox.js +1 -1
  273. package/dist/ui/forms/FormErrorList.d.ts +0 -16
  274. package/dist/ui/forms/FormErrorList.js +34 -1
  275. package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
  276. package/dist/ui/forms/apply-zod-issues.js +23 -1
  277. package/dist/ui/index.d.ts +2 -0
  278. package/dist/ui/index.js +6 -0
  279. package/dist/ui/rich-text/RichText.style.css +48 -79
  280. package/package.json +1 -1
  281. package/scripts/seed-cli.mjs +1831 -1822
@@ -54,6 +54,7 @@ export const DEFAULT_DARK_THEME = {
54
54
  "appkit-color-text": "#fafafa",
55
55
  "appkit-color-text-muted": "#b4b4bd",
56
56
  "appkit-color-text-faint": "#8b8b94",
57
+ "appkit-color-link": "#22d3ee",
57
58
  "appkit-color-success": "#34d399",
58
59
  "appkit-color-success-surface": "rgba(6, 78, 59, 0.25)",
59
60
  "appkit-color-warning": "#fbbf24",
@@ -57,6 +57,7 @@ export const DEFAULT_LIGHT_THEME = {
57
57
  "appkit-color-text-muted": "#5b5b63",
58
58
  "appkit-color-text-faint": "#6f6f78",
59
59
  "appkit-color-text-on-primary": "#ffffff",
60
+ "appkit-color-link": "#0f766e",
60
61
  "appkit-color-success": "#15803d",
61
62
  "appkit-color-success-surface": "#f0fdf4",
62
63
  "appkit-color-warning": "#b45309",
@@ -10,6 +10,6 @@ import type { GradientKey } from "./types";
10
10
  * unknown keys are rejected; missing keys fall back to the active mode's
11
11
  * default theme value (so a theme can override only a subset).
12
12
  */
13
- export declare const REQUIRED_THEME_TOKENS: readonly ["appkit-color-primary", "appkit-color-primary-50", "appkit-color-primary-100", "appkit-color-primary-200", "appkit-color-primary-300", "appkit-color-primary-400", "appkit-color-primary-500", "appkit-color-primary-600", "appkit-color-primary-700", "appkit-color-primary-800", "appkit-color-primary-900", "appkit-color-primary-950", "appkit-color-secondary", "appkit-color-secondary-50", "appkit-color-secondary-100", "appkit-color-secondary-200", "appkit-color-secondary-300", "appkit-color-secondary-400", "appkit-color-secondary-500", "appkit-color-secondary-600", "appkit-color-secondary-700", "appkit-color-secondary-800", "appkit-color-secondary-900", "appkit-color-secondary-950", "appkit-color-bg", "appkit-color-surface", "appkit-color-surface-elevated", "appkit-color-surface-input", "appkit-color-border", "appkit-color-border-subtle", "appkit-color-text", "appkit-color-text-muted", "appkit-color-text-faint", "appkit-color-text-on-primary", "appkit-color-success", "appkit-color-success-surface", "appkit-color-warning", "appkit-color-warning-surface", "appkit-color-error", "appkit-color-error-surface", "appkit-color-info", "appkit-color-info-surface", "appkit-color-focus-ring", "appkit-shadow-glow", "appkit-shadow-glow-pink", "appkit-font-display", "appkit-font-sans", "appkit-font-editorial", "appkit-font-mono", "appkit-space-0-5", "appkit-space-1", "appkit-space-1-5", "appkit-space-2", "appkit-space-2-5", "appkit-space-3", "appkit-space-3-5", "appkit-space-4", "appkit-space-5", "appkit-space-6", "appkit-space-8", "appkit-space-10", "appkit-space-12", "appkit-space-14", "appkit-space-16", "appkit-space-18", "appkit-space-20", "appkit-space-24", "appkit-text-2xs", "appkit-text-xs", "appkit-text-sm", "appkit-text-base", "appkit-text-lg", "appkit-text-xl", "appkit-text-2xl", "appkit-text-3xl", "appkit-text-4xl", "appkit-text-5xl"];
13
+ export declare const REQUIRED_THEME_TOKENS: readonly ["appkit-color-primary", "appkit-color-primary-50", "appkit-color-primary-100", "appkit-color-primary-200", "appkit-color-primary-300", "appkit-color-primary-400", "appkit-color-primary-500", "appkit-color-primary-600", "appkit-color-primary-700", "appkit-color-primary-800", "appkit-color-primary-900", "appkit-color-primary-950", "appkit-color-secondary", "appkit-color-secondary-50", "appkit-color-secondary-100", "appkit-color-secondary-200", "appkit-color-secondary-300", "appkit-color-secondary-400", "appkit-color-secondary-500", "appkit-color-secondary-600", "appkit-color-secondary-700", "appkit-color-secondary-800", "appkit-color-secondary-900", "appkit-color-secondary-950", "appkit-color-bg", "appkit-color-surface", "appkit-color-surface-elevated", "appkit-color-surface-input", "appkit-color-border", "appkit-color-border-subtle", "appkit-color-text", "appkit-color-text-muted", "appkit-color-text-faint", "appkit-color-text-on-primary", "appkit-color-link", "appkit-color-success", "appkit-color-success-surface", "appkit-color-warning", "appkit-color-warning-surface", "appkit-color-error", "appkit-color-error-surface", "appkit-color-info", "appkit-color-info-surface", "appkit-color-focus-ring", "appkit-shadow-glow", "appkit-shadow-glow-pink", "appkit-font-display", "appkit-font-sans", "appkit-font-editorial", "appkit-font-mono", "appkit-space-0-5", "appkit-space-1", "appkit-space-1-5", "appkit-space-2", "appkit-space-2-5", "appkit-space-3", "appkit-space-3-5", "appkit-space-4", "appkit-space-5", "appkit-space-6", "appkit-space-8", "appkit-space-10", "appkit-space-12", "appkit-space-14", "appkit-space-16", "appkit-space-18", "appkit-space-20", "appkit-space-24", "appkit-text-2xs", "appkit-text-xs", "appkit-text-sm", "appkit-text-base", "appkit-text-lg", "appkit-text-xl", "appkit-text-2xl", "appkit-text-3xl", "appkit-text-4xl", "appkit-text-5xl"];
14
14
  export type RequiredThemeToken = (typeof REQUIRED_THEME_TOKENS)[number];
15
15
  export declare const REQUIRED_GRADIENT_KEYS: readonly GradientKey[];
@@ -47,6 +47,7 @@ export const REQUIRED_THEME_TOKENS = [
47
47
  "appkit-color-text-muted",
48
48
  "appkit-color-text-faint",
49
49
  "appkit-color-text-on-primary",
50
+ "appkit-color-link",
50
51
  // Status
51
52
  "appkit-color-success",
52
53
  "appkit-color-success-surface",
@@ -302,6 +302,15 @@
302
302
  --appkit-color-text-on-primary: #ffffff;
303
303
  --appkit-color-text-on-dark: #ffffff;
304
304
 
305
+ /* Link ink for long-form prose. A SEPARATE token because the right link
306
+ * colour is not the same ramp in every theme: default-light wants primary
307
+ * (teal), default-dark wants secondary (cyan) because its primary is a RED
308
+ * that reads as danger, and cobalt-night's primary IS blue. Binding prose
309
+ * links to `primary-600` instead would be #dc2626 on the dark theme.
310
+ * primary-700, not -600: #0d9488 is only 3.59:1 on --appkit-color-bg and
311
+ * fails AA for normal text; #0f766e is 5.24:1. */
312
+ --appkit-color-link: #0f766e; /* primary-700 — 5.24:1 on --appkit-color-bg */
313
+
305
314
  --appkit-color-success: #15803d; /* green-700 — 4.5:1 AA against success-surface, distinct from the new teal primary */
306
315
  --appkit-color-success-surface: #f0fdf4; /* green-50 */
307
316
  --appkit-color-warning: #b45309; /* amber-700 — 4.5:1 AA against warning-surface */
@@ -500,6 +509,8 @@
500
509
  --appkit-color-text-muted: #b4b4bd; /* lightened from zinc-400 #a1a1aa — ~9:1 on the dark page */
501
510
  --appkit-color-text-faint: #8b8b94; /* lightened from zinc-500 #71717a, which was ~3:1 and read as barely present */
502
511
 
512
+ --appkit-color-link: #22d3ee; /* secondary-400 — 11.16:1 on --appkit-color-bg. NOT primary: this theme's primary is crimson */
513
+
503
514
  --appkit-color-success: #34d399; /* emerald-400 */
504
515
  --appkit-color-success-surface: rgba(6, 78, 59, 0.25);
505
516
  --appkit-color-warning: #fbbf24; /* amber-400 */
@@ -620,6 +631,8 @@
620
631
  --appkit-color-text-muted: #bcd4ff; /* primary-200 */
621
632
  --appkit-color-text-faint: #8eb9ff; /* primary-300 */
622
633
 
634
+ --appkit-color-link: #5992ff; /* primary-500 — 5.17:1 on --appkit-color-bg */
635
+
623
636
  --appkit-color-success: #4ade80;
624
637
  --appkit-color-success-surface: rgba(20, 83, 45, 0.3);
625
638
  --appkit-color-warning: #fbbf24;
@@ -652,6 +665,8 @@
652
665
  --appkit-color-surface-elevated: #ffffff;
653
666
  --appkit-color-surface-input: #fffbeb; /* amber-50 */
654
667
 
668
+ --appkit-color-link: #b45309; /* amber-700 — 4.73:1 on --appkit-color-bg; primary-600 #d97706 is only 3.0:1 */
669
+
655
670
  /* --- Interactive surfaces ------------------------------------------------
656
671
  * `-hover` is the neutral chrome hover fill; `-raised` is a resting panel one
657
672
  * step off the page surface; `primary-surface` is the brand-tinted hover.
@@ -19,13 +19,21 @@ export function Checkbox({ label, suffix, error, indeterminate = false, type = "
19
19
  const generatedId = React.useId();
20
20
  const inputId = id ?? generatedId;
21
21
  const inputRef = React.useRef(null);
22
+ /*
23
+ * A caller's own `aria-invalid` wins. `{...props}` is spread first, so
24
+ * re-specifying the attribute afterwards used to overwrite an incoming
25
+ * `"true"` with `undefined` — and `FieldCheckbox` sets it itself while
26
+ * withholding `error` (to avoid a double message). See the full writeup in
27
+ * `Input.tsx`; the same bug existed here.
28
+ */
29
+ const ariaInvalid = props["aria-invalid"] ?? (error ? "true" : undefined);
22
30
  React.useEffect(() => {
23
31
  if (inputRef.current) {
24
32
  inputRef.current.indeterminate = indeterminate;
25
33
  }
26
34
  }, [indeterminate]);
27
35
  if (bare) {
28
- return (_jsx("input", { ...props, ref: inputRef, id: inputId, type: type, checked: checked, disabled: disabled, "aria-invalid": error ? "true" : undefined, className: className }));
36
+ return (_jsx("input", { ...props, ref: inputRef, id: inputId, type: type, checked: checked, disabled: disabled, "aria-invalid": ariaInvalid, className: className }));
29
37
  }
30
38
  // A checkbox with no label/suffix has no text to lay out beside the box, so
31
39
  // the default `width: 100%` on the root serves no purpose and actively harms
@@ -43,7 +51,7 @@ export function Checkbox({ label, suffix, error, indeterminate = false, type = "
43
51
  disabled ? UI_CHECKBOX.labelDisabled : "",
44
52
  ]
45
53
  .filter(Boolean)
46
- .join(" "), children: [_jsxs("span", { className: UI_CHECKBOX.boxWrap, children: [_jsx("input", { ...props, ref: inputRef, id: inputId, type: type, checked: checked, disabled: disabled, "aria-invalid": error ? "true" : undefined, className: [
54
+ .join(" "), children: [_jsxs("span", { className: UI_CHECKBOX.boxWrap, children: [_jsx("input", { ...props, ref: inputRef, id: inputId, type: type, checked: checked, disabled: disabled, "aria-invalid": ariaInvalid, className: [
47
55
  UI_CHECKBOX.input,
48
56
  error ? UI_CHECKBOX.inputError : "",
49
57
  className,
@@ -5,7 +5,14 @@ import { Label, Span, Text } from "./Typography.js";
5
5
  export const DateInput = React.forwardRef(function DateInput({ label, error, helperText, value, onChange, disabled, required, id, className = "", ...props }, ref) {
6
6
  const generatedId = React.useId();
7
7
  const inputId = id ?? generatedId;
8
- return (_jsxs("div", { className: "w-full", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, className: "appkit-form-field__label mb-1.5", children: label })), _jsx("input", { ...props, ref: ref, id: inputId, type: "date", value: value, disabled: disabled, required: required, "aria-invalid": error ? "true" : undefined, onChange: (e) => onChange?.(e.target.value), className: ["appkit-input", error ? "appkit-input--error" : "", className].filter(Boolean).join(" ") }), error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", className: "mt-1.5", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
8
+ /*
9
+ * A caller's own `aria-invalid` wins. `{...props}` is spread first, so
10
+ * re-specifying the attribute afterwards overwrites an incoming "true"
11
+ * with `undefined` — the defect that left every Field*-built form in the
12
+ * app with no aria-invalid at all. See the full writeup in `Input.tsx`.
13
+ */
14
+ const ariaInvalid = props["aria-invalid"] ?? (error ? "true" : undefined);
15
+ return (_jsxs("div", { className: "w-full", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, className: "appkit-form-field__label mb-1.5", children: label })), _jsx("input", { ...props, ref: ref, id: inputId, type: "date", value: value, disabled: disabled, required: required, "aria-invalid": ariaInvalid, onChange: (e) => onChange?.(e.target.value), className: ["appkit-input", error ? "appkit-input--error" : "", className].filter(Boolean).join(" ") }), error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", className: "mt-1.5", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
9
16
  });
10
17
  export function DateRangeInput({ label, startLabel = "From", endLabel = "To", startValue, endValue, onStartChange, onEndChange, error, helperText, disabled, required, startMin, startMax, endMin, endMax, }) {
11
18
  return (_jsxs("div", { className: "w-full", children: [label && (_jsx(Text, { size: "sm", className: "appkit-form-field__label mb-1.5 block font-medium", children: label })), _jsxs("div", { className: "flex items-end gap-2", children: [_jsx(DateInput, { label: startLabel, value: startValue, onChange: onStartChange, disabled: disabled, required: required, min: startMin, max: startMax ?? endValue }), _jsx(Span, { size: "sm", className: "mb-2.5 shrink-0 text-[var(--appkit-color-text-muted)]", children: "\u2013" }), _jsx(DateInput, { label: endLabel, value: endValue, onChange: onEndChange, disabled: disabled, min: endMin ?? startValue, max: endMax })] }), error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", className: "mt-1.5", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
@@ -0,0 +1,19 @@
1
+ import React from "react";
2
+ import { type DisclaimerId } from "../../_internal/shared/content/disclaimers";
3
+ import { type PublishedValues } from "../../_internal/shared/content/published-values";
4
+ export interface DisclaimerProps {
5
+ id: DisclaimerId;
6
+ /** Override the money figures with the admin's current configuration. */
7
+ publishedValues?: PublishedValues;
8
+ /**
9
+ * Render without the surrounding box — for a disclaimer that sits inside a
10
+ * card that already has chrome.
11
+ *
12
+ * 🛑 Does NOT make it smaller or quieter. A `warning`-tone entry keeps its
13
+ * tone inline, because the entries marked `warning` are the ones where
14
+ * ignoring the text has a consequence the reader cannot undo.
15
+ */
16
+ inline?: boolean;
17
+ className?: string;
18
+ }
19
+ export declare function Disclaimer({ id, publishedValues, inline, className, }: DisclaimerProps): React.JSX.Element | null;
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { Div, Stack, Text } from "../index.js";
4
+ import { DISCLAIMERS, } from "../../_internal/shared/content/disclaimers.js";
5
+ import { resolvePublishedCopy, resolvePublishedValues, } from "../../_internal/shared/content/published-values.js";
6
+ /**
7
+ * The one way to render a disclaimer (Phase 12).
8
+ *
9
+ * ## 🛑 `id` only — there is no `text` prop, deliberately
10
+ *
11
+ * A `text` escape hatch would make this a styled box, and then the legally
12
+ * material disclaimers would be written at their call sites again, drift, and
13
+ * end up weakest exactly where they matter most. That is the failure this
14
+ * registry exists to close. If a surface needs a disclaimer that does not
15
+ * exist, the fix is a new registry entry — which is also what gets it
16
+ * reviewed.
17
+ *
18
+ * ## Tokens resolve here
19
+ *
20
+ * The protection entry quotes `{{saleProtectionMaxValue}}` and the claim
21
+ * windows, so the published limits come from the constants that ENFORCE them
22
+ * (Phase 9's mechanism). A change to `SALE_PROTECTION_MAX_VALUE` updates the
23
+ * terms rather than contradicting them.
24
+ *
25
+ * Code-derived values are the default; a caller with settings in hand passes
26
+ * better ones for the money figures.
27
+ *
28
+ * ## Safe in a Server Component
29
+ *
30
+ * No hooks, no state, and the registry is a plain object — so this renders on
31
+ * either side of the boundary. Do not add state here without checking every
32
+ * RSC call site (Root Cause #76).
33
+ */
34
+ const CODE_DERIVED_VALUES = resolvePublishedValues();
35
+ /**
36
+ * Renders `**bold**` and nothing else.
37
+ *
38
+ * 🛑 A deliberately tiny subset, not a markdown renderer. The emphasis is
39
+ * load-bearing in exactly the places it is used — "**not insurance**" in the
40
+ * protection entry, "**under review**" versus "**verified**" in the scam
41
+ * registry — and those are the phrases a reader skims past without it.
42
+ *
43
+ * 🛑 And it returns ELEMENTS, never HTML. The bodies are our own constants so
44
+ * injection is not the risk; the risk is that a `dangerouslySetInnerHTML`
45
+ * here would invite someone to pass admin-editable text through it later,
46
+ * which is how a disclaimer box becomes a stored-XSS surface.
47
+ */
48
+ function renderEmphasis(text) {
49
+ 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)));
50
+ }
51
+ export function Disclaimer({ id, publishedValues = CODE_DERIVED_VALUES, inline = false, className = "", }) {
52
+ const entry = DISCLAIMERS[id];
53
+ /*
54
+ * A `Record<DisclaimerId, …>` means this cannot happen through the type, but
55
+ * an `as DisclaimerId` cast at a call site could — and rendering nothing is
56
+ * the wrong failure here. A missing disclaimer is invisible, which is the
57
+ * whole problem; so it renders nothing only when the registry genuinely has
58
+ * no entry, and the audit is what catches that case before it ships.
59
+ */
60
+ if (!entry)
61
+ return null;
62
+ const body = resolvePublishedCopy(entry.body, publishedValues);
63
+ 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) })] }));
64
+ if (inline)
65
+ return _jsx(Div, { className: className, children: content });
66
+ /*
67
+ * 🛑 `surface` + the matching ink, never a status background with literal
68
+ * white text. `-surface` is a near-white tint in light themes and a dark
69
+ * tint in dark ones, so a fixed `text-white` is invisible in exactly one
70
+ * theme — Root Cause #67, which shipped 26 invisible surfaces that way.
71
+ * `Text color="muted"` above pairs correctly with both.
72
+ */
73
+ return (_jsx(Div, { surface: entry.tone === "warning" ? "warning-surface" : "muted", rounded: "lg", padding: "sm", className: className, children: content }));
74
+ }
@@ -13,6 +13,26 @@ export const Input = React.forwardRef(function Input({ label, error, helperText,
13
13
  : variant === "ghost"
14
14
  ? "border-transparent bg-transparent shadow-none"
15
15
  : "";
16
+ /*
17
+ * 🛑 A CALLER'S OWN `aria-invalid` MUST WIN — it did not, and that broke
18
+ * every form in the app.
19
+ *
20
+ * `{...props}` is spread FIRST on both `<input>` elements below, so
21
+ * re-specifying `aria-invalid={error ? "true" : undefined}` afterwards
22
+ * OVERWROTE an incoming `"true"` with `undefined` (JSX: later wins).
23
+ *
24
+ * That is not hypothetical. `FieldInput` sets `aria-invalid` itself and
25
+ * deliberately WITHHOLDS `error` from this component, so the message is not
26
+ * rendered twice — which means `error` here is `undefined` exactly when the
27
+ * field is invalid. Net effect: no `Field*`-built form ever emitted
28
+ * `aria-invalid`, and `SectionForm`'s jump-to-error
29
+ * (`querySelector("[aria-invalid='true']")?.focus()`) silently focused
30
+ * nothing, every time.
31
+ *
32
+ * Same precedence rule as `bareAriaLabel` below: fill the gap, never
33
+ * override an explicit choice.
34
+ */
35
+ const ariaInvalid = props["aria-invalid"] ?? (error ? "true" : undefined);
16
36
  const inputClassName = [
17
37
  "appkit-input",
18
38
  icon ? "pl-10" : "",
@@ -45,10 +65,10 @@ export const Input = React.forwardRef(function Input({ label, error, helperText,
45
65
  * overrides an explicit choice.
46
66
  */
47
67
  const bareAriaLabel = props["aria-label"] ?? (typeof label === "string" ? label : undefined);
48
- return (_jsx("input", { ...props, ref: ref, id: inputId, disabled: disabled, "aria-label": bareAriaLabel, "aria-invalid": error ? "true" : undefined, className: inputClassName }));
68
+ return (_jsx("input", { ...props, ref: ref, id: inputId, disabled: disabled, "aria-label": bareAriaLabel, "aria-invalid": ariaInvalid, className: inputClassName }));
49
69
  }
50
70
  const adornment = rightIcon ??
51
71
  (success && !error ? (_jsx("svg", { className: CLS_INPUT_SUCCESS_ICON, viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", d: "M16.704 5.29a1 1 0 010 1.42l-7.2 7.2a1 1 0 01-1.415 0l-3.2-3.2a1 1 0 111.414-1.42l2.493 2.494 6.493-6.494a1 1 0 011.415 0z", clipRule: "evenodd" }) })) : null);
52
- return (_jsxs("div", { className: "w-full", "data-section": "input-div-527", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsxs("div", { className: "relative", "data-section": "input-div-528", children: [icon && (_jsx(Div, { className: "pointer-events-none absolute inset-y-0 left-3 flex items-center text-[var(--appkit-color-text-faint)]", "data-section": "input-div-529", children: icon })), _jsx("input", { ...props, ref: ref, id: inputId, disabled: disabled, "aria-invalid": error ? "true" : undefined, className: inputClassName }), adornment && (_jsx(Div, { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center text-[var(--appkit-color-text-faint)]", "data-section": "input-div-530", children: adornment }))] }), error ? (_jsx(Text, { size: "sm", variant: "error", className: "mt-1.5", role: "alert", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
72
+ return (_jsxs("div", { className: "w-full", "data-section": "input-div-527", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsxs("div", { className: "relative", "data-section": "input-div-528", children: [icon && (_jsx(Div, { className: "pointer-events-none absolute inset-y-0 left-3 flex items-center text-[var(--appkit-color-text-faint)]", "data-section": "input-div-529", children: icon })), _jsx("input", { ...props, ref: ref, id: inputId, disabled: disabled, "aria-invalid": ariaInvalid, className: inputClassName }), adornment && (_jsx(Div, { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center text-[var(--appkit-color-text-faint)]", "data-section": "input-div-530", children: adornment }))] }), error ? (_jsx(Text, { size: "sm", variant: "error", className: "mt-1.5", role: "alert", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "mt-1.5", children: helperText })) : null] }));
53
73
  });
54
74
  Input.displayName = "Input";
@@ -20,6 +20,14 @@ interface BaseProps<V> {
20
20
  disabled?: boolean;
21
21
  className?: string;
22
22
  ariaLabel?: string;
23
+ /** Field name — resolves this control's error from `FormShellContext`, as `Field*` do. */
24
+ name?: string;
25
+ /**
26
+ * Validation message. Explicit prop wins; else read from context by `name`.
27
+ * Without these two this control had no error affordance at all: a required
28
+ * taxonomy pick could fail and the control showed nothing.
29
+ */
30
+ error?: React.ReactNode;
23
31
  /** "+ Create new …" button. Provide `renderCreateForm` OR (`createFields` + `onCreateSubmit`). */
24
32
  createLabel?: string;
25
33
  drawerTitle?: string;
@@ -59,24 +67,5 @@ interface MultiProps<V> extends BaseProps<V> {
59
67
  maxChipsVisible?: number;
60
68
  }
61
69
  export type PaginatedSelectProps<V = string> = SingleProps<V> | MultiProps<V>;
62
- /**
63
- * PaginatedSelect — searchable, async-paginated select with optional multi-mode
64
- * and optional inline create-new drawer.
65
- *
66
- * @example single-select
67
- * <PaginatedSelect value={storeId} onChange={setStoreId} loadOptions={loadStores} />
68
- *
69
- * @example single-select with inline create
70
- * <PaginatedSelect
71
- * value={categoryId}
72
- * onChange={(v) => setCategoryId(v ?? "")}
73
- * loadOptions={loadCategories}
74
- * createLabel="Category"
75
- * renderCreateForm={({ onCreated, onCancel }) => <CategoryForm ... />}
76
- * />
77
- *
78
- * @example multi-select
79
- * <PaginatedSelect multiple value={tagIds} onChange={setTagIds} loadOptions={loadTags} />
80
- */
81
70
  export declare function PaginatedSelect<V = string>(props: PaginatedSelectProps<V>): React.JSX.Element;
82
71
  export {};
@@ -1,7 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useRef, useState } from "react";
3
+ import React, { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { Input } from "./Input.js";
5
+ import { toUserMessage } from "../../errors/error-display-map.js";
6
+ import { Text } from "./Typography.js";
7
+ import { FormShellContext } from "../forms/FormShell.js";
5
8
  import { SideDrawer } from "./SideDrawer.js";
6
9
  import { Button } from "./Button.js";
7
10
  import { QuickFormDrawer } from "../../features/shell/QuickFormDrawer.js";
@@ -87,9 +90,43 @@ function isValueSelected(value, props, isMulti) {
87
90
  * @example multi-select
88
91
  * <PaginatedSelect multiple value={tagIds} onChange={setTagIds} loadOptions={loadTags} />
89
92
  */
93
+ /**
94
+ * Resolve this control's validation message and the id that labels it.
95
+ *
96
+ * A hook rather than four lines in the component body: `PaginatedSelect` is
97
+ * already at `audit-code-quality`'s size limit, and the error affordance is
98
+ * self-contained — explicit prop wins, else `FormShellContext` by `name`,
99
+ * exactly as the `Field*` primitives resolve theirs.
100
+ */
101
+ function usePaginatedSelectError(name, errorProp) {
102
+ const ctx = React.useContext(FormShellContext);
103
+ const error = errorProp ?? (name ? ctx?.errors[name] : undefined);
104
+ const errorId = `${React.useId()}-error`;
105
+ return { error, errorId };
106
+ }
107
+ /** The message, under the trigger it describes. */
108
+ function PaginatedSelectError({ id, error }) {
109
+ if (!error)
110
+ return null;
111
+ return (_jsx(Text, { id: id, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error }));
112
+ }
113
+ /**
114
+ * The selected-values row, multi-select only.
115
+ *
116
+ * Its own component because `PaginatedSelect` sits against
117
+ * `audit-code-quality`'s size limit, and this block needs none of the
118
+ * control's twelve state variables — only what it is handed.
119
+ */
120
+ function PaginatedSelectChips({ chips, labelFor, overflowCount, disabled, onRemove, }) {
121
+ return (_jsxs("div", { className: C.chips, children: [chips.map((v) => (_jsxs("span", { className: C.chip, children: [_jsx("span", { className: C.chipLabel, children: labelFor(v) }), !disabled && (_jsx("button", { type: "button", "aria-label": "Remove", className: C.chipRemove, onClick: (e) => {
122
+ e.stopPropagation();
123
+ onRemove(v);
124
+ }, children: "\u2715" }))] }, String(v)))), overflowCount > 0 && (_jsxs("span", { className: C.overflowChip, children: ["+", overflowCount, " more"] }))] }));
125
+ }
90
126
  export function PaginatedSelect(props) {
91
- const { options, loadOptions, placeholder = "Select...", searchPlaceholder = "Search...", noResultsText = "No results", disabled, className = "", ariaLabel, createLabel, drawerTitle, renderCreateForm, createFields, createSchema, onCreateSubmit, createSubmitLabel, editLabel, editDrawerTitle, renderEditForm, editFields, editSchema, onEditLoad, onEditSubmit, editSubmitLabel, } = props;
127
+ const { options, loadOptions, placeholder = "Select...", searchPlaceholder = "Search...", noResultsText = "No results", disabled, className = "", ariaLabel, name, error: errorProp, createLabel, drawerTitle, renderCreateForm, createFields, createSchema, onCreateSubmit, createSubmitLabel, editLabel, editDrawerTitle, renderEditForm, editFields, editSchema, onEditLoad, onEditSubmit, editSubmitLabel, } = props;
92
128
  const isMulti = props.multiple === true;
129
+ const { error, errorId } = usePaginatedSelectError(name, errorProp);
93
130
  const maxChipsVisible = isMulti ? props.maxChipsVisible ?? 3 : 0;
94
131
  const [query, setQuery] = useState("");
95
132
  const [open, setOpen] = useState(false);
@@ -222,7 +259,7 @@ export function PaginatedSelect(props) {
222
259
  // Same staleness rule as the success path: a superseded request must
223
260
  // not paint its failure over a newer request's results.
224
261
  if (requestId === requestIdRef.current) {
225
- setLoadError(normalized.message || "Could not load options.");
262
+ setLoadError(toUserMessage(normalized.code, undefined, { fallback: "Could not load options." }));
226
263
  if (reset)
227
264
  setAsyncOptions([]);
228
265
  }
@@ -370,12 +407,9 @@ export function PaginatedSelect(props) {
370
407
  const multiValue = isMulti ? props.value : [];
371
408
  const visibleChips = multiValue.slice(0, maxChipsVisible);
372
409
  const overflowCount = multiValue.length - maxChipsVisible;
373
- return (_jsxs(_Fragment, { children: [_jsxs("div", { ref: containerRef, className: [C.root, className].filter(Boolean).join(" "), "aria-label": ariaLabel, children: [isMulti && multiValue.length > 0 && (_jsxs("div", { className: C.chips, children: [visibleChips.map((v) => (_jsxs("span", { className: C.chip, children: [_jsx("span", { className: C.chipLabel, children: labelMap.current.get(v) ?? String(v) }), !disabled && (_jsx("button", { type: "button", "aria-label": "Remove", className: C.chipRemove, onClick: (e) => {
374
- e.stopPropagation();
375
- removeChip(v);
376
- }, children: "\u2715" }))] }, String(v)))), overflowCount > 0 && (_jsxs("span", { className: C.overflowChip, children: ["+", overflowCount, " more"] }))] })), _jsxs("button", { type: "button", disabled: disabled, onClick: () => setOpen((prev) => !prev), className: [C.trigger, disabled ? C.triggerDisabled : ""]
410
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { ref: containerRef, className: [C.root, className].filter(Boolean).join(" "), children: [isMulti && multiValue.length > 0 && (_jsx(PaginatedSelectChips, { chips: visibleChips, labelFor: (v) => labelMap.current.get(v) ?? String(v), overflowCount: overflowCount, disabled: disabled, onRemove: removeChip })), _jsxs("button", { type: "button", disabled: disabled, onClick: () => setOpen((prev) => !prev), className: [C.trigger, disabled ? C.triggerDisabled : ""]
377
411
  .filter(Boolean)
378
- .join(" "), "aria-expanded": open, "aria-haspopup": "listbox", children: [_jsx("span", { className: C.triggerLeft, children: _jsx("span", { className: triggerEmpty ? C.placeholder : "", children: triggerLabel }) }), _jsx("span", { "aria-hidden": "true", children: "\u25BE" })] }), hasEdit && !disabled ? (_jsx("button", { type: "button", className: C.editBtn, "aria-label": `Edit ${editLabel}`, title: `Edit ${editLabel}`, onClick: () => {
412
+ .join(" "), "aria-label": ariaLabel, "aria-expanded": open, "aria-haspopup": "listbox", "aria-invalid": error ? "true" : undefined, "aria-describedby": error ? errorId : undefined, children: [_jsx("span", { className: C.triggerLeft, children: _jsx("span", { className: triggerEmpty ? C.placeholder : "", children: triggerLabel }) }), _jsx("span", { "aria-hidden": "true", children: "\u25BE" })] }), hasEdit && !disabled ? (_jsx("button", { type: "button", className: C.editBtn, "aria-label": `Edit ${editLabel}`, title: `Edit ${editLabel}`, onClick: () => {
379
413
  setOpen(false);
380
414
  setEditOpen(true);
381
415
  }, children: "\u270E" })) : null, open && (_jsxs("div", { className: [C.dropdown, opensUp ? C.dropdownUp : ""].filter(Boolean).join(" "), role: "listbox", "aria-multiselectable": isMulti || undefined, children: [_jsx(Input, { autoFocus: true, value: query, onChange: (e) => setQuery(e.target.value), placeholder: searchPlaceholder, className: C.search }), _jsxs("ul", { className: C.list, children: [filtered.map((opt, idx) => {
@@ -386,7 +420,7 @@ export function PaginatedSelect(props) {
386
420
  }), loadError && !loading && (_jsxs("li", { className: C.empty, role: "alert", children: [loadError, " ", _jsx(Button, { variant: "ghost", size: "sm", onClick: () => void load(query, 1, true), children: "Try again" })] })), !loadError && filtered.length === 0 && !loading && (_jsx("li", { className: C.empty, children: noResultsText })), loading && _jsx("li", { className: C.loading, children: "Loading\u2026" }), hasMore && !loading && (_jsx("li", { children: _jsx("button", { type: "button", className: C.loadMore, onClick: () => void load(query, page + 1), children: "Load more" }) })), hasCreate && (_jsx("li", { children: _jsxs("button", { type: "button", className: C.createBtn, onClick: () => {
387
421
  setOpen(false);
388
422
  setDrawerOpen(true);
389
- }, children: ["+ Create new ", createLabel] }) }, CREATE_SENTINEL))] })] }))] }), hasCreate && renderCreateForm ? (_jsx(SideDrawer, { isOpen: drawerOpen, onClose: () => setDrawerOpen(false), title: drawerTitle ?? `Create ${createLabel}`, mode: "create", footer: _jsx(Button, { variant: "ghost", onClick: () => setDrawerOpen(false), children: "Cancel" }), children: renderCreateForm({
423
+ }, children: ["+ Create new ", createLabel] }) }, CREATE_SENTINEL))] })] })), _jsx(PaginatedSelectError, { id: errorId, error: error })] }), hasCreate && renderCreateForm ? (_jsx(SideDrawer, { isOpen: drawerOpen, onClose: () => setDrawerOpen(false), title: drawerTitle ?? `Create ${createLabel}`, mode: "create", footer: _jsx(Button, { variant: "ghost", onClick: () => setDrawerOpen(false), children: "Cancel" }), children: renderCreateForm({
390
424
  onCreated: handleCreated,
391
425
  onCancel: () => setDrawerOpen(false),
392
426
  }) })) : hasCreate && createFields && onCreateSubmit ? (_jsx(QuickFormDrawer, { isOpen: drawerOpen, onClose: () => setDrawerOpen(false), title: drawerTitle ?? `Create ${createLabel}`, fields: createFields, schema: createSchema, submitLabel: createSubmitLabel ?? `Create ${createLabel}`, onSubmit: async (values) => {
@@ -66,5 +66,12 @@ export function RadioGroup({ name, options, value, onChange, label, error, orien
66
66
  export function RadioItem({ label, suffix, error, className = "", checked, disabled, id, ...props }) {
67
67
  const generatedId = React.useId();
68
68
  const inputId = id ?? generatedId;
69
- return (_jsxs("div", { className: "appkit-radio-item", "data-section": "radio-item-div", children: [_jsxs(Label, { htmlFor: inputId, className: ["appkit-radio-item__label", disabled ? "appkit-radio-item__label--disabled" : ""].filter(Boolean).join(" "), children: [_jsx("span", { className: "appkit-radio-item__box-wrap", children: _jsx("input", { ...props, id: inputId, type: "radio", checked: checked, disabled: disabled, "aria-invalid": error ? "true" : undefined, className: ["appkit-radio-item__input", error ? "appkit-radio-item__input--error" : "", className].filter(Boolean).join(" ") }) }), (label || suffix) && (_jsxs("span", { className: "appkit-radio-item__content", children: [label ? _jsx(Span, { className: "appkit-radio-item__text", children: label }) : _jsx("span", {}), suffix] }))] }), error && (_jsx(Text, { size: "sm", variant: "error", className: "appkit-radio-item__error", role: "alert", children: error }))] }));
69
+ /*
70
+ * A caller's own `aria-invalid` wins. `{...props}` is spread first, so
71
+ * re-specifying the attribute afterwards overwrites an incoming "true"
72
+ * with `undefined` — the defect that left every Field*-built form in the
73
+ * app with no aria-invalid at all. See the full writeup in `Input.tsx`.
74
+ */
75
+ const ariaInvalid = props["aria-invalid"] ?? (error ? "true" : undefined);
76
+ return (_jsxs("div", { className: "appkit-radio-item", "data-section": "radio-item-div", children: [_jsxs(Label, { htmlFor: inputId, className: ["appkit-radio-item__label", disabled ? "appkit-radio-item__label--disabled" : ""].filter(Boolean).join(" "), children: [_jsx("span", { className: "appkit-radio-item__box-wrap", children: _jsx("input", { ...props, id: inputId, type: "radio", checked: checked, disabled: disabled, "aria-invalid": ariaInvalid, className: ["appkit-radio-item__input", error ? "appkit-radio-item__input--error" : "", className].filter(Boolean).join(" ") }) }), (label || suffix) && (_jsxs("span", { className: "appkit-radio-item__content", children: [label ? _jsx(Span, { className: "appkit-radio-item__text", children: label }) : _jsx("span", {}), suffix] }))] }), error && (_jsx(Text, { size: "sm", variant: "error", className: "appkit-radio-item__error", role: "alert", children: error }))] }));
70
77
  }
@@ -6,6 +6,14 @@ import { Label, Text } from "./Typography.js";
6
6
  export function Select({ options, value, onChange, onValueChange, placeholder, label, error, helperText, disabled = false, required, className = "", id, variant = "default", bare = false, wrapperClassName, ...props }) {
7
7
  const generatedId = React.useId();
8
8
  const inputId = id ?? generatedId;
9
+ /*
10
+ * A caller's own `aria-invalid` wins. `{...props}` is spread first, so
11
+ * re-specifying the attribute afterwards used to overwrite an incoming
12
+ * `"true"` with `undefined` — and `FieldSelect` sets it itself while
13
+ * withholding `error` (to avoid a double message). See the full writeup in
14
+ * `Input.tsx`; the same bug existed here.
15
+ */
16
+ const ariaInvalid = props["aria-invalid"] ?? (error ? "true" : undefined);
9
17
  const handleChange = (event) => {
10
18
  onChange?.(event);
11
19
  onValueChange?.(event.target.value);
@@ -28,7 +36,7 @@ export function Select({ options, value, onChange, onValueChange, placeholder, l
28
36
  .join(" ");
29
37
  const optionsMarkup = (_jsxs(_Fragment, { children: [placeholder ? (_jsx("option", { value: "", disabled: true, children: placeholder })) : null, options.map((option) => (_jsx("option", { value: option.value, disabled: option.disabled, children: option.label }, String(option.value))))] }));
30
38
  if (bare) {
31
- return (_jsx("select", { ...props, id: inputId, value: value, onChange: handleChange, disabled: disabled, "aria-invalid": error ? "true" : undefined, className: selectClassName, children: optionsMarkup }));
39
+ return (_jsx("select", { ...props, id: inputId, value: value, onChange: handleChange, disabled: disabled, "aria-invalid": ariaInvalid, className: selectClassName, children: optionsMarkup }));
32
40
  }
33
- return (_jsxs("div", { className: ["appkit-select", wrapperClassName].filter(Boolean).join(" "), "data-section": "select-div-592", children: [label && (_jsx(Label, { htmlFor: inputId, className: "appkit-select__label", required: required, children: label })), _jsxs("div", { className: "relative group", "data-section": "select-div-593", children: [_jsx("select", { ...props, id: inputId, value: value, onChange: handleChange, disabled: disabled, "aria-invalid": error ? "true" : undefined, className: selectClassName, children: optionsMarkup }), _jsx(Div, { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center text-[var(--appkit-color-text-faint)]", "data-section": "select-div-594", children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) }) })] }), error ? (_jsx(Text, { size: "sm", variant: "error", className: "appkit-select__error", role: "alert", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-select__error", children: helperText })) : null] }));
41
+ return (_jsxs("div", { className: ["appkit-select", wrapperClassName].filter(Boolean).join(" "), "data-section": "select-div-592", children: [label && (_jsx(Label, { htmlFor: inputId, className: "appkit-select__label", required: required, children: label })), _jsxs("div", { className: "relative group", "data-section": "select-div-593", children: [_jsx("select", { ...props, id: inputId, value: value, onChange: handleChange, disabled: disabled, "aria-invalid": ariaInvalid, className: selectClassName, children: optionsMarkup }), _jsx(Div, { className: "pointer-events-none absolute inset-y-0 right-3 flex items-center text-[var(--appkit-color-text-faint)]", "data-section": "select-div-594", children: _jsx("svg", { className: "h-4 w-4", viewBox: "0 0 20 20", fill: "currentColor", "aria-hidden": "true", children: _jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) }) })] }), error ? (_jsx(Text, { size: "sm", variant: "error", className: "appkit-select__error", role: "alert", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-select__error", children: helperText })) : null] }));
34
42
  }
@@ -7,5 +7,12 @@ export interface TagInputProps {
7
7
  placeholder?: string;
8
8
  className?: string;
9
9
  helperText?: string;
10
+ /**
11
+ * Validation message. This control had no error slot at all, so a schema
12
+ * failure on a tags field could only ever appear in the summary — the field
13
+ * itself showed nothing and was not announced as invalid. Mirrors the
14
+ * contract Input/Select/Textarea/Checkbox already have.
15
+ */
16
+ error?: React.ReactNode;
10
17
  }
11
- export declare function TagInput({ value, onChange, disabled, label, placeholder, className, helperText, }: TagInputProps): React.JSX.Element;
18
+ export declare function TagInput({ value, onChange, disabled, label, placeholder, className, helperText, error, }: TagInputProps): React.JSX.Element;
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useRef, useState } from "react";
3
+ import React, { useRef, useState } from "react";
4
4
  import { Label, Span } from "./Typography.js";
5
- export function TagInput({ value, onChange, disabled = false, label, placeholder = "Add a tag...", className = "", helperText = "Press Enter or comma to add a tag", }) {
5
+ export function TagInput({ value, onChange, disabled = false, label, placeholder = "Add a tag...", className = "", helperText = "Press Enter or comma to add a tag", error, }) {
6
6
  const [draft, setDraft] = useState("");
7
7
  const inputRef = useRef(null);
8
+ const errorId = React.useId();
8
9
  const normalizeTag = (raw) => raw.trim().replace(/,+$/, "").trim();
9
10
  const addTag = (raw) => {
10
11
  const tag = normalizeTag(raw);
@@ -69,5 +70,5 @@ export function TagInput({ value, onChange, disabled = false, label, placeholder
69
70
  disabled ? "appkit-tag-input__text--disabled" : "",
70
71
  ]
71
72
  .filter(Boolean)
72
- .join(" "), "aria-label": label ?? "Tag input" })] }), helperText && (_jsx(Span, { size: "xs", className: "appkit-tag-input__helper", children: helperText }))] }));
73
+ .join(" "), "aria-label": label ?? "Tag input", "aria-invalid": error ? "true" : undefined, "aria-describedby": error ? errorId : undefined })] }), error ? (_jsx(Span, { id: errorId, size: "xs", className: "appkit-tag-input__helper appkit-form-field__error", role: "alert", children: error })) : helperText ? (_jsx(Span, { size: "xs", className: "appkit-tag-input__helper", children: helperText })) : null] }));
73
74
  }
@@ -15,12 +15,20 @@ export const Textarea = React.forwardRef(function Textarea({ label, error, helpe
15
15
  : variant === "ghost"
16
16
  ? "border-transparent bg-transparent shadow-none"
17
17
  : "";
18
+ /*
19
+ * A caller's own `aria-invalid` wins. `{...props}` is spread first, so
20
+ * re-specifying the attribute afterwards used to overwrite an incoming
21
+ * `"true"` with `undefined` — and `FieldTextarea` sets it itself while
22
+ * withholding `error` (to avoid a double message). See the full writeup in
23
+ * `Input.tsx`; the same bug existed here.
24
+ */
25
+ const ariaInvalid = props["aria-invalid"] ?? (error ? "true" : undefined);
18
26
  const textareaClassName = ["appkit-textarea", variantClass, className]
19
27
  .filter(Boolean)
20
28
  .join(" ");
21
29
  if (bare) {
22
- return (_jsx("textarea", { ...props, ref: ref, id: inputId, value: value, defaultValue: defaultValue, maxLength: maxLength, "aria-invalid": error ? "true" : undefined, className: textareaClassName }));
30
+ return (_jsx("textarea", { ...props, ref: ref, id: inputId, value: value, defaultValue: defaultValue, maxLength: maxLength, "aria-invalid": ariaInvalid, className: textareaClassName }));
23
31
  }
24
- return (_jsxs("div", { className: "w-full", "data-section": "textarea-div-620", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsx("textarea", { ...props, ref: ref, id: inputId, value: value, defaultValue: defaultValue, maxLength: maxLength, "aria-invalid": error ? "true" : undefined, className: textareaClassName }), (error || helperText || (showCharCount && maxLength)) && (_jsxs("div", { className: "mt-1.5 flex items-start justify-between gap-3", "data-section": "textarea-div-621", children: [_jsx("div", { className: "min-w-0 flex-1", "data-section": "textarea-div-622", children: error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", children: helperText })) : null }), showCharCount && maxLength ? (_jsxs(Text, { size: "xs", variant: charCount >= maxLength ? "error" : "muted", className: "shrink-0 tabular-nums", children: [charCount, "/", maxLength] })) : null] }))] }));
32
+ return (_jsxs("div", { className: "w-full", "data-section": "textarea-div-620", children: [label && (_jsx(Label, { htmlFor: inputId, required: required, children: label })), _jsx("textarea", { ...props, ref: ref, id: inputId, value: value, defaultValue: defaultValue, maxLength: maxLength, "aria-invalid": ariaInvalid, className: textareaClassName }), (error || helperText || (showCharCount && maxLength)) && (_jsxs("div", { className: "mt-1.5 flex items-start justify-between gap-3", "data-section": "textarea-div-621", children: [_jsx("div", { className: "min-w-0 flex-1", "data-section": "textarea-div-622", children: error ? (_jsx(Text, { size: "sm", variant: "error", role: "alert", children: error })) : helperText ? (_jsx(Text, { size: "sm", variant: "secondary", children: helperText })) : null }), showCharCount && maxLength ? (_jsxs(Text, { size: "xs", variant: charCount >= maxLength ? "error" : "muted", className: "shrink-0 tabular-nums", children: [charCount, "/", maxLength] })) : null] }))] }));
25
33
  });
26
34
  Textarea.displayName = "Textarea";