@mohasinac/appkit 4.60.1 → 4.62.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 (202) 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/auctions/actions.d.ts +1 -1
  6. package/dist/_internal/server/features/auctions/actions.js +12 -1
  7. package/dist/_internal/server/features/blog/actions.d.ts +1 -1
  8. package/dist/_internal/server/features/blog/actions.js +12 -1
  9. package/dist/_internal/server/features/brands/actions.d.ts +1 -1
  10. package/dist/_internal/server/features/brands/actions.js +12 -1
  11. package/dist/_internal/server/features/cart/actions.d.ts +1 -1
  12. package/dist/_internal/server/features/cart/actions.js +12 -1
  13. package/dist/_internal/server/features/checkout/actions.js +0 -1
  14. package/dist/_internal/server/features/events/actions.d.ts +1 -1
  15. package/dist/_internal/server/features/events/actions.js +12 -1
  16. package/dist/_internal/server/features/gateway-events/redact.js +14 -2
  17. package/dist/_internal/server/features/history/actions.d.ts +1 -1
  18. package/dist/_internal/server/features/history/actions.js +12 -1
  19. package/dist/_internal/server/features/lottery/actions.d.ts +1 -1
  20. package/dist/_internal/server/features/lottery/actions.js +12 -1
  21. package/dist/_internal/server/features/orders/actions.d.ts +1 -1
  22. package/dist/_internal/server/features/orders/actions.js +12 -1
  23. package/dist/_internal/server/features/orders/cancellation-refund.js +0 -1
  24. package/dist/_internal/server/features/orders/service.js +31 -1
  25. package/dist/_internal/server/features/payouts/actions.d.ts +1 -1
  26. package/dist/_internal/server/features/payouts/actions.js +12 -1
  27. package/dist/_internal/server/features/points/award.js +9 -1
  28. package/dist/_internal/server/features/pre-orders/actions.d.ts +1 -1
  29. package/dist/_internal/server/features/pre-orders/actions.js +12 -1
  30. package/dist/_internal/server/features/prize-draws/actions.d.ts +1 -1
  31. package/dist/_internal/server/features/prize-draws/actions.js +12 -1
  32. package/dist/_internal/server/features/products/actions.d.ts +1 -1
  33. package/dist/_internal/server/features/products/actions.js +12 -1
  34. package/dist/_internal/server/features/promotions/actions.d.ts +1 -1
  35. package/dist/_internal/server/features/promotions/actions.js +12 -1
  36. package/dist/_internal/server/features/protection-claims/actions.d.ts +2 -2
  37. package/dist/_internal/server/features/protection-claims/actions.js +21 -12
  38. package/dist/_internal/server/features/raffle/actions.d.ts +1 -1
  39. package/dist/_internal/server/features/raffle/actions.js +12 -1
  40. package/dist/_internal/server/features/refunds/actions.d.ts +1 -3
  41. package/dist/_internal/server/features/refunds/actions.js +14 -2
  42. package/dist/_internal/server/features/reviews/actions.d.ts +1 -1
  43. package/dist/_internal/server/features/reviews/actions.js +12 -1
  44. package/dist/_internal/server/features/wishlist/actions.d.ts +1 -1
  45. package/dist/_internal/server/features/wishlist/actions.js +12 -1
  46. package/dist/_internal/server/jobs/core/obligationSweep.js +0 -1
  47. package/dist/_internal/server/jobs/core/paymentReconcile.js +93 -76
  48. package/dist/_internal/server/jobs/core/testDataCleanup.d.ts +1 -1
  49. package/dist/_internal/server/jobs/core/testDataCleanup.js +22 -1
  50. package/dist/_internal/server/jobs/handlers/onShipmentAllocationSync.js +16 -4
  51. package/dist/_internal/shared/actions/action-registry.js +149 -9
  52. package/dist/_internal/shared/features/promotions/schema.d.ts +66 -0
  53. package/dist/_internal/shared/features/promotions/schema.js +7 -0
  54. package/dist/_internal/shared/media/limits.d.ts +10 -0
  55. package/dist/_internal/shared/media/limits.js +10 -0
  56. package/dist/_internal/shared/obligations/deadlines.js +11 -1
  57. package/dist/_internal/shared/tester/window.d.ts +25 -0
  58. package/dist/_internal/shared/tester/window.js +66 -0
  59. package/dist/_internal/shared/types/action-result.js +15 -1
  60. package/dist/client/api/surface-error.js +16 -2
  61. package/dist/client/api/useApiMutation.js +17 -1
  62. package/dist/client.d.ts +1 -1
  63. package/dist/client.js +1 -1
  64. package/dist/constants/api-endpoints.d.ts +3 -0
  65. package/dist/constants/api-endpoints.js +1 -0
  66. package/dist/constants/field-names.d.ts +17 -0
  67. package/dist/constants/field-names.js +23 -0
  68. package/dist/core/server-action.js +19 -3
  69. package/dist/core/unit-of-work.js +15 -1
  70. package/dist/errors/error-mapping.js +26 -1
  71. package/dist/errors/firestore-codes.d.ts +37 -0
  72. package/dist/errors/firestore-codes.js +52 -0
  73. package/dist/errors/index.d.ts +0 -1
  74. package/dist/errors/index.js +12 -2
  75. package/dist/features/account/components/UserOffersPanel.js +5 -3
  76. package/dist/features/admin/components/AdminActionIndexView.js +100 -5
  77. package/dist/features/admin/components/AdminAddressClustersView.js +21 -2
  78. package/dist/features/admin/components/AdminBlogEditorView.js +8 -8
  79. package/dist/features/admin/components/AdminCarouselEditorView.js +1 -1
  80. package/dist/features/admin/components/AdminCarouselView.js +96 -4
  81. package/dist/features/admin/components/AdminCouponEditorView.js +8 -0
  82. package/dist/features/admin/components/AdminFulfillmentView.js +3 -2
  83. package/dist/features/admin/components/AdminGatewayEventsView.js +2 -2
  84. package/dist/features/admin/components/AdminMediaView.js +36 -3
  85. package/dist/features/admin/components/AdminNavigationView.js +4 -1
  86. package/dist/features/admin/components/AdminOffersView.js +2 -1
  87. package/dist/features/admin/components/AdminPaymentClustersView.js +19 -2
  88. package/dist/features/admin/components/AdminProductEditorView.js +2 -2
  89. package/dist/features/admin/components/AdminSectionsView.d.ts +10 -0
  90. package/dist/features/admin/components/AdminSectionsView.js +486 -224
  91. package/dist/features/admin/components/DataListingView.js +2 -0
  92. package/dist/features/admin/components/ReferenceRecordEditor.js +1 -1
  93. package/dist/features/admin/components/analytics/AdminStatCard.js +4 -1
  94. package/dist/features/admin/hooks/useOptimisticRowFlag.d.ts +33 -0
  95. package/dist/features/admin/hooks/useOptimisticRowFlag.js +87 -0
  96. package/dist/features/admin/repository/notification.repository.d.ts +15 -0
  97. package/dist/features/admin/repository/notification.repository.js +32 -0
  98. package/dist/features/admin/repository/site-settings.repository.d.ts +2 -1
  99. package/dist/features/admin/repository/site-settings.repository.js +7 -0
  100. package/dist/features/admin/schemas/action-index-form.d.ts +34 -0
  101. package/dist/features/admin/schemas/action-index-form.js +115 -0
  102. package/dist/features/auctions/components/MarketplaceAuctionCard.js +1 -1
  103. package/dist/features/auth/components/RegisterForm.js +2 -1
  104. package/dist/features/catalogue/actions/approve-catalogue-listing.d.ts +1 -1
  105. package/dist/features/catalogue/actions/approve-catalogue-listing.js +12 -1
  106. package/dist/features/catalogue/actions/list-from-catalogue.d.ts +1 -1
  107. package/dist/features/catalogue/actions/list-from-catalogue.js +12 -1
  108. package/dist/features/catalogue/actions/submit-for-approval.d.ts +1 -1
  109. package/dist/features/catalogue/actions/submit-for-approval.js +12 -1
  110. package/dist/features/catalogue/components/CatalogueItemEditorView.js +3 -3
  111. package/dist/features/categories/repository/price-observation.repository.d.ts +19 -0
  112. package/dist/features/categories/repository/price-observation.repository.js +21 -2
  113. package/dist/features/events/actions/event-actions.js +4 -1
  114. package/dist/features/events/components/AdminEventEditorView.js +79 -50
  115. package/dist/features/events/components/EventsListPageView.js +2 -1
  116. package/dist/features/events/schemas/firestore.d.ts +17 -0
  117. package/dist/features/events/schemas/firestore.js +1 -0
  118. package/dist/features/events/utils/sort-pinned.d.ts +33 -0
  119. package/dist/features/events/utils/sort-pinned.js +46 -0
  120. package/dist/features/gateway-events/repository/gateway-events.repository.js +3 -40
  121. package/dist/features/gateway-events/schemas/index.d.ts +2 -2
  122. package/dist/features/inventory/components/AdminStorageBoxesView.js +55 -39
  123. package/dist/features/inventory/components/ReceiveStationView.js +61 -17
  124. package/dist/features/inventory/repository/storage-boxes.repository.d.ts +3 -1
  125. package/dist/features/inventory/repository/storage-boxes.repository.js +36 -3
  126. package/dist/features/layout/hooks/useFormBottomActions.js +20 -1
  127. package/dist/features/obligations/repository/obligations.repository.js +10 -1
  128. package/dist/features/orders/components/EmiInstallmentsPanel.js +25 -7
  129. package/dist/features/orders/components/RefundRequestView.js +4 -4
  130. package/dist/features/orders/schemas/firestore.d.ts +19 -0
  131. package/dist/features/orders/schemas/firestore.js +19 -0
  132. package/dist/features/orders/schemas/index.d.ts +6 -6
  133. package/dist/features/payment-cases/repository/payment-cases.repository.d.ts +1 -0
  134. package/dist/features/payment-cases/repository/payment-cases.repository.js +3 -14
  135. package/dist/features/pre-orders/components/MarketplacePreorderCard.js +1 -1
  136. package/dist/features/products/components/GroupMemberPicker.js +4 -1
  137. package/dist/features/products/components/MarketplaceBundleCard.js +1 -1
  138. package/dist/features/products/components/MarketplacePrizeDrawCard.js +1 -1
  139. package/dist/features/products/components/PrizeDrawEntryActions.js +2 -1
  140. package/dist/features/products/components/ProductDetailActions.js +2 -1
  141. package/dist/features/products/components/ProductForm.js +2 -2
  142. package/dist/features/products/components/ProductGrid.js +1 -1
  143. package/dist/features/promotions/repository/claimed-coupons.repository.js +31 -1
  144. package/dist/features/promotions/schemas/firestore.d.ts +22 -0
  145. package/dist/features/promotions/schemas/firestore.js +22 -0
  146. package/dist/features/promotions/schemas/index.d.ts +479 -6
  147. package/dist/features/promotions/schemas/index.js +153 -0
  148. package/dist/features/protection-claims/repository/protection-claims.repository.js +22 -15
  149. package/dist/features/seller/components/FulfillmentView.js +3 -2
  150. package/dist/features/seller/components/SellerAddressesView.js +32 -4
  151. package/dist/features/seller/components/SellerAnalyticsAlertsView.js +62 -3
  152. package/dist/features/seller/components/SellerBundlesView.js +53 -14
  153. package/dist/features/seller/components/SellerGroupedListingsView.js +23 -3
  154. package/dist/features/seller/components/SellerListingTypeView.js +56 -10
  155. package/dist/features/seller/components/SellerPayoutMethodsView.js +33 -1
  156. package/dist/features/seller/components/SellerPayoutSettingsView.js +48 -9
  157. package/dist/features/seller/components/SellerProductsView.js +5 -1
  158. package/dist/features/seller/components/SellerShippingConfigsView.js +33 -1
  159. package/dist/features/seller/components/SellerShippingView.js +2 -1
  160. package/dist/features/seller/components/SellerStoreCategoriesView.js +33 -1
  161. package/dist/features/seller/components/SellerSupportView.js +69 -2
  162. package/dist/features/seller/schemas/coupon-form.d.ts +5 -0
  163. package/dist/features/seller/schemas/coupon-form.js +18 -0
  164. package/dist/features/shell/SectionForm.js +35 -9
  165. package/dist/features/shell/build-sections.js +14 -1
  166. package/dist/features/shipments/components/AdminShipmentLotItemsView.js +54 -1
  167. package/dist/features/shipments/components/AdminShipmentProjectionsView.js +3 -2
  168. package/dist/features/sticker-trace/components/StickerCommitPanel.js +2 -1
  169. package/dist/features/sticker-trace/components/StickerPrintPanel.js +2 -1
  170. package/dist/features/sticker-trace/components/StickerRetryPanel.js +24 -4
  171. package/dist/features/sticker-trace/components/StickerTraceStudio.js +2 -1
  172. package/dist/features/sticker-trace/server.d.ts +2 -0
  173. package/dist/features/tester/components/AdminTesterFeedbackIssuesView.js +3 -2
  174. package/dist/http/api-handler.js +4 -1
  175. package/dist/index.d.ts +8 -4
  176. package/dist/index.js +6 -4
  177. package/dist/next/routing/route-map.d.ts +4 -2
  178. package/dist/next/routing/route-map.js +19 -1
  179. package/dist/schemas/registry.d.ts +15 -10
  180. package/dist/schemas/sieve.d.ts +3 -3
  181. package/dist/server.d.ts +1 -2
  182. package/dist/server.js +6 -4
  183. package/dist/styles.css +51 -0
  184. package/dist/ui/components/Checkbox.js +10 -2
  185. package/dist/ui/components/DateInput.js +8 -1
  186. package/dist/ui/components/Input.js +22 -2
  187. package/dist/ui/components/PaginatedSelect.d.ts +8 -19
  188. package/dist/ui/components/PaginatedSelect.js +0 -0
  189. package/dist/ui/components/Radio.js +8 -1
  190. package/dist/ui/components/Select.js +10 -2
  191. package/dist/ui/components/TagInput.d.ts +8 -1
  192. package/dist/ui/components/TagInput.js +4 -3
  193. package/dist/ui/components/Textarea.js +10 -2
  194. package/dist/ui/forms/FieldCheckbox.js +1 -1
  195. package/dist/ui/forms/FormErrorList.d.ts +0 -16
  196. package/dist/ui/forms/FormErrorList.js +34 -1
  197. package/dist/ui/forms/apply-zod-issues.d.ts +19 -1
  198. package/dist/ui/forms/apply-zod-issues.js +23 -1
  199. package/package.json +1 -1
  200. package/scripts/seed-cli.mjs +1822 -1812
  201. package/dist/features/tester/utils/phases.d.ts +0 -48
  202. package/dist/features/tester/utils/phases.js +0 -139
@@ -1994,6 +1994,9 @@ export declare const SCHEMAS: {
1994
1994
  }, import("zod").ZodTypeAny, "passthrough">;
1995
1995
  userId: string;
1996
1996
  couponCode: string;
1997
+ claimedAt: string | Date | import("zod").objectOutputType<{
1998
+ toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
1999
+ }, import("zod").ZodTypeAny, "passthrough">;
1997
2000
  couponId: string;
1998
2001
  couponSnapshot: {
1999
2002
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
@@ -2015,9 +2018,6 @@ export declare const SCHEMAS: {
2015
2018
  storeId?: string | undefined;
2016
2019
  description?: string | undefined;
2017
2020
  };
2018
- claimedAt: string | Date | import("zod").objectOutputType<{
2019
- toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2020
- }, import("zod").ZodTypeAny, "passthrough">;
2021
2021
  expiresAt?: string | Date | import("zod").objectOutputType<{
2022
2022
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2023
2023
  }, import("zod").ZodTypeAny, "passthrough"> | null | undefined;
@@ -2034,6 +2034,9 @@ export declare const SCHEMAS: {
2034
2034
  }, import("zod").ZodTypeAny, "passthrough">;
2035
2035
  userId: string;
2036
2036
  couponCode: string;
2037
+ claimedAt: string | Date | import("zod").objectInputType<{
2038
+ toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2039
+ }, import("zod").ZodTypeAny, "passthrough">;
2037
2040
  couponId: string;
2038
2041
  couponSnapshot: {
2039
2042
  type: "fixed" | "percentage" | "free_shipping" | "buy_x_get_y";
@@ -2055,9 +2058,6 @@ export declare const SCHEMAS: {
2055
2058
  storeId?: string | undefined;
2056
2059
  description?: string | undefined;
2057
2060
  };
2058
- claimedAt: string | Date | import("zod").objectInputType<{
2059
- toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2060
- }, import("zod").ZodTypeAny, "passthrough">;
2061
2061
  expiresAt?: string | Date | import("zod").objectInputType<{
2062
2062
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2063
2063
  }, import("zod").ZodTypeAny, "passthrough"> | null | undefined;
@@ -2105,16 +2105,19 @@ export declare const SCHEMAS: {
2105
2105
  bxgy: import("zod").ZodOptional<import("zod").ZodObject<{
2106
2106
  buyQuantity: import("zod").ZodNumber;
2107
2107
  getQuantity: import("zod").ZodNumber;
2108
+ maxItemPrice: import("zod").ZodOptional<import("zod").ZodEffects<import("zod").ZodEffects<import("zod").ZodNumber, number, number>, number, number>>;
2108
2109
  applicableProducts: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
2109
2110
  applicableCategories: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
2110
2111
  }, "strip", import("zod").ZodTypeAny, {
2111
2112
  buyQuantity: number;
2112
2113
  getQuantity: number;
2114
+ maxItemPrice?: number | undefined;
2113
2115
  applicableProducts?: string[] | undefined;
2114
2116
  applicableCategories?: string[] | undefined;
2115
2117
  }, {
2116
2118
  buyQuantity: number;
2117
2119
  getQuantity: number;
2120
+ maxItemPrice?: number | undefined;
2118
2121
  applicableProducts?: string[] | undefined;
2119
2122
  applicableCategories?: string[] | undefined;
2120
2123
  }>>;
@@ -2261,6 +2264,7 @@ export declare const SCHEMAS: {
2261
2264
  bxgy?: {
2262
2265
  buyQuantity: number;
2263
2266
  getQuantity: number;
2267
+ maxItemPrice?: number | undefined;
2264
2268
  applicableProducts?: string[] | undefined;
2265
2269
  applicableCategories?: string[] | undefined;
2266
2270
  } | undefined;
@@ -2319,6 +2323,7 @@ export declare const SCHEMAS: {
2319
2323
  bxgy?: {
2320
2324
  buyQuantity: number;
2321
2325
  getQuantity: number;
2326
+ maxItemPrice?: number | undefined;
2322
2327
  applicableProducts?: string[] | undefined;
2323
2328
  applicableCategories?: string[] | undefined;
2324
2329
  } | undefined;
@@ -2796,8 +2801,8 @@ export declare const SCHEMAS: {
2796
2801
  }, import("zod").ZodTypeAny, "passthrough">>]>>;
2797
2802
  }, "strip", import("zod").ZodTypeAny, {
2798
2803
  status: "paid" | "pending" | "overdue";
2799
- amount: number;
2800
2804
  index: number;
2805
+ amount: number;
2801
2806
  dueDate: string | Date | import("zod").objectOutputType<{
2802
2807
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2803
2808
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -2811,8 +2816,8 @@ export declare const SCHEMAS: {
2811
2816
  }, import("zod").ZodTypeAny, "passthrough"> | undefined;
2812
2817
  }, {
2813
2818
  status: "paid" | "pending" | "overdue";
2814
- amount: number;
2815
2819
  index: number;
2820
+ amount: number;
2816
2821
  dueDate: string | Date | import("zod").objectInputType<{
2817
2822
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
2818
2823
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -3075,8 +3080,8 @@ export declare const SCHEMAS: {
3075
3080
  emiSurchargeAmount?: number | undefined;
3076
3081
  emiInstallments?: {
3077
3082
  status: "paid" | "pending" | "overdue";
3078
- amount: number;
3079
3083
  index: number;
3084
+ amount: number;
3080
3085
  dueDate: string | Date | import("zod").objectOutputType<{
3081
3086
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3082
3087
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -3229,8 +3234,8 @@ export declare const SCHEMAS: {
3229
3234
  emiSurchargeAmount?: number | undefined;
3230
3235
  emiInstallments?: {
3231
3236
  status: "paid" | "pending" | "overdue";
3232
- amount: number;
3233
3237
  index: number;
3238
+ amount: number;
3234
3239
  dueDate: string | Date | import("zod").objectInputType<{
3235
3240
  toDate: import("zod").ZodFunction<import("zod").ZodTuple<[], import("zod").ZodUnknown>, import("zod").ZodDate>;
3236
3241
  }, import("zod").ZodTypeAny, "passthrough">;
@@ -23,12 +23,12 @@ export declare const sieveFilterClauseSchema: z.ZodObject<{
23
23
  value: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean]>, "many">]>;
24
24
  }, "strip", z.ZodTypeAny, {
25
25
  value: string | number | boolean | (string | number | boolean)[] | null;
26
- op: "==" | "!=" | "<" | "<=" | ">" | ">=" | "array-contains" | "in" | "not-in" | "array-contains-any";
27
26
  field: string;
27
+ op: "==" | "!=" | "<" | "<=" | ">" | ">=" | "array-contains" | "in" | "not-in" | "array-contains-any";
28
28
  }, {
29
29
  value: string | number | boolean | (string | number | boolean)[] | null;
30
- op: "==" | "!=" | "<" | "<=" | ">" | ">=" | "array-contains" | "in" | "not-in" | "array-contains-any";
31
30
  field: string;
31
+ op: "==" | "!=" | "<" | "<=" | ">" | ">=" | "array-contains" | "in" | "not-in" | "array-contains-any";
32
32
  }>;
33
33
  export type SieveFilterClause = z.infer<typeof sieveFilterClauseSchema>;
34
34
  export type SieveWhereOp = z.infer<typeof sieveWhereOpSchema>;
@@ -36,8 +36,8 @@ export declare const sieveSortClauseSchema: z.ZodObject<{
36
36
  field: z.ZodString;
37
37
  direction: z.ZodDefault<z.ZodEnum<["asc", "desc"]>>;
38
38
  }, "strip", z.ZodTypeAny, {
39
- direction: "desc" | "asc";
40
39
  field: string;
40
+ direction: "desc" | "asc";
41
41
  }, {
42
42
  field: string;
43
43
  direction?: "desc" | "asc" | undefined;
package/dist/server.d.ts CHANGED
@@ -502,7 +502,7 @@ export { LISTING_TYPE_REGISTRY, pluginFor, detectListingTypeFromSlug } from "./_
502
502
  export type { ListingTypePlugin } from "./_internal/shared/listing-types/_registry";
503
503
  export { applyMediaContextGuards, CONTEXT_LIMITS, } from "./_internal/server/features/media/contextGuards";
504
504
  export type { GuardResult, GuardError, GuardSuccess } from "./_internal/server/features/media/contextGuards";
505
- export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
505
+ export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, REVIEW_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits";
506
506
  export type { MediaKind, AllowedImageMime, AllowedVideoMime, AllowedDocMime, AllowedMime, } from "./_internal/shared/media/limits";
507
507
  export { getServerPermissions, checkPermission, checkAnyPermission, makeAdminSectionLayout, } from "./_internal/server/features/auth/permissions";
508
508
  export type { ResolvedPermissions, AdminSectionLayoutOpts, } from "./_internal/server/features/auth/permissions";
@@ -571,7 +571,6 @@ export type { ActionIndexValidationContext, ActionIndexValidationIssue, } from "
571
571
  export { ActionIndexRepository, actionIndexRepository, } from "./features/search/action-index/repository";
572
572
  export type { ActionIndexDocument } from "./features/search/action-index/repository";
573
573
  export { REFUND_COPY } from "./_internal/shared/features/orders/refund-copy";
574
- export { wouldBeFullRefund } from "./_internal/shared/checkout/refundable-base";
575
574
  export { RETURN_REASON, FINAL_SALE_EXEMPT_REASONS, RETURN_REASON_LABEL, RETURN_REASON_GROUP_LABEL, isFinalSaleExempt, isReturnReason, selectableReturnReasons, } from "./_internal/shared/features/orders/return-reasons";
576
575
  export type { ReturnReason } from "./_internal/shared/features/orders/return-reasons";
577
576
  export { draftToProductInput, productToDraft, } from "./features/seller/utils/product-draft-mapping";
package/dist/server.js CHANGED
@@ -1435,7 +1435,7 @@ export { LISTING_TYPE_REGISTRY, pluginFor, detectListingTypeFromSlug } from "./_
1435
1435
  // Media context guards — shared by /api/media/sign + legacy /api/media/upload.
1436
1436
  export { applyMediaContextGuards, CONTEXT_LIMITS, } from "./_internal/server/features/media/contextGuards.js";
1437
1437
  // Media upload limits — shared by /api/media/sign + /api/media/finalize + /api/media/upload.
1438
- export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits.js";
1438
+ export { MEGABYTE, MAX_IMAGE_BYTES, MAX_PDF_BYTES, MAX_VIDEO_BYTES, MAX_LABEL, MAX_BYTES, ALLOWED_IMAGE_MIMES, IMAGE_ACCEPT_ATTR, ALLOWED_VIDEO_MIMES, ALLOWED_DOC_MIMES, ALLOWED_MIMES, ALLOWED_TYPES_LABEL, MIME_TO_EXT, PDF_MAGIC, VIDEO_CONVERSION_HINTS, classifyMime, isAllowedMime, maxBytesFor, getConversionHint, PRODUCT_MAX_IMAGES, REVIEW_MAX_IMAGES, PRODUCT_MAX_VIDEOS, PRODUCT_IMAGE_INDEX_MAX, } from "./_internal/shared/media/limits.js";
1439
1439
  // ── RBAC server helpers ───────────────────────────────────────────────────────
1440
1440
  // getServerPermissions, checkPermission, makeAdminSectionLayout
1441
1441
  export { getServerPermissions, checkPermission, checkAnyPermission, makeAdminSectionLayout, } from "./_internal/server/features/auth/permissions.js";
@@ -1529,10 +1529,12 @@ export { validateActionIndexEntry, normaliseRoute, } from "./features/search/act
1529
1529
  export { ActionIndexRepository, actionIndexRepository, } from "./features/search/action-index/repository.js";
1530
1530
  export { REFUND_COPY } from "./_internal/shared/features/orders/refund-copy.js";
1531
1531
  /*
1532
- * `wouldBeFullRefund` — from refundable-base, NOT return-reasons. It was
1533
- * never exported at all, so the admin refund route could not import it.
1532
+ * 🛑 `wouldBeFullRefund` is deliberately NOT exported. I added it here, and
1533
+ * `audit-appkit-reexports` was right to refuse it: `_internal/shared/checkout/`
1534
+ * is not a `features/` module, so exporting it would have made a money
1535
+ * predicate public purely so one route could duplicate a decision
1536
+ * `processRefundAction` already makes — see the note on `ProcessRefundInput`.
1534
1537
  */
1535
- export { wouldBeFullRefund } from "./_internal/shared/checkout/refundable-base.js";
1536
1538
  // Return / refund reasons — the closed enum the final-sale gate branches on.
1537
1539
  // See the module docstring for why the exempt set is "seller or carrier fault"
1538
1540
  // rather than "serious reasons".
package/dist/styles.css CHANGED
@@ -11350,6 +11350,57 @@ body:has(.appkit-page-breadcrumb) .appkit-auto-breadcrumbs {
11350
11350
  * plain file like this one can never win an importance tie.
11351
11351
  */
11352
11352
 
11353
+ /* The kiss-cut label sheet. Shipped unbundled when it was written — the
11354
+ `.appkit-label-sheet` grid and its `@page` rules reached no consumer, so
11355
+ every printed sheet fell back to browser defaults and the die-cut
11356
+ alignment the component exists to produce could not happen. */
11357
+ /*
11358
+ * Print-layout classes for physical labels and printable documents.
11359
+ *
11360
+ * 🛑 THESE LIVE IN A STATIC STYLESHEET, NOT ONLY IN `labelPageCss`'s STRING.
11361
+ *
11362
+ * `labelPageCss()` and `documentPageCss()` emit an `@page` rule plus these
11363
+ * same `break-*` declarations into a runtime `<style>`. That is necessary for
11364
+ * `@page` — its `size` is computed from the chosen profile, so it cannot be
11365
+ * static — but it meant `.appkit-label` and `.appkit-label-sheet` existed
11366
+ * ONLY inside a template literal.
11367
+ *
11368
+ * Consequence: `audit-dead-css-classes` reported them as classes generating no
11369
+ * CSS rule, correctly, because nothing it can read defines them. A class whose
11370
+ * only definition is a string concatenation is invisible to every tool — the
11371
+ * audit, an editor's go-to-definition, and anyone grepping the stylesheets.
11372
+ * Declaring them here makes them real; the runtime string still carries its
11373
+ * own copy because a print view may be opened in a context where the bundled
11374
+ * stylesheet has not loaded, and a duplicated `break-inside: avoid` is
11375
+ * harmless where a missing one splits a label across two pages.
11376
+ *
11377
+ * @tag domain:print
11378
+ * @tag layer:feature
11379
+ */
11380
+
11381
+ @media print {
11382
+ /*
11383
+ * A label must never be split across a page boundary. On thermal stock a
11384
+ * page IS a label, so a split would print the top half on one label and the
11385
+ * bottom half on the next — and the operator discovers it after both have
11386
+ * been peeled and stuck to something.
11387
+ */
11388
+ .appkit-label {
11389
+ break-inside: avoid;
11390
+ page-break-inside: avoid;
11391
+ }
11392
+
11393
+ /*
11394
+ * A sheet is one A4 page of die-cut labels, or one contents sheet. Same
11395
+ * rule, different unit — splitting it would offset every label after the
11396
+ * break from its die-cut hole.
11397
+ */
11398
+ .appkit-label-sheet {
11399
+ break-inside: avoid;
11400
+ page-break-inside: avoid;
11401
+ }
11402
+ }
11403
+
11353
11404
  /*
11354
11405
  * Third-party stylesheet, vendored into dist/vendor by copy-assets.mjs.
11355
11406
  * It used to be a JS import inside ImageEditor.tsx, which made it invisible to
@@ -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] }));
@@ -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 {};
@@ -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";
@@ -34,5 +34,5 @@ export function FieldCheckbox({ name, label, hint, checked, onChange, onBlur, di
34
34
  ctx?.setFieldTouched(name);
35
35
  onBlur?.();
36
36
  }
37
- return (_jsxs("div", { className: ["appkit-form-field", className].filter(Boolean).join(" "), children: [_jsx(Checkbox, { id: inputId, name: name, checked: checked, onChange: handleChange, onBlur: handleBlur, label: label, disabled: disabled, "aria-describedby": showError ? errorId : undefined }), !showError && hint && (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-form-field__hint", children: hint })), showError && (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error }))] }));
37
+ return (_jsxs("div", { className: ["appkit-form-field", className].filter(Boolean).join(" "), children: [_jsx(Checkbox, { id: inputId, name: name, checked: checked, onChange: handleChange, onBlur: handleBlur, label: label, disabled: disabled, "aria-invalid": showError || undefined, "aria-describedby": showError ? errorId : undefined }), !showError && hint && (_jsx(Text, { size: "sm", variant: "secondary", className: "appkit-form-field__hint", children: hint })), showError && (_jsx(Text, { id: errorId, size: "sm", variant: "error", className: "appkit-form-field__error", role: "alert", children: error }))] }));
38
38
  }
@@ -9,21 +9,5 @@ export interface FormErrorListProps {
9
9
  ctx?: FormShellContextValue;
10
10
  className?: string;
11
11
  }
12
- /**
13
- * The `<ul>` of current validation errors, with a jump link per entry when
14
- * the form declares sections.
15
- *
16
- * WHY it is its own file: two surfaces render this exact list — the inline
17
- * `<FormErrorSummary>` beside the submit button on desktop, and the mobile
18
- * "Fix N issues" sheet in the bottom-chrome tier. Writing it twice would mean
19
- * a jump-link fix, an empty-state fix or a wording fix landing in one and not
20
- * the other, which is the bug-fix multiplier the Duplication Framework calls
21
- * out. There are exactly two consumers and they are the same list, so this is
22
- * a consolidation, not speculative abstraction.
23
- *
24
- * Renders nothing when there are no errors. It does NOT gate on
25
- * `submitAttempted` — that is the *summary's* decision, not the list's, and
26
- * the sheet needs to keep rendering while open.
27
- */
28
12
  export declare function FormErrorList({ ctx: ctxProp, className }: FormErrorListProps): import("react").JSX.Element | null;
29
13
  export default FormErrorList;
@@ -19,6 +19,39 @@ import { Span } from "../components/Typography.js";
19
19
  * `submitAttempted` — that is the *summary's* decision, not the list's, and
20
20
  * the sheet needs to keep rendering while open.
21
21
  */
22
+ /*
23
+ * 🛑 The two sides of this lookup use DIFFERENT key shapes, so an exact match
24
+ * silently loses every nested error.
25
+ *
26
+ * `errors` is keyed by the full dotted Zod path — `applyZodIssues` does
27
+ * `issue.path.map(String).join(".")`, giving `"address.line1"`. But
28
+ * `fieldToStepIndex` is built by `buildFieldToSectionIndex` from
29
+ * `SectionDef.fields`, which lists BARE field names — `"address"`. So
30
+ * `fieldToStepIndex["address.line1"]` was always `undefined`, the entry fell
31
+ * through to the plain `<Span>`, and the error that a section demonstrably
32
+ * owned rendered with no jump link.
33
+ *
34
+ * `SectionForm` already solved this for its own badge counts
35
+ * (`erroringSectionIds`, `errorCountFor`) with a prefix match. This is the same
36
+ * rule, so the list and the badges can no longer disagree about which section
37
+ * an error belongs to.
38
+ *
39
+ * Exact match is tried first: a field genuinely named `"address.line1"` must
40
+ * beat a prefix match on `"address"`.
41
+ */
42
+ function resolveStepIndex(ctx, key) {
43
+ const map = ctx.fieldToStepIndex;
44
+ if (!map)
45
+ return undefined;
46
+ const exact = map[key];
47
+ if (exact != null)
48
+ return exact;
49
+ for (const [field, index] of Object.entries(map)) {
50
+ if (key.startsWith(`${field}.`))
51
+ return index;
52
+ }
53
+ return undefined;
54
+ }
22
55
  export function FormErrorList({ ctx: ctxProp, className }) {
23
56
  const ambient = useContext(FormShellContext);
24
57
  const ctx = ctxProp ?? ambient;
@@ -28,7 +61,7 @@ export function FormErrorList({ ctx: ctxProp, className }) {
28
61
  if (entries.length === 0)
29
62
  return null;
30
63
  return (_jsx("ul", { className: ["appkit-formshell__error-summary__list", className].filter(Boolean).join(" "), children: entries.map(([key, message]) => {
31
- const stepIndex = ctx.fieldToStepIndex?.[key];
64
+ const stepIndex = resolveStepIndex(ctx, key);
32
65
  const stepLabel = stepIndex != null ? ctx.steps[stepIndex]?.label : undefined;
33
66
  return (_jsx("li", { children: stepLabel ? (_jsxs("button", { type: "button", className: "appkit-formshell__error-summary__step-link", onClick: () => ctx.goToStep(stepIndex), children: [_jsxs(Span, { weight: "semibold", children: [stepLabel, ":"] }), " ", message] })) : (_jsx(Span, { children: message })) }, key));
34
67
  }) }));
@@ -22,7 +22,16 @@
22
22
  *
23
23
  * Root-level `.refine()` issues (empty `path: []`, no owning field) are still
24
24
  * skipped — there is no field to attach them to, so the caller must surface
25
- * those some other way (they fall through to a toast in `surfaceError`).
25
+ * those some other way.
26
+ *
27
+ * 🛑 That last sentence used to end "(they fall through to a toast in
28
+ * `surfaceError`)", which was only true when EVERY issue was root-level.
29
+ * In the mixed case — one field error plus one cross-field `.refine()`, which
30
+ * is the common shape for "passwords must match" or "end date must be after
31
+ * start date" — `hasAttachableIssue` returned true, this function skipped the
32
+ * root issue, and `surfaceError` returned without a toast. The cross-field
33
+ * rule was enforced, the save was refused, and the user was told nothing
34
+ * about why. `rootLevelMessages` below is how a caller surfaces them.
26
35
  */
27
36
  export declare function applyZodIssues(issues: ReadonlyArray<{
28
37
  path?: ReadonlyArray<PropertyKey>;
@@ -32,3 +41,12 @@ export declare function applyZodIssues(issues: ReadonlyArray<{
32
41
  export declare function hasAttachableIssue(issues: ReadonlyArray<{
33
42
  path?: ReadonlyArray<PropertyKey>;
34
43
  }> | undefined): boolean;
44
+ /**
45
+ * The messages `applyZodIssues` cannot attach — the root-level `.refine()`
46
+ * ones with no owning field. A caller that has just run `applyZodIssues`
47
+ * should surface these separately, or they are silently discarded.
48
+ */
49
+ export declare function rootLevelMessages(issues: ReadonlyArray<{
50
+ path?: ReadonlyArray<PropertyKey>;
51
+ message: string;
52
+ }> | undefined): string[];